The two regressions that reached the creator both lived in preview XAML that no test loads: the
appearance controls painted nothing, then the bar vanished when the plate was off. Every existing
test was green through both.
This test drives the real drawer on a real MainWindow (hermetic temp DB, per the layout-pollution
rule) and reads the real preview elements:
- the bar is VISIBLE, whatever the plate switch says
- a drawer text colour reaches the glyph
- the plate carries its colour and ~50% alpha, and its 18,6 padding only when enabled
- the shadow is an actual DropShadowEffect on the glyph, and off by default
- Cancel restores the pre-edit look
It paid for itself on the first run: Cancel did NOT revert. The snapshot held the same config
object the live preview mutates, so "restore" restored the edited object. The snapshot is now the
six values, written back on cancel — entries untouched.
Also fixes the harness details that made the first attempts fail: everything runs on the app's STA
thread (window creation AND close), and the exception is rethrown with its stack so a failure is
diagnosable next time.
My own regression from the previous commit: the plate Border WRAPPED the bar's content and its
Visibility was bound to BarBackgroundEnabled — so with the plate off (the default) the preview
collapsed the entire social bar. Nothing was wrong with the data; the element was there and
invisible.
Fix: the wrapper is always laid out. "Plate off" now means a TRANSPARENT background
(`SocialBarBackgroundBrush` returns Transparent when disabled) and zero padding, "on" means the
colour/opacity plate with 18,6 padding — which also matches what the renderer bakes for the
output, so preview and broadcast agree.
Guard (new fact in SocialBarAppearanceTests): the preview must never visibility-gate the bar on
BarBackgroundEnabled, the wrapper must bind SocialBarBackgroundBrush, and the plate's switching
must be Padding.
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.