your personal website on atproto - mirror blento.app

some cleanup

Florian 8925c6bc 00cbedce

+45 -28
+3
.env.example
··· 1 + PUBLIC_HANDLE=blento.app 2 + PUBLIC_IS_SELFHOSTED= 3 + PUBLIC_DOMAIN=https://blento.app
+15 -1
CLAUDE.md
··· 19 19 ## Architecture 20 20 21 21 ### Tech Stack 22 + 22 23 - **Framework**: SvelteKit 2 with Svelte 5 (using runes: `$state`, `$derived`, `$props`) 23 24 - **Styling**: Tailwind CSS 4 with container queries (`@container`) 24 25 - **Deployment**: Cloudflare Workers via `@sveltejs/adapter-cloudflare` 25 26 - **UI Components**: `@foxui/core`, `@foxui/social` (custom component libraries) 26 27 27 28 ### Grid System 29 + 28 30 The site uses an 8-column grid layout (`COLUMNS = 8` in `src/lib/index.ts`). Each card has: 31 + 29 32 - Desktop position/size: `x`, `y`, `w`, `h` 30 33 - Mobile position/size: `mobileX`, `mobileY`, `mobileW`, `mobileH` 31 34 ··· 34 37 ### Key Components 35 38 36 39 **Website Rendering:** 40 + 37 41 - `Website.svelte` - Read-only view of a user's bento grid 38 42 - `EditableWebsite.svelte` - Full editing interface with drag-and-drop, card creation, and save functionality 43 + - Styling: two colors: base color (one the gray-ish tailwind colors: `gray`, `neutral`, `stone`, ...) and accent color (one of the not-gray-ish tailwind color: `rose`, `red`, `amber`, ...) 39 44 40 45 **Card System (`src/lib/cards/`):** 46 + 41 47 - `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` 48 + - Each card type exports a definition with: `type`, `contentComponent`, optional `editingContentComponent`, `creationModalComponent`, `sidebarComponent`, `loadData`, `upload` (see more info and description in `src/lib/cards/types.ts`) 43 49 - Card types: Text, Link, Image, Youtube, BlueskyPost, Embed, Map, Livestream, ATProtoCollections, Section 44 50 - `AllCardDefinitions` and `CardDefinitionsByType` in `index.ts` aggregate all card types 51 + - See e.g. `src/lib/cards/EmbedCard/` and `src/lib/cards/LivestreamCard/` for examples of implementation. 52 + - Cards should be styled to work in light and dark mode (with `dark:` class modifier) as well as when cards are colorful (= bg-color-500 for the card background) (with `accent:` modifier). 45 53 46 54 **ATProto Integration (`src/lib/oauth/`):** 55 + 47 56 - `auth.svelte.ts` - OAuth client state and login/logout flow using `@atcute/oauth-browser-client` 48 57 - `atproto.ts` - ATProto API helpers: `resolveHandle`, `listRecords`, `getRecord`, `putRecord`, `deleteRecord`, `uploadImage` 49 58 - Data is stored in user's PDS under collection `app.blento.card` 50 59 51 60 **Data Loading (`src/lib/website/`):** 61 + 52 62 - `load.ts` - Fetches user data from their PDS, with Cloudflare KV caching (`USER_DATA_CACHE`) 53 63 - `data.ts` - Defines which collections/records to fetch 54 64 - `context.ts` - Svelte contexts for passing DID, handle, and data down the component tree 55 65 56 66 ### Routes 67 + 57 68 - `/` - Landing page 58 69 - `/[handle]` - View a user's bento site (loads from their PDS) 59 70 - `/[handle]/edit` - Edit mode for the user's site ··· 62 73 - `/api/geocoding` - Geocoding API for map cards 63 74 64 75 ### Item Type 76 + 65 77 Cards are represented by the `Item` type (`src/lib/types.ts`) with grid position, size, cardType, and cardData properties. 66 78 67 79 ### Collision/Layout Helpers 80 + 68 81 `src/lib/helper.ts` contains grid layout algorithms: 82 + 69 83 - `fixCollisions` - Push cards down when they overlap 70 84 - `compactItems` - Move cards up to fill gaps 71 85 - `simulateFinalPosition` - Preview where a dragged card will land
+10 -1
README.md
··· 4 4 5 5 your personal website in a bento style layout, using your bluesky PDS as a backend. 6 6 7 - made with svelte, tailwind. 7 + made with svelte, tailwind and hosted on cloudflare workers. 8 + 9 + ## Development 10 + 11 + ``` 12 + git clone https://github.com/flo-bit/blento.git 13 + cp .env.example .env 14 + pnpm install 15 + pnpm run dev 16 + ``` 8 17 9 18 ## Selfhosting 10 19
+3 -15
docs/CustomCards.md
··· 2 2 3 3 WORK IN PROGRESS, EARLY STATE, MIGHT CHANGE. 4 4 5 - see `src/lib/cards` for how cards are made. 6 - 7 - Current card definition: 8 - 9 - ```ts 10 - export type CardDefinition = { 11 - type: string; 12 - contentComponent: Component<ContentComponentProps>; // this is what your card shows 13 - 14 - editingContentComponent?: Component<ContentComponentProps>; // if this is not given, defaults to showing contentComponent in edit mode too 15 - creationModalComponent?: Component<CreationModalComponentProps>; // if this is not given will just add a card 5 + see `src/lib/cards` for how cards are made (and e.g. `src/lib/cards/EmbedCard/` and `src/lib/cards/LivestreamCard/` for examples of implementation). 16 6 17 - createNew?: (item: Item) => void; // this is run before the card is added, set some settings here 7 + Notes: 18 8 19 - sidebarComponent?: Component<SidebarComponentProps>; // this is the button that will be shown in the sidebar to add your card 20 - }; 21 - ``` 9 + Cards should be styled to work in light and dark mode (with dark: class modifier) as well as when cards are colorful (= bg-color-500 for the card) (with accent: modifier).
+13 -9
src/lib/cards/types.ts
··· 22 22 }; 23 23 24 24 export type CardDefinition = { 25 - type: string; 26 - contentComponent: Component<ContentComponentProps>; 27 - editingContentComponent?: Component<ContentComponentProps>; 25 + type: string; // should be unique 26 + contentComponent: Component<ContentComponentProps>; // content of card 27 + editingContentComponent?: Component<ContentComponentProps>; // optional content of card in editing mode 28 28 29 - createNew?: (item: Item) => void; 29 + createNew?: (item: Item) => void; // set some custom cardData stuff here (or custom default sizes) 30 30 31 31 creationModalComponent?: Component<CreationModalComponentProps>; 32 32 33 - settingsModalComponent?: Component<SettingsModalComponentProps>; 34 - 35 - upload?: (item: Item) => Promise<Item>; 33 + upload?: (item: Item) => Promise<Item>; // optionally upload some other data needed for this card 36 34 35 + // one of those two has to be set for a card to appear in the sidebar 37 36 sidebarComponent?: Component<SidebarComponentProps>; 38 37 sidebarButtonText?: string; 39 38 39 + // if this component exists, a settings button with a popover will be shown containing this component 40 40 settingsComponent?: Component<ContentComponentProps>; 41 41 42 + // optionally load some extra data 42 43 loadData?: ( 43 44 items: Item[], 44 45 { did, handle, platform }: { did: string; handle: string; platform?: App.Platform } 45 46 ) => Promise<unknown>; 46 - dataKey?: string; 47 47 48 + // show color selection popup 48 49 allowSetColor?: boolean; 49 50 51 + // default card background color one of 'base', 'accent', 'transparent', or one of the tailwind colors 52 + // (actual colors only, without 'gray', 'neutral', 'stone', etc) 50 53 defaultColor?: string; 51 54 55 + // for resizing: 52 56 minW?: number; 53 57 maxW?: number; 54 58 ··· 56 60 maxH?: number; 57 61 58 62 canResize?: boolean; 59 - }; 63 + };
+1 -2
wrangler.jsonc
··· 42 42 "kv_namespaces": [ 43 43 { 44 44 "binding": "USER_DATA_CACHE", 45 - "id": "d6ff203259de48538d332b0a5df258a7", 46 - "remote": true 45 + "id": "d6ff203259de48538d332b0a5df258a7" 47 46 } 48 47 ] 49 48 /**