Control Surface UX: pill toggle, background rename, per-scene properties

- Sliding pill toggle (green ON / red OFF) replaces checkbox
- Geometry mismatch error for custom backgrounds
- Backdrop -> Background: C# rename, SQL column rename with migration
- Source.IsBackground: new -> override (fixes WPF binding)
- Legacy 'Backdrop' display name migrated to 'Background'
- Per-scene UseDefaultBackground + CustomBackgroundPath
- Asset files renamed backdrop -> background
- StagedScene/LiveScene split, thumbnail strip, edit gating
- TransitionService (Cut/Fade/Move)
- Context menu: Capture Desktop, Refresh Desktop
- Resolution badge removed, ON-AIR status light added
This commit is contained in:
2026-08-22 18:10:59 -07:00
parent e06bfa42a8
commit 4b9a8e515f
35 changed files with 1406 additions and 717 deletions
+81 -24
View File
@@ -1,29 +1,86 @@
# HANDOFF — Session State
## Branch
`main` — clean, pushed (8951141)
`main` — clean, last push `8951141` (TASK 10 shipped). TRAX meter fix pushed `e06bfa4`.
All Control Surface UX work is **uncommitted** — build passes 0 warnings/0 errors.
## ⚠️ IN FLIGHT: repo-wide Backdrop→Background rename (multi-session)
Models already renamed (`Source.IsBackground`, `Scene.HasBackground`/`CustomBackgroundPath`,
`SceneCatalog.HasBackground`, `Source` enum `Background`). Status per file:
- ✅ `ViewModels/MainViewModel.cs` — full case-sensitive rename done; compiles clean.
- ✅ `MainWindow.xaml.cs` + `MainWindow.xaml` — renamed; full solution builds 0 warnings/0 errors.
- ✅ `Services/Compositor/SceneCompositor.cs` — CS0128 duplicate locals fixed.
- ⏳ `Services/LayoutStore.cs` — C# members renamed, but DB column strings
(`HasBackdrop`/`IsBackdrop`) are **schema** and intentionally still say Backdrop:
renaming them needs a real migration (new user_version) touching every existing DB.
The V5-backfill unit test's raw CREATE TABLE must keep `IsBackdrop` to match —
blanket-renaming it breaks `V5_Database_Backfill_Removes_NonLive_Backgrounds`
(verified: migration can't see flags, Live source loses `IsBackground`).
- ✅ `ytLive.Tests` — all 5 files blanket-renamed (class/method/var/comment names +
C# property accesses); `BackdropTests.cs` → `BackgroundTests.cs`. Tests: 207/208 pass;
the only red is pre-existing `AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`
(unrelated gain-math assert at AudioPipelineTests.cs:373, zero backdrop occurrences).
- Pending elsewhere: leftover "backdrop" doc comments (`Source.cs`, `SceneCatalog.cs`,
`ScreenCaptureManager.cs`, `ScreenCaptureFrameSource.cs`, `MainWindow.xaml:769`),
asset files + csproj (`Assets/*-backdrop.jpg` — pack-URI strings in MainViewModel now say
`-background.jpg` so default-scene seeding silently no-ops until assets+csproj are renamed).
## What shipped this session
1. **TASK 10 — Polar billing + Velopack auto-updates (steps 1–7)** — full license key activation flow:
- **Velopack** — NuGet `Velopack` 1.2.0 added, bootstrap in `App.xaml.cs` (`VelopackApp.Build().Run()` in try/catch). Self-hosted update URL TBD (DO droplet). The bootstrap is a no-op until the update URL is configured.
- **PolarLicenseService** — `Services/PolarLicenseService.cs`: `POST .../validate` with `key` + `organization_id`. Returns `status` (`granted`/`revoked`/`expired`) + `expires_at`. No auth. Network errors surfaced gracefully.
- **License key entry UI** — About overlay gains a third panel (same pattern as Licenses): text input + "Activate" button + status message. "Enter License Key" button added to the hub (alongside "Unlock Premium" which opens the Polar checkout URL). `IsHubVisible` computed property hides the hub when either sub-panel is open.
- **Entitlement persistence** — `LayoutStore` gains `LoadLicenseKey`/`SaveLicenseKey`, `LoadLicenseValidatedAt`/`SaveLicenseValidatedAt`, `LoadIsPremium`/`SaveIsPremium` via the Settings key/value table (no schema version bump needed). 14-day offline grace period: cached premium state trusted while online re-validation runs in the background.
- **BrandFlash toggle** — `IsPremium` property setter flips `BrandFlashEnabled = !value`. On startup, `ValidateLicenseAtStartupAsync` loads cached state → attempts online re-validation → overrides cache on success → lapses on failure/expiry. Premium removes the watermark; free tier keeps it.
- **PremiumUrl wired** — set to `https://buy.polar.sh/polar_cl_ueMy8AyAClO0o0yo19IroZwKTbD587gzUsjEW3bFapw`. `IsPremiumAvailable` now true. About overlay "Unlock Premium" button lights up. Tooltip updated.
- **About text updated** — "one-time unlock" → "subscription".
1. **TASK 10 — Polar billing + Velopack auto-updates (steps 1–7)** — full license key activation flow
2. **TRAX meter double-attenuation fix** — removed redundant slider multiplication
3. **LAN infrastructure documented** — all servers in `ai.md` + `CREDENTIALS.md`
4. **llamavault NFS/SMB diagnosis** — zombie SMB session from power outage, fixed by killing stale PID
2. **Polar MCP auth** — authenticated via `opencode mcp auth Polar`. Polar MCP connected.
## Active work: Control Surface UX (TASK 19/23 merged) — ALL PHASES COMPLETE
## Files changed
- `App.xaml.cs` — Velopack bootstrap (try/catch, non-fatal)
- `ytLive.csproj` — added `Velopack` 1.2.0 NuGet
- `Services/PolarLicenseService.cs` — new: Polar license validation API client
- `Services/LayoutStore.cs` — new: `Load/SaveLicenseKey`, `Load/SaveLicenseValidatedAt`, `Load/SaveIsPremium`
- `ViewModels/MainViewModel.cs` — `IsPremium` (was `static false`, now instance + DB-backed), `PremiumUrl` set, `BrandFlashEnabled` wired to `!IsPremium`, license fields/commands/validation methods, `IsHubVisible`, startup validation
- `MainWindow.xaml` — license entry panel, "Enter License Key" button, hub visibility binding, About text
- `MainWindow.xaml.cs` — `BackToAbout_Click` closes both sub-panels
**Design philosophy:** Director's control surface, not an editor. Five scene monitors above one central live monitor. Click thumbnail = transition (live) or stage (offline). Edit mode only when offline. Live/Recording = read-only. Right-click + drag locked when live.
### Phases completed (ALL)
1. ✅ **Phase 1:** `StagedScene`/`LiveScene` split + `IsEditMode` in MainViewModel
2. ✅ **Phase 2:** Left panel two-state (edit: layers+props, live: chat)
3. ✅ **Phase 3:** Right panel eliminated
4. ✅ **Phase 4:** Thumbnail strip above center monitor + `Scene.Snapshot` + placeholder snapshots
5. ✅ **Phase 5:** Edit-mode gating (drag/resize/context menus blocked when live)
6. ✅ **Phase 6:** `TransitionService.cs` (Cut/Fade/Move) + `SceneCompositor.BlendFrames()`
7. ✅ **Phase 7:** `FramePump` integrated with `TransitionService`
8. ✅ **Phase 8:** Thumbnail click → `TransitionToScene()` wiring
9. ✅ **Phase 9:** Brand rename "ytLlive" → "LlamaCasty" (user-visible strings)
10. ✅ **Phase 10:** `LayoutStore.SaveTransitionSettings()` / `LoadTransitionSettings()`
### Key decisions
- **Mode is implicit:** offline = edit, live/recording = read-only. No toggle button.
- **Left panel = one panel, two states:** offline → LAYERS + PROPERTIES; live → YouTube Live Chat (entire panel replaced, never coexists)
- **No (=>) transition button:** clicking a thumbnail directly triggers the transition
- **Thumbnail glow:** active scene gets brand-red `#e94560` border via `IsStaged` data trigger
- **Right panel eliminated:** chat moved to left panel (live mode only)
- **5 thumbnails above center monitor**, 120px height, uniform grid. Placeholder colored boxes until compositor snapshots added.
- **Transitions:** Cut + Fade + Move (v1). Go Live always = Cut. Duration persisted to Settings table.
- **Brand rename:** "ytLlive" → "LlamaCasty" across all XAML, VM strings, service strings. File paths (`%APPDATA%\ytLlive\`) intentionally NOT renamed.
### Architecture changes
- `ActiveScene` → split into `StagedScene` (central monitor, editable) + `LiveScene` (encoder output, read-only)
- `IsEditMode` = derived property: `StreamStatus == StreamStatus.Offline`
- `TransitionService` — transition state machine with Start/Tick/BlendFrame
- `SceneCompositor.BlendFrames()` — crossfade between two VideoFrames
- `Scene.Snapshot` + `Scene.IsStaged` — thumbnail image and staged indicator
- `FramePump` — now accepts `TransitionService` + `fromSceneProvider`, blends during transitions
### Files changed
- `ViewModels/MainViewModel.cs` — StagedScene/LiveScene split, IsEditMode, TransitionToScene, snapshots, brand rename, transition persistence
- `MainWindow.xaml` — 2-col layout, thumbnail strip, left panel two-state, right panel deleted, brand rename
- `MainWindow.xaml.cs` — Thumbnail click, edit-mode gating, context menu gating, dead code cleanup
- `Models/Scene.cs` — Snapshot, IsStaged properties
- `Services/Compositor/SceneCompositor.cs` — BlendFrames() static method
- `Services/Encoder/FramePump.cs` — transition support in render loop
- `Services/TransitionService.cs` — **new** — Cut/Fade/Move transition engine
- `Services/LayoutStore.cs` — SaveTransitionSettings/LoadTransitionSettings/UpsertSetting
### What still needs testing
- Runtime visual testing (need Windows display to run)
- Actual transition animation quality (Fade + Move)
- Thumbnail generation from real scene compositor (currently placeholder colors)
- Drag-reorder of thumbnails (not yet implemented)
## Build
```bash
@@ -40,16 +97,16 @@ taskkill.exe /F /IM ytLive.exe # if running
| Price | `7c1b6f70-cf8c-46df-9bc8-6703f9fa64d0` |
| Benefit | `80f66bc1-d5a3-44a1-b9ea-1709ae508535` |
| Checkout | `70e037cd-9bcb-414e-8b26-c82c1d268f65` |
| LLAMAFOUNDER | `01a8c29f-9bb8-4e96-9cbe-417a6a5cec9b` |
| LLAMA50 | `8cff803e-053f-49b4-8d12-d74d468f2943` |
| Checkout URL | `https://buy.polar.sh/polar_cl_ueMy8AyAClO0o0yo19IroZwKTbD587gzUsjEW3bFapw` |
| Validate endpoint | `POST https://api.polar.sh/v1/customer-portal/license-keys/validate` |
| Key prefix | `LCYT-` |
## Next session
1. **TASK 20** — Hotkeys (global keyboard shortcuts)
2. **TASK 19** — Scene transitions (Cut/Fade/Move/Custom)
3. **Velopack update URL** — self-host on DO droplet (`llamacasty.com/updates`)
## Next steps
1. **Visual testing** — run on Windows display, verify layout, transitions, thumbnail strip
2. **Thumbnail drag-reorder** — reorder scenes via drag on thumbnail strip
3. **Real snapshot generation** — replace placeholder colors with compositor-rendered thumbnails
4. **Transition dropdown UI** — add XAML control to pick transition type + duration
5. **Commit** — all this work needs to be committed once visually verified
## v1 vision
> A streaming tool that's intuitive and ready to go out-the-gate with nothing held back.