Restyles the karaoke-maker web UI from its bare-bones state (a flat form, an unstyled table, and a <pre> log dump) into a themed control surface, and adds four functional improvements while the markup was being rewritten.
Design
Black/purple base with aqua-blue accents, cyberpunk-leaning at deliberately restrained intensity — this is a tool you read logs in, so the palette, typography and layout do the work rather than effects. Aqua is reserved for "live or interactive" (running job, active stage, focus rings) so it carries meaning instead of decorating. Job state is conveyed by glyph and colour, never colour alone.
Layout is a Spotify-style shell: fixed sidebar queue, scrolling main panel, and a fixed bottom bar. Because the pipeline runs exactly one job at a time, that bottom bar maps cleanly onto Spotify's now-playing strip — it becomes "now running" with the live stage track.
Typography is Chakra Petch (OFL-1.1) for display, system sans for body, and ui-monospace for machine data (paths, time ranges, elapsed, logs). The font is self-hosted — vendored from the @fontsource npm package rather than a CDN — because the server may sit on an isolated intranet where an external font request would simply fail. The page makes zero off-origin requests.
Functional changes
Low-confidence warnings are now first-class. They were buried in a text dump; they are the user's actual to-do list for manual touch-up in an UltraStar editor. Each is now a scannable row with the note count and time range, and the parsed count/start/end are stored on data- attributes so the planned in-app note-editing tools can attach without re-parsing.
Live stage track showing all eight pipeline stages with done/active/pending states and an n/8 counter.
Form polish: required fields visible with the rest collapsed into Advanced, drag-and-drop for video/cover, and values preserved after a failed submit so lyrics never need retyping.
Queue quality-of-life: elapsed time, auto-select newest (never overriding an explicit selection), copy-path button, empty states, and polling that pauses when nothing is running.
Backend
Three small additions the frontend could not fake: job timestamps (created_at/started_at/finished_at), GET /api/stages returning STAGE_ORDER so the track cannot drift from the real pipeline, and a widened stage-log regex that also matches cached, skipping — without it, a warm-cache re-run left the track apparently stuck at the first uncached stage.
Notable bugs caught in review
The copy-path button was dead on every real deployment.navigator.clipboard is secure-context-only; this ships bind_host = "0.0.0.0" and is reached over http://<host>:8000, so COPY always failed. It worked on 127.0.0.1, which is why per-task review missed it. Now falls back to execCommand, verified over a real LAN address with the clipboard read back from a separate secure context.
A stale detail response could permanently mis-pair the review panel with the selected job. The shape pre-existed, but idle-aware polling turned a self-correcting 3s glitch into a permanent one. Fixed with a request-sequence guard.
Polling destroyed keyboard focus on queue cards every 3s, introduced by promoting rows to focusable buttons. Fixed by diff-and-patch rendering rather than wholesale replacement; the same defect on the copy button was fixed too.
Testing
163 passed, 1 skipped (the opt-in e2e, gated on KARAOKE_E2E) — up from 131 on main. Backend changes have pytest coverage; the three non-negotiable frontend constraints have automated guards: no innerHTML/outerHTML/insertAdjacentHTML anywhere in the page (a stored XSS through job title/artist was real in this file once), no off-origin requests, and prefers-reduced-motion disabling all animation.
The UI was verified in a real headless browser at 1280/1180/1000/700px — including the XSS payload, focus retention across poll cycles, the clipboard fallback over a non-secure origin, drag-and-drop, reduced motion, and the accessibility tree. Contrast was computed for every foreground/surface pair: worst text pair is 4.72:1, control borders 3.68:1.
Not verified: no JS test framework was added (per the design), so the frontend guards are structural checks rather than DOM-level behavioural tests. They pin the known regressions but a deliberately-crafted mutation can evade them; the behaviour itself was verified live. Follow-up polish items are listed in the branch's progress ledger.
**Summary generated by LLM agent**
Restyles the karaoke-maker web UI from its bare-bones state (a flat form, an unstyled table, and a `<pre>` log dump) into a themed control surface, and adds four functional improvements while the markup was being rewritten.
## Design
Black/purple base with aqua-blue accents, cyberpunk-leaning at deliberately **restrained** intensity — this is a tool you read logs in, so the palette, typography and layout do the work rather than effects. Aqua is reserved for "live or interactive" (running job, active stage, focus rings) so it carries meaning instead of decorating. Job state is conveyed by glyph *and* colour, never colour alone.
Layout is a Spotify-style shell: fixed sidebar queue, scrolling main panel, and a fixed bottom bar. Because the pipeline runs exactly one job at a time, that bottom bar maps cleanly onto Spotify's now-playing strip — it becomes "now running" with the live stage track.
Typography is Chakra Petch (OFL-1.1) for display, system sans for body, and `ui-monospace` for machine data (paths, time ranges, elapsed, logs). The font is **self-hosted** — vendored from the `@fontsource` npm package rather than a CDN — because the server may sit on an isolated intranet where an external font request would simply fail. The page makes zero off-origin requests.
## Functional changes
- **Low-confidence warnings are now first-class.** They were buried in a text dump; they are the user's actual to-do list for manual touch-up in an UltraStar editor. Each is now a scannable row with the note count and time range, and the parsed count/start/end are stored on `data-` attributes so the planned in-app note-editing tools can attach without re-parsing.
- **Live stage track** showing all eight pipeline stages with done/active/pending states and an n/8 counter.
- **Form polish:** required fields visible with the rest collapsed into Advanced, drag-and-drop for video/cover, and values preserved after a failed submit so lyrics never need retyping.
- **Queue quality-of-life:** elapsed time, auto-select newest (never overriding an explicit selection), copy-path button, empty states, and polling that pauses when nothing is running.
## Backend
Three small additions the frontend could not fake: job timestamps (`created_at`/`started_at`/`finished_at`), `GET /api/stages` returning `STAGE_ORDER` so the track cannot drift from the real pipeline, and a widened stage-log regex that also matches `cached, skipping` — without it, a warm-cache re-run left the track apparently stuck at the first uncached stage.
## Notable bugs caught in review
- **The copy-path button was dead on every real deployment.** `navigator.clipboard` is secure-context-only; this ships `bind_host = "0.0.0.0"` and is reached over `http://<host>:8000`, so COPY always failed. It worked on `127.0.0.1`, which is why per-task review missed it. Now falls back to `execCommand`, verified over a real LAN address with the clipboard read back from a separate secure context.
- **A stale detail response could permanently mis-pair the review panel with the selected job.** The shape pre-existed, but idle-aware polling turned a self-correcting 3s glitch into a permanent one. Fixed with a request-sequence guard.
- **Polling destroyed keyboard focus** on queue cards every 3s, introduced by promoting rows to focusable buttons. Fixed by diff-and-patch rendering rather than wholesale replacement; the same defect on the copy button was fixed too.
## Testing
163 passed, 1 skipped (the opt-in e2e, gated on `KARAOKE_E2E`) — up from 131 on main. Backend changes have pytest coverage; the three non-negotiable frontend constraints have automated guards: no `innerHTML`/`outerHTML`/`insertAdjacentHTML` anywhere in the page (a stored XSS through job title/artist was real in this file once), no off-origin requests, and `prefers-reduced-motion` disabling all animation.
The UI was verified in a real headless browser at 1280/1180/1000/700px — including the XSS payload, focus retention across poll cycles, the clipboard fallback over a non-secure origin, drag-and-drop, reduced motion, and the accessibility tree. Contrast was computed for every foreground/surface pair: worst text pair is 4.72:1, control borders 3.68:1.
**Not verified:** no JS test framework was added (per the design), so the frontend guards are structural checks rather than DOM-level behavioural tests. They pin the known regressions but a deliberately-crafted mutation can evade them; the behaviour itself was verified live. Follow-up polish items are listed in the branch's progress ledger.
Final whole-branch review fix wave (5 items):
- copyPath: fall back to execCommand("copy") via an off-screen textarea,
then to selecting the path text, since navigator.clipboard is undefined
outside secure contexts (i.e. this app's normal http:// deployment)
- refreshDetail: snapshot selectedId before fetching and discard the
response if selection changed while in flight, so a slow response can no
longer permanently mis-pair the review panel with the wrong job now that
polling stops when idle
- .st-queued: give the sidebar rail a colour that clears 3:1 against --panel
- now-bar: #now-label reflects running/idle state instead of always reading
"NOW RUNNING"
- empty-state copy: distinguish a failed job from "nothing to review", fix
the queue-empty messages to not ask the user to select something that
doesn't exist, and normalise voice to lowercase/unpunctuated
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Restyles the karaoke-maker web UI from its bare-bones state (a flat form, an unstyled table, and a
<pre>log dump) into a themed control surface, and adds four functional improvements while the markup was being rewritten.Design
Black/purple base with aqua-blue accents, cyberpunk-leaning at deliberately restrained intensity — this is a tool you read logs in, so the palette, typography and layout do the work rather than effects. Aqua is reserved for "live or interactive" (running job, active stage, focus rings) so it carries meaning instead of decorating. Job state is conveyed by glyph and colour, never colour alone.
Layout is a Spotify-style shell: fixed sidebar queue, scrolling main panel, and a fixed bottom bar. Because the pipeline runs exactly one job at a time, that bottom bar maps cleanly onto Spotify's now-playing strip — it becomes "now running" with the live stage track.
Typography is Chakra Petch (OFL-1.1) for display, system sans for body, and
ui-monospacefor machine data (paths, time ranges, elapsed, logs). The font is self-hosted — vendored from the@fontsourcenpm package rather than a CDN — because the server may sit on an isolated intranet where an external font request would simply fail. The page makes zero off-origin requests.Functional changes
data-attributes so the planned in-app note-editing tools can attach without re-parsing.Backend
Three small additions the frontend could not fake: job timestamps (
created_at/started_at/finished_at),GET /api/stagesreturningSTAGE_ORDERso the track cannot drift from the real pipeline, and a widened stage-log regex that also matchescached, skipping— without it, a warm-cache re-run left the track apparently stuck at the first uncached stage.Notable bugs caught in review
navigator.clipboardis secure-context-only; this shipsbind_host = "0.0.0.0"and is reached overhttp://<host>:8000, so COPY always failed. It worked on127.0.0.1, which is why per-task review missed it. Now falls back toexecCommand, verified over a real LAN address with the clipboard read back from a separate secure context.Testing
163 passed, 1 skipped (the opt-in e2e, gated on
KARAOKE_E2E) — up from 131 on main. Backend changes have pytest coverage; the three non-negotiable frontend constraints have automated guards: noinnerHTML/outerHTML/insertAdjacentHTMLanywhere in the page (a stored XSS through job title/artist was real in this file once), no off-origin requests, andprefers-reduced-motiondisabling all animation.The UI was verified in a real headless browser at 1280/1180/1000/700px — including the XSS payload, focus retention across poll cycles, the clipboard fallback over a non-secure origin, drag-and-drop, reduced motion, and the accessibility tree. Contrast was computed for every foreground/surface pair: worst text pair is 4.72:1, control borders 3.68:1.
Not verified: no JS test framework was added (per the design), so the frontend guards are structural checks rather than DOM-level behavioural tests. They pin the known regressions but a deliberately-crafted mutation can evade them; the behaviour itself was verified live. Follow-up polish items are listed in the branch's progress ledger.
Final whole-branch review fix wave (5 items): - copyPath: fall back to execCommand("copy") via an off-screen textarea, then to selecting the path text, since navigator.clipboard is undefined outside secure contexts (i.e. this app's normal http:// deployment) - refreshDetail: snapshot selectedId before fetching and discard the response if selection changed while in flight, so a slow response can no longer permanently mis-pair the review panel with the wrong job now that polling stops when idle - .st-queued: give the sidebar rail a colour that clears 3:1 against --panel - now-bar: #now-label reflects running/idle state instead of always reading "NOW RUNNING" - empty-state copy: distinguish a failed job from "nothing to review", fix the queue-empty messages to not ask the user to select something that doesn't exist, and normalise voice to lowercase/unpunctuated