e69db4d26c
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)
4.3 KiB
4.3 KiB
Controls — index
The phase-2 carved pieces of what used to be one monolithic MainWindow.xaml
(1,681 lines → a 123-line shell). Each is a UserControl in the ytLive.Controls
namespace, extracted one roll-back commit at a time (refactor-commit-12..18).
See schema.md for the memory-map conventions.
| File | Purpose |
|---|---|
BottomBar.xaml(.cs) |
Footer (Row 3): stream stats (bitrate/fps/dropped/duration/health), resolution dropdown (gear menu moved up to TopBar 2026-09-22) |
HealthBanner.xaml(.cs) |
Row 1 status banner + the 7 value→visibility converters live in the app theme, not here |
TopBar.xaml(.cs) |
Row 0 session surface (2026-09-22 redesign): brand + gear menu (left), one segmented REC |
PreviewPane.xaml(.cs) |
Row 1 Col 1 center: the 1920×1080 OverlayCanvas/SelectionOverlay/BrandFlashLayer/SocialBarElement, text pull-out drawer, and the live-controls row (TRAX / game / mic meters + sliders + speakers + socials) |
SceneThumbnailStrip.xaml(.cs) |
Row 0 scene-picker strip (ItemsControl over Scenes), staged-red / live-green border highlight |
LeftPanel.xaml(.cs) |
Row 1 Col 0 left panel: edit mode → layers+properties; live → YouTube chat. Source add/edit/remove, list drag-to-reorder, snapshot revert-accept |
OverlayHost.xaml(.cs) |
Root-grid overlay dialogs: Settings / Report Bug / Feature Request / About (+ licenses + license-key entry). BackToAbout_Click lives here |
SceneThumbnailStrip.xaml.cs |
Code-behind for the strip above |
LeftPanel.xaml.cs |
Code-behind for the panel above |
Window ↔ control contract
The shell (MainWindow.xaml(.cs)) wires up each control and routes the few
cross-cutting calls through it:
- Ctor finds each control by
x:Name(TopBar,PreviewPane,LeftPanel) AFTERInitializeComponent, before any method touches it (null-ref guard). _previewPane.IsClickInsidePreview/Drawer+_leftPanel.IsClickInside→ deselection guard inWindow_PreviewMouseLeftButtonDown._previewPane.UpdateSelectionOverlay(bool)+_leftPanel.OnSelectionChanged(vm)→ selection/edit-mode routing from the VM'sSelectedElementchange._topBar.RefreshAvatar()onIsConnected/AccountAvatarUrlchange.- Handlers in moved controls cast
(DataContext as MainViewModel)— controls inherit the window's DataContext; never pass the VM through a property.
Gotchas learned the hard way
- A UserControl cannot see
Window.Resources(StaticResource resolves by element lookup, so it stops at the control boundary). The 7 converters (BoolToVis,InverseBoolToVis,NotNullToVis,NullToVis,HiddenBoolToVis,EnumToBool,AllTrueToVis) were promoted fromMainWindow.ResourcesintoThemes/Controls.xaml(seeThemes/index.md). - Moved XAML can lose its namespace: a block cut from MainWindow carries no
root xmlns, so any
models:/Helpers:ref in it breaks the build withMC2000: Value cannot be null (key). Declarexmlns:models/xmlns:Helperson each control's root (LeftPanel hit this). - Name-looked-up host stubs must stay in the window, not a control:
WebViewHostPanel(ctorInitWebView2(WebViewHostPanel)) andToastArea(NotificationService.InArea("ToastArea")) are resolved by name from the window root — soOverlayHostdeliberately leaves them as thin window-root children (Commit 18). Since the 2026-09-14 composition-capture redesign onlyToastArearemains —WebViewHostPanelis deleted (web sources no longer host a multiline XAML control;InitWebView2()runs fromMainWindow_Loadedwith no panel argument). - When cutting visual wrappers, don't drop the outer
Border's background/glow/margin — Commit 15 initially lost the preview's outer border and had to restore it same-commit.
Related: ViewModels/index.md (MainViewModel the
controls bind to), Themes/index.md (styles/converters),
Helpers/index.md (ViewModelBase, converters), the
MainWindow.xaml shell at repo root that hosts these.