TASK 4 ship step 5.5: social bar bug fixes + bar on the live output — direction-snap drag (SocialBarSnap.Decide + ClearValue, local Canvas.Top was overriding the binding), fediverse nodeinfo subdomain probing + load-time heal (settable FediverseSoftware), SocialBarRenderer strip blitted above the flash via a re-read FramePump socialBar seam — 155 tests passing, 0 warnings

This commit is contained in:
2026-08-13 09:29:58 -07:00
parent e72ba71165
commit ac60a26d82
16 changed files with 666 additions and 52 deletions
+40
View File
@@ -475,6 +475,46 @@ added, `MainViewModel` wired end-to-end (resolver + both option builders + pump
the pump reads the active scene on a background thread while the UI can still edit it; a concurrent-mutation
exception is contained (logged + `Failed` + pump stops) rather than crashing.
#### Ship step 5.5 — Social bar bug fixes + the bar on the live output (2026-08-13)
**Bug 1 — drag doesn't snap (root cause found):** `SocialBar_PreviewMouseMove` set `Canvas.SetTop(bar, …)`
with a local value, which permanently overrides the `Canvas.Top="{Binding SocialBarTop}"` binding — the
release-time `SetSocialBarPosition` → `PropertyChanged(SocialBarTop)` could never beat it, so the bar stayed
wherever it was dropped. Fixed by direction-snapping **during** the drag (`SocialBarSnap.Decide` — deadzone
±6px, once the direction commits the bar rides the edge it's dragged toward, never mid-screen) and on release
`bar.ClearValue(Canvas.TopProperty)` re-engages the binding before `SetSocialBarPosition`.
**Bug 2 — Mastodon showed the generic 7-star honeycomb (root cause found):** the DB row
`@gramps@llamachile.tube` had `Software = NULL` — nodeinfo was only ever resolved against the identity domain
(`llamachile.tube`, a landing page), never probed for the real instance at `mastodon.llamachile.tube`.
Fixed on three fronts: `HttpSocialValidator` now **probes well-known subdomains** (mastodon. → social. →
… `FediverseSubdomainCandidates`) when the identity domain and its redirect both come up empty, under a
~15s linked-CTS budget, with an optional `Action<string>` log; `MainViewModel` **heals** any fediverse entry
missing a software name on layout load (`HealFediverseSoftwareAsync` — static, testable; instance wrapper
runs it off the UI thread, applies via the dispatcher, saves); `SocialEntry.FediverseSoftware` is now
**settable** and raises `PropertyChanged` for `LogoData`, so the heal updates the icon in place. If the user
later re-saves the entry with the icon fixed, the name persists with it.
**Compositor rendering (user-approved scope):** the social bar now appears **on the live output**, not just
the preview — `Compositor/SocialBarRenderer.cs` rasterizes the entries into a transparent straight-alpha
BGRA strip (1920-wide, 40px content + 24px glow pad, green `#2ecc71` glow baked in) via `RenderTargetBitmap`
(WPF glue like `StaticPixelCache`; the compositor core stays pure). `SceneCompositor.Render` takes optional
`socialBarFrame` + `socialBarTop` (master space) and blits it **last — above the branding flash** (the old
`BlitFlash` generalized to offset `BlitOverlay`). `FramePump` gains a `socialBar:` seam
(`Func<(VideoFrame?, SocialBarPosition)>`, re-read every frame) and places the bar at `0` or
`SourceRectHeight bar height`. `MainViewModel` owns the frame (`RenderSocialBarFrame`, re-rendered on
load/save/notify) and feeds the seam.
**Tests (+8 → 155 passing, 0 warnings):** `SocialBarSnap.Decide` units (direction + deadzone + custom
deadzone), settable `FediverseSoftware` updates `LogoData`, subdomain-probe unit + null-when-silent unit,
the branch's **one integration test** `Socials_HealMissingFediverseSoftware_RoundTripsThroughDb`
(temp-DB roundtrip: NULL software → healed via the validator → persisted), compositor bar overlay
(top/bottom + above-flash), `FramePump` bar pass-through (Top then flipped to Bottom mid-run — the seam is
re-read each frame), and both `ISocialValidator` fakes (`FakeValidator`/`BlockingValidator`) gained
`ResolveFediverseSoftwareAsync`.
**Not included (say the word):** rewriting the healed entry's `ProfileUrl` to `https://mastodon.llamachile.tube/@gramps`.
---
## TASK 5 — YouTube Live Stream Management