# Spec: Account menu and global chrome availability **Status:** Implemented **Scope:** Frontend only. No Rust changes required. **Requirements:** UR-054 → DR-075, DR-076, DR-077 (see [requirements.md](../requirements.md)). **UX spec:** [ux-flows.md §1.2–1.4](../ux-flows.md). ## Summary Account actions — Settings, Downloads, Display preferences, Sign out — are currently reachable **only from `/library/*`**. Move them into a single shared account menu anchored to the user's name, and make that menu available on every authenticated non-immersive screen. ## Motivation A user sitting on the home screen cannot open Settings or sign out. The bottom nav offers Home / Search / Library only, and the header that hosts those actions belongs to the library layout. The user has to guess that account actions live *inside* Library — an unrelated section — and navigate there first. Desktop and mobile also disagree today: desktop shows an unlabeled logout icon with no grouped menu, mobile shows a three-dot overflow with labelled items. The same two actions are found two different ways. ## Background: verified current state 1. **The header is not global.** It is defined in [library/+layout.svelte](../../src/routes/library/+layout.svelte). The root layout [+layout.svelte](../../src/routes/+layout.svelte) renders no header at all. 2. **`routeOwnsLayout`** in [layoutShell.ts](../../src/lib/utils/layoutShell.ts) returns true for `/library`, `/player/`, `/login` — those routes own their own full-height flex column. Everything else renders into the root scroller with the root's `BottomUi` below it. 3. **Bottom nav is Home / Search / Library only** ([BottomNav.svelte](../../src/lib/components/BottomNav.svelte)) — no Settings or account entry. 4. **Net effect:** on `/`, `/search`, and `/downloads` there is no route to Settings or Sign out. 5. **Desktop username is inert text** — a `` next to the icons, not a trigger. 6. **The mobile overflow menu already has the right contents** (Downloads, Settings, divider, Sign out) and the right dismissal behaviour (backdrop click, keyboard handler). **Extract and reuse it rather than rewriting it.** 7. **`viewMode` is already a persisted store** in [library.ts](../../src/lib/stores/library.ts) (`jellytau-view-mode`, `localStorage`). The Display setting is a second view onto it — **no new state, no migration.** ## Design ### `AccountMenu` component (DR-075) One component used by both breakpoints. Contents in fixed order: ``` Signed in as ← identity block, not interactive ──────────────────────── Downloads Settings Display ← grid/list preference ──────────────────────── Sign out ← destructive, last, after a divider ``` - **Trigger is the username/avatar**, not a bare three-dot icon. On mobile where horizontal space is tight, the avatar (or initial) alone is acceptable; the name shows inside the open menu regardless. - **Same items, same order, both platforms.** - Preserve the existing dismissal behaviour: click-outside backdrop, `Escape`, and focus return to the trigger on close. - Menu items are real links/buttons — keyboard reachable, correct roles, `aria-expanded` on the trigger. "Display" may either navigate to the Settings Display section or expose the grid/list choice inline. Prefer navigating — it keeps one source of truth for preferences and avoids a nested control inside a dropdown. ### Global chrome (DR-076) Make the header — and therefore the account menu — available on `/`, `/search`, and `/downloads`. The cleanest route is to lift the header out of the library layout into a shared component rendered by the root layout, with the library layout consuming the same component rather than defining its own. **Do not duplicate the markup into each route.** Constraints that must survive the change: - `/player/*` and `/login` stay chrome-free. - `/settings` already owns its layout; it needs no account menu (the user is already there), but must not double up on chrome. - The root layout's flex/scroller structure is deliberate — the comments in [layoutShell.ts](../../src/lib/utils/layoutShell.ts) and [+layout.svelte](../../src/routes/+layout.svelte) explain why routes own their own column. Preserve the scroll containment; a regression here reintroduces the "last row hidden behind the nav" bug called out in those comments. - Mini-player and bottom-nav visibility rules (`showGlobalMiniPlayer`, `showBottomNav`) must be unchanged. ### Display section in Settings (DR-077) Add a Display section to [settings/+page.svelte](../../src/routes/settings/+page.svelte) with the grid/list control bound to the existing `viewMode` store via `library.setViewMode(...)`. The page-header toggle in `LibraryGrid` stays — both controls drive the same store, so they stay in sync for free. ## Out of scope - Redesigning the Settings page or reorganising its existing sections. - Multi-server / account switching (UR-047) — the identity block displays the active server but offers no switcher. - Changing the bottom nav's three destinations. ## Acceptance criteria - [ ] Settings and Sign out are reachable from `/`, `/search`, and `/downloads` without first navigating into Library. - [ ] Desktop and mobile show the same account menu items in the same order. - [ ] The username/avatar opens the menu; it is a real button with `aria-expanded`. - [ ] Sign out is last, after a divider, and still logs out + resets library state + redirects as it does today. - [ ] `/player/*` and `/login` remain chrome-free. - [ ] Settings has a Display section that changes grid/list, and the change is immediately reflected by the library page-header toggle (same store). - [ ] No regression in scroll containment, mini-player visibility, or bottom-nav visibility on any route. - [ ] `bun run check` and `bun run test` pass. ## Testing - Extend the existing `layoutShell` tests: chrome-visibility for `/`, `/search`, `/downloads` (now true) and `/player/*`, `/login` (still false). - `AccountMenu`: renders the documented items in order; trigger toggles `aria-expanded`; `Escape` and backdrop click close it; Sign out invokes the logout handler. - Display setting: writes through to the `viewMode` store and persists. New requirement-implementing code needs `TRACES:` comments — see [CLAUDE.md](../../CLAUDE.md). Suggested: `AccountMenu` → `UR-054 | DR-075`, shell/header changes → `UR-054 | DR-076`, Settings Display section → `UR-054, UR-029 | DR-077`. ## Notes for the implementer - Read [ux-flows.md §1.2–1.4](../ux-flows.md) first — behavioural spec; this is the implementation plan. - The layout shell is subtle and the existing comments record real bugs that were fixed there. Read them before restructuring. - Another session may be active in this repo, including in `src/routes/settings/+page.svelte`. Check `git diff` before "repairing" unexpected changes, and expect to coordinate on that file.