your personal website on atproto - mirror blento.app

small fixes

Florian 55aa33c4 22600539

+77 -4
+71
CLAUDE.md
···
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 + 5 + ## Project Overview 6 + 7 + Blento is a Bluesky-powered customizable bento grid website builder. Users authenticate via ATProto OAuth and can create personalized websites with draggable/resizable cards that are stored directly in their Bluesky PDS (Personal Data Server) using the `app.blento.card` collection. 8 + 9 + ## Commands 10 + 11 + - `pnpm dev` - Start development server (Vite) 12 + - `pnpm build` - Build for production 13 + - `pnpm preview` - Build and preview with Wrangler (Cloudflare Workers) 14 + - `pnpm check` - Run svelte-check for type checking 15 + - `pnpm lint` - Run Prettier check and ESLint 16 + - `pnpm format` - Format code with Prettier 17 + - `pnpm deploy` - Build and deploy to Cloudflare Workers 18 + 19 + ## Architecture 20 + 21 + ### Tech Stack 22 + - **Framework**: SvelteKit 2 with Svelte 5 (using runes: `$state`, `$derived`, `$props`) 23 + - **Styling**: Tailwind CSS 4 with container queries (`@container`) 24 + - **Deployment**: Cloudflare Workers via `@sveltejs/adapter-cloudflare` 25 + - **UI Components**: `@foxui/core`, `@foxui/social` (custom component libraries) 26 + 27 + ### Grid System 28 + The site uses an 8-column grid layout (`COLUMNS = 8` in `src/lib/index.ts`). Each card has: 29 + - Desktop position/size: `x`, `y`, `w`, `h` 30 + - Mobile position/size: `mobileX`, `mobileY`, `mobileW`, `mobileH` 31 + 32 + Grid margins: 20px desktop, 12px mobile. 33 + 34 + ### Key Components 35 + 36 + **Website Rendering:** 37 + - `Website.svelte` - Read-only view of a user's bento grid 38 + - `EditableWebsite.svelte` - Full editing interface with drag-and-drop, card creation, and save functionality 39 + 40 + **Card System (`src/lib/cards/`):** 41 + - `CardDefinition` type in `types.ts` defines the interface for card types 42 + - Each card type exports a definition with: `type`, `contentComponent`, `editingContentComponent`, optional `creationModalComponent`, `sidebarComponent`, `loadData`, `upload` 43 + - Card types: Text, Link, Image, Youtube, BlueskyPost, Embed, Map, Livestream, ATProtoCollections, Section 44 + - `AllCardDefinitions` and `CardDefinitionsByType` in `index.ts` aggregate all card types 45 + 46 + **ATProto Integration (`src/lib/oauth/`):** 47 + - `auth.svelte.ts` - OAuth client state and login/logout flow using `@atcute/oauth-browser-client` 48 + - `atproto.ts` - ATProto API helpers: `resolveHandle`, `listRecords`, `getRecord`, `putRecord`, `deleteRecord`, `uploadImage` 49 + - Data is stored in user's PDS under collection `app.blento.card` 50 + 51 + **Data Loading (`src/lib/website/`):** 52 + - `load.ts` - Fetches user data from their PDS, with Cloudflare KV caching (`USER_DATA_CACHE`) 53 + - `data.ts` - Defines which collections/records to fetch 54 + - `context.ts` - Svelte contexts for passing DID, handle, and data down the component tree 55 + 56 + ### Routes 57 + - `/` - Landing page 58 + - `/[handle]` - View a user's bento site (loads from their PDS) 59 + - `/[handle]/edit` - Edit mode for the user's site 60 + - `/edit` - Self-hosted edit mode 61 + - `/api/links` - Link preview API 62 + - `/api/geocoding` - Geocoding API for map cards 63 + 64 + ### Item Type 65 + Cards are represented by the `Item` type (`src/lib/types.ts`) with grid position, size, cardType, and cardData properties. 66 + 67 + ### Collision/Layout Helpers 68 + `src/lib/helper.ts` contains grid layout algorithms: 69 + - `fixCollisions` - Push cards down when they overlap 70 + - `compactItems` - Move cards up to fill gaps 71 + - `simulateFinalPosition` - Preview where a dragged card will land
+3 -3
src/lib/cards/ATProtoCollectionsCard/ATProtoCollectionsCard.svelte
··· 32 } 33 </script> 34 35 - <div class="h-full overflow-y-scroll p-4"> 36 - <div class="mb-4 inline-flex w-full items-center justify-between font-semibold"> 37 <span>My AT Protocol Collections</span> 38 39 {#if collections} 40 <Badge size="md">{collections.length}</Badge> 41 {/if} 42 </div> 43 - <div class="flex flex-wrap gap-2 overflow-y-scroll"> 44 {#each collections ?? [] as collection} 45 <Button target="_blank" href={getLink(collection)} size="sm">{collection}</Button> 46 {/each}
··· 32 } 33 </script> 34 35 + <div class="h-full overflow-y-scroll py-4"> 36 + <div class="mb-4 inline-flex w-full items-center justify-between font-semibold px-4"> 37 <span>My AT Protocol Collections</span> 38 39 {#if collections} 40 <Badge size="md">{collections.length}</Badge> 41 {/if} 42 </div> 43 + <div class="flex flex-wrap overflow-y-scroll gap-2 px-4 overflow-x-hidden w-full"> 44 {#each collections ?? [] as collection} 45 <Button target="_blank" href={getLink(collection)} size="sm">{collection}</Button> 46 {/each}
+3 -1
src/lib/cards/LinkCard/LinkCard.svelte
··· 12 {#if item.cardData.favicon} 13 <img class="mb-2 size-8 rounded-lg object-cover" src={item.cardData.favicon} alt="" /> 14 {/if} 15 - <div class="text-base-900 dark:text-base-50 text-lg font-semibold">{item.cardData.title}</div> 16 <!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> --> 17 <div class="text-accent-600 dark:text-accent-400 mt-2 text-xs font-light"> 18 {item.cardData.domain}
··· 12 {#if item.cardData.favicon} 13 <img class="mb-2 size-8 rounded-lg object-cover" src={item.cardData.favicon} alt="" /> 14 {/if} 15 + <div class={["text-base-900 dark:text-base-50 text-lg font-semibold", 16 + ((isMobile() && item.mobileH < 8) || (!isMobile() && item.h < 4)) ? 'line-clamp-2' : '' 17 + ]}>{item.cardData.title}</div> 18 <!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> --> 19 <div class="text-accent-600 dark:text-accent-400 mt-2 text-xs font-light"> 20 {item.cardData.domain}