feat(ui): Show Desktop toggle, avatar login, chat preview, backdrop seeding, border animations
- Source.ShowDesktop bool persisted in DB (schema migration + LayoutStore) - ClearBackdropCaptureAsync clears CaptureKey + VideoImageSource for static fallback - Top bar: red/green/pulsing status light, avatar via BitmapImage, Log In button - ShowStartStream now requires IsConnected - RunMockChatPreviewAsync: simple async loop, ChatPreviewEnabled property on Source - EnsureDefaultBackdrop called on every scene switch, seeds all 5 scenes - SeedEndingBackdrop replaces existing, SeedChatBackdrop added - SceneCatalog: Chat scene display name corrected (layer = 'YouTube Chat') - Left panel spacing consistency, context menu reordered - Border animation rendering via code-behind Storyboard approach - 3 new backdrop assets (ending-backdrop.jpg, chat-backdrop.jpg, live-backdrop.jpg) - Build 0 warnings, 178 tests passing
This commit is contained in:
@@ -132,7 +132,7 @@ C# / WPF (.NET 8) following MVVM:
|
||||
- `YouTubeStreamService` manages the **variable reusable stream** (shipped 2026-08-16): `GetOrCreateReusableStreamAsync` lists `liveStreams?mine=true` and reuses the existing `cdn.isReusable` stream, creating it only on first use (`resolution=variable`, `frameRate=variable`); the stream is cached via `LayoutStore` (`SaveReusableStream`/`LoadReusableStream`, Settings table) and bound at broadcast insert (`boundStreamId`). Health (shipped 2026-08-16): `GetStreamHealthAsync(streamId)` polls `liveStreams?part=status` for `healthStatus` + `configurationIssues[]` → `StreamHealth`; banner decision in pure `StreamHealthReporter`
|
||||
- Webcam capture is shipped (milestone 1); the live desktop/game backdrop is shipped (ship task #1); **the output compositor (TASK 4 ship step 1) is SHIPPED**, **the FFmpeg locator (TASK 4 ship step 2) is SHIPPED**, **the encoder + RTMP push (TASK 4 ship step 3) is SHIPPED**, **WASAPI audio capture (TASK 4 ship step 4) is SHIPPED** — full plan in `TASKS.md`; the frame-pipeline wiring follows (its own PR)
|
||||
- `StreamConfig` defaults (`TargetBitrate=6000`, `Resolution="1920x1080"`) are stale — the live dropdown drives `StreamHealth.CurrentBitrate`/`FPS` instead
|
||||
- **v1 task queue (2026-08-19):** tasks 19-23 added for v1 feature completeness; TASK 3.18 (chat box) shipped; TASK 10 (Polar billing) ready to execute (org ID obtained):
|
||||
- **v1 task queue (2026-08-19):** tasks 19-23 added for v1 feature completeness; TASK 3.18 (chat box) shipped; TASK 16 (infinity display) shipped; TASK 10 (Polar billing) ready to execute (org ID obtained):
|
||||
- TASK 10: Polar billing — license key entry, offline entitlement, brand flash gating, Velopack auto-updates
|
||||
- TASK 19: Scene transitions (Cut/Fade/Move/Custom) — required for professional polish
|
||||
- TASK 20: Hotkeys (global keyboard shortcuts) — the single biggest UX gap
|
||||
@@ -163,19 +163,26 @@ instead of a normal draggable source.
|
||||
(the old `ToggleSceneBackdropCommand` is gone); "Change Capture…"/"Refresh Capture"/"Capture Display"
|
||||
only show in the Live scene's preview menu (`CanChangeBackdrop`). The static Background, if any,
|
||||
renders **above** the backdrop.
|
||||
- **Detection (launch + focus only, no live session listener):** `Win32FullScreenDetector` =
|
||||
- **Detection (event-driven):** `Win32FullScreenDetector` hooks `SetWinEventHook(EVENT_SYSTEM_FOREGROUND)`
|
||||
— fires on the UI thread whenever the foreground window changes. The callback runs the same
|
||||
`GetForegroundWindow` + `DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS)` + `MonitorFromWindow` +
|
||||
`GetMonitorInfo`; a window is full-screen when its frame covers all four monitor edges; own process is
|
||||
excluded; monitor **index = `EnumDisplayMonitors` enumeration order** (the same order
|
||||
`GetMonitorInfo` check; a window is full-screen when its frame covers all four monitor edges; own
|
||||
process is excluded; monitor **index = `EnumDisplayMonitors` enumeration order** (the same order
|
||||
`ScreenCaptureSourceFactory` uses to map index→HMONITOR via `Win32FullScreenDetector.GetMonitorHandle`).
|
||||
`IFullScreenDetector` also exposes `GetDisplays()` (`DisplayInfo`: index/name/resolution/bounds/`IsPrimary`,
|
||||
friendly name via `EnumDisplayDevices`) + `PrimaryMonitorIndex()` for the in-app "Capture Display"
|
||||
submenu. At launch `ReacquireScreenCaptures` keys the backdrop to the full-screen game's monitor (or the
|
||||
**primary** display — never assumed monitor 0).
|
||||
On **Deactivated**, `NoteBackgroundWindow` samples the foreground ~250ms later (so an alt-tab to a game
|
||||
lands before the app re-activates); on the next **Activated**, `RefreshBackdropAutoCapture` re-runs
|
||||
detection against that sample and re-designates. Null detection (our app, the desktop, a normal window)
|
||||
leaves the current capture alone.
|
||||
submenu + `FullscreenMonitorChanged` event + `StartWatching()`/`StopWatching()`. At launch
|
||||
`ReacquireScreenCaptures` seeds the static fallback image (`Assets/live-backdrop.jpg`) onto the
|
||||
backdrop's `AssetId` via `SeedLiveBackdropAsset()`, then keys the backdrop to the full-screen game's
|
||||
monitor; when **no game is detected**, `ResolveAutoCaptureKey()` returns `null` — stale `CaptureKey`
|
||||
values are cleared, no capture session is started, and `Source.DisplaySource` falls back to
|
||||
`_imageSource` (the static asset on the backdrop element). `StartWatching()` is called at the end of
|
||||
`ReacquireScreenCaptures`. `OnFullscreenMonitorChanged` auto-redesignates when a fullscreen game appears
|
||||
on a different monitor; null (no fullscreen / own window) is ignored — the existing capture keeps
|
||||
running so loading screens / transitions don't break the stream. `RefreshBackdropAutoCapture` (bound to
|
||||
"Refresh Capture" context menu + `Activated` handler) reads the current foreground directly as a manual
|
||||
fallback. Deduplication: `_lastReportedMonitor` prevents redundant redesignates from rapid foreground
|
||||
changes.
|
||||
- **Capture (WinRT GraphicsCapture):** `ScreenCaptureFrameSource` creates a free-threaded
|
||||
`Direct3D11CaptureFramePool` (2 buffers, `B8G8R8A8UIntNormalized`) + `GraphicsCaptureSession`; frames →
|
||||
`SoftwareBitmap.CreateCopyFromSurfaceAsync` (alpha ignored) → `VideoFrame` (BGRA8), bytes read via
|
||||
@@ -206,7 +213,7 @@ instead of a normal draggable source.
|
||||
game are behind the picker; picker-based captures don't survive reload.
|
||||
- **Focus-loss capture lag (known OS limit, NOT an in-app throttle — deferred):** when the app window loses
|
||||
focus the preview visibly slows (mouse-movement lag). Nothing in the repo checks focus to slow capture —
|
||||
the only focus hooks (`NoteBackgroundWindow`/`RefreshBackdropAutoCapture`) re-target the backdrop, never
|
||||
the only focus hooks (`FullscreenMonitorChanged` event + `RefreshBackdropAutoCapture`) re-target the backdrop, never
|
||||
pace it. The lag is inherited: `Windows.Graphics.Capture` is content-driven and DWM/OS-throttled while the
|
||||
app is in the background (worse under a full-screen game on 24H2/26100), GPU readback
|
||||
(`CreateCopyFromSurfaceAsync`) contends with the foreground game, the source's one-in-flight `_framePending`
|
||||
|
||||
Reference in New Issue
Block a user