fix(web): inject transparency BEFORE page scripts — CapturePreviewAsync honors page CSS

The widget page paints html/body opaque, so CapturePreviewAsync output had
zero alpha-0 margins — every take since bccdb48 built the crop on 'the
capture is transparent' and never verified it. WebView2 spec is explicit:
'WebView will always honor a webpage's background content' and
DefaultBackgroundColor only shows through pages with no background style
(MicrosoftEdge/WebView2Feedback specs/BackgroundColor.md). The late
ExecuteScriptAsync injection (NavStarting/NavCompleted) ran after page CSS
and lost the fight.

Fix via CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync — the
documented pre-parse hook ('before the HTML document has been parsed and
before any other script included by the HTML document is run'), the same
mechanism OBS user.css uses. Nav handlers kept as post-load re-assertion.

Restores the take-23/24 stride-correct crop path (my take-25 removal was
wrong — it regressed the bounding box into a solid black box).

Adds one-shot diagnostics: raw capture PNG -> %TEMP%/ytLive-web-<id>.png
plus alpha min/max/mean/%zero and FindContentBounds result in startup.log,
so the next run proves the capture is transparent instead of guessing.

Build 0 warnings, 289/289 tests pass. MyMistakes.md records the full chain.
This commit is contained in:
2026-09-08 14:19:30 -07:00
parent e564e9884c
commit 1a39b091a0
3 changed files with 130 additions and 56 deletions
+30 -22
View File
@@ -2,37 +2,45 @@
## Branch / Commit State
**`main` HEAD = `081e4c1`** — take-24: PasteKey includes CropBounds — stale raster cache fixed.
**`main` HEAD = `081e4c1`** — take-24 (PasteKey CropBounds). Working tree carries the
real fix (uncommitted): pre-parse transparency injection + diagnostics.
```
081e4c1 fix(web): add CropBounds to PasteKey — stale raster cache was corrupting transparency
f6802c7 fix(web): stride-correct CropBounds Fill rendering in compositor (take-23)
84aa70f docs: MyMistakes.md — take-22 full fix for widget scale/position + transparency
ed9d7c1 fix(web): CropBounds metadata + Fill-style scaling in compositor — transparent margins preserved, widget fills element rect (BROKEN — reverted)
5ca9778 docs: update HANDOFF + MyMistakes.md — take-21 web transparency fix landed
e002847 fix(web): feed full canvas to compositor — transparent margins reveal layers beneath (take-21)
70db344 perf(camera): MJPEG negotiation before frame reader — camera drives at its best rate
87509bc (origin/main) docs: restructure TASKS.md into a catalog
```
## Web Overlay — the ROOT CAUSE (finally named, not guessed)
**Pushed to origin: `87509bc`.** 8 unpushed commits after it.
WebView2's `CapturePreviewAsync` **always honors the page's own background** — the widget
page paints html/body opaque, so the captured PNG has **no alpha-0 margins, ever**.
`DefaultBackgroundColor=Transparent` only shows through pages without a background style.
All prior takes (19-24) assumed the capture had transparent margins — it never did.
`FindContentBounds` therefore had nothing to crop to, and every blend saw alpha=255 =
black box + "transparency broken" + lost resizing. One bug, three symptoms.
## Web Overlay — Current State
**Fix applied (verify by running):**
1. `WebView2Manager.InitializeAsync`: `AddScriptToExecuteOnDocumentCreatedAsync(TransparentBackgroundScript)`
— injects html/body `background:transparent` BEFORE the page parses/scripts run (the
OBS user.css equivalent). NavStarting/NavCompleted keep the script as post-load re-assert.
2. Restored the take-23/24 compositor crop path (stride-correct `BlitContentRaw` +
`CropBounds` in PasteKey) — my previous take-25 gutting was wrong.
3. Diagnostics: first capture per source dumps the RAW WebView2 PNG to
`%TEMP%\ytLive-web-<id>.png` and logs alpha stats + FindContentBounds result to
startup.log.
take-22 (`ed9d7c1`) BROKE: stride bug (crop width used for canvas buffer indexing) — REVERTED.
take-23 (`f6802c7`): stride fixed, but PasteKey missing CropBounds → stale raster cached → transparent pixels corrupted to opaque.
take-24 (`081e4c1`): FIX. `PasteKey` now includes `frame.CropBounds`. Different crop bounds = cache miss = fresh raster with correct alpha.
## NEXT STEP (user run required once)
## Still open (needs your run + log)
Run the app with the web overlay, then:
- Read `%APPDATA%\ytLlive\startup.log` — the `alpha[min=..,max=..,mean=..,zero=..%]`
line PROVES whether the capture is transparent now (mean near 0 + high zero% + a tight
contentBounds = fixed) or still opaque (mean ~255 → injection didn't beat the page).
- Agent can PIL-analyze `%TEMP%\ytLive-web-<id>.png` for the true alpha bbox.
- **Chat overlay missing from recording:** Chat shows in preview but not in recorded output.
- **Audio/sync issues:** unspecified, no log yet.
- **Webcam/capture speed:** unspecified, no log yet.
## Still Open
- Chat overlay missing from recording (separate issue — not yet touched this session)
- Audio/sync: non-event (headset volume), closed
## Landmines
- testhost shares startup.log with app — filter by time when triaging
- Stale testhost/exe locks DLL: `taskkill /F /IM testhost.exe /IM ytLive.exe` first
- testhost/exe lock DLLs: `taskkill /F /IM testhost.exe /IM ytLive.exe` before rebuild
- Do NOT run full-suite vstest (WASAPI hang); flow = clean build + per-class + scope-check
- Kill app before build: `/mnt/c/Windows/System32/taskkill.exe /F /IM ytLive.exe`
- ffmpeg month-end pinned; `Startup.log` "Recording saved:" shows real final path
- The PNG dump is written ONCE per session per source (DebugPngWritten flag)