your personal website on atproto - mirror
blento.app
1# CLAUDE.md
2
3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
5## Project Overview
6
7Blento is a Bluesky-powered customizable bento grid website builder. Users authenticate via ATProto OAuth and create draggable/resizable cards stored 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## Code Quality Requirements
20
21Before submitting changes:
22
231. **Run `pnpm check`** - Must complete with 0 errors and 0 warnings
242. **Run `pnpm format`** - Format all code with Prettier
25
26## Architecture
27
28### Tech Stack
29
30- **Framework**: SvelteKit 2 with Svelte 5 (using runes: `$state`, `$derived`, `$props`)
31- **Styling**: Tailwind CSS 4 with container queries (`@container`)
32- **Deployment**: Cloudflare Workers via `@sveltejs/adapter-cloudflare`
33- **UI Components**: `@foxui/core`, `@foxui/social` (custom component libraries)
34
35### Grid System
36
37The site uses an 8-column grid layout (`COLUMNS = 8` in `src/lib/index.ts`). Each card has:
38
39- Desktop position/size: `x`, `y`, `w`, `h`
40- Mobile position/size: `mobileX`, `mobileY`, `mobileW`, `mobileH`
41
42Grid margins: 16px desktop, 12px mobile.
43
44### Key Components
45
46**Website Rendering:**
47
48- `src/lib/website/Website.svelte` - Read-only view of a user's bento grid
49- `src/lib/website/EditableWebsite.svelte` - Full editing interface with drag-and-drop, card creation, and save functionality
50- `src/lib/website/Settings.svelte` and `src/lib/website/Profile.svelte` - Editing panels and profile UI
51- 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`, ...)
52
53**Card System (`src/lib/cards/`):**
54
55- `CardDefinition` type in `types.ts` defines the interface for card types
56- Each card type exports a definition with: `type`, `contentComponent`, optional `editingContentComponent`, `creationModalComponent`, `sidebarButtonText`, `loadData`, `loadDataServer`, `upload` (see more info and description in `src/lib/cards/types.ts`)
57- `loadData` fetches external data on the client (via remote functions). `loadDataServer` is the server-side equivalent used during SSR to avoid self-referential HTTP requests on Cloudflare Workers.
58- Cards that need external data use `.remote.ts` files (SvelteKit remote functions) co-located in the card folder (e.g. `GitHubProfileCard/api.remote.ts`, `LastFMCard/api.remote.ts`). These use `query()` from `$app/server` and run server-side, with SvelteKit generating fetch wrappers for client use.
59- Card types include Text, Link, Image, Bluesky, Embed, Map, Livestream, ATProto collections, and special cards (see `src/lib/cards`).
60- `AllCardDefinitions` and `CardDefinitionsByType` in `index.ts` aggregate all card types
61- See e.g. `src/lib/cards/EmbedCard/` and `src/lib/cards/LivestreamCard/` for examples of implementation.
62- 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).
63
64**ATProto Integration (`src/lib/oauth/`):**
65
66- `auth.svelte.ts` - OAuth client state and login/logout flow using `@atcute/oauth-browser-client`
67- `atproto.ts` - ATProto API helpers: `resolveHandle`, `listRecords`, `getRecord`, `putRecord`, `deleteRecord`, `uploadImage`
68- Data is stored in user's PDS under collection `app.blento.card`
69- **Important**: ATProto does not allow floating point numbers in records. All numeric values must be integers.
70
71**Caching (`src/lib/cache.ts`):**
72
73- `CacheService` class wraps a single Cloudflare KV namespace (`USER_DATA_CACHE`) with namespaced keys
74- Keys are stored as `namespace:key` (e.g. `blento:did:plc:abc`, `github:someuser`, `lastfm:method:user:period:limit`)
75- Per-namespace default TTLs via KV `expirationTtl`: `blento` (24h), `identity` (7d), `github` (12h), `gh-contrib` (12h), `lastfm` (1h, overridable), `npmx` (12h), `meta` (no expiry)
76- Blento data is keyed by DID with bidirectional handle↔DID identity mappings (`identity:h:{handle}` → DID, `identity:d:{did}` → handle)
77- `getBlento(identifier)` accepts either a handle or DID and resolves automatically
78- `putBlento(did, handle, data)` writes data + both identity mappings
79- Generic `get(namespace, key)` / `put(namespace, key, value, ttl?)` and JSON variants `getJSON` / `putJSON` for all namespaces
80- `createCache(platform)` factory function creates a `CacheService` from `platform.env`
81- `CUSTOM_DOMAINS` KV namespace is separate and accessed directly for custom domain → DID resolution
82
83**Data Loading (`src/lib/website/`):**
84
85- `load.ts` - Fetches user data from their PDS, with optional caching via `CacheService`
86- `data.ts` - Defines which collections/records to fetch
87- `context.ts` - Svelte contexts for passing DID, handle, and data down the component tree
88
89### Routes
90
91- `/` - Landing page
92- `/[handle]/[[page]]` - View a user's bento site (loads from their PDS)
93- `/[handle]/[[page]]/edit` - Edit mode for a user's site page
94- `/edit` - Self-hosted edit mode
95- `/api/links` - Link preview API
96- `/api/geocoding` - Geocoding API for map cards
97- `/api/reloadRecent`, `/api/update` - Additional data endpoints
98
99### Item Type
100
101Cards are represented by the `Item` type (`src/lib/types.ts`) with grid position, size, cardType, and cardData properties.
102
103### Collision/Layout Helpers
104
105`src/lib/helper.ts` contains grid layout algorithms:
106
107- `fixAllCollisions` - Push cards down when they overlap
108- `compactItems` - Move cards up to fill gaps
109- `simulateFinalPosition` - Preview where a dragged card will land