WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
4
fork

Configure Feed

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

docs: clarify preview endpoint is unauthenticated in CLAUDE.md

+10 -10
+1 -1
CLAUDE.md
··· 204 204 **Invariants:** 205 205 - User preference cookies are always validated against the current `policy.availableThemes` on read. Stale or removed theme URIs silently fall through to the forum default. 206 206 - `resolveTheme()` never throws -- it always returns a usable `ResolvedTheme`. 207 - - Settings routes (`/settings`, `/settings/appearance`, `/settings/preview`) require authentication. The preference form is only rendered when `policy.allowUserChoice` is true. 207 + - Settings routes (`/settings`, `/settings/appearance`) require authentication. The preview endpoint (`/settings/preview`) is unauthenticated — it returns only public theme data (color swatches). The preference form is only rendered when `policy.allowUserChoice` is true. 208 208 209 209 ## Middleware Patterns 210 210
+9 -9
docs/design-plans/2026-03-20-user-theme-preferences.md
··· 45 45 - **user-theme-preferences.AC5.2 Success:** Theme resolution ignores user preference cookies when `allowUserChoice: false` and uses forum default 46 46 47 47 ### user-theme-preferences.AC6: Preview endpoint 48 - - **user-theme-preferences.AC6.1 Success:** `GET /settings/preview?theme=<valid-uri>` returns an HTML fragment with swatches and theme name 49 - - **user-theme-preferences.AC6.2 Edge:** `GET /settings/preview?theme=<unknown-uri>` returns an empty `<div id="theme-preview">` fragment without crashing 48 + - **user-theme-preferences.AC6.1 Success:** `GET /settings/preview?lightThemeUri=<valid-uri>` (or `?darkThemeUri=`) returns an HTML fragment with swatches and theme name 49 + - **user-theme-preferences.AC6.2 Edge:** `GET /settings/preview?lightThemeUri=<unknown-uri>` (or `?darkThemeUri=`) returns an empty `<div id="theme-preview">` fragment without crashing 50 50 51 51 ## Glossary 52 52 ··· 74 74 A new route file (`apps/web/src/routes/settings.tsx`) registers three endpoints: 75 75 76 76 - `GET /settings` — fetches the theme policy, partitions available themes by `colorScheme`, reads existing preference cookies to pre-select values, and renders the settings page inside `BaseLayout` 77 - - `GET /settings/preview?theme=<uri>` — HTMX endpoint; fetches the theme by URI and returns an HTML fragment of color swatches (used to preview a theme before saving) 77 + - `GET /settings/preview?lightThemeUri=<uri>` (or `?darkThemeUri=<uri>`) — HTMX endpoint; whichever select fired sends its name/value via `hx-include="this"`. Fetches the theme by rkey and returns an HTML fragment of color swatches. 78 78 - `POST /settings/appearance` — validates `lightThemeUri` and `darkThemeUri` against the current policy's `availableThemes`, sets `atbb-light-theme` and `atbb-dark-theme` cookies, redirects 302 to `/settings?saved=1` 79 79 80 80 The theme resolution waterfall in `apps/web/src/lib/theme-resolution.ts` gains a new first lookup step: read the user preference cookie for the active color scheme and validate the stored URI is still present in `availableThemes`. If valid, use it; if stale or missing, fall through to the existing forum-default and preset-fallback steps. ··· 106 106 107 107 **Done when:** Tests pass for all cases; `pnpm test` passes; theme resolution uses user cookie when valid. 108 108 109 - **Covers:** user-theme-preferences.AC3.1, user-theme-preferences.AC3.2, user-theme-preferences.AC3.3, user-theme-preferences.AC5.1, user-theme-preferences.AC5.2 109 + **Covers:** user-theme-preferences.AC3.1, user-theme-preferences.AC3.2, user-theme-preferences.AC3.3, user-theme-preferences.AC5.2 110 110 <!-- END_PHASE_1 --> 111 111 112 112 <!-- START_PHASE_2 --> ··· 125 125 126 126 **Done when:** All integration tests pass; `pnpm test` passes; preferences round-trip correctly. 127 127 128 - **Covers:** user-theme-preferences.AC1.1, user-theme-preferences.AC1.2, user-theme-preferences.AC2.1, user-theme-preferences.AC2.2, user-theme-preferences.AC2.3, user-theme-preferences.AC4.1, user-theme-preferences.AC4.2, user-theme-preferences.AC4.3, user-theme-preferences.AC4.4, user-theme-preferences.AC6.1, user-theme-preferences.AC6.2 128 + **Covers:** user-theme-preferences.AC1.2, user-theme-preferences.AC1.5, user-theme-preferences.AC2.1, user-theme-preferences.AC2.2, user-theme-preferences.AC2.3, user-theme-preferences.AC4.1, user-theme-preferences.AC4.2, user-theme-preferences.AC4.3, user-theme-preferences.AC4.4, user-theme-preferences.AC5.1 129 129 <!-- END_PHASE_2 --> 130 130 131 131 <!-- START_PHASE_3 --> ··· 134 134 **Goal:** Add the color swatch preview fragment returned when the user changes a `<select>`. 135 135 136 136 **Components:** 137 - - `GET /settings/preview?theme=<uri>` handler in `apps/web/src/routes/settings.tsx` — fetches theme from cache, extracts key tokens (`color-bg`, `color-surface`, `color-primary`, `color-text`, `color-border`), returns an HTML fragment with swatches and theme name; returns empty `<div id="theme-preview">` on unknown URI or fetch failure 138 - - `<select>` elements in the settings page carry `hx-get="/settings/preview?theme={value}"`, `hx-trigger="change"`, `hx-target="#theme-preview"`, `hx-swap="outerHTML"` 137 + - `GET /settings/preview` handler in `apps/web/src/routes/settings.tsx` — accepts `?lightThemeUri=` or `?darkThemeUri=` (whichever select fired via `hx-include="this"`); fetches theme from AppView by rkey, extracts key tokens (`color-bg`, `color-surface`, `color-primary`, `color-text`, `color-border`), returns an HTML fragment with swatches and theme name; returns empty `<div id="theme-preview">` on unknown URI or fetch failure 138 + - `<select>` elements in the settings page carry `hx-get="/settings/preview"`, `hx-trigger="change"`, `hx-target="#theme-preview"`, `hx-swap="outerHTML"`, `hx-include="this"` 139 139 - Tests: preview returns swatch fragment for valid URI; returns empty div for unknown URI; select attributes are present in rendered page HTML 140 140 141 141 **Dependencies:** Phase 2 (settings page and route file exist) 142 142 143 143 **Done when:** Tests pass; selecting a theme in the UI swaps in a swatch preview; `pnpm test` passes. 144 144 145 - **Covers:** user-theme-preferences.AC1.3, user-theme-preferences.AC1.4 145 + **Covers:** user-theme-preferences.AC1.3, user-theme-preferences.AC1.4, user-theme-preferences.AC6.1, user-theme-preferences.AC6.2 146 146 <!-- END_PHASE_3 --> 147 147 148 148 <!-- START_PHASE_4 --> ··· 158 158 159 159 **Done when:** Tests pass; authenticated users see Settings link; unauthenticated users do not; `pnpm test` passes. 160 160 161 - **Covers:** user-theme-preferences.AC1.1 (discoverability), user-theme-preferences.AC2.1 161 + **Covers:** user-theme-preferences.AC1.1 162 162 <!-- END_PHASE_4 --> 163 163 164 164 <!-- START_PHASE_5 -->