Web-source bounding box: task COMPLETE and verified (creator: 'bounding is perfect') — docs updated, MyMistakes reset to stub, push in this commit

Root-cause note: the apparent remaining gap was a WIDGET GLOW EFFECT (the widget's own CSS glow pushed
out its perceived borders), not a code bug — the alpha-bbox already hugged the glow halo. Verified
perfect with a second widget without the effect.
This commit is contained in:
2026-08-28 12:16:43 -07:00
parent 9b56e88800
commit e75059a795
3 changed files with 32 additions and 66 deletions
+22 -19
View File
@@ -2,22 +2,25 @@
## Branch / Commit State ## Branch / Commit State
**`main`**, dirty working tree being committed as one unit (web source rendering fix — user-confirmed **`main`**, working tree clean. Web-source bounding work is **DONE, verified, committed, and
working). **NOT pushed — user rule: never push without explicit instruction (2026-08-24).** No feature PUSHED (2026-08-28)** — the first push in a while; the web feature is complete and on origin.
branches pre-1.0: all work lands on `main` per work unit. No feature branches pre-1.0: all work lands on `main` per work unit.
## The web-source rendering work (2026-08-28) — CURRENT MODEL: canvas-size viewport + ALPHA-BBOX crop ## The web-source rendering work (2026-08-28) — DONE: canvas-size viewport + ALPHA-BBOX crop
**History of failures (see MyMistakes.md for the full log):** box-size rendering clipped full-bleed **VERDICT (creator, 2026-08-28):** bounding is PERFECT. Verified with a second widget resource that
widgets to their upper-left chunk (fixed — page renders at the master canvas 1920×1080, stable, the has no glow effect. The earlier "remaining defect / dead-space gap" was the WIDGET'S OWN GLOW
one render the creator confirmed solid). Then four crop attempts failed in different ways: (1) the effect pushing out its perceived borders — NOT a code bug. The alpha-bbox was already hugging the
`JSON.stringify` return double-encoded under `ExecuteScriptAsync`, silently disabling the crop; glow halo; with a plain widget the dashed selection border sits exactly on the widget edge.
(2) `scrollWidth/scrollHeight` at canvas size returns the whole viewport for full-canvas wrapper
bodies → no trim (ghost boundary); (3) union-of-DOM-elements measured an unsettled layout on
NavigationCompleted and BROKE the image (creator's "don't break the rendering" boundary crossed).
(4) Reverting to f39ebd9's scroll-crop was another dead end (#2 again).
**The CURRENT MODEL (this commit):** **History of failures (log wiped from MyMistakes.md per task-complete rule; see git history):**
box-size rendering clipped full-bleed widgets to their upper-left chunk; `JSON.stringify` return
double-encoded under `ExecuteScriptAsync` (crop silently disabled); `scrollWidth/scrollHeight` at
canvas size = whole viewport for full-canvas wrappers (no trim); DOM-element-union measured an
unsettled layout on NavigationCompleted (BROKE the image); sizing the container to the crop
(`ab29ec8`) made things worse (reverted).
**The model that shipped:**
- `Services/WebView2Manager.cs` — page renders at 1920×1080 (stable, never tracked); `overflow:hidden` - `Services/WebView2Manager.cs` — page renders at 1920×1080 (stable, never tracked); `overflow:hidden`
+ transparent-bg injection. `FindContentBounds` scans the Bgra32 capture per frame and crops to the + transparent-bg injection. `FindContentBounds` scans the Bgra32 capture per frame and crops to the
bounding box of NON-TRANSPARENT pixels — the widget's true rendered extent, measured from the frame bounding box of NON-TRANSPARENT pixels — the widget's true rendered extent, measured from the frame
@@ -27,7 +30,7 @@ NavigationCompleted and BROKE the image (creator's "don't break the rendering" b
DELETED. DELETED.
- `MainWindow.xaml` — web `<Image>` Stretch set to Fill via the IsWebSource DataTrigger (unchanged). - `MainWindow.xaml` — web `<Image>` Stretch set to Fill via the IsWebSource DataTrigger (unchanged).
- Invariant: **render at canvas size; crop to the alpha extent; Fill to the box.** - Invariant: **render at canvas size; crop to the alpha extent; Fill to the box.**
- Unit tests: 3 new for `FindContentBounds` (tight box, no-alpha no-crop, full-alpha no-crop). - Unit tests: 7/7 (3 `FindContentBounds`, 4 control-size.)
**Property panel / sliders (2026-08-28):** web URI ✕ clears the textbox; web ✓/✕ icon buttons **Property panel / sliders (2026-08-28):** web URI ✕ clears the textbox; web ✓/✕ icon buttons
`IsTabStop="False"` so Tab walks X→Y→W→H→URI; Slider style `IsMoveToPointEnabled="True"` `IsTabStop="False"` so Tab walks X→Y→W→H→URI; Slider style `IsMoveToPointEnabled="True"`
@@ -35,7 +38,8 @@ restores click-anywhere-on-bar. All three confirmed fixed by the creator.
Build: 0 warnings; tests 7/7 (WebView2Manager). Rejected for the record: box-sized viewport (upper-left Build: 0 warnings; tests 7/7 (WebView2Manager). Rejected for the record: box-sized viewport (upper-left
truncation), fixed 1920×1080 + `Uniform` (letterbox dead space), CSS margin/100% reset (enlarged gap), truncation), fixed 1920×1080 + `Uniform` (letterbox dead space), CSS margin/100% reset (enlarged gap),
viewport==box (reflow/truncation/scrollbars), DOM-union crop (broke the image). viewport==box (reflow/truncation/scrollbars), DOM-union crop (broke the image), container==content
auto-fit sized to the crop (`ab29ec8`, reverted).
## Key facts / landmines ## Key facts / landmines
@@ -52,8 +56,7 @@ viewport==box (reflow/truncation/scrollbars), DOM-union crop (broke the image).
- No `CODING.md`; conventions live in `AGENTS.md` + `ai.md`. - No `CODING.md`; conventions live in `AGENTS.md` + `ai.md`.
## Next steps ## Next steps
1. AWAIT creator's verdict on the ALPHA-BBOX crop — reload a widget URL at several box sizes; the 1. Done (2026-08-28): web-source bounding verified PERFECT and pushed. If an animated widget causes
selection frame must snap flush to the widget with no dead space, image must stay identical for visible crop jitter later, add hysteresis as a follow-up — not queued.
full-bleed widgets. If an animated widget causes visible jitter, add crop hysteresis as a follow-up. 2. Graceful handling of navigation errors / invalid URIs (TASK 17 ×9) — only if the creator asks.
2. Follow-up only if the creator asks: graceful handling of navigation errors / invalid URIs (TASK 17 ×9).
3. Continue queue: TASK 18 local recording, TASK 21 media source, TASK 22 audio sync offset. 3. Continue queue: TASK 18 local recording, TASK 21 media source, TASK 22 audio sync offset.
-42
View File
@@ -3,45 +3,3 @@
> One task at a time. Updated before every commit touching that task: current iteration + > One task at a time. Updated before every commit touching that task: current iteration +
> why the last one failed. On task complete, committed AND pushed → truncate to this stub. > why the last one failed. On task complete, committed AND pushed → truncate to this stub.
> A new task does NOT seed this file until its first failure. > A new task does NOT seed this file until its first failure.
## Task (active — committed `582d1f4`, NOT pushed, awaiting creator verification)
Stream widget bounding box: hug the widget on all four sides, pinned to canvas (0,0).
## Iterations that failed
1. **e917c41 — fixed 1920×1080 viewport + `Stretch="Uniform"` + startup registration.**
Failed: `Uniform` letterboxed the 16:9 canvas into arbitrary‑sized boxes → asymmetric dead
padding read as a "gap". Lesson: Fill the box with the cropped frame, never Uniform.
2. **9d4103c — per‑document CSS reset (`html/body` width/height 100%) + (0,0) anchor.**
Failed ("enlarged the gap"): forcing dimensions overrode the widget's own layout and expanded
the dead space. Lesson: inject only `overflow:hidden` + transparent background — never set sizes.
3. **8d793af — viewport == element‑box size + `Stretch="Fill"`.**
Failed (janky / truncating / scrollbars): hosting at the box size clips full‑bleed widgets to
their upper‑left chunk, and tracking the box reflows the page on drag. Lesson: render at a
fixed canvas size, never track the box.
4. **f39ebd9 — crop capture to `scrollWidth/scrollHeight` bounds + (0,0) + Fill.**
Partially passed (panel fixes 1a/1b/4 confirmed) but the box stayed loose; the apparent
"tight right/bottom" was just the small‑viewport clip edge. **ROOT CAUSE: the crop never ran.**
The script returned `JSON.stringify(...)` (a string); `ExecuteScriptAsync` JSON‑encodes every
result, so the JSON was double‑encoded → `JsonDocument.Parse` got a string node →
`GetProperty` threw → try/catch swallowed → bounds unset → no crop. Lesson: return a real JS
object, never a stringified one. Also: scroll extents ≠ widget rect (they include page margins).
5. **70fe2b8 — fixed canvas‑size viewport (1920×1080) + crop + Fill.**
Image render/resize became solid (creator confirmed) but the box still gapped right/bottom and
wasn't 0,0. Same silent JSON bug — the crop still never executed — and the measure was scroll
extents regardless. Lesson: verify declared mechanics actually execute; assume nothing ran.
## Current iteration (web bounding box — UNCOMMITTED, verification pending)
ALPHA-BBOX CROP: `FindContentBounds` scans the captured Bgra32 frame and returns the bounding box
of non-transparent pixels (the background is injected transparent, so alpha = the widget's extent).
Capture is cropped to that rect and `Stretch="Fill"` maps it flush under the box. No DOM query, no
layout timing — the crop rect IS the rendered widget by construction, so it can NEVER truncate
visible content. Worst case (widget fills the canvas) = full frame = the confirmed-good render.
Canvas-size viewport + Fill untouched. `QueryContentBoundsAsync` + the JS script are DELETED.
## Iteration that just failed (`a5b9952` — wrapper-excluded DOM-union crop + image alignment)
Broke the image rendering — creator could not evaluate the box because the image was gone/ruined.
Why: the visible-element-union rect is measured ONCE on NavigationCompleted; widget layout isn't
settled then (async fonts/images, load animations, iframes), so the crop rect comes out tiny/wrong
and the Fill-stretched frame renders a broken/zoomed sliver. This was against the creator's explicit
"don't break the rendering" instruction. Lesson: measure actual RENDERED PIXELS (alpha), never
transient DOM state; and never ship a crop blind.
+9 -4
View File
@@ -917,16 +917,21 @@ capturing the primary display (which causes the infinity mirror effect).
8. ✅ Preview shows live web content (not just a blank rectangle) 8. ✅ Preview shows live web content (not just a blank rectangle)
9. ☐ Handle navigation errors, invalid URIs, timeout gracefully 9. ☐ Handle navigation errors, invalid URIs, timeout gracefully
**Rendering model (2026-08-28, canvas-size viewport + ALPHA-BBOX crop):** **Rendering model (2026-08-28, canvas-size viewport + ALPHA-BBOX crop — DONE, creator-verified):**
the page renders at the MASTER CANVAS size (1920×1080), stable, never tracked (no reflow/truncation; the page renders at the MASTER CANVAS size (1920×1080), stable, never tracked (no reflow/truncation;
scrollbars suppressed via `overflow:hidden`). `FindContentBounds` scans the Bgra32 capture and crops scrollbars suppressed via `overflow:hidden`). `FindContentBounds` scans the Bgra32 capture and crops
to the bounding box of non-transparent pixels — the widget's true rendered extent, measured from the to the bounding box of non-transparent pixels — the widget's true rendered extent, measured from the
frame itself (NO DOM query, immune to layout timing; can never truncate content; full-canvas widgets frame itself (NO DOM query, immune to layout timing; can never truncate content; full-canvas widgets
fall through to the full frame = prior verified-good image). `Stretch="Fill"` maps the cropped frame fall through to the full frame = prior verified-good image). `Stretch="Fill"` maps the cropped frame
flush under the selection box → box hugs the widget on all four sides. `QueryContentBoundsAsync` + flush under the selection box → box hugs the widget on all four sides. `QueryContentBoundsAsync` +
the JS content-bounds script are DELETED. ⚠️ Lesson bank: never return `JSON.stringify` from the JS content-bounds script are DELETED. **RESULT: bounding verified PERFECT by the creator with
`ExecuteScriptAsync` (double-encodes); never measure DOM stuff on NavigationCompleted (unsettled two widgets.** Root-cause note on the earlier "remaining defect/gap": that was a WIDGET GLOW EFFECT
layout broke the image — `a5b9952`); measure rendered pixels instead. (the widget's own CSS glow pushes out its perceived borders), not a code bug — the alpha-bbox was
already hugging the glow halo. Lesson: test with a second, plain widget before changing code.
⚠️ Lesson bank: never return `JSON.stringify` from `ExecuteScriptAsync` (double-encodes); never
measure DOM stuff on NavigationCompleted (unsettled layout broke the image — `a5b9952`); avoid
sizing the container to the crop (`ab29ec8` reverted — made it worse); a widget glow effect can
fake a gap — verify with a plain widget first; measure rendered pixels instead.
**Properties panel (2026-08-28):** web URI ✓/✕ icon buttons are `IsTabStop="False"` so Tab flows **Properties panel (2026-08-28):** web URI ✓/✕ icon buttons are `IsTabStop="False"` so Tab flows
X→Y→W→H→URI; the ✕ button now clears the URI textbox (was reverting to the pre-accept snapshot). X→Y→W→H→URI; the ✕ button now clears the URI textbox (was reverting to the pre-accept snapshot).