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 -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).
// 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 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 =
"(function(){" +
"if(document.getElementById('ytl-transparent-bg'))return;" +
"var s=document.createElement('style');" +
"s.id='ytl-transparent-bg';" +
"s.appendChild(document.createTextNode(" +
"'html,body{background-color:transparent!important;margin:0!important;overflow:hidden!important;}'" +
"'html,body,html *{background-color:transparent!important;}'" +
"'html,body{background-color:transparent!important;background-image:none!important;margin:0!important;overflow:hidden!important;}'" +
"'html,body,html *{background-color:transparent!important;background-image:none!important;}'" +
"));" +
"(document.head||document.documentElement).appendChild(s);" +
"})();";
@@ -210,6 +217,16 @@ public sealed class WebView2Manager : IDisposable
// transparent margins the injection promises has never been
// verified (MyMistakes: "? VERIFY").
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);
};