Add specs for the account menu, downloads-as-offline-library, offline downloaded-only filter, and scoped search (+ boundary revision). Add the new UR/DR entries to requirements.md, update ux-flows, and regenerate the traceability matrix. TRACES: UR-049, UR-050, UR-052, UR-053, UR-054, UR-055, UR-056
7.1 KiB
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). UX spec: ux-flows.md §1.2–1.4.
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
-
The header is not global. It is defined in library/+layout.svelte. The root layout +layout.svelte renders no header at all.
-
routeOwnsLayoutin 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'sBottomUibelow it. -
Bottom nav is Home / Search / Library only (BottomNav.svelte) — no Settings or account entry.
-
Net effect: on
/,/search, and/downloadsthere is no route to Settings or Sign out. -
Desktop username is inert text — a
<span>next to the icons, not a trigger. -
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.
-
viewModeis already a persisted store in 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 <name> ← identity block, not interactive
<server host>
────────────────────────
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-expandedon 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/loginstay chrome-free./settingsalready 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 and +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
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/downloadswithout 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/loginremain 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 checkandbun run testpass.
Testing
- Extend the existing
layoutShelltests: chrome-visibility for/,/search,/downloads(now true) and/player/*,/login(still false). AccountMenu: renders the documented items in order; trigger togglesaria-expanded;Escapeand backdrop click close it; Sign out invokes the logout handler.- Display setting: writes through to the
viewModestore and persists.
New requirement-implementing code needs TRACES: comments — see
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 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. Checkgit diffbefore "repairing" unexpected changes, and expect to coordinate on that file.