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:
+44
-48
@@ -1,63 +1,59 @@
|
||||
# HANDOFF — 2026-09-10 (late afternoon)
|
||||
# HANDOFF — 2026-09-10 (transparency hardening applied)
|
||||
|
||||
## Branch / Commit State
|
||||
|
||||
`main` HEAD currently = `f3d578c` (slice 12 audio telemetry). **Slice 13 (web transparency
|
||||
diagnostic re-point) is uncommitted** in the working tree. Ahead of origin by 18, NOT pushing
|
||||
(user ruling: no push until web-overlay transparency AND audio-silence are addressed).
|
||||
`main` HEAD will be = the new transparency-hardening commit (slice 14). Ahead of
|
||||
origin by 19, NOT pushing (user ruling: no push until web-overlay transparency AND
|
||||
audio-silence are addressed).
|
||||
|
||||
## ACTIVE THREAD (the user's directive: ONE problem at a time)
|
||||
## ACTIVE THREAD (ONE problem at a time)
|
||||
|
||||
**Web-uri transparency: still a black, opaque block** (take `ty-20260910-1202-0000-2.mp4` —
|
||||
user: "the web-uri resource transparency is still a black, opaque block. The desired animation
|
||||
runs just fine."). The animation-speed work is done; transparency is now THE web problem.
|
||||
**Web-uri transparency — branch (a) CONFIRMED and FIX-APPLIED, awaiting the verify take.**
|
||||
The 12:54/13:50 dumps proved the REAL widget document captures TRANSPARENT
|
||||
(`%TEMP%\ytLive-web-8d7234ec…-w1..5.png` alpha max 0/logged alpha 100% zero) YET the
|
||||
recording kept showing a black opaque box over the element rect (1231,679,705,396 — crisp
|
||||
edges, interior mean RGB ≈(1,6,13), NOT the desktop). Conclusion: the OLD inline
|
||||
`element.style.background='transparent'` injection only holds while the page has nothing
|
||||
to paint; once the widget connects and repaints a container background-COLOR, the capture
|
||||
re-opaques → black box. That is the OBS-solved class ("all web-uri resources paint their
|
||||
own background").
|
||||
|
||||
Institutional memory (MyMistakes, spin-guard entry): WebView2 `CapturePreviewAsync` honors the
|
||||
page's CSS — the pre-parse injection (`1a39b09`, `AddScriptToExecuteOnDocumentCreatedAsync`)
|
||||
is the recorded fix and is INTACT in `WebView2Manager.cs` (line ~157). The "? VERIFY" was never
|
||||
closed because the diagnostic dump + alpha log bound to the FIRST capture ever = the about:blank
|
||||
placeholder (blind; 5/5 sessions alpha=0 rgb=0).
|
||||
**Slice 14 change (commit target):** `WebView2Manager.TransparentBackgroundScript` is now a
|
||||
pre-parse `<style id='ytl-transparent-bg'>` wiping `background-color:transparent !important`
|
||||
on `html,body,html *` (background-images/art survive — only background-COLOR targeted).
|
||||
Idempotent by element id; `!important` outranks every page rule (OBS forums 2016 `body {
|
||||
background-color: rgba(0,0,0,0) !important }` + div variant, woahtech OBS custom-CSS guide —
|
||||
both cited in the commit message and `MyMistakes.md`).
|
||||
- `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.
|
||||
|
||||
**Slice 13 change (uncommitted):** re-point the instrument at reality —
|
||||
- `WebSourceSession.WidgetDumpRemaining`; `NavigationCompleted` arms `= 5` when the newly-loaded
|
||||
document is NOT about:blank.
|
||||
- Next 5 captures dump `%TEMP%\ytLive-web-<id>-w1..5.png` + `AlphaStats(...)` + FindContentBounds
|
||||
to startup.log.
|
||||
- `AlphaStats` extracted from the old inline first-capture block.
|
||||
## THE ONE REMAINING STEP (verify, no further analysis)
|
||||
|
||||
Build 0 warnings; 8/8 WebView2 tests pass (runtime not instantiable — re-point is log-only, no
|
||||
new test). Suite otherwise 290/291 (pre-existing compositor pixel).
|
||||
|
||||
## THE DECISION LADDER (no more cargo-culting)
|
||||
|
||||
1. User launches the app with the widget sources active, waits ~10s, closes. (NO recording needed.)
|
||||
2. Read the `widget capture [1..5/5]` lines in startup.log:
|
||||
- **zero% ≈ 0 (opaque):** pre-parse injection did NOT hold for this widget's own CSS →
|
||||
FIX = stronger transparent-background enforcement (document-level `!important` stylesheet
|
||||
injected pre-parse + re-asserted, per the OBS user.css precedent) OR chroma-key the known
|
||||
backdrop color in `CaptureFrame`. Pick after seeing the `-w1..5.png` PNGs.
|
||||
- **zero% large + tight contentBounds:** the capture HAS transparent margins → the recording's
|
||||
black block lives downstream → audit the compositor web-layer blend/underlay (PMA-vs-straight
|
||||
alpha, black pre-fill). Do NOT touch the capture path.
|
||||
3. Implement the branch's fix with ONE integration test (chroma-key math or compositor blend has
|
||||
testable seams), docs same-commit, then ONE verification recording.
|
||||
1. User REPLACES the app (build is current) and records the Live scene, widget animating,
|
||||
like the 12:54/13:50 takes.
|
||||
2. Verdict: element rect shows scene bg with widget art over it (NO black box) → transparency
|
||||
C LOSED, push gate #1 clears. If it still shows a solid black box you can SEE at a glance
|
||||
inside the widget's 705×396 area, bring it + then (and ONLY then) audit what other layer
|
||||
paints that rect in the composite — the web capture has been exonerated twice.
|
||||
3. Then the queued layer-order-save bug (dragging an element over another doesn't persist
|
||||
`SortOrder`) is the next single use case.
|
||||
|
||||
## Other threads (paused)
|
||||
|
||||
- **Audio silence** — `f3d578c` added per-5s `Audio live:` telemetry; next take with desktop
|
||||
audio ACTIVE + those lines names the stage. Push gate #2.
|
||||
- **Web capture speed** — 30Hz NOT achieved (35-117ms/capture → effective ~10-14Hz). The
|
||||
animation "runs fine" so the user is satisfied; revisit only if they want more.
|
||||
- **Webcam missing** in the 10:38/12:02 takes — "MJPG negotiation refused (being used by
|
||||
another process)" at startup. Queued behind transparency+audio.
|
||||
- **Audio silence** — `f3d578c` has per-5s `Audio live:` telemetry; next take with desktop
|
||||
audio ACTIVE names the stage. Push gate #2.
|
||||
- **Webcam missing** — "MJPG negotiation refused (being used by another process)". Queued.
|
||||
- **Web capture speed** — ~10-14Hz effective, user satisfied. Revisit only on request.
|
||||
|
||||
## Landmines
|
||||
|
||||
- testhost shares startup.log with app — filter by time.
|
||||
- `taskkill //F //IM testhost.exe //IM ytLive.exe` before rebuild.
|
||||
- testhost shares startup.log with the app — filter by time.
|
||||
- App was running at commit time; `taskkill //F //IM ytLive.exe` (Windows `taskkill.exe`,
|
||||
bash-quoted `//F //IM`) before rebuilds, and re-run if `MSB3021` copy-lock appears.
|
||||
- Build/tests: `/mnt/c/Program Files/dotnet/dotnet.exe build …` / vstest.
|
||||
- Probing: `/mnt/c/Program Files/Krita (x64)/bin/ffmpeg.exe` / `ffprobe.exe`.
|
||||
- Do NOT delete the crop/compositor paths while debugging source alpha (MyMistakes rule 3 —
|
||||
take-25 vandalism).
|
||||
- The user is frustrated with take-loops; the widget dump needs NO recording — an app launch
|
||||
suffices. Ask for launch + 10s + close, not a take.
|
||||
- Probing: `/mnt/c/Program Files/Krita (x64)/bin/ffmpeg.exe` / `ffprobe.exe` — Windows exes
|
||||
take Windows-style paths.
|
||||
- Never re-derive the transparency story again — MyMistakes "SPIN GUARD → RESOLVED" is the
|
||||
record (wash-rinse-repeat cost the user a whole session).
|
||||
Reference in New Issue
Block a user