Skip to content

UI overhaul: labels instead of essays, and a deck you can read - #29

Merged
MyNamesEMurray merged 7 commits into
mainfrom
claude/live-mic-transcription-accuracy-x5ptio
Sep 7, 2026
Merged

MyNamesEMurray merged 7 commits into
mainfrom
claude/live-mic-transcription-accuracy-x5ptio

Conversation

@MyNamesEMurray

Copy link
Copy Markdown
Owner

Summary

A full pass over all four pages for distribution polish, driven by screenshots of the running app rather than by reading markup. The theme was already coherent; what was wrong was density, hierarchy, and a handful of things that were simply broken at sizes nobody had looked at.

Changes

Shared components first (theme.css, DESIGN.md). Each page had grown its own copy of the same patterns, so the pass starts by settling them: .setting + .why (a short label with its explanation folded into a disclosure), .section, .card-head, .seg, .empty-state, .toolbar, .kbd. DESIGN.md gains the two principles these encode — labels, not essays (depth on demand, not depth by default) and nothing in the app is only a colour.

Settings: documentation turned back into a UI. Most controls carried their whole explanation inside the <label>, five to seven lines each, four stacked in a row on the Voice tab. All 16 paragraph-labels became a short label, a one-line hint, and the control, with the prose kept verbatim behind 24 "why?" disclosures — nothing deleted, just not shouted while a stream is live. Tabs became sections with real headings; Voice went from one five-part scroll to Where speech comes from → How the cast handles what you say → Accuracy → Second channel. The Simple/Advanced split was also wrong in a way that mattered: the Twitch channel name was Advanced-only while "pull game + title from Twitch" was Simple, so a Simple setting depended on a field you could not see.

The deck: a conversation instead of a wall. The column is contained and centred instead of spanning the monitor, consecutive messages group under one header, and the cast rail dropped the four-times-repeated "watching" and the ring around every tile. The 🤖 AI badge deliberately does not group — it moved onto the bubble, trailing the text, so every cast message still carries it.

The wizard got a map. Six anonymous dashes became a named rail with ticks and a step counter; "Next" says where it goes; choice cards became real radios with a check mark (they were previously selected by a faint tint alone); and errors became callouts that say what to do instead of showing raw provider JSON like connect ECONNREFUSED 127.0.0.1:4455.

Real defects fixed along the way: the settings save bar was fixed-position over the content and hid the bottom of long tabs; the deck's status orbs dropped their labels below 1180px, leaving four unreadable coloured dots; "Say something" wrapped onto two lines; cost printed three decimals and rounded anything under a cent to $0.00; and the overlay's compact theme was genuinely illegible over a bright scene (white text, two soft shadows, white video) and now has a proper halo.

Verification

  • Full CI reproduced locally: node --check over every JS file, config.example.json parse, npm test (161 pass, 0 fail, 1 pre-existing Windows-only SAPI skip), npm run smoke → SMOKE OK.
  • Everything was judged from screenshots of the app running headless with a seeded four-ghost session, at 1440 / 1000 / 900 and at the 1.25 font scale, before and after. Roughly 100 screenshots across the pass; every wizard step was walked, not just the first.
  • Settings: data-path set is byte-identical to the previous revision, every pre-existing id still resolves, and a save round-trip was verified for one control per tab.
  • Deck: automated check that every cast message carries .badge-ai (11/11 and 9/9 in two runs), plus 3× zooms proving the badge is legible against all four ghost colours at both font scales. Send, energy drag, room switch, pause/resume and the command palette all still work.
  • Overlay: rendered over bright, pure-white, dark and busy backdrops in both themes. A regression was caught mid-pass — the new halo bled into the AI badge and filled in its letters — and fixed by having the badge opt out; 3× zoom over a bright scene proves badge and watermark intact.
  • Only console message on any page is a pre-existing GET /favicon.ico 404.

Risk & rollout

  • Presentation only. No server, config, protocol or API change; no new config keys; nothing to migrate. src/ is untouched by this PR.
  • The ethics guarantees were the thing to watch, and they are intact. The AI badge is the same .badge-ai component at the same size and contrast on every surface — it moved position on the deck and opted out of the overlay's new text halo, both so it stays more legible, never less. The "simulated viewers, not real people" watermark was strengthened only: full white on a bordered plate that can never be squeezed, same size, same position, same copy, still unconditional.
  • Settings binding is the main review risk — a control that silently stops saving would be invisible. Hence the byte-identical data-path check and the per-tab round-trip, but a second pair of eyes on that file is worth it.
  • The overlay renders on air, so its diff was kept deliberately small: the card theme was fine over every backdrop tested and was left alone.
  • Two things noticed and deliberately not fixed here, both server-side: /favicon.ico 404s on every page load, and /api/config serves stale in-memory config after a non-hot save under npm run serve (the Electron host relaunches, so this only bites headless).

Checklist

  • npm run smoke passes locally (or CI is green)
  • Follows the design language in DESIGN.md (if UI changed)
  • AI-labeling / "simulated viewers" guarantees intact (if bot output or overlay changed)
  • Docs updated (README / config reference) if behavior or config changed
  • Targets main

🤖 Generated with Claude Code

https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM


Generated by Claude Code

Every page had grown its own version of the same handful of patterns, and
the settings page had grown something worse: controls whose entire
explanation was crammed into the <label>, five to seven lines each. That
is documentation wearing a UI's clothes, and it buries the one control a
streamer opened the page for.

theme.css gains `.setting` + `.why` (a short label with the paragraph
folded into a closed disclosure), `.section`, `.card-head`, `.seg`,
`.empty-state`, `.toolbar`/`.spacer` and `.kbd`. Buttons no longer wrap
their labels, which is what turned "Say something" into two lines on the
deck.

DESIGN.md gains the two principles these encode: labels, not essays --
depth on demand rather than depth by default; and nothing in the app is
only a colour, since the deck's status orbs currently drop their labels
on a narrow window and leave four unreadable dots behind.

Groundwork for the page-level overhaul that follows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
The wizard is the first thing a new user sees, and it was asking them to
walk six anonymous dashes with no idea how many were left or what "Next"
was about to commit to. The progress bar is now a named rail (Goal ·
Ghost · Brain · Stream · Energy · Extras) with ticks behind and a step
counter, and the button says where it goes ("Next: your stream",
"Finish setup") next to a note that nothing is saved until the end.

The choice cards were only ever a faint tint when selected, which is a
colour carrying meaning on its own. They are radios now, with a check
mark, arrow-key movement between them, and the aria roles to match.

Failures used to be a scrap of red text under a button, sometimes raw
provider JSON: `connect ECONNREFUSED 127.0.0.1:4455` is not something to
show a streamer. Errors are callouts that say what to do about it, and a
refused connection reads as a sentence.

The overlay's compact theme was genuinely illegible over a bright scene
-- two soft shadows under white text on white video. It has a real halo
now. That halo then bled into the AI badge and filled in its letters, so
the badge opts out of it: the badge carries its own contrast and stays
pixel-identical to the deck's, as the ethics guarantee requires. The
watermark was strengthened only -- full white on a bordered plate that
can never be squeezed -- and the card theme, which was fine over every
backdrop tested, was left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
Most controls on this page carried their entire explanation inside the
<label> -- five to seven lines each, four stacked in a row on the Voice
tab. Everything was equally loud, so the one control you opened the page
for was the hardest thing to find.

All 16 paragraph-labels become a short label, a one-line hint, and the
control, with the original prose kept verbatim behind a "why?"
disclosure. Nothing was deleted: that writing is the only documentation
some users will read, it just isn't what they need while a stream is
live. Tabs are now sections with real headings, and Voice in particular
went from one five-part scroll to Where speech comes from → How the cast
handles what you say → Accuracy → Second channel, with the channel a
streamer sets up once sitting last instead of level with the mode they
change often.

Fixes along the way: the save bar was fixed-position over the content and
hid the bottom of long tabs, and is now sticky in flow; the shell was a
narrow column in a wide window, so hints wrapped in a 30-character
gutter; the level toggle floated in the header far from the nav it
changes, and now sits at the top of that nav saying what it hides.

The Simple/Advanced split was also wrong in a way that mattered: the
Twitch channel name was Advanced-only while "pull game + title from
Twitch" was Simple, so the Simple setting depended on a field you could
not see. Machinery (cadence seconds, ports, thresholds, backoffs) moved
out; the channel name moved in.

The "why?" summaries were styled at --faint, where they read as leftover
markup rather than an affordance, so they sit at --dim now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
At 1440 the ghosts' messages hugged the far left and yours the far
right, so reading your own chat meant sweeping across the whole monitor.
The conversation is a contained, centred column now, with the speaker
sides kept inside it.

Consecutive messages from one author group under a single header, so
three lines of chrome no longer carry one line of chat. The 🤖 AI badge
does not group: it moved onto the bubble itself, trailing the message
text, so every cast message still carries it exactly as before. That is
the product's ethic and it does not bend for layout.

The cast rail dropped the four-times-repeated "watching" and the heavy
ring around every tile; a ghost's hue is still unmistakably theirs, and
the loud treatment is saved for the one that is actually doing
something. Rail status is one line that cannot reflow the tiles beneath
it.

Also fixed: "Say something" wrapped onto two lines next to Pause; the
status orbs dropped their labels below 1180px and left four unreadable
coloured dots behind, which is status carrying meaning in colour alone;
cost printed three decimals, and rounded anything under a cent to
$0.00; and the empty state was a small mark floating in a large void.
Moments lead with ✨ as the icon table says they should. The page's
private copies of .seg and .kbd are gone in favour of the shared ones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
The copy explains too much of the wrong thing. Not the app-specific
parts -- that speech-to-text invents filler during silence, or that image
cost scales with resolution, are things a smart user cannot guess -- but
the sentences that explain nothing: "fill in the one that matches your
mode", "paste the path -- either works", "read these out loud at your
normal streaming volume", and "applies live on save" repeated down a
dozen rows until it reads as noise.

Principle 11 makes the rule explicit rather than fixing the instances
and letting them grow back: explain what is specific to this app or
genuinely surprising, and nothing else; say it once; treat reassurance
as padding. The copy passes that follow are held to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
The wizard told a new user the price of the Claude API three times on one
screen -- in the provider dropdown, in the hint under the key field, and
again in "what does this cost me?" -- and promised that everything is
changeable later four separate times. The deck's tooltip on a
transcribed line stated one fact twice and then reassured them about it.

32 strings tightened, 5 deleted. Gone: "Just looking?", "Good pick --",
"100% locally", "Entirely optional, and read-only either way", a
title="Command palette" on a button labelled Commands, and a note
explaining that "Finish setup" finishes setup.

What stayed is what a smart user cannot guess: that an outdated
LocalVocal can crash OBS, that screenshots go to the brain provider and
nowhere else, that Twitch access never touches their chat or metrics. The
local-model note actually got a fact it was missing while getting
shorter -- nothing leaves your PC, but the model shares your GPU with the
game -- because that trade-off is the whole decision and the wizard
wasn't saying it.

The pitch under the title now shows on step one only. It is there to sell
the doorway; from step two the rail says where you are, and the line is
one the reader has already read five times.

The overlay needed nothing: its only prose is the watermark, which is
fixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
41 strings tightened, 5 deleted. The deletions are all the same shape: a
hint that restated the label above it or the two field labels below it,
and "fill in the one that matches your mode", which tells a reader
nothing they cannot see.

"Applies live on save" appeared three times plus its own section
heading. It survives once, on the Behavior card where cadence tuning
makes it the point, and Word fixes gets the better fact instead -- live
from the next line you speak, which is when it actually bites.

The text-to-speech "why?" repeated the SAPI-versus-browser distinction
verbatim from the hint two rows below it; it now carries only what is
unique to it. The game screen guide opened by re-explaining its own
label and then said the same thing twice more; it opens on the concrete
list now and keeps every real fact.

Two things were kept against the brief, deliberately. "Either way they
read it: unanswered speech is still in the transcript" stays, compressed
to a clause -- without it a reader concludes that ticking the box makes
the cast deaf, which is wrong and changes the setting they pick. And the
engine-mode why stays long because every clause in it is behaviour you
cannot infer from two fields.

The About tab is untouched, as is every line about where the API key
lives, audio never leaving the machine, and Twitch being read-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWa54BF5RjHWC5GFaENZMM
@MyNamesEMurray
MyNamesEMurray merged commit b7077eb into main Sep 7, 2026
2 checks passed
@MyNamesEMurray
MyNamesEMurray deleted the claude/live-mic-transcription-accuracy-x5ptio branch September 7, 2026 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants