fix(web): wipe background-image too — the widget's black void is a CSS backdrop gradient, not a color

The 15:34 take's box interior is the widget's OWN full-canvas opaque paint — a
black void with wide content strips at the top/bottom and a right-edge bar —
arriving AFTER the first-second blank-transparent dumps. A background-color-only
!important wipe (b4bba4b) can't touch it because CSS gradients/backdrops are
background-IMAGE, not background-color.

OBS's fix for this exact symptom is background-image:none (obsproject/obs-studio#6659:
"set the CSS for html and body to background: none !important"). Injection now wipes
background-image:none!important on html,body,html * alongside the color wipe; HTML
overlay art (<img>/DOM/CSS shapes) survives — that is browser-source semantics.

Also re-arms WidgetDumpRemaining=5 ~30s after nav so the next take dumps the real
document INSIDE the recording window (the previous dumps proved blank at +1s and
missed the later backdrop paint).

9/9 WebView2Manager tests, 0 warnings.
This commit is contained in:
2026-09-10 15:42:26 -07:00
parent b4bba4bf68
commit 0f72c53aa5
4 changed files with 57 additions and 39 deletions
+20 -27
View File
@@ -8,37 +8,30 @@ audio-silence are addressed).
## ACTIVE THREAD (ONE problem at a time) ## ACTIVE THREAD (ONE problem at a time)
**Web-uri transparency — branch (a) CONFIRMED and FIX-APPLIED, awaiting the verify take.** **Web-uri transparency — second hardening applied (background-image wipe), awaiting take.**
The 12:54/13:50 dumps proved the REAL widget document captures TRANSPARENT The 15:34 take: the recording's box interior is the widget's OWN full-canvas opaque paint
(`%TEMP%\ytLive-web-8d7234ec…-w1..5.png` alpha max 0/logged alpha 100% zero) YET the (bright content strips at top/bottom + right bar over a black void) — NOT desktop, NOT a
recording kept showing a black opaque box over the element rect (1231,679,705,396 — crisp compositor blend. The real-document dumps showed blank-transparent at +1s and the page's
edges, interior mean RGB ≈(1,6,13), NOT the desktop). Conclusion: the OLD inline backdrop appears later; a background-COLOR-only `!important` wipe (b4bba4b) leaves CSS
`element.style.background='transparent'` injection only holds while the page has nothing background-IMAGE (gradient/backdrop) intact — the OBS answer is `background: none
to paint; once the widget connects and repaints a container background-COLOR, the capture !important`, background-image too (obsproject/obs-studio#6659).
re-opaques → black box. That is the OBS-solved class ("all web-uri resources paint their
own background").
**Slice 14 change (commit target):** `WebView2Manager.TransparentBackgroundScript` is now a **Slice 15 (commit target, the fix):**
pre-parse `<style id='ytl-transparent-bg'>` wiping `background-color:transparent !important` - `TransparentBackgroundScript` now also wipes `background-image:none!important` on
on `html,body,html *` (background-images/art survive — only background-COLOR targeted). `html,body,html *` (gradients/backdrops die; `<img>`/DOM art survives — OBS semantics).
Idempotent by element id; `!important` outranks every page rule (OBS forums 2016 `body { - `NavigateCompleted` re-arms `WidgetDumpRemaining = 5` ~30s in, so this take dumps the
background-color: rgba(0,0,0,0) !important }` + div variant, woahtech OBS custom-CSS guide — real document INSIDE the recording window (the missing evidence last time).
both cited in the commit message and `MyMistakes.md`). - Test extended (asserts `background-image:none!important` present, weak inline form absent).
- `internal const` + regression test `TransparentBackgroundScript_Is_A_Important_Element_Wide_Wipe`
(asserts element-wide `!important`, style-element form, and that the losing inline
`.style.background=` form is gone).
- Build 0 warnings; 9/9 WebView2Manager tests pass.
## THE ONE REMAINING STEP (verify, no further analysis) ## THE ONE REMAINING STEP (verify, no further analysis)
1. User REPLACES the app (build is current) and records the Live scene, widget animating, 1. User records the Live scene, widget animating (pre-record ~35s so the 30s re-arm dump
like the 12:54/13:50 takes. fires mid-take).
2. Verdict: element rect shows scene bg with widget art over it (NO black box) → transparency 2. Verdict: element rect shows the scene backdrop behind the widget art (no black box/void)
C LOSED, push gate #1 clears. If it still shows a solid black box you can SEE at a glance → transparency CLOSED, push gate #1 clears. If a black void persists BEYOND a visible
inside the widget's 705×396 area, bring it + then (and ONLY then) audit what other layer background-image, it's not background painting and only chroma-key remains — bring the
paints that rect in the composite — the web capture has been exonerated twice. take and we do the compositor key, not more capture investigation.
3. Then the queued layer-order-save bug (dragging an element over another doesn't persist 3. Then the queued layer-order-save bug (reorder doesn't persist `SortOrder`) is next.
`SortOrder`) is the next single use case.
## Other threads (paused) ## Other threads (paused)
+16 -9
View File
@@ -76,15 +76,22 @@ answer (valid for arbitrary pages for a decade) is an injected pre-parse `<style
with `!important` beating every page rule: with `!important` beating every page rule:
https://obsproject.com/forum/threads/translucent-transparent-browser-source.59549/ https://obsproject.com/forum/threads/translucent-transparent-browser-source.59549/
(`body { background-color: rgba(0,0,0,0) !important }`) + the div-level variant for (`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 stubborn widgets (woahtech.com OBS custom-CSS guide). Applied 2026-09-10 (b4bba4b):
injection now appends a style element wiping `background-color:transparent!important` injection appends a style element wiping `background-color:transparent!important`
on `html,body,html *` (background-IMAGE and art survive). Self-inflicted again: the on `html,body,html *`. **Second half (2026-09-10, 15:34 take): a background-COLOR wipe
agent re-derived the whole transparency story (recording pixel archaeology, is NOT enough.** The 15:34 interior ASCII shows a black void with bright content strips
compositor blend re-verification) instead of reading this entry — the premise was at top/bottom + a right-edge bar — the widget's full-canvas CSS **background-image**
already wrong once and the instrument said transparent, which it did because the (gradient/backdrop) painted after connect. The OBS fix for that is
capture had NOT been repainted yet. Verify take: if the 705×396 element rect shows `background: none !important` / `background-image:none!important`
the scene bg behind the widget art (animation visible, no black box) the loop closes. (obsproject/obs-studio#6659 — "set the CSS for html and body to background: none
Do NOT re-derive this story a third time. !important"). Wipe both moving forward; overlay art is `<img>`/DOM and survives.
Also the dumps only covered +1s (blank-transparent); the widget's backdrop paint
arrives later, so the mid-recording dump set is now re-armed ~30s in. Verify take:
element rect shows scene bg behind the widget art (animation, no black void) → loop
closes. Self-inflicted again: the agent re-derived the whole transparency story
(recording pixel archaeology, compositor blend re-verification) instead of reading
this entry — the instrument said transparent because the capture had NOT been
repainted yet. Do NOT re-derive this story a third time.
### Shrink / re-encode an image for the README (screenshots → small hero image) ### Shrink / re-encode an image for the README (screenshots → small hero image)
+20 -3
View File
@@ -167,15 +167,22 @@ public sealed class WebView2Manager : IDisposable
// and the div-level variant for stubborn widgets, woahtech.com OBS custom-CSS guide). // and the div-level variant for stubborn widgets, woahtech.com OBS custom-CSS guide).
// A `<style>` node injected before parse, with `!important`, outranks every page rule // A `<style>` node injected before parse, with `!important`, outranks every page rule
// (only an author !important beats a later document-order !important; ours runs first). // (only an author !important beats a later document-order !important; ours runs first).
// Only background-COLOR is wiped — background images and the widget art survive. // Only background-COLOR was wiped at first — the 15:34 take showed the widget's own
// full-canvas backdrop surviving as the box's black void: that paint is a CSS
// background-IMAGE (gradient/backdrop), and the OBS fix for the same symptom is
// `background: none !important` / background-image none, not color alone
// (obsproject/obs-studio#6659 — "set the CSS for html and body to background: none
// !important"). Wipe background-image too; HTML overlay art (the widget's content) is
// <img>/DOM/CSS shapes and survives — only container decorations are removed, which is
// OBS-browser-source semantics for a transparent overlay.
internal const string TransparentBackgroundScript = internal const string TransparentBackgroundScript =
"(function(){" + "(function(){" +
"if(document.getElementById('ytl-transparent-bg'))return;" + "if(document.getElementById('ytl-transparent-bg'))return;" +
"var s=document.createElement('style');" + "var s=document.createElement('style');" +
"s.id='ytl-transparent-bg';" + "s.id='ytl-transparent-bg';" +
"s.appendChild(document.createTextNode(" + "s.appendChild(document.createTextNode(" +
"'html,body{background-color:transparent!important;margin:0!important;overflow:hidden!important;}'" + "'html,body{background-color:transparent!important;background-image:none!important;margin:0!important;overflow:hidden!important;}'" +
"'html,body,html *{background-color:transparent!important;}'" + "'html,body,html *{background-color:transparent!important;background-image:none!important;}'" +
"));" + "));" +
"(document.head||document.documentElement).appendChild(s);" + "(document.head||document.documentElement).appendChild(s);" +
"})();"; "})();";
@@ -210,6 +217,16 @@ public sealed class WebView2Manager : IDisposable
// transparent margins the injection promises has never been // transparent margins the injection promises has never been
// verified (MyMistakes: "? VERIFY"). // verified (MyMistakes: "? VERIFY").
session.WidgetDumpRemaining = 5; session.WidgetDumpRemaining = 5;
// The first five dumps fire within ~1s of completion (usually BEFORE the
// recording starts) — re-arm ~30s in so the same real document gets dumped
// INSIDE a recording's window too. Next take therefore nukes the gap that
// hid what the page actually painted while recording.
var rearm = session;
_ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(30));
rearm.WidgetDumpRemaining = 5;
});
} }
_ = CaptureFrame(source.Id); _ = CaptureFrame(source.Id);
}; };
+1
View File
@@ -121,6 +121,7 @@ public sealed class WebView2ManagerTests
var script = WebView2Manager.TransparentBackgroundScript; var script = WebView2Manager.TransparentBackgroundScript;
Assert.Contains("background-color:transparent!important", script); Assert.Contains("background-color:transparent!important", script);
Assert.Contains("background-image:none!important", script);
Assert.Contains("html,body,html *", script); Assert.Contains("html,body,html *", script);
Assert.Contains("document.createElement('style')", script); Assert.Contains("document.createElement('style')", script);
Assert.DoesNotContain(".style.background=", script); Assert.DoesNotContain(".style.background=", script);