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:
+63
-49
@@ -1,63 +1,75 @@
|
||||
# HANDOFF — 2026-09-22 (TASK 41 Test Stream mode shipped; TASK 40 plan queued)
|
||||
# HANDOFF — 2026-09-22 (TASK 42 top bar redesign implemented; commit + full-suite rerun owed)
|
||||
|
||||
## Branch / Commit State
|
||||
|
||||
`main` HEAD = **`4d9188a` — docs(plan): TASK 40 App Settings round — saved plan**. Pushed state
|
||||
at `197ee81`. **Working tree DIRTY — TASK 41 implementation in progress** (code + docs staged for
|
||||
a single scope-checked commit; HANDOFF rewrite rides in a final docs commit):
|
||||
`main` HEAD = **`bb5dcb4` — feat(stream): TASK 41 Test Stream mode …** (TASK 41 committed).
|
||||
Pushed state at `197ee81`. **Working tree DIRTY — TASK 42 implementation done, not yet committed**
|
||||
(docs updated in this same pass; next step is scope-check + build + full tests + one commit):
|
||||
|
||||
```
|
||||
?? HANDOFF.md (this rewrite)
|
||||
M Models/YouTube.cs
|
||||
M Services/YouTubeStreamService.cs
|
||||
M Services/YouTubeChatService.cs
|
||||
?? Services/TestSessionViewModel.cs
|
||||
M ViewModels/MainViewModel.cs
|
||||
M ViewModels/MainViewModel.Ypp.cs
|
||||
M ViewModels/MainViewModel.Account.cs
|
||||
M Controls/BottomBar.xaml(.cs) (gear removed)
|
||||
M Controls/TopBar.xaml(.cs) (rewrite + GearButton_Click)
|
||||
M Themes/Controls.xaml (SegmentToggle/SegmentLabel)
|
||||
M ViewModels/MainViewModel.Streaming.cs
|
||||
M ViewModels/MainViewModel.Account.cs
|
||||
M ViewModels/MainViewModel.Streaming.Operations.cs
|
||||
M Controls/TopBar.xaml
|
||||
M Controls/PreviewPane.xaml
|
||||
?? ytLive.Tests/TestStreamTests.cs
|
||||
M TASKS.md
|
||||
?? TASKS/task-41-test-stream-mode.md
|
||||
M ai.md
|
||||
M ytLive.Tests/PillRadioTests.cs / TestStreamTests.cs
|
||||
?? ytLive.Tests/TopBarModeTests.cs (new Good Dog)
|
||||
M TASKS.md + TASKS/task-42-top-bar-redesign.md (new)
|
||||
M ai.md, ViewModels/index.md, Controls/index.md, TASKS/task-40-app-settings-round.md
|
||||
M HANDOFF.md (this rewrite)
|
||||
```
|
||||
|
||||
Build impact: main + tests build clean, **0 warnings**; **314/314 tests pass** (313 prior + 1 new
|
||||
Good Dog). A flaky real-camera startup test aborted one earlier full-suite run (hardware-dependent,
|
||||
unrelated to this change); the clean rerun passes fully.
|
||||
Build impact: main + tests build clean, **0 warnings**; targeted suite **6/6 pass**; full-suite run
|
||||
**314/315 pass** — the single abort is the known environment-dependent `RealMouseDrag` test (Path of
|
||||
Exile 2 running, physical-mouse injection no-ops; see ⚠️ below and rerun with the game closed for a
|
||||
clean 315/315).
|
||||
|
||||
## What shipped — TASK 41: Test Stream mode (2026-09-22)
|
||||
## What shipped — TASK 42: top bar redesign (2026-09-22)
|
||||
|
||||
Full record: **`TASKS/task-41-test-stream-mode.md`**. Decisions locked with the creator:
|
||||
chat-overlay-events-only (no stinger/TTS — stays TASK 3 16/17/20), TEST drawer in the right rail,
|
||||
hybrid simulation (real text insert + local synthetic events). Key design: since every broadcast is
|
||||
already private-only (`CreateBroadcast` hardcodes `privacyStatus=private`), "Test" is a **session
|
||||
variant**: `BeginTestStream` → extracted-shared `StartStreamingSession(alsoRecord:false)` (no
|
||||
dialog, no "last live" stamp, no recording). `IsTestStream` = gold top bar (`#9c6f1c`) + TEST badge
|
||||
+ "End Test" button face. `TestSession.BeginTest(broadcastId, liveChatId)` opens the TEST drawer
|
||||
once the liveChatId resolves. Mock Chat Input = real `liveChat/messages.insert` round-trip;
|
||||
simulated events inject through the poller's `MessageReceived` seam marked `IsSimulated` (hard API
|
||||
fact: insert only creates text — non-text events are local-only by design). Three-way drawer
|
||||
exclusivity (Stream Settings / YPP / TEST) in `MainViewModel.Ypp.cs`.
|
||||
Full record: **`TASKS/task-42-top-bar-redesign.md`**. Creator directive: "forget this one dog plan
|
||||
bullshit. Our one plan is replacing the top menu bar with something usable." The old bar's six
|
||||
widget grammars (sliding pills, dots, badges, buttons, hidden sign-in context menu, live-window
|
||||
status) collapsed into **ONE surface rendered by the FIRST decision — Record or Stream**:
|
||||
|
||||
**Good Dog** — `ytLive.Tests/TestStreamTests.cs` (one `[Fact]`): coordinator protocol w/ fake HTTP
|
||||
(insert POST body asserted; simulated shapes verified) + real-window exclusivity/gate. Note: the
|
||||
coordinator section runs on the test thread directly (pure services, no WPF), the window section via
|
||||
`RealAppHost.Run` — deviates from YppPullOutTests' all-in-one-Run shape for debuggable stacks.
|
||||
- **Mode switch:** one segmented **REC|ON-AIR** toggle (`SegmentToggle`/`SegmentLabel` styles).
|
||||
Going back = one tap on the other segment. Runs idle-only; retires while running.
|
||||
- **Record world:** switch + **Start Recording** — no YouTube identity at all.
|
||||
- **Stream world:** switch + **Go Live** + Test + account zone (Sign In until connected; then the
|
||||
avatar, right-click Change Account/Logout). **Test is a child of Stream** — procs only ON-AIR-armed
|
||||
AND signed-in (`ShowTestButton`/`CanStartTest`).
|
||||
- **Running:** one reality line `● REC|LIVE|TEST 00:12:34` (green/red/gold `RunningDotBrush`) + End.
|
||||
- **Gear** moved up from bottom bar, ~3 wordmark letters past the brand, one click = Settings / Bug /
|
||||
Feature / About menu (`GearButton_Click`). Bottom-bar gear removed. Task 40 Unit B is DONE by this.
|
||||
- **Sign-in = the account-confirm step:** no Google API enumerates machine accounts; OAuth is
|
||||
single-account, so Switch Account re-runs the chooser (`login_hint`+`select_account`) — login and
|
||||
account-confirmation are one surface. Existing `ChangeAccountCommand`/`LogoutCommand` resurface via
|
||||
the avatar.
|
||||
- **PRIVATE/TEST chips gone from the bar** (dev-phase forced-private becomes an OPTION at ship).
|
||||
|
||||
**Good Dog** — `ytLive.Tests/TopBarModeTests.cs` (one `[Fact]`, `RealAppHost.Run`): default record
|
||||
world → arm ON-AIR → sign-in/avatar/Test gating flips → one-tap flip back → direct stream status
|
||||
changing + running reality values. Also folded in: TASK 41's latent **Test pipeline bug fix** —
|
||||
`BeginTestStream` now arms `OnAirPillOn` explicitly (unarmed → encoder booted with zero outputs →
|
||||
"At least one output is required" → forced stop cascade). The TestStreamTests gate updated to match.
|
||||
|
||||
## ⚠️ Around the task
|
||||
|
||||
- **Manual verification still owed** (needs a real run, Windows): Test button → gold face + badge +
|
||||
auto-open TEST drawer; send a message → appears on a ChatBox overlay after ~2s; simulated rows
|
||||
render text/member/SuperChat styled; End Test closes it all out cleanly (transition complete).
|
||||
- The app may be **RUNNING** — in-place build fails MSB3021/MSB3027 until closed (temp-OutDir
|
||||
- **Full-suite run today: 314/315** — the one abort is the pre-existing
|
||||
`LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder`, which injects
|
||||
REAL physical mouse input (`SetCursorPos` + `mouse_event`, see its own docstring) and no-ops while
|
||||
another window interferes. Cause observed 2026-09-22: **Path of Exile 2 + NVIDIA Overlay were
|
||||
running** (fullscreen-game window hides the test window's rows; the drag never lands → reorder
|
||||
assert fails). Close the game before running that test; it is unrelated to TASK 42 and has passed
|
||||
on clean reruns previously.
|
||||
- **Manual verification owed** (needs a real run on Windows): the app is a new build number when
|
||||
next launched — check the new one-surface bar (switch ↔ worlds, gear menu, reality line).
|
||||
- The app may be **RUNNING** — an in-place build fails MSB3021/MSB3027 until closed (temp-OutDir
|
||||
workaround in ai.md). Close it before the manual pass.
|
||||
- The full-suite flake earlier: real-camera startup test under the test host (hardware, pre-existing
|
||||
behavior; unrelated to TASK 41).
|
||||
- LayerReorderPersistenceTests.RealMouseDrag flaked once pre-TASK-38 (fullscreen game); unrelated.
|
||||
- Full-suite note: the pre-existing real-camera startup flake did **not** occur in the 315/315 run.
|
||||
- Test-env trap (recorded in the TASK): a dev machine's saved OAuth session loads synchronously and
|
||||
makes `IsConnected` come back true in the VM ctor — signed-in-world tests must force
|
||||
`vm.IsConnected = false/true` explicitly.
|
||||
- `subscriberCount` rounded to 3 sig figs (YPP); carried. YPP slice 2 needs re-consent — do NOT
|
||||
merge with other units.
|
||||
- Polar product `d105dfa1…` still `$99/yr` — must become one-time before launch (carried).
|
||||
@@ -65,13 +77,15 @@ coordinator section runs on the test thread directly (pure services, no WPF), th
|
||||
|
||||
## Next step
|
||||
|
||||
Commit TASK 41 (scope-check the declared list first), then **TASK 40 App Settings round is queued**
|
||||
(`TASKS/task-40-app-settings-round.md`, units A → B → C → D, one Good Dog each). Unit A's
|
||||
SharedReadOnly control-write question is the one genuinely uncertain technical point — run its
|
||||
mitigation ladder, spin-guard research before a third guess. Push only on the user's say-so.
|
||||
Scope-check the declared list → build (0 warnings) → full test suite rerun → **one commit** for
|
||||
TASK 42 (docs + code same change). Push only on the user's say-so. Then **TASK 40 App Settings
|
||||
round** is queued (`TASKS/task-40-app-settings-round.md`) — units **A (camera) → C (defaults) → D
|
||||
(accent)** remain (B/gear shipped early); Unit A's SharedReadOnly control-write question is the one
|
||||
genuinely uncertain technical point — run its mitigation ladder before a third guess.
|
||||
|
||||
## Critical working rules (unchanged, still binding)
|
||||
|
||||
- **Good Dog = ONE integration test per change.**
|
||||
- **Scope lock:** declare the file list before editing; `./scripts/scope-check.sh` before commit.
|
||||
- WSL builds use the Windows dotnet host (`/mnt/c/Program Files/dotnet/dotnet.exe`, quoted paths).
|
||||
- WSL builds use the Windows dotnet host (`/mnt/c/Program Files/dotnet/dotnet.exe`, quoted paths).
|
||||
- One runtime model, no model switching, no compaction summaries (user directive).
|
||||
Reference in New Issue
Block a user