diff --git a/HANDOFF.md b/HANDOFF.md index 719cedf..eb511f1 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -7,19 +7,26 @@ Pushed state at `197ee81` (36 commits ahead locally — push only at a sub-milestone / on the user's say-so). -**Working tree DIRTY — two finished hotfix work units from the live build #77 session:** +**Working tree DIRTY — account zone made world-independent (creator 2026-09-23):** ``` - M Controls/PreviewPane.xaml (audio-sync status dot removed + YPP standing row) - M Themes/Controls.xaml (IntToSyncBrush converter resource removed) - D Helpers/IntToSyncBrushConverter.cs (converter dead — deleted) - M Services/ChannelStatsService.cs (YPP auditDetails part dropped; 403 body logged) - M Services/YppTrackerViewModel.cs (standing flags removed; honest deep-link text) - M ytLive.Tests/YppPullOutTests.cs (fixture loses the standing flags) -?? ytLive.Tests/ChannelStatsServiceTests.cs (NEW Good Dog: no auditDetails in URL + parses) - M ai.md, TASKS/task-22, TASKS/task-39, TASKS.md, MyMistakes.md, HANDOFF.md + M ViewModels/MainViewModel.Streaming.cs (ShowSignInButton = !IsConnected, ShowAvatarButton = IsConnected) + M ytLive.Tests/TopBarModeTests.cs (REC-world asserts: avatar stays, Sign In visible signed-out) + M ai.md, TASKS/task-42, HANDOFF.md ``` +Previous two hotfix units (audio-sync dot + YPP 403/statistics-string) are COMMITTED (`cb75066`); +top-bar feedback COMMITTED (`d72949e`). + +## ⚠️ The account-zone unit (this turn) + +Creator, clicking RECORD 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 the Stream world ("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 world-independent (`!IsConnected` / +`IsConnected`) — the right column never pops in/out, bar height pinned. Test stays a child of Stream. +`TopBarModeTests` updated: signed-out Record world shows Sign In; flipping back to REC keeps the avatar. + ## ⚠️ The app is RUNNING, and that's the story of this round Ac6e67a **could not be verified through the normal gate** because the user was *looking at the @@ -83,17 +90,11 @@ the copy stage with XAML/markup compiling clean (only the 2 copy errors → code ## Next step -1. **Close the running app (PID 17752 holds the exe), then run the full gate** — - `./scripts/verify.sh` with the full declared scope (audio-dot unit + YPP fix): clean build - 0-warnings + full suite + scope check. Then commit BOTH units — they share PreviewPane.xaml / - ai.md, so a single hotfix commit ("build-77 polish + YPP refresh 403") is the honest shape, or - two commits if the user prefers strict splits. - - ⚠️ The YPP Good Dog (`ChannelStatsServiceTests`) and the updated `YppPullOutTests` cannot even - BUILD until the app closes (the app csproj copy fails on the locked exe) — they were written but - not yet executed. - -2. After the gate: push decisions pending (user's call). TASK 40 Unit A next. +1. **Run the gate on the account-zone unit** (declared scope: `ViewModels/MainViewModel.Streaming.cs`, + `ytLive.Tests/TopBarModeTests.cs`, `ai.md`, `TASKS/task-42-top-bar-redesign.md`, `HANDOFF.md`) — + `./scripts/verify.sh`: clean build 0-warnings + full suite + scope check. Then commit it + ("account zone is world-independent — avatar stays in Record world", code + docs in one). + Repo is 36 commits ahead of pushed `197ee81`; push decision is the creator's (sub-milestone). ## Critical working rules (unchanged, still binding) diff --git a/TASKS/task-42-top-bar-redesign.md b/TASKS/task-42-top-bar-redesign.md index 6183818..8e13490 100644 --- a/TASKS/task-42-top-bar-redesign.md +++ b/TASKS/task-42-top-bar-redesign.md @@ -21,7 +21,10 @@ 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.** +- **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). diff --git a/ViewModels/MainViewModel.Streaming.cs b/ViewModels/MainViewModel.Streaming.cs index 3184acb..d022b65 100644 --- a/ViewModels/MainViewModel.Streaming.cs +++ b/ViewModels/MainViewModel.Streaming.cs @@ -195,21 +195,22 @@ public partial class MainViewModel : ViewModelBase public bool RunningVisible => IsLive || IsRecording; /// Record world: the switch sits on REC and Go says "Start Recording". - /// No account zone, no Test — YouTube doesn't exist here. + /// No Test — YouTube's stream actions don't exist here. The account zone IS + /// here: it's app-level identity, not a stream-world widget (creator 2026-09-23). public bool ShowRecordAction => ShowIdleWorld && !OnAirPillOn; /// Stream world: the switch sits on ON-AIR; Go says "Go Live" and Test - /// appears. The account zone belongs to this world because login is a step in - /// THIS sequence. + /// appears. public bool ShowStreamActions => ShowIdleWorld && OnAirPillOn; - /// Sign In appears on the right only inside the Stream world, signed-out - /// — the pending step of the stream sequence, made visible exactly when it's next. - public bool ShowSignInButton => ShowStreamActions && !IsConnected; + /// Account zone is WORLD-INDEPENDENT (creator 2026-09-23): Sign In / + /// avatar stay put in Record and Stream worlds alike, so the right column never + /// pops in and out — which pins the bar height (no more bounce between worlds). + public bool ShowSignInButton => !IsConnected; - /// The avatar confirms WHICH account is about to stream — only in the - /// Stream world, signed-in. Right-click for Change Account / Logout. - public bool ShowAvatarButton => ShowStreamActions && IsConnected; + /// The avatar — right-click Change Account / Logout. Always visible when + /// signed in, Record or Stream. + public bool ShowAvatarButton => IsConnected; /// The reality word: TEST while a test broadcast runs, LIVE while on the /// air, REC during a local-record session. diff --git a/ai.md b/ai.md index 71a69a4..d4f999b 100644 --- a/ai.md +++ b/ai.md @@ -639,10 +639,13 @@ lights when a session is actually running. `RaiseTopBarModes()`). The old sign-lights + two pill toggles + status dot + PRIVATE/TEST chips are gone; the mode is one **segmented REC|ON-AIR switch** (`SegmentToggle`/`SegmentLabel` styles), and "going back" is one tap on the other segment. Idle worlds: Record = switch + "Start Recording" - (`PrimaryStartButtonLabel` names the armed world) and **no YouTube identity at all**; Stream = switch + (`PrimaryStartButtonLabel` names the armed world); Stream = switch + "Go Live" + Test (Test is a **child of Stream** — visible only stream-armed AND signed-in) plus the - account zone on the right (`ShowSignInButton` = stream world signed-out; `ShowAvatarButton` = the - avatar, right-click Change Account/Logout). Running retires the switch and worlds; one reality line + account zone on the right. **The account zone is WORLD-INDEPENDENT (2026-09-23):** + `ShowSignInButton` = signed-out and `ShowAvatarButton` = the signed-in avatar, in Record and Stream + worlds alike — it's app-level identity, and keeping it put pins the bar height (it no longer pops in + and out between worlds). Right-click the avatar = Change Account/Logout. Running retires the switch + and worlds; one reality line `● word elapsed` + End replace them (`RunningVisible`/`RunningText`/`RunningDotBrush` — TEST/LIVE/REC, gold/red/green). The account **status dot is gone** — the avatar/Sign In pair carries connectedness. `ShowPrimaryStartButton` remains the always-idle face. `StopStream` clears both pills (see "Stop ends diff --git a/ytLive.Tests/TopBarModeTests.cs b/ytLive.Tests/TopBarModeTests.cs index f74a4bf..b674288 100644 --- a/ytLive.Tests/TopBarModeTests.cs +++ b/ytLive.Tests/TopBarModeTests.cs @@ -10,11 +10,12 @@ namespace ytLive.Tests; /// /// Top bar redesign — the bar is ONE surface rendered by the FIRST decision -/// (Record or Stream). Each world is the whole bar: the Record world has no -/// YouTube identity at all, the Stream world owns the account zone (Sign In or -/// avatar) plus Test. The switch is a two-position gate, so "going back" is one -/// tap on the other segment. While running, the switch retires and a single -/// reality line (dot + word + elapsed) + End replace it. +/// (Record or Stream). Each world is the whole bar: Test is a child of Stream, +/// but the account zone (Sign In / avatar) is WORLD-INDEPENDENT (creator +/// 2026-09-23) so the right column never pops in and out and the bar height +/// stays pinned. The switch is a two-position gate, so "going back" is one tap +/// on the other segment. While running, the switch retires and a single reality +/// line (dot + word + elapsed) + End replace it. /// [Collection("RealApp")] public sealed class TopBarModeTests @@ -45,11 +46,12 @@ public sealed class TopBarModeTests vm.IsConnected = false; // Default (nothing armed) = the Record world: Go says "Start Recording", - // no account zone, no Test — YouTube doesn't exist in this world. + // no Test — but the account zone is app-level and stays (signed out → + // Sign In visible even here; no avatar). Assert.True(vm.ShowIdleWorld); Assert.True(vm.ShowRecordAction); Assert.False(vm.ShowStreamActions); - Assert.False(vm.ShowSignInButton); + Assert.True(vm.ShowSignInButton); Assert.False(vm.ShowAvatarButton); Assert.False(vm.ShowTestButton); Assert.Equal("Start Recording", vm.PrimaryStartButtonLabel); @@ -72,11 +74,12 @@ public sealed class TopBarModeTests Assert.True(vm.ShowAvatarButton); Assert.True(vm.ShowTestButton); - // Going back is one tap: flipping REC returns the bar to the Record world. + // Going back is one tap: flipping REC returns the bar to the Record world — + // the avatar STAYS (world-independent account zone), only Test retires. vm.RecordPillOn = true; Assert.False(vm.ShowStreamActions); Assert.True(vm.ShowRecordAction); - Assert.False(vm.ShowAvatarButton); + Assert.True(vm.ShowAvatarButton); Assert.False(vm.ShowTestButton); Assert.Equal("Start Recording", vm.PrimaryStartButtonLabel);