experimental bluesky client
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update handoff, fix title

+58 -23
+57 -22
HANDOFF.md
··· 1 - # Handoff: UI polish — feed, login, and shell 1 + # Handoff: threaded reply views 2 2 3 3 ## What this project is 4 4 5 5 Dudesky is a Bluesky client built with TanStack Start (React, SSR, file-based routing). It uses ATProto OAuth to authenticate users via their Bluesky handle. Stack: TanStack Start + Vite, Tailwind CSS v4, `@atproto/oauth-client-node` for OAuth, `better-sqlite3` for persistence. 6 6 7 - ## What was built last session 7 + ## What was built in previous sessions 8 8 9 9 ### Database-backed OAuth stores (`src/lib/db.ts`, `src/lib/oauth-client.ts`) 10 10 ··· 25 25 26 26 The server function returns a mapped subset of the feed data (not raw ATProto types) to avoid a type incompatibility with `createServerFn`'s serialization constraints (`{ [x: string]: unknown }` vs `{ [x: string]: {} }`). 27 27 28 + ### Feed UI polish (`src/routes/feed.tsx`) 29 + 30 + - Cards now use `.island-shell` (frosted glass style) instead of bare `border rounded-lg` 31 + - Outer wrapper uses `bg-[--bg-base]` for the page background color token 32 + 28 33 ## Current state of key files 29 34 30 35 ``` 31 36 src/lib/db.ts ← SQLite setup, creates oauth_state + oauth_session tables 32 37 src/lib/oauth-client.ts ← NodeOAuthClient with DB-backed stateStore + sessionStore 33 38 src/routes/callback.tsx ← sets DID cookie, redirects to /feed 34 - src/routes/feed.tsx ← createServerFn fetches timeline; basic card UI (needs polish) 39 + src/routes/feed.tsx ← createServerFn fetches timeline; island-shell card UI 35 40 src/routes/login.tsx ← plain unstyled form; working, needs polish 36 41 src/routes/__root.tsx ← shell with Header + Footer components, devtools, theme init script 37 42 src/components/Header.tsx ← sticky nav with "Feed" link chip; uses design tokens ··· 39 44 src/styles.css ← Tailwind v4 + custom design system (see below) 40 45 ``` 41 46 42 - ## What needs to be done: UI cleanup 43 - 44 - The auth flow and data loading all work end-to-end. The feed renders but looks rough. The next session is purely visual polish — no logic changes needed. 45 - 46 - ### Design system already in place 47 + ## Design system 47 48 48 - `src/styles.css` has a full set of CSS custom properties and utility classes to use: 49 + `src/styles.css` has a full set of CSS custom properties and utility classes: 49 50 50 51 **Color tokens** (light + dark + `prefers-color-scheme` variants): 51 52 - `--sea-ink` / `--sea-ink-soft` — primary text colors ··· 66 67 - `.rise-in` — entrance animation (opacity + translateY, 700ms) 67 68 - `.feature-card` — card with hover lift 68 69 70 + To use design tokens as Tailwind values: `text-[--sea-ink-soft]`, `bg-[--surface]`, `border-[--line]` etc. Tailwind v4 wraps `--` prefixed values in `var()` automatically. 71 + 69 72 **Fonts**: Manrope (sans body), Fraunces (serif display) 70 73 71 - ### Specific things to polish 74 + ## Remaining UI polish (carry-over) 75 + 76 + These were not finished last session: 77 + 78 + **`src/routes/feed.tsx`:** 79 + - `text-gray-500` on the handle should be `text-[--sea-ink-soft]` (hardcoded color, won't respect dark mode) 80 + - No timestamp — add `createdAt: (item.post.record as { createdAt?: string }).createdAt ?? null` to the mapped return in `getTimeline`, then render it 81 + - No avatar fallback for posts where `author.avatar` is null 82 + - No `pendingComponent` or `errorComponent` on the route 83 + 84 + **`src/routes/login.tsx`:** 85 + - Form has zero styling — needs centered card layout, `.island-shell`, lagoon accent on the button 86 + 87 + **`src/routes/__root.tsx`:** 88 + - Page title is still "TanStack Start Starter" — should be "Dudesky" 89 + 90 + ## Next: threaded reply views 91 + 92 + Goal: clicking a post opens a thread view showing the post, its ancestors (parent chain), and its replies. 72 93 73 - **`src/routes/feed.tsx` — the main job:** 74 - - Post cards are bare `border rounded-lg` — should use `.island-shell` and the design token colors 75 - - Avatar images have no fallback for missing avatars 76 - - No timestamp on posts (it's available: `(item.post.record as { createdAt?: string }).createdAt`) 77 - - The feed data shape currently mapped in `getTimeline` only extracts `uri`, `text`, `author.{handle,displayName,avatar}` — if you need `createdAt` or other fields, add them to the mapped return object in the server function 78 - - No loading/pending state (`pendingComponent` on the route) 79 - - No error state (`errorComponent` on the route) 94 + ### ATProto API 95 + 96 + `agent.getPostThread({ uri })` returns a `ThreadViewPost` which has: 97 + - `post` — the post itself 98 + - `parent` — `ThreadViewPost | NotFoundPost | BlockedPost | undefined` (walk up for ancestors) 99 + - `replies` — `ThreadViewPost[] | ...` (direct replies) 100 + 101 + The `uri` for each post is already in the mapped feed data as `item.uri`. 102 + 103 + ### Suggested route 104 + 105 + `src/routes/post.$uri.tsx` — file-based dynamic route. The `uri` param will be the AT URI, but since it contains `/` characters (`at://did:plc:.../app.bsky.feed.post/...`) it needs to be encoded when used as a URL segment (use `encodeURIComponent` / `decodeURIComponent`). 106 + 107 + ### Data shape to map in the server fn 108 + 109 + Same `createServerFn` pattern as `feed.tsx`. Map to a plain serializable object — don't pass raw ATProto types through. Suggested shape: 80 110 81 - **`src/routes/login.tsx` — unstyled:** 82 - - The form (`<input>`, `<button>`) has zero styling 83 - - Should match the app's aesthetic — centered card layout, `.island-shell`, lagoon accent on the button 111 + ```ts 112 + { 113 + post: { uri, text, author, createdAt }, 114 + ancestors: Array<{ uri, text, author, createdAt }>, // ordered root-first 115 + replies: Array<{ uri, text, author, createdAt }>, 116 + } 117 + ``` 84 118 85 - **`src/routes/__root.tsx` — minor:** 86 - - Page title is still "TanStack Start Starter" — should be "Dudesky" or similar 119 + Walk `parent` recursively to build `ancestors`, collect `replies` array directly. 87 120 88 121 ## Conventions 89 122 ··· 93 126 - `createServerFn` for any server-only code called from loaders (DB, cookies, secrets) 94 127 - Env vars: `VITE_APP_URL`, `PRIVATE_KEY_0/1/2`, optionally `DB_PATH` 95 128 - Read AGENTS.md and load the relevant SKILL.md files before working on TanStack-related tasks 129 + - Playwright is configured for WSL2 with `--no-sandbox` in `~/.claude/plugins/cache/claude-plugins-official/playwright/unknown/.mcp.json` 130 + - To inject auth into the headless browser: set cookie `did=<url-encoded-did>` on `localhost:3001` via `browser_evaluate`, then navigate to `/feed`
+1 -1
src/routes/__root.tsx
··· 19 19 content: 'width=device-width, initial-scale=1', 20 20 }, 21 21 { 22 - title: 'TanStack Start Starter', 22 + title: 'Dudesky', 23 23 }, 24 24 ], 25 25 links: [