# 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 `<span>` 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 <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-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.
