fix(web): harden the transparency injection to the OBS-standard element-wide !important wipe

The real-widget dumps (12:54/13:50) proved the OLD inline
element.style.background='transparent' injection holds only while the page has
nothing to paint: the capture was alpha-transparent, yet the recording showed a
black opaque box over the whole element rect (1231,679 705x396) once the widget
connected and repainted a container background-COLOR — CapturePreviewAsync always
honors page CSS (MicrosoftEdge/WebView2Feedback specs/BackgroundColor.md), so any
page-painted background wins over DefaultBackgroundColor.

This is the OBS-solved class (all web-uri resources paint their own background):
browser sources use a Custom CSS override, and the decade-validated formula for
arbitrary pages is a pre-parse <style> with 'background-color: transparent
!important' — https://obsproject.com/forum/threads/translucent-transparent-browser-source.59549/
('body { background-color: rgba(0,0,0,0) !important }') plus the div-level variant
for stubborn widgets (woahtech.com OBS custom-CSS guide).

Injection is now an idempotent pre-parse style element wiping background-color on
html,body,html * with !important (outranks every page rule, runs before page parse
via AddScriptToExecuteOnDocumentCreatedAsync). Only background-COLOR is targeted —
background images and widget art survive. Regression test asserts the element-wide
!important form and that the losing inline form is gone.

9/9 WebView2Manager tests, 0 warnings.
This commit is contained in:
2026-09-10 14:04:11 -07:00
parent ea347f211c
commit b4bba4bf68
4 changed files with 106 additions and 66 deletions
+23 -12
View File
@@ -15,7 +15,7 @@
## 🔬 Recipes registry
### ⚠ SPIN GUARD TRIGGERED → RESOLVED (? VERIFY) — web overlay transparency + bounding box
### SPIN GUARD → RESOLVED — web overlay transparency + bounding box (RECIPE)
**THE ONE ROOT CAUSE THAT EXPLAINS EVERY FAILED TAKE:** WebView2's `CapturePreviewAsync`
produces an **OPAQUE** PNG. From the WebView2 spec (sender: MicrosoftEdge/WebView2Feedback
@@ -63,17 +63,28 @@ do the same via a pre-parse user.css. Kept the nav handlers as a post-load re-as
3. Do not delete code paths that fix one axis (crop=bbox) while debugging another
(source alpha). Revert scope creep; keep layer contributions separable.
**2026-09-10 follow-up (still not verified, now being resolved):** the "? VERIFY" above was
never closed because the diagnostic itself was blind — the dump + alpha log fired on the FIRST
capture ever, which is always the initial about:blank placeholder document (alpha=0, rgb=0,
5/5 sessions). The REAL widget frame was never seen. The 2026-09-10 take still showed a black,
opaque block with the animation running fine, so either (a) the pre-parse injection does not
hold for this widget's own CSS (capture opaque) — fix = stronger injection / chroma-key — or
(b) the capture has transparent margins and the black lives in the compositor blend. `1a39b09`'s
injection is intact; `WidgetDumpRemaining = 5` now dumps the real widget document post-paint
(`%TEMP%\ytLive-web-<id>-w1..5.png`) + shared `AlphaStats` line → the next app launch names
which branch. Do NOT re-derive the whole transparency story again from this text; the 5-line
diagnostic is the shortest path.
**2026-09-10 follow-up → NOW VERIFIED and hardened.** The 12:54 and 13:50 sessions
showed the REAL widget document capturing TRANSPARENT (widget dumps `w1..5` for
`8d7234ec`: alpha 100% zero, content 12×3; whole-file decodes of
`%TEMP%\ytLive-web-*.png` alpha max = 0) while the RECORDING kept showing a black,
opaque box over the element rect (crisp edges at the exact rect 1231,679 705×396 —
NOT the desktop showing through). Concluson: branch (a) — the OLD inline
`element.style.background='transparent'` injection only wins while the page has
nothing to paint; once the widget connects and paints its own container
background-COLOR, the capture goes opaque again → black box. The OBS-validated
answer (valid for arbitrary pages for a decade) is an injected pre-parse `<style>`
with `!important` beating every page rule:
https://obsproject.com/forum/threads/translucent-transparent-browser-source.59549/
(`body { background-color: rgba(0,0,0,0) !important }`) + the div-level variant for
stubborn widgets (woahtech.com OBS custom-CSS guide). Applied 2026-09-10: the
injection now appends a style element wiping `background-color:transparent!important`
on `html,body,html *` (background-IMAGE and art survive). Self-inflicted again: the
agent re-derived the whole transparency story (recording pixel archaeology,
compositor blend re-verification) instead of reading this entry — the premise was
already wrong once and the instrument said transparent, which it did because the
capture had NOT been repainted yet. Verify take: if the 705×396 element rect shows
the scene bg behind the widget art (animation visible, no black box) the loop closes.
Do NOT re-derive this story a third time.
### Shrink / re-encode an image for the README (screenshots → small hero image)