From d90b5ded0d25509e416c588a84ab0eb1fce6d874 Mon Sep 17 00:00:00 2001 From: gramps Date: Thu, 13 Aug 2026 18:50:56 -0700 Subject: [PATCH] =?UTF-8?q?TASK=207=20UI=20polish=20batch:=20scene/source?= =?UTF-8?q?=20row=20cleanup,=20dedup=20naming,=20full=20social=20handles?= =?UTF-8?q?=20=E2=80=94=20scenes=20are=20now=20pure=20selection=20rows=20(?= =?UTF-8?q?edit/trash/visibility=20icons=20and=20inline=20rename=20removed?= =?UTF-8?q?=20with=20EditSceneCommand/RemoveSceneCommand/ToggleSceneVisibi?= =?UTF-8?q?lityCommand=20+=20Scene.IsEditing);=20source=20rows=20gained=20?= =?UTF-8?q?the=20trio=20(new=20EditElementCommand=20=E2=86=92=20inline=20r?= =?UTF-8?q?ename=20via=20SceneElement.IsEditing,=20ToggleElementVisibility?= =?UTF-8?q?Command=20=E2=86=92=20eye=20flips=20IsVisible,=20open/slashed?= =?UTF-8?q?=20style=20rebound,=20hidden=20rows=20dim=20to=2045%);=20duplic?= =?UTF-8?q?ate=20resource=20names=20get=20a=20no-space=20incrementing=20su?= =?UTF-8?q?ffix=20via=20shared=20NextSourceName=20(Image,=20Image2,=20Imag?= =?UTF-8?q?e3=E2=80=A6)=20derived=20from=20actual=20names=20so=20deletions?= =?UTF-8?q?=20never=20collide=20(AddSource=20+=20AddReusedImage);=20social?= =?UTF-8?q?=20bar=20renders=20the=20full=20validated=20handle=20(MaxWidth?= =?UTF-8?q?=3D200=20+=20TextTrimming=20removed=20from=20SocialBarRenderer?= =?UTF-8?q?=20and=20the=20preview=20template);=20side=20panels=20stay=20fi?= =?UTF-8?q?xed=20220/300;=20focus-loss=20capture=20lag=20documented=20in?= =?UTF-8?q?=20ai.md=20as=20a=20known=20OS=20limit=20(deferred)=20=E2=80=94?= =?UTF-8?q?=20the=20two=20real-App=20tests=20now=20share=20RealAppHost,=20?= =?UTF-8?q?a=20dedicated=20STA=20thread=20owning=20the=20single=20WPF=20Ap?= =?UTF-8?q?p,=20instead=20of=20each=20calling=20new=20App();=20new=20Sourc?= =?UTF-8?q?eNamingTests=20integration=20test=20(Text/Text2/Text3,=20delete?= =?UTF-8?q?-middle=20re-add=20no-collision)=20=E2=80=94=20170=20tests=20pa?= =?UTF-8?q?ssing,=200=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HANDOFF.md | 84 +++++++------- MainWindow.xaml | 127 ++++++++++------------ MainWindow.xaml.cs | 16 +-- Models/Scene.cs | 7 -- Models/SceneElement.cs | 3 + Services/Compositor/SocialBarRenderer.cs | 2 - TASKS.md | 20 ++++ ViewModels/MainViewModel.cs | 60 +++++----- ViewModels/index.md | 2 +- ai.md | 8 ++ ytLive.Tests/RealAppCollection.cs | 83 ++++++++++++++ ytLive.Tests/RoundClipInteractionTests.cs | 30 ++--- ytLive.Tests/SourceNamingTests.cs | 65 +++++++++++ 13 files changed, 333 insertions(+), 174 deletions(-) create mode 100644 ytLive.Tests/RealAppCollection.cs create mode 100644 ytLive.Tests/SourceNamingTests.cs diff --git a/HANDOFF.md b/HANDOFF.md index 8e9b271..7782556 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -7,34 +7,39 @@ ## Session state (last updated: 2026-08-13) -- **Branch:** `main`. The audio UX follow-up (`9f9ed34`), round 2 (`18abe42`), - and round 3 (`dc29adf`) are **committed and pushed**. -- **This session (TASK 4 ship step 6 — health stats in the bottom bar):** - 1. **Wiring:** `MainViewModel.OnFramePumpHealthUpdated` subscribes to - `FramePump.HealthUpdated` and marshals onto the UI thread (the encoder's - stderr loop raises on a background thread — same pattern as the audio - level handlers), copying the parsed bitrate/FPS/dropped/duration into - `CurrentHealth`, which the bottom bar's left stats group already binds - (bitrate/FPS/dropped/duration/health-message). - 2. **Session hygiene:** `ResetHealth(status)` zeroes dropped frames + the - elapsed duration (and clears `LastError`) on go-live and on End so stats - never linger from a previous stream; bitrate/FPS stay on the tier's - targets from `ApplyStreamQuality`. - 3. **Reality:** the bar lights up with REAL encoder values once TASK 5 fills - `_rtmpUrlProvider` with the reusable stream's ingest URL — until then the - pump skips the encoder (logged) and the bar shows the tier's targets. - No new tests needed: `FramePumpTests.HealthUpdated_ForwardsEncoderHealth` - already covers the pump→event seam; the VM handler is a thin marshal+copy. -- **Mic status contract (creator's rule, verified — do NOT "fix"):** the status dot is - **red until a mic resource is actually connected**. `MicStatus` starts `NotConnected` - (red); it goes green ONLY when the mixer's `MicConnected` fires, which comes strictly - from the mic source's `Started` event raised after `StartRecording()` succeeds. Zero - devices at startup → stays red and the mixer is never started; capture failure → yellow. - Green must never be raised earlier (e.g. on loopback start or on `Start()` being called). -- **Uncommitted:** this session — `ViewModels/MainViewModel.cs` (health wiring + - `ResetHealth`), `TASKS.md` (ship step 6 ✅), `ai.md`, `ViewModels/index.md`, `HANDOFF`. -- **Verified:** pending (build + full test run right before commit). +- **Branch:** `main`, in sync with `origin/main`. +- **This session (TASK 7 — UI polish batch, gramps's 6-point review):** + 1. **Scenes list cleaned:** the per-row edit/trash/visibility icons and the inline + rename TextBox are gone. Scenes are pure selection rows; `IsHidden` stays + persisted and still dims a hidden row to 45%. Removed dead surface: + `EditSceneCommand`/`RemoveSceneCommand`/`ToggleSceneVisibilityCommand` + + `BeginEditScene`/`ToggleSceneVisibility`/`RemoveScene` handlers + `Scene.IsEditing`. + 2. **Sources list upgraded:** each row now has edit + visibility eye + trash. New + `EditElementCommand` (`SceneElement.IsEditing` → inline rename TextBox, Enter/Esc/ + lost-focus commits) and `ToggleElementVisibilityCommand` (flips `SceneElement.IsVisible`); + the eye style now binds `IsVisible` and hidden rows dim to 45%. + 3. **Duplicate naming:** shared `NextSourceName(scene, baseName)` → `Image`, `Image2`, + `Image3`… (no space), next free number derived from actual names so deletions never + collide. Used by both `AddSource` and `AddReusedImage`. + 4. **Social bar:** `MaxWidth=200` + `CharacterEllipsis` removed from BOTH + `SocialBarRenderer.cs` and the preview DataTemplate — full validated handle renders. + 5. **Panels:** left 220 / right 300 fixed widths are deliberate — panels never re-layout + on resize; the preview absorbs it. No change. + 6. **Focus-loss capture lag:** recorded in `ai.md` as a known OS limit (DWM/WGC + throttling when unfocused + GPU readback contention + the `_framePending` / + `DispatcherPriority.Render` gates). NOT an in-app throttle; deferred by user decision. +- **Test infra change:** the two real-WPF-App tests (round-clip + new source-naming) now + share `RealAppHost` — a dedicated STA thread owning the single `App` — via the `RealApp` + serial collection. WPF allows exactly one `Application` per AppDomain; never add a test + that calls `new App()` directly again — marshal onto `RealAppHost` instead. +- **Uncommitted:** `MainWindow.xaml` + `MainWindow.xaml.cs` (rows), `ViewModels/MainViewModel.cs` + (commands + naming), `Models/SceneElement.cs` (+`IsEditing`), `Models/Scene.cs` (−`IsEditing`), + `Services/Compositor/SocialBarRenderer.cs` (no truncation), `ytLive.Tests/SourceNamingTests.cs` + (new), `ytLive.Tests/RealAppCollection.cs` (new), `ytLive.Tests/RoundClipInteractionTests.cs` + (now uses the shared host), `TASKS.md` (TASK 7 ✅), `ai.md`, `ViewModels/index.md`, `HANDOFF`. +- **Verified:** build 0 warnings / 0 errors; **170/170 tests pass** (169 + the naming test). - **Landmines:** + - Never add another test that constructs `new App()` — use `RealAppHost.Run(...)`. - Never set a local `Canvas.SetTop` on the social bar — a local value permanently overrides `{Binding SocialBarTop}` (the `ClearValue` lesson from 5.5). - `AudioMixer` meter `Push` is unconditional **by design now**: `OnMicSample`/ @@ -44,7 +49,7 @@ - `MicConnected` comes from the source `Started` event, raised right after `StartRecording()` succeeds — tests must `MarkStarted()` the fake source before asserting connection state. - - The mic dot is red until a resource connects (see contract above) — green + - The mic dot is red until a resource connects (see contract below) — green only after `Started`, yellow on `Failed`. - Zero mic devices at startup = red dot AND the mixer is never started, so loopback + the game bar can't run either (no capture at all) — acceptable. @@ -56,17 +61,22 @@ - `StopAsync` must stop the encoder (closes stdin) **before** awaiting the pump loop — closing stdin unblocks a write stuck on pipe backpressure; the reverse order deadlocks. - - Tests never instantiate `MainViewModel` directly except the round-clip - integration test (a real `MainWindow`), which never goes live — keep it that way. + - Tests never instantiate `MainViewModel` directly except via a real `MainWindow` + on the `RealAppHost` STA thread (round-clip + naming), which never go live. - Sandbox can't reach outbound HTTPS — `HttpSocialValidator` stub-handler tests only, never the real instance. -- **Next step:** commit + push this ship-step-6 work (one commit). Then the remaining - TASK 4 requirement is **ship step 7 — one-click go live + private-only enforcement** - (honor the dialog's chosen visibility / enforce `privacyStatus = "private"`; also - fixes the REC sign's private state — see round-3 note above). Also queued: task 21 - (logo + About hub), task 22 (voice filters). Nothing else queued — do not expand - the task queue on your own. Optional, not queued: rewriting the healed entry's - `ProfileUrl` to `https://mastodon.llamachile.tube/@gramps` (user must say the word). +- **Mic status contract (creator's rule, verified — do NOT "fix"):** the status dot is + **red until a mic resource is actually connected**. `MicStatus` starts `NotConnected` + (red); it goes green ONLY when the mixer's `MicConnected` fires, which comes strictly + from the mic source's `Started` event raised after `StartRecording()` succeeds. Zero + devices at startup → stays red and the mixer is never started; capture failure → yellow. +- **Next step:** commit + push this TASK 7 batch (one commit). Then the remaining TASK 4 + requirement is **ship step 7 — one-click go live + private-only enforcement** (honor the + dialog's chosen visibility / enforce `privacyStatus = "private"`; also fixes the REC sign's + private state — see round-3 note). Also queued: task 21 (logo + About hub), task 22 (voice + filters). Nothing else queued — do not expand the task queue on your own. Optional, not + queued: rewriting the healed entry's `ProfileUrl` to `https://mastodon.llamachile.tube/@gramps` + (user must say the word). - **Secret/DB/port facts live:** OAuth client id/secret in `Helpers/OAuthCredentials.cs`; OAuth session token in `Helpers/TokenStore.cs` (DPAPI → `%APPDATA%\ytLlive\ytLlive.auth`); diff --git a/MainWindow.xaml b/MainWindow.xaml index 5d09d35..d46a109 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -49,21 +49,21 @@ - + @@ -193,64 +193,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - + @@ -287,6 +231,11 @@ @@ -295,6 +244,8 @@ + + @@ -310,9 +261,51 @@ + Padding="4,2" TextTrimming="CharacterEllipsis"> + + + + - + + + +