0
2
Grain hatk Template — MVP Design
Goal
Rebuild grain-next (Lit Web Components + GraphQL/Quickslice) as a hatk-powered SvelteKit app, using hatk-template-teal as the scaffold. MVP: shell + auth + timeline with gallery cards, running end-to-end.
Source Projects
- hatk-template-teal (
~/code/hatk-template-teal): SvelteKit 5 + hatk scaffold for teal.fm music tracker. Provides the application shell, auth flow, component architecture, and build tooling. - grain-next (
~/code/grain-next): Lit Web Components PWA for Grain photo sharing. Provides lexicons, data models, and feature reference.
Architecture
Stack
- SvelteKit 5 + Svelte 5 (from teal template)
- hatk framework (XRPC endpoints, feeds, OAuth, SQLite)
- TanStack Svelte Query (client-side data fetching/caching)
- Vite build tooling
Key Translation
| grain-next | hatk-template-grain |
|---|---|
| Lit Web Components | Svelte 5 components |
| GraphQL via Quickslice | hatk XRPC + feeds |
| Client-side SPA router | SvelteKit file-based routing |
| Custom query/record cache | TanStack Svelte Query |
| Quickslice OAuth | hatk OAuth |
Project Setup
- Copy hatk-template-teal structure wholesale into hatk-template-grain.
- Replace
lexicons/fm/teal/withlexicons/social/grain/(copied from grain-next). - Keep
lexicons/app/bsky/,lexicons/com/atproto/,lexicons/dev/hatk/. - Regenerate types with
npx hatk generate types. - Update
hatk.config.ts:signalCollections: ["social.grain.actor.profile"]- OAuth scopes for all Grain collections (request upfront).
- Update
package.jsonname/description.
Styling
Keep teal's dark theme and CSS variables unchanged. Tweak later.
Routes (MVP)
app/routes/
├── +layout.svelte # Shell (3-column, from teal)
├── +layout.server.ts # Session parsing
├── +layout.ts # Query client setup
├── +page.svelte # Timeline (gallery cards)
├── +page.ts # Timeline feed query
├── oauth/callback/ # OAuth redirect
└── profile/[did]/ # Profile page (placeholder)
Server (MVP)
server/
├── feeds/
│ └── recent.ts # Recent galleries feed
└── xrpc/ # Empty for now
Components
Keep from teal (as-is)
Shell.svelte— 3-column layoutSidebar.svelte— left nav (logo, home, auth)SidebarRight.svelte— right sidebar (strip trending artists/genres, keep search bar placeholder)Avatar.svelte,Button.svelte,Skeleton.svelte,Modal.svelte— generic atomsAuthBar.svelte,LoginModal.svelte— auth organisms
Replace
| teal | grain |
|---|---|
PlayCard.svelte | GalleryCard.svelte |
ScrobbleModal.svelte | Remove |
BookmarkButton.svelte | Remove |
FeedTabs.svelte | Simplify to single "Recent" tab |
GalleryCard.svelte
Displays:
- Thumbnail grid (first ~4 photos from gallery)
- Gallery title
- Author chip (avatar + handle)
- Photo count
Feed: Recent Galleries
Server (server/feeds/recent.ts):
- Query
social.grain.galleryordered bycreated_at DESC - Hydrate with author profile (
social.grain.actor.profile) - Join
social.grain.gallery.itemfor photo refs, thensocial.grain.photofor blobs/aspect ratios - Cursor-based pagination
Client:
+page.tscallsdev.hatk.getFeedwith feedrecent- TanStack Query caches + paginates
+page.svelterendersGalleryCardlist
Auth
Identical to teal's hatk OAuth flow. Update scopes to:
atproto
repo:social.grain.gallery
repo:social.grain.gallery.item
repo:social.grain.photo
repo:social.grain.actor.profile
repo:social.grain.graph.follow
repo:social.grain.favorite
repo:social.grain.comment
hatk.config.ts
signalCollections: ["social.grain.actor.profile"];
What's Deferred
- Comments, favorites, notifications
- Search / explore
- Gallery create/edit UI
- Photo upload
- EXIF extraction
- Profile editing
- Follow/unfollow
- Rich text / facets
- PWA / service worker
- Custom accent color / branding