Skip to content

Commit ebc5318

Browse files
committed
fix: improve local capture fidelity
1 parent 1fa4041 commit ebc5318

24 files changed

Lines changed: 1882 additions & 238 deletions

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@ All user-visible changes to Web2UI — Copy for Figma are recorded here.
44

55
## Unreleased
66

7+
## 0.1.3 — 2026-08-02
8+
9+
- Recovered failed image and CSS-background assets with bounded, single-frame screenshots while
10+
preserving editable child layers; fallback slots now prioritize image nodes and larger regions.
11+
- Normalized WebP, AVIF, safely styled SVG, modern-color SVG, and tinted raster assets to
12+
Figma-compatible PNG data before clipboard export, without increasing the public asset budget.
13+
- Reserved the local asset budget for page imagery before optional generated glyphs and fixed
14+
transformed linear, radial, and angular gradients in the clipboard SVG.
15+
- Added real Manifest V3 regression coverage that verifies page images and fallback screenshots
16+
remain embedded exactly once and never become external clipboard references.
17+
- Preserved browser-measured legacy and image clipping, nested clip-path ownership, and
18+
positioned-auto paint order instead of flattening a larger page shell.
19+
- Added bounded screenshot recovery for browser-resolved generated content such as CSS counters,
20+
including pseudo-element paint outside the host box. Thin unsupported masked decorations may be
21+
omitted with a scoped warning so their editable host content remains intact.
22+
- Locked the measured capture viewport and preserved authored horizontal scroll positions while
23+
capturing nested fallback regions, preventing fallback pixels from being shifted by reflow or
24+
contaminated by neighboring content.
25+
726
## 0.1.2 — 2026-07-24
827

928
- Moved the public source, issue, security, and release links to `Lynavo/web2ui` after the

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ feature or reproduce the full semantic model of a native Figma document.
8686
- **Full Page capture** with bounded scrolling and page-settling behavior.
8787
- **Viewport presets** for Browser, 1920, 1440, 1024, 768, and 390 pixels.
8888
- **Theme selection** using the browser preference or forced light/dark emulation.
89-
- **Local asset recovery** for page-declared images, SVG, fonts, and CSS backgrounds.
89+
- **Local asset recovery** for page-declared images, SVG, fonts, and CSS backgrounds, including
90+
Figma-compatible PNG normalization for browser-decodable formats that are unsafe to paste
91+
directly.
9092
- **Bounded single-frame raster fallbacks** for content that cannot be represented safely as
9193
editable vectors.
9294
- **Editable text where possible**, carrying measured geometry, paints, effects, clipping, and
@@ -275,8 +277,11 @@ Canvas, video, WebGL, unsupported SVG, very large assets, browser-only effects,
275277
and rapidly changing pages use the `local-static-v1` profile. It takes at most one current-frame
276278
sample for each attempted fallback region, attempts no more than 12 regions, does not converge
277279
animation timelines or recover transparent layers from multiple screenshots, and may rasterize,
278-
approximate, omit, or report the region as a warning. The clipboard output does not preserve native
279-
components, variables, prototypes, or Auto Layout semantics.
280+
approximate, omit, or report the region as a warning. Within that fixed limit, image nodes are
281+
attempted before CSS-background hosts and larger regions are attempted first. Browser-resolved
282+
generated content such as CSS counters may become a local screenshot, while unsupported 1–2px
283+
masked decorations may be omitted to keep the host's text and structure editable. The clipboard
284+
output does not preserve native components, variables, prototypes, or Auto Layout semantics.
280285

281286
## Troubleshooting
282287

docs/ARCHITECTURE.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,27 @@ operations, performs conversion, and stores the current result.
4646
computed paints, text measurements, stacking information, fonts, and asset requests. It is the
4747
correct place to measure facts that only a browser can know. Later modules should consume those
4848
facts rather than infer them from class names, URL suffixes, text shape, or layer names.
49+
This includes legacy rectangular clips, image clip bounds, positioned-auto paint bands, and the
50+
nearest owner of a dense unsupported clip-path cluster.
4951

5052
Consent-overlay handling, full-page scrolling, asset recovery, and single-frame raster fallbacks
5153
are coordinated by the extension adapters. Cleanup runs in `finally` paths so capture markers,
5254
hidden overlays, emulation, debugger attachment, and scroll position are restored on success or
5355
failure.
56+
The debugger locks both Browser and preset captures to the viewport measured at capture start;
57+
this prevents clipped screenshots from changing Chromium's layout width between fallback regions.
5458

5559
The public dynamic-content behavior is `local-static-v1`. It takes at most one current-frame sample
5660
for each attempted fallback region, attempts no more than 12 regions, and does not finish animation
5761
timelines, perform multi-frame quality retries, recover transparent layers from a second screenshot,
58-
or use a hosted rendering environment. Failed or deferred regions become explicit labeled
59-
placeholders and retain the extractor's node-scoped warning.
62+
or use a hosted rendering environment. The fixed queue prioritizes image nodes, then CSS-background
63+
hosts, and uses captured area as the tie-breaker. Failed or deferred image regions become explicit
64+
labeled placeholders; unresolved background paints remain warning-scoped when their isolated
65+
single-frame recovery is unavailable. Browser-resolved generated content that cannot be expressed
66+
as literal text uses one host screenshot whose bounds include measurable pseudo-element paint.
67+
Unsupported masked pseudo-elements no thicker than 2px are omitted only when they have no filter,
68+
shadow, or outline that would extend their paint; the omission is warning-scoped and the host tree
69+
remains editable.
6070

6171
### Contract module
6272

@@ -117,8 +127,20 @@ The content runtime discovers assets declared by the page. The service worker ad
117127
only HTTP(S) URLs, omits credentials and referrer data, follows bounded time and byte limits, and
118128
returns bytes to the active capture. Inline SVG is checked before it enters a portable plan.
119129
These reads may contact the page's own origin or CDN; they never target a Web2UI service.
120-
If a declared asset is unavailable, conversion keeps the rest of the page usable: image nodes
121-
receive a neutral placeholder, missing paints are omitted, and the node-scoped warning is retained.
130+
PNG, JPEG, and GIF can remain direct clipboard assets. WebP, AVIF, tinted rasters, narrowly safe
131+
styled SVGs, and SVG colors that Figma may not preserve are browser-decoded to PNG under the same
132+
4 MiB per-asset and approximately 18 MiB total budgets. Required page imagery is admitted before
133+
optional generated glyphs.
134+
135+
If a declared image is unavailable, it is marked for the bounded screenshot flow instead of being
136+
silently omitted. An unresolved CSS background is captured in isolation and replaces only the
137+
host's background fill, leaving its child text and elements editable. Fallbacks are scrolled into
138+
view and captured with a live page-coordinate clip while Chromium's beyond-viewport screenshot mode
139+
remains disabled; this avoids that path temporarily reflowing the page. The live box is used when
140+
its measured dimensions remain stable, while the captured rectangle preserves expanded
141+
pseudo-element paint. `scrollIntoView` may move nested scrollers, so their authored horizontal
142+
positions are restored before pixel capture and all ancestor scroll positions are restored during
143+
cleanup. If recovery still fails, the node-scoped warning is retained.
122144

123145
### Local persistence
124146

docs/TESTING.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ out/e2e/playwright/local-copy/
7373

7474
The test verifies both capture modes, popup behavior, viewport/theme choices, clipboard
7575
preparation, result survival across a service-worker restart, and absence of capture data sent to
76-
a Web2UI host. The browser profile and screenshots are disposable and must not be committed.
76+
a Web2UI host. It also checks the raw clipboard SVG for exactly-once inline page assets, a recovered
77+
CSS-background screenshot, the preservation of a nested horizontal scroller during a canvas
78+
fallback, fixed pixel canaries for the canvas frame and isolated background, and the absence of
79+
external image references. Clipboard previews render in an independent clean browser so transient
80+
debugger viewport emulation cannot corrupt the evidence. The browser profile and screenshots are
81+
disposable and must not be committed.
7782

7883
## Writing reliable tests
7984

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "Web2UI — Copy for Figma",
4-
"version": "0.1.2",
4+
"version": "0.1.3",
55
"minimum_chrome_version": "106",
66
"description": "Capture the visible area or full page locally, then copy it for Figma.",
77
"action": {

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "web2ui-extension",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"private": true,
55
"description": "Local-only Chrome extension that captures web pages and copies them for Figma.",
66
"license": "AGPL-3.0-only",
@@ -53,5 +53,11 @@
5353
"typescript": "^5.9.3",
5454
"typescript-eslint": "^8.62.1",
5555
"vitest": "^3.2.6"
56+
},
57+
"pnpm": {
58+
"overrides": {
59+
"brace-expansion@<2.0.0": "1.1.18",
60+
"brace-expansion@>=4.0.0 <5.1.0": "5.0.9"
61+
}
5662
}
5763
}

pnpm-lock.yaml

Lines changed: 13 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)