diff --git a/.planning/NOTES.md b/.planning/NOTES.md index 4fb55b8..188c78a 100644 --- a/.planning/NOTES.md +++ b/.planning/NOTES.md @@ -4302,3 +4302,51 @@ compiles against HEAD with a single shim (`SetPlaybackFinishedHandler` gained a `srcErr error` parameter), which makes "did the backend fix cause this" a ten-minute question instead of a full checkout. + +## An overlay band is not a notification, it is a lid (measured 2026-08-20) + +#62 asks for background jobs to become "a notification" on the phone, +and the app has exactly one notification surface, so the first version +of the fix put `` in `notification-host`'s band — which is +`position: fixed` under the header. It renders correctly, it is on top, +it is inside the viewport, and it is unusable. + +At the device's 424x439 viewport a **compact** panel showing two active +jobs is ~216px — half the screen — drawn over the content, with +`pointer-events: auto` so it swallows every tap underneath. Nothing in +the component tier could see it. The e2e suite could: four specs failed, +and *none* of them was about jobs — two `phone-shell` journeys into the +full-screen Now Playing and `header-action-overflow`'s phone case, all +three because the band was intercepting taps meant for the app. + +`` is in the shell's grid instead, as a row between the top +bar and the main panel, so it **pushes**. That is #24's one sentence +("no action is ever unreachable at any supported size") deciding a +layout question: a band that hides the app in order to say the app is +busy has traded the popover's fault for a worse one. + +Two things fell out of it worth keeping: + +- **A finished row in flow is furniture.** The overlay could afford to + keep terminal jobs around; a row that holds the content down after + the work is done cannot. `job-panel` grew `active-only` for the band, + and Settings keeps finished rows because that is where "did the last + scan work" is asked. +- **`job-row` already had the right density.** `variant="compact"` is + described in its own source as "the popover density", which is + exactly what the band is replacing — 216px against 259px for the + same two jobs, and no per-job statistics that a phone has no room + for. + +## The e2e suite is the tier that sees a shell regression (2026-08-20) + +Worth stating because it decided how #62 was verified. The change is +one component, one stylesheet and one line of `index.html`; `make +ui-test` (955 tests) passed on the broken overlay version and so did +`tsc`, `lint` and the whole Go suite. The failure was three specs that +have nothing to do with jobs, failing on `click()` timeouts. + +The corollary for anything that draws over the shell: **run the whole +e2e suite, not the spec you wrote.** A spec written for a feature +asserts the feature works; what a new overlay breaks is everything +else, and only the suite is looking at that. diff --git a/CLAUDE.md b/CLAUDE.md index 43b4d8b..ed31b8f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -593,11 +593,38 @@ rather than renaming them. `ClearFinishedJobs` is global — a Clear under Libraries would discard the index build's history too; a finished row dismisses itself. - The header `job-indicator` is untouched and is still the one view of - everything at once, from every page. One consequence worth knowing - before writing a spec: a section holding a `job-panel` also holds a - `job-details-drawer`, whose own header carries `.header` — so + The header `job-indicator` is still the one view of everything at + once, from every page — **on a desktop.** One consequence worth + knowing before writing a spec: a section holding a `job-panel` also + holds a `job-details-drawer`, whose own header carries `.header` — so `config-section .header` is ambiguous the moment a job exists. + + **Below 600px that indicator stands down and `` takes + over** (#62), because a popover is a *disclosure* and background work + is the one thing a phone should not make you open something to see — + and because #57 deletes the bar it is anchored to and is blocked on + it having somewhere else to live. The band is the same `job-panel`, + so `applyJobControl` and its index-build confirmation come along + rather than being reimplemented; `kinds="*"` is how it says "every + kind", which is what the indicator was for. + + Three things about it are load-bearing. **It is in the layout, not + over it**, as its own grid row above the main panel: the first + version put it in `notification-host`'s fixed band, which reads fine + in a screenshot and is unusable — at 424×439 a compact panel is + ~200px of a 439px screen and it *covers* what is under it, which four + e2e specs caught by failing on taps it was intercepting. **It shows + active work only** (`active-only`), because in flow a finished row is + furniture that keeps the content pushed down after the work is done; + finished rows stay where the work was started, which is #27's rule. + And **it renders nothing above 600px**, from `matchMedia` rather than + a media query, because that decides whether the element *exists* — + Settings already holds four `job-panel`s and a fifth answering for + every kind is `bottom-nav`'s "resolved to 2 elements" trap again. + `index.css` keeps it `display: none` outside the phone for a second + reason: an in-flow grid child with no named area is auto-placed into + one of the shell's rows, which is what the skip link is absolutely + positioned to avoid. - `config` — TOML-based settings. Settings page uses HTMX + templ for server-rendered HTML fragments. - `playlist` / `smartplaylist` — Playlist CRUD and rule-based smart playlists. - `mediacontrols` — OS media controls behind one `Handler`: MPRIS over