Web source: crop to the WIDGET rect (visible-element union excluding full-viewport wrappers) — kills the ghost-boundary dead space

- scrollWidth/scrollHeight returns the whole 1920x1080 canvas for widgets whose body/background
  spans it, so the crop kept massive transparent margins and the content floated in the Fill box.
- ContentBoundsScript now unions every visible element's getBoundingClientRect, excluding elements
  that span >=98% of the viewport (bodies, full-canvas background layers); full-bleed widgets fall
  back to the full rect. Crop taken AT the rect's top-left (l,t) — content pins to bitmap (0,0).
- Web Image explicitly Margin=0 + Left/Top alignment so the frame and widget coincide at the origin.
- Canvas-size render untouched.
This commit is contained in:
2026-08-28 11:45:31 -07:00
parent b862a0fb04
commit a5b9952cbd
4 changed files with 82 additions and 36 deletions
+10 -9
View File
@@ -917,16 +917,17 @@ capturing the primary display (which causes the infinity mirror effect).
8. ✅ Preview shows live web content (not just a blank rectangle)
9. ☐ Handle navigation errors, invalid URIs, timeout gracefully
**Rendering model (2026-08-28, canvas-size viewport + real-content-bounds crop):**
**Rendering model (2026-08-28, canvas-size viewport + wrapper-safe content-rect crop):**
the page renders at the MASTER CANVAS size (1920×1080), stable, never tracked (no reflow/truncation;
scrollbars suppressed via `overflow:hidden`). After each navigation the manager measures the widget's
ACTUAL visible content rect — union of every visible `body *` `getBoundingClientRect` (excludes the
page's own margins/dead space, includes its top-left offset) — and **crops the capture to that rect**,
anchoring the widget at (0,0). `Stretch="Fill"` maps the frame flush under the box: the selection
box bounds the widget tight on all four sides and is pinned to (0,0) for any widget geometry.
⚠️ `ExecuteScriptAsync` JSON-encodes the returned value — the script must return a real OBJECT (not
`JSON.stringify`, which double-encodes → parse exception → crop silently skipped). Graceful-error
handling (item 9) is a follow-up.
scrollbars suppressed via `overflow:hidden`). After each navigation the manager measures the WIDGET
rect — union of every visible element's `getBoundingClientRect`, EXCLUDING elements that span
≥98% of the viewport (bodies, full-canvas background/overlay layers are layout wrappers, not
content; full-bleed widgets fall back to the full rect) — and crops the capture AT that rect's
top-left (l,t), pinning the widget to the bitmap's (0,0). `Stretch="Fill"` (+ explicit `Margin=0`,
Left/Top alignment) maps the frame flush under the box: the selection box bounds the widget tight
on all four sides, no dead space. ⚠️ `ExecuteScriptAsync` JSON-encodes the returned value — the
script must return a real OBJECT (not `JSON.stringify`, which double-encodes → parse exception →
crop silently skipped). Graceful-error handling (item 9) is a follow-up.
**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).