Files
LlamaCasty/TASKS/task-42-top-bar-redesign.md
gramps 3a4e17c735 Account zone is world-independent: avatar stays in Record world
Creator on the live build: 'why do you make the youtube user icon go away?'
The TASK 42 one-surface bar had scoped the account zone to Stream only
('Record world has no YouTube identity at all'), so arming REC dropped the
avatar and the bar height bounced with it ('really fucking annoying').

Fix: ShowSignInButton / ShowAvatarButton are now plain !IsConnected /
IsConnected — app-level identity, present in both worlds, so the right
column never pops in/out and the bar height stays pinned. Test remains a
child of Stream (still stream-armed AND signed-in).

TopBarModeTests updated to the new contract: signed-out Record world shows
Sign In; flipping back to REC keeps the avatar. PillRadioTests untouched
(it only exercises the ON-AIR face). Docs corrected in-place (ai.md state
model, TASK 42 scope). Gate: clean build 0-warnings, 316/316, scope passed.
2026-09-23 16:53:25 -07:00

4.4 KiB

TASK 42 — Top bar redesign (shipped 2026-09-22)

One work unit, no sub-tasks (creator directive: "forget this one dog plan bullshit. Our one plan is replacing the top menu bar with something usable"). Good Dog = ONE integration test: ytLive.Tests/TopBarModeTests.cs.

The problem

The old bar packed three widget grammars (sliding pills = intent, dots/badges = state, buttons = action), a hidden sign-in context menu, an account status dot that collided with live red, six simultaneous "you're running" signals, and a dead pieces (PRIVATE chip — dev-phase forced option, becomes real at ship; status light whose pulsing spec never shipped). The creator: "I wrote the fucking thing and I still can't figure-out how to do stuff."

The product model (locked in conversation)

The bar is ONE surface rendered by the FIRST decision — Record or Stream — each world is the whole bar; the other world never exists on screen.

  • Mode switch: one segmented REC|ON-AIR toggle (former two pills are a binary, radio-exclusive decision). Going back = one tap on the other segment. Idle-only; while running the switch retires.
  • Record world: switch + Start Recording. No Test — stream actions don't exist here. The account zone IS present (2026-09-23 correction, creator "why do you make the youtube user icon go away?"): the avatar / Sign In are world-independent, keeping the right column — and the bar height — pinned.
  • Stream world: switch + Go Live + Test and the account zone on the right (Sign In while signed-out — the pending sequence step rendered exactly when needed; the avatar once connected, right-click = Change Account/Logout).
  • Test is a child of Stream (creator ruling): it procs only when ON-AIR is armed AND signed in — you can't dry-run a recording.
  • Running: the worlds retire; one reality line ● REC|LIVE|TEST 00:12:34 (green/red/gold) + End. Full-bar color flip stays.
  • Gear moved up from the bottom bar to ~3 wordmark-letters past the brand (task 40 Unit B spec): one menu — App Settings / Report Bug / Feature Request / About. Removed from BottomBar.
  • Sign-in flow: Stream → (Sign In appears, or avatar confirms the account) → Go Live → Stop. Account switch = one re-auth → Google's own chooser pre-selected (no accounts-list API exists; login_hint+select_account is the source of truth).

Files (declared scope)

  • Themes/Controls.xaml — SegmentToggle / SegmentLabel (segmented switch)
  • Controls/TopBar.xaml(.cs) — full rewrite; GearButton_Click moved in
  • Controls/BottomBar.xaml(.cs) — gear removed
  • ViewModels/MainViewModel.Streaming.cs — world/reality props + RaiseTopBarModes() (called from StreamStatus/IsRecording/IsTestStream/pill setters); PrimaryStartButtonLabel = "Start Recording"/"Go Live"; ShowTestButton/CanStartTest = stream world AND connected
  • ViewModels/MainViewModel.Streaming.Operations.cs — BeginTestStream arms OnAirPillOn explicitly (the TASK 41 pipeline bug: a test that never armed the pill booted a pump with zero encoder outputs → "At least one output is required")
  • ViewModels/MainViewModel.Account.cs — IsConnected raises RaiseTopBarModes()
  • ytLive.Tests/PillRadioTests.cs / TestStreamTests.cs — updated to the new gates (label + middle-mover + Test-on-Stream)
  • ytLive.Tests/TopBarModeTests.cs — NEW Good Dog
  • Docs: ai.md, ViewModels/index.md, Controls/index.md, TASKS.md, HANDOFF.md

Build / test

Main + tests build clean, 0 warnings; full-suite run: 314/315 pass — the single abort is the known environment-dependent LayerReorderPersistenceTests.RealMouseDrag (Path of Exile 2 was open; its injected physical mouse events no-op when a game window interferes — documented in the test's own docstring). 314 prior tests + the new Good Dog, all green; the drag test passes on a clean desktop. Test environment note: a dev machine may hold a real saved OAuth session that loads synchronously in the ctor — signed-in-world assertions must force IsConnected first (the record of that trap lives here and in the tests).

Not in this task (carried)

  • App Settings round (TASK 40) — camera picker, defaults, accent units still queued; its Unit B (gear) is now DONE early by this task; BottomBar gear already removed.
  • Account-confirm via Google chooser is behavior folded into Sign In (no new code).