···11+meta {
22+ name: Get Settings Page
33+ type: http
44+ seq: 1
55+}
66+77+get {
88+ url: {{web_url}}/settings
99+}
1010+1111+assert {
1212+ res.status: eq 200
1313+}
1414+1515+docs {
1616+ Renders the user settings page.
1717+1818+ Requires authentication (session cookie). Unauthenticated users are redirected
1919+ to /login (302).
2020+2121+ Returns HTML page containing:
2222+ - Light-theme <select id="lightThemeUri"> when allowUserChoice is true
2323+ - Dark-theme <select id="darkThemeUri"> when allowUserChoice is true
2424+ - Informational banner when allowUserChoice is false
2525+ - "Preferences saved." banner when ?saved=1 query param is present
2626+ - Error banner when ?error=<message> query param is present
2727+ - <div id="theme-preview"> HTMX swap target
2828+2929+ Both selects carry HTMX attributes:
3030+ hx-get="/settings/preview"
3131+ hx-trigger="change"
3232+ hx-target="#theme-preview"
3333+ hx-swap="outerHTML"
3434+ hx-include="this"
3535+3636+ Query parameters:
3737+ - saved: "1" (optional) — shows "Preferences saved." success banner
3838+ - error: string (optional) — shows decoded error message in error banner
3939+4040+ Error codes:
4141+ - 302: Not authenticated → redirects to /login
4242+ - 200: Success — page rendered (with or without selects depending on policy)
4343+}
+50
bruno/AppView API/Settings/Preview Theme.bru
···11+meta {
22+ name: Preview Theme
33+ type: http
44+ seq: 2
55+}
66+77+get {
88+ url: {{web_url}}/settings/preview?lightThemeUri=at://{{forum_did}}/space.atbb.forum.theme/3lbllight
99+}
1010+1111+params:query {
1212+ lightThemeUri: at://{{forum_did}}/space.atbb.forum.theme/3lbllight
1313+}
1414+1515+assert {
1616+ res.status: eq 200
1717+}
1818+1919+docs {
2020+ HTMX endpoint — returns an HTML fragment with color swatches for the given theme.
2121+2222+ Called automatically by the settings page when the user changes a theme select.
2323+ Returns a <div id="theme-preview"> fragment that HTMX swaps into the page.
2424+2525+ Accepts exactly one of:
2626+ - lightThemeUri: AT URI of a light theme
2727+ - darkThemeUri: AT URI of a dark theme
2828+2929+ The endpoint fetches the theme from the AppView by rkey, extracts these tokens:
3030+ color-bg, color-surface, color-primary, color-text, color-border
3131+3232+ Returns the fragment:
3333+ <div id="theme-preview" class="theme-preview">
3434+ <span class="theme-preview__name">Theme Name</span>
3535+ <div class="theme-preview__swatches">
3636+ <span class="theme-preview__swatch" style="background:#hex" title="color-bg" />
3737+ ...
3838+ </div>
3939+ </div>
4040+4141+ Returns empty fragment on any of:
4242+ - No query param provided
4343+ - URI fails rkey extraction (malformed)
4444+ - AppView returns non-ok status (unknown theme)
4545+ - Network error reaching AppView
4646+4747+ Error codes:
4848+ - 200: Always — never errors, returns empty fragment on failure
4949+ Empty fragment: <div id="theme-preview"></div>
5050+}
+49
bruno/AppView API/Settings/Save Appearance.bru
···11+meta {
22+ name: Save Appearance
33+ type: http
44+ seq: 3
55+}
66+77+post {
88+ url: {{web_url}}/settings/appearance
99+}
1010+1111+body:form {
1212+ lightThemeUri: at://{{forum_did}}/space.atbb.forum.theme/3lbllight
1313+ darkThemeUri: at://{{forum_did}}/space.atbb.forum.theme/3lbldark
1414+}
1515+1616+assert {
1717+ res.status: eq 302
1818+ res.headers.location: contains /settings
1919+}
2020+2121+docs {
2222+ Saves the user's light and dark theme preferences as cookies.
2323+2424+ Requires authentication (session cookie). Unauthenticated users are redirected
2525+ to /login (302).
2626+2727+ Body parameters (application/x-www-form-urlencoded):
2828+ - lightThemeUri: string (required) — AT URI of the chosen light theme
2929+ - darkThemeUri: string (required) — AT URI of the chosen dark theme
3030+3131+ Validation:
3232+ 1. Both URIs must be non-empty strings
3333+ 2. Forum theme policy is re-fetched fresh (bypasses cache) to prevent stale themes
3434+ 3. allowUserChoice must be true in the policy
3535+ 4. Both URIs must be present in policy.availableThemes
3636+3737+ On success:
3838+ - Sets cookie: atbb-light-theme=<uri>; Path=/; Max-Age=31536000; SameSite=Lax
3939+ - Sets cookie: atbb-dark-theme=<uri>; Path=/; Max-Age=31536000; SameSite=Lax
4040+ - Redirects 302 to /settings?saved=1
4141+4242+ Error codes:
4343+ - 302 → /login: Not authenticated
4444+ - 302 → /settings?error=invalid: Missing or non-string body fields
4545+ - 302 → /settings?error=unavailable: Policy fetch failed (no cookies set)
4646+ - 302 → /settings?error=not-allowed: allowUserChoice is false
4747+ - 302 → /settings?error=invalid-theme: URI not in availableThemes
4848+ - 302 → /settings?saved=1: Success (cookies set)
4949+}