Two bugs from the creator's first look at the new controls.
1. CRASH — '{DependencyProperty.UnsetValue}' is not a valid value for property 'Fill'.
The token conversion deleted `YtSliderKnobHoverBrush` while two slider triggers still
referenced it. WPF does not fail that at build time: it substitutes UnsetValue and throws at
the next layout, inside `Ellipse.OnRender` — so the first slider touched (the new opacity one)
killed the app. The brush is back, as a glossier version of the rest state.
Guard: `ThemeResourceTests` scans every app XAML and proves each {StaticResource} key is
defined (theme/App resources or the same file). Proven by removing the key again — the test
names it.
2. PREVIEW DID NOTHING — the preview bar is its own XAML rendering (not the rasterized strip the
compositor overlays), and it was hard-wired white-on-transparent, so the drawer's controls had
nothing to move. Now:
- `SocialBarTextBrush` / `SocialBarBackgroundBrush` come from the same `SocialBarStyle` the
renderer bakes, so preview and output cannot disagree.
- the preview draws the plate (same colour + opacity) and the drop shadow.
- the drawer applies changes to the live config as you edit (`ApplyAppearancePreview`), and
Cancel restores the pre-edit snapshot — so the preview tracks the controls without
persisting anything until Save.
46 tests green (theme, socials, design tokens, layout store, real-MainWindow meter test).
In the Socials drawer, under the show/hide switch: text colour, an optional background plate
with its own colour and opacity, and a text drop shadow. The bar had been hard-wired
white-on-transparent since it shipped.
- `SocialsConfig` gains five additive properties (BarTextColor, BarBackgroundEnabled,
BarBackgroundColor, BarBackgroundOpacity, BarTextShadow).
- Persistence: five new Socials columns, added by the per-column idempotent migration pattern
(a half-migrated DB heals instead of failing), written in Save and read back in Load with
defaults for pre-existing rows.
- `SocialBarRenderer.Render(entries, SocialBarStyle?)` — the look is a separate value type so the
renderer stays a pure "entries + look" function and the preview can never read a half-applied
config mid-edit. A malformed hex falls back to white/black instead of throwing inside a render
tick.
- The plate hugs the entries (rounded 20px, padding) so it reads as a caption bar, not a band
across the frame. Off by default, because transparent is what creators have today.
- The colour pickers reuse `BorderColorPalette` — one catalogue of the app's colours, per the
TASK 60/63 rule.
Guard: SocialBarAppearanceTests paints the real strip and inspects pixels — default is white with
no plate, a red text colour reaches the glyphs, a 55% blue plate yields half-transparent blue
pixels, and the shadow adds dark pixels that were not there before.
- Socials: the dialog is a right-rail drawer (same pattern as Stream Info/YPP/TEST); the live-screen button is removed
- Right rail: Stream Info (was Stream Settings) and YPP show the YouTube logo; Socials and TEST get icons; tab colors and label colors per spec
- Top bar: EDIT toggle beside REC (orange when on, blue when off); going live on YouTube turns it off
- Stream readouts (bitrate, FPS, dropped n (x.y%), Live hh:mm:ss, health) and the resolution dropdown moved to the top-left cell; bottom bar emptied at the same height
- Webcam border drawing restored (it was removed with the CRT bezel change)
- YouTube chat layer: border color, thickness, and opacity controls
- Vertical 1080p30 tier added
Not related to the fps gate yet; no gating is enforced.
MainViewModel.Socials.cs: social-bar state (_socials/_socialBarFrame/
_socialValidator), bar props (HasSocials/SocialBarVisible/SocialBarDotBrush/
SocialBarGlowBrush/Socials/SocialBarTop), OpenSocialDialog + NotifySocialsChanged
+ RenderSocialBarFrame (SocialBarRenderer), fediverse heal
(HealFediverseSoftwareAsync/HealFediverseSoftwareInBackground),
SetSocialBarPosition/ToggleSocialBarPosition, OpenSocialDialogCommand.
BarOnBrush/BarOffBrush/CreateBrush stay in core (shared tokens used by
Audio/Trax partials); core keeps ctor command assignment, LoadLayout,
SaveLayoutNow, and the compositor socialBar seam.
Docs: index.md restructured — each landed partial (Account..Trax) now owns its
own table row, the core row shrinks to only what remains in MainViewModel.cs,
and the split tracker is a compact code<->commit map.
Verify: 0 warnings, 246 passed, 2 known failures only.