feat(ui): TASK 42 top bar redesign — one Record-or-Stream surface (2026-09-22)
The bar now renders ONE surface from the FIRST decision the creator makes — Record or Stream — with each world being the whole bar. Creator directive: "I wrote the fucking thing and I still can't figure-out how to do stuff", "forget this one dog plan bullshit". OBS/streaming-tool reference for the one-surface paradigm: https://obsproject.com (single mode toggle + context actions) — cited per spin-guard habit; the go-live/test pattern follows CEV (https://cev-desktop.aSean.xyz) precedent. - Mode: single segmented REC|ON-AIR switch (SegmentToggle/SegmentLabel in Themes/Controls.xaml); radio-exclusive world selection, one tap to flip. - Record world: switch + Start Recording, zero YouTube identity. - Stream world: switch + Go Live + Test + account zone right (Sign In until connected, then avatar with Change Account/Logout context menu). - Test is a child of Stream: procs only stream-armed AND signed-in. - Running: one reality line "dot word elapsed" (REC/LIVE/TEST; green/red/ gold) + End; worlds + switch retire. - Gear moved up from bottom bar, ~3 wordmark letters past the brand, single click opens Settings/Bug/Feature/About menu (TASK 40 Unit B shipped early). - Fix folded in: BeginTestStream now arms OnAirPillOn explicitly (unarmed pump booted with zero encoder outputs -> "At least one output is required" forced-stop cascade from TASK 41's pipeline). - VM: world/reality props + RaiseTopBarModes() wired into StreamStatus, pill, IsRecording, IsTestStream, IsConnected setters. Doc: ai.md top-bar model, ViewModels/index.md, Controls/index.md, TASKS.md, HANDOFF.md, new TASKS/task-42-top-bar-redesign.md; task-40 note. Tests: TopBarModeTests.cs (new Good Dog), PillRadioTests + TestStreamTests gates updated. Build 0 warnings; full suite 314/315 — single abort is the pre-existing env-dependent RealMouseDrag test (Path of Exile 2 running)
This commit is contained in:
@@ -91,12 +91,21 @@ drives `CameraConfigViewModel` — Auto + slider changes forward to the probe.
|
||||
|
||||
## Unit B — gear to top-left (your ask 2)
|
||||
|
||||
> ✅ **SHIPPED 2026-09-22** as part of TASK 42 (top bar redesign) — gear is top-right
|
||||
> of the brand on `Controls/TopBar.xaml`, single click opens the Settings/Bug/Feature/
|
||||
> About menu (`GearButton_Click` moved from BottomBar). See
|
||||
> [`task-42-top-bar-redesign.md`](task-42-top-bar-redesign.md).
|
||||
|
||||
- `Controls/TopBar.xaml`: `IconButton` gear **right of the brand Button**, gap ≈ 3 wordmark
|
||||
letters; left-click `OpenSettingsCommand`; ContextMenu keeps Report Bug / Feature
|
||||
Request / About. Remove the gear (+ its click handler) from `Controls/BottomBar.xaml(.cs)`.
|
||||
- Bonus: TopBar sits inside the overlay scrim span (`OverlayHost Grid.RowSpan=3` covers rows
|
||||
0–2), so the gear is correctly covered while an overlay is open — today it sits below the scrim.
|
||||
|
||||
> **Left as follow-ups** (not part of the shipped gear): the overlay scrim row-span bonus (still
|
||||
> open) — the new gear sits in `OverlayHost`'s Row 0 but the scrim rows are not yet spanned over
|
||||
> it. Revisit when rows are next touched.
|
||||
|
||||
## Unit C — the small defaults (your ask 3, rest)
|
||||
|
||||
- **Default output resolution** — persist `SelectedQuality` (e.g. Settings key
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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 YouTube identity at all.**
|
||||
- **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).
|
||||
Reference in New Issue
Block a user