Acquire section paragraphs once, reuse proven fitting layouts, and bound retained text with borrowed native-resource lifetimes. Share document text family selection and Arabic/CJK coverage resolution rather than preserving missing glyphs. Validate exact zoom parity with real existing font fixtures.
Share indexed recency, count/weight budgets and native disposal across six caches while preserving domain invalidation and eviction policies. Keep pools, weak memos and dependency-owned resources separate.\n\nAccount for effect pixels incrementally and preserve caller ownership on rejection. Validate disposal, slot reuse, exact integer accounting and unchanged raster output.
Choose readable opposite edges for rotated frame titles and size badges, sharing placement with hit testing. Render section titles as compact inset badges with contrast-aware borders and hover feedback.
Backing installation advances the preview baseline but previously stamped old whole-scene pictures with the new scene version. Zooming outside backing coverage could replay deleted content and hide newly created shapes.
Discard mismatched pictures before advancing that baseline, preserving valid pictures. Cover scene, preview, page and font invalidation plus exact visible pixels through zoom reversals in retained and tiled renderers.
Keep successful glyph coverage weakly owned by source nodes rather than repeating shaping when dense scans evict native paragraphs. Preserve font/input invalidation and bound pending ID invalidations without increasing native cache limits.
Add a dense-preview regression with exact raster comparison and count paragraph builds across all canvas renderers. The previous compiled build rebuilds 3,600 paragraphs over three held frames; the fixed build rebuilds none.
Skia analytic coverage depends on framebuffer dimensions as well as raster origin. Replay existing retained subtree pictures into the live viewport at settlement, while preserving overscan images for navigation. Keep settlement pending until the viewport pass and report the presentation path accurately. No additional viewport cache is allocated.
Preserve exact held/released assertions and cover odd/even/fractional pans at DPR 1, 1.25, 1.5 and 2. Verify native picture and backing identity reuse; update one pixel in the existing baseline only after proving equality with direct rendering.
Validation: full check, 735 scoped unit tests, and 58 targeted browser tests pass. The targeted set still excludes the previously classified paint-field-width baseline. An additional tiled large-blur test fails identically on parent d16400b3e, with byte-identical actual PNGs. Broader release acceptance remains separate.
Unify preview-aware transforms across scene drawing, labels, selection and input. Give live property and creation edits explicit preview ownership, preserve cancellation and one-step undo, and reuse bounded text preparation resources.
Include retained device-grid handling, worktree HMR coverage, and nested/reflected interaction regressions in this cohesive progress checkpoint.
Validation: full check passes; 734 scoped unit tests and 53 targeted browser tests pass. This is NOT merge-ready: the unchanged exact nested filled-section held/released regression still fails with 63 differing pixels (maximum channel delta 5/255). The browser pass count excludes that separately run failure and the previously classified paint-field-width baseline. Raster-origin investigation and broader acceptance remain outstanding.
Reject unknown or incompatible references while the owning component definitions are available, including inherited component-set definitions. Keep nested component scopes separate.
- Add a typed, modular custom Oxlint rule package with direct regression coverage
- Replace complex conditional object spreads with explicit construction across the repository
- Preserve all existing custom rule registrations and diagnostic behavior
ARROW_LINES and ARROW_EQUILATERAL stroke caps were accepted by the schema
and round-tripped from .fig files, but the renderer mapped every cap to
Butt, so imported arrows lost their heads and arrows could not be drawn
locally.
Add a pure arrow-cap geometry module to scene-graph (open-endpoint
collection with per-vertex cap overrides, weight-scaled head geometry)
and draw heads in the stroke color for LINE nodes and open VECTOR
centerline strokes, including the dashed-centerline path. Fold the head
reach into strokeOverflow and the node picture margin so heads are not
cropped by visual bounds, caches, or culling. Round-trip per-vertex
stroke caps through the .fig vector style override table so one-ended
Figma arrows import correctly. Expose both caps in the stroke inspector
cap picker with localized labels, and cover the renderer with an
arrow-stroke-caps Playwright canvas snapshot.
Claude-Session: https://claude.ai/code/session_012zjEbyLSBYyPpJP4fu1XNB
* perf(canvas): add traced navigation benchmarks
- Record and replay timestamped pan and zoom gestures through DOM and CDP input paths\n- Correlate input, viewport, render, long-task, and retained-backing events in Chromium traces\n- Report frame pacing, latency, jump, anchor drift, and crisp-settlement metrics
* perf(canvas): stabilize navigation comparisons
- Separate low-overhead metric runs from optional CPU-profile traces\n- Warm scenarios before recording and use a consistent SwiftShader browser configuration\n- Add a canonical momentum-pan reversal gesture alongside pinch reversal
* fix(canvas): require hardware GPU navigation benchmarks
- Run macOS performance captures through Metal-backed ANGLE and reject accidental SwiftShader fallback\n- Record the GL renderer and reserve software GPU mode for portable correctness smoke runs
* perf(canvas): cache shadow rasters for crisp backing
- Rasterize local drop and inner shadows only while constructing retained scene backing\n- Bound native image memory and invalidate cached entries with node and renderer lifecycle changes\n- Quantize zoom-aware raster resolution and reuse nearby scales without lowering normal scene quality
* test(canvas): verify retained shadow raster fidelity
- Compare settled retained-backing shadow output with direct CanvasKit rendering\n- Keep backdrop blur on the picture fallback and exercise graph-driven cache invalidation\n- Cover updates, deletion, and reparenting through actual SceneGraph events
* perf(canvas): benchmark real FIG fixtures
- Serve exact local fixture bytes through an isolated Playwright route for production preview runs\n- Wait for document loading and page population before zooming to fit and recording navigation\n- Record the resolved fixture path in benchmark environment artifacts
* fix(canvas): preserve nested effect subtree pictures
- Keep deeply nested shadow documents on one retained subtree picture instead of exploding them into per-node image draws\n- Restrict shadow raster acceleration to effect-bearing page children\n- Cover nested shadow fallback and restore gold-preview FIG pinch performance to master levels
* refactor(canvas): share recorded wheel sample type
* perf(canvas): defer backing settlement across zoom reversals
- Track explicit navigation phases and gesture generations instead of inferring idle from viewport timing\n- Cancel or defer retained backing construction while pan, zoom, momentum, or tentative settlement is active\n- Add a repeated short-pause pinch reversal fixture based on the user trace
* perf(canvas): index bounded render chunks
- Split oversized painter subtrees into self-paint and bounded descendant chunks without dropping container visuals\n- Bulk-load chunk visual bounds into RBush for selective world-space queries\n- Cover bounded updates and gold-preview build/query complexity before tile rendering consumes the index
* refactor(canvas): namespace render chunk coverage
* perf(canvas): model chunk paint context
- Preserve ancestor transform and clip dependencies for independently renderable chunks\n- Keep opacity, blend, blur, and mask isolation subtrees atomic until command-level splitting exists\n- Report oversized atomic chunks and lock gold-preview to bounded painter units
* perf(canvas): record pixel-correct render chunks
- Record interruptible chunks in world coordinates with ancestor transforms, clips, and chunk-local culling bounds\n- Draw opacity, blend, blur, and mask isolation chunks directly into destination surfaces in painter order\n- Compare composited chunk output with direct CanvasKit rendering instead of relaxing visual thresholds
* perf(canvas): render selective world tiles
- Map world regions to fixed 256-device-pixel tile targets and quantized sharpness levels\n- Query only intersecting render chunks and preserve atomic destination compositing\n- Match multi-tile CanvasKit output against direct rendering and measure gold-preview tile cost
* perf(canvas): cache chunk pictures across tiles
- Reuse world-space chunk command pictures for every intersecting tile\n- Pool 256-pixel tile surfaces and expose allocation, draw, flush, and snapshot timings\n- Keep expensive atomic foreground blur visible as an over-budget scheduler constraint
* perf(canvas): schedule cached tile rendering
- Bound tile images with an LRU cache and reuse pooled CanvasKit surfaces\n- Plan mandatory holes, stale visible refreshes, and overscan by navigation and content generation\n- Stop jobs at a strict deadline while reporting fallbacks, stale work, overruns, and over-budget effects
* perf(canvas): integrate progressive tiled rendering
- Keep retained scene output as the interaction fallback while exact tiles refine only after navigation becomes idle
- Centralize runtime URL flags and pass renderer selection through the typed Vue canvas API
- Replace benchmark sleeps with explicit mode-aware renderer settlement and report exact tiled coverage
- Preserve bounded scheduler metrics, generation cancellation, native resource cleanup, and shared visual-bounds logic
* refactor(app): centralize runtime query configuration
- Parse collaboration, recent-files, benchmark, presentation, and renderer flags in one typed app module
- Remove ad hoc URL parsing from workspace and collaboration runtime consumers
- Cover supported values and production-safe defaults without adding a repository lint rule
* fix(canvas): replace fallback pixels with exact tiles
- Render opaque page-background tile cells and install them with source replacement instead of double-compositing translucent scene content
- Exercise the live progressive controller against direct rendering across masks, effects, blend isolation, images, fallback text, transforms, and clipping
- Preserve the bounded reversal path with zero Long Tasks and exact settlement near 128 ms on gold-preview.fig
* perf(canvas): invalidate tiled content selectively
- Index chunk dependencies across contained nodes and transform or clipping ancestors
- Re-record affected chunk pictures and invalidate tiles intersecting old or new visual bounds
- Advance unaffected cached tiles to the new scene generation instead of rebuilding the full chunk index and tile cache
- Keep structural graph mutations on the safe full-rebuild path and cover selective refresh end to end
* perf(canvas): bound atomic blur tile refresh
- Render atomic blur chunks with tile-local isolation bounds and blur halos instead of replaying full-subtree layers
- Keep content refresh behind the retained fallback, cap GPU submissions to four tile jobs per frame, and adapt estimates from measured work
- Preserve large-radius CPU over-budget visibility while preventing Metal-backed refresh bursts and deferred GPU overload
- Add deterministic node-mutation benchmarks and summarize scheduler throughput, job duration, overruns, and exact content settlement
* perf(canvas): cancel obsolete tile refresh generations
- Count and report queued jobs removed by content or navigation generation changes
- Add deterministic mutation-then-reversal benchmark support without sleeps
- Assert exact tile work remains suspended during navigation and resumes for the final viewport
- Summarize cancellation alongside scheduler throughput, overruns, and settlement metrics
* test(canvas): cover live tiled blur settlement
- Load gold-preview.fig through the real tiled canvas surface and wait on explicit renderer settlement
- Commit the settled radius-210 large-blur browser snapshot
- Replay the canonical zoom reversal during refresh and require byte-identical final canvas convergence
* fix(canvas): harden renderer resource lifecycle
- Release tiled surfaces, images, pictures, and queued work across surface, font, graph, page, structure, and renderer lifecycle boundaries
- Restore pooled canvas, viewport, and backing state through exception-safe native recording and raster paths
- Rebuild tiled chunk topology only when isolation requirements actually change, preserving selective blur mutation performance
- Document deterministic active-renderer settlement and add lifecycle, graph replacement, cache failure, and surface replacement regressions
* test(canvas): remove source-matching renderer claims
- Delete the autopsy suite that inferred runtime correctness from source text, regexes, line placement, and symbol counts
- Keep renderer ordering, cache cleanup, effect behavior, and pixel fidelity covered by executable behavioral and lifecycle tests
* perf(canvas): present retained backing during tiled navigation
- Profile production reversal traces and attribute tiled p95 cost to GPU command-buffer flushes from full-scene fallback replay and tile presentation
- Use the retained backing as the moving fallback while tile scheduling and cached lookup remain allocation-free
- Defer tile image presentation until idle and expose visible versus presented tile counts in navigation telemetry
- Reduce tiled reversal render p95 from about 8ms to 0.3ms while preserving exact idle replacement and visual parity
* perf(canvas): prioritize visible tile settlement
- Profile per-tile allocation, draw, flush, snapshot, and chunk costs through scheduler telemetry
- Defer overscan until all visible exact tiles are covered
- Replace the four-job idle cap with a higher safety ceiling while the measured five-millisecond deadline controls cheap work
- Reduce mutation-plus-reversal exact settlement from about 272ms to 160ms without Long Tasks, overruns, or over-budget jobs
* refactor(canvas): clarify renderer lifecycle boundaries
- Extract retained backing state types and navigation preview timing\n- Isolate tiled scheduler telemetry from frame orchestration\n- Document settlement and CanvasKit ownership invariants\n- Preserve hot drawing loops, budgets, cache limits, and rendering decisions
* fix(canvas): preserve current label rendering
Retain the merged paragraph-label cache lifecycle and substituted-font readiness while reconstructing the renderer stack on current master.
* test(canvas): keep tile benchmark assertions deterministic
Keep performance timing in benchmark telemetry while asserting structural tile selectivity and cache behavior in CI.
* feat(canvas): expose experimental tiled rendering
- Persist retained or tiled canvas mode in General settings\n- Keep retained rendering as the default and apply changes after reload\n- Preserve URL overrides for deterministic benchmarks and support reproduction
* refactor(app): centralize renderer preference state
Expose renderer override provenance from runtime configuration and keep the settings control's derived state separate from its explicit persistence action.
* refactor(app): share settings layout anatomy
Reuse slot-based section headers and bordered groups while keeping each settings control row explicit.
* fix(canvas): harden tiled renderer boundaries
- Bound low-zoom tile planning and handle failed tile surface allocation\n- Preserve effect raster dependencies, runtime-safe clocks, and navigation timing contracts\n- Keep benchmarks deterministic, backward compatible, and accurately localized
* fix(canvas): invalidate dependent node pictures
Track first-child shadow dependencies for retained node pictures so child geometry updates cannot leave stale parent shadows.
* fix(canvas): edit section labels inline
- Vertically center shaped section titles using cached paragraph metrics\n- Add generalized canvas-label edit state and a reusable inline label control\n- Match dark and light label presentation while editing and commit renames with undo
* fix(canvas): align label contrast handling
Use shared WCAG contrast selection for rendered and edited canvas labels while retaining exact node-fill presentation in the editing overlay.
* fix(canvas): composite translucent label colors
Choose label foreground contrast after compositing translucent section fills against the active canvas background.
* fix(text): prepare browser fonts atomically
- Fetch approved Fontsource resources directly in browsers with bounded responses and retryable provider failures
- Limit page font resolution concurrency and retain live Inter substitution paragraphs after baked glyph invalidation
- Shape frame, section, and component labels through a bounded native Paragraph cache
- Cover real Geist, Geist Mono, and Roboto Mono browser loading plus substitution and cache lifecycle behavior
* test(canvas): cover substitution and label shaping
- Capture baked missing-font text before editing, live Inter substitution on first input, and visible undo output
- Assert text-picture and derived-glyph invalidation with finalized substituted readiness
- Cover shaped frame, rotated frame, section, component, component-set, ellipsis, kerning, and zoom label presentation
* fix(text): preserve same-origin fetches during font resolution
- Route same-origin application and fixture requests through native fetch while Unifont temporarily proxies global HTTP requests
- Keep the external provider HTTPS allowlist enforced for cross-origin font resources
- Cover the production race discovered while loading gold-preview.fig during provider initialization
* fix(text): preserve substituted path glyphs
- Keep imported derived curved glyphs for text-on-path layers when exact fonts finalize as substituted
- Cover substituted path rendering through the runtime renderer and refresh the corrected visual oracle
- Update shaped Inter measurement badges and merged typography panel snapshots after visual inspection
- Search virtualized font catalogs explicitly and restore the canvas screenshot helper used by broad E2E coverage
* fix(text): use browser font transport without desktop warning
- Keep the browser provider implementation aligned with current settings behavior after splitting from preparation
* test(text): inject same-origin browser font context
- Keep the transport test deterministic outside a Window global
* fix(text): preserve final browser font transport hardening
- Carry the same-origin large-document bypass and bounded cross-origin provider checks from the preserved integration snapshot
* fix(text): initialize font transport outside Window contexts
- Keep unit and headless module imports safe while browsers use their current origin
* test(text): satisfy async visual fixture contract
- Return from the resolver setup continuation after requesting render
* test(canvas): include paragraph cache lifecycle
* fix(text): cancel queued browser font loads
- Race serialized provider work against preparation cancellation\n- Release queue slots after cancelled waiters without disturbing active requests\n- Reuse one section-title paragraph cache entry for measurement and drawing
* fix(text): close font queue cancellation races
- Release reserved proxy queue slots when cancellation lands after queue acquisition\n- Skip paragraph work for zero-width section labels
* fix(text): abort active provider resolution
- Race active provider resolution against its preparation signal and restore the temporary fetch proxy promptly\n- Forward cancellation through proxied provider requests\n- Align the renderer font-readiness facade with substituted text
- Dispose CanvasKit dash effects and render nested frame guides
- Validate imported guide GUIDs and invalidate stale raw guide metadata
- Resolve frame owners through nested hit ancestry and require primary-button ruler drags
- Share guide preview types through a neutral editor module
* refactor(canvas): adopt immutable CanvasKit paths
- Upgrade CanvasKit to 0.41.1 across app, Core, CLI, and Vue peers
- Build mutable geometry with PathBuilder and detach immutable paths for drawing
- Replace in-place strokes, transforms, and boolean operations with immutable results
- Update renderer mocks and path construction tests for the new API
* test(canvas): model immutable path ownership
- Return detached path instances from CanvasKit builder mocks
- Assert shadow ring and combined path disposal
- Preserve recorded vector operations across ownership transfer
* fix(build): align CanvasKit workspace versions
- Snap vector points, moved layers, and resized edges to geometry, objects, guides, and pixels
- Add persistent snapping preferences with browser and native menu controls
- Normalize canvas and layout guides across Scene Graph and .fig conversion
- Clear transient snapping feedback across interrupted interactions
* feat(code): isolate Design JSX execution
- Transform JSX with the existing Sucrase dependency and execute it in a disposable opaque-origin iframe worker
- Block ambient network capabilities and enforce source, timeout, output, depth, and element limits
- Validate that only bounded plain structured data returns to the application
* feat(code): add editable Design JSX
- Add a lazy CodeMirror editor with JSX syntax support, completion, diagnostics, and bounded scrolling
- Convert validated sandbox output into trusted Design JSX helpers before rendering
- Apply or insert JSX as one undoable graph transaction while preserving dirty drafts
* feat(code): localize JSX editor actions
- Add translated-message fallbacks for editing, applying, inserting, and draft state
- Document the editable JSX workflow in the unreleased changelog
* fix(code): satisfy typed sandbox validation
- Keep the sandbox document terminator literal and preserve optional selection handling under type-aware lint
* test(code): resolve sandbox probes through app aliases
* fix(code): bound sandbox results before cloning
- Enforce string, array, object, depth, element, and byte limits inside the disposable worker
- Retain host-side validation as a second structured-data boundary
* fix(code): keep JSX and HTML editing modes separate
- Switch generated Tailwind output back to OpenPencil JSX before editing
- Close the JSX editor when opening HTML/CSS import and avoid stacking both editors
* feat(code): support authored Design JSX programs
- Allow local constants, function components, arrays, conditionals, fragments, and multiple roots
- Preserve replacement positions for multiple selected roots and reject mixed-parent or locked selections
- Cover multi-root undo, redo, and all-or-nothing rollback
* feat(code): add explicit JSX view mode
- Let authors leave CodeMirror without applying a draft
- Keep the editable surface and HTML/CSS importer mutually exclusive
* feat(code): diagnose unknown JSX vocabulary
- Warn on OpenPencil elements and properties that are absent from the canonical schema
- Surface diagnostics inline through CodeMirror lint markers
* test(code): accept WebKit isolation diagnostics
- Cover the sandbox architecture against Playwright WebKit
- Accept engine-specific wording while preserving the same unavailable-window assertion
* refactor(code): consolidate Design JSX vocabulary
- Drive renderer warnings, completion, and diagnostics from one supported-property schema
- Recognize locally declared components and add focused schema and transform tests
- Replace CodeMirror basicSetup with an explicit feature set and remove the umbrella package
* fix(code): support Design JSX variable helpers
* refactor(code): unify JSX sandbox validation
* fix(code): account for complete sandbox output
* fix(code): preserve locked JSX descendants
* fix(code): preserve JSX sibling order
* fix(code): recompute JSX parent layouts
* feat(code): add live code previews
* test(code): centralize graph assertions
* fix(code): harden live preview sessions
* docs: describe live code editing
* fix(code): update editor accessibility labels
* fix(code): use theme-aware error colors
* fix(dev): stop Vite disconnect error loops
* fix(code): clarify live preview status
* fix(ui): avoid tooltip attribute warnings
* test(code): assert semantic preview status
* refactor(code): remove obsolete editor messages
* fix(code): harden preview concurrency and isolation
* fix(code): cancel stale reset previews
* feat(canvas): measure distances between layers
- Show temporary Figma-style guides while Option or Alt is held
- Support deep nested targets with Command or Control plus Alt
- Measure transformed world bounds and container padding
* fix(canvas): anchor diagonal measurement guides
- Join diagonal distances at the nearest object corners
- Separate horizontal and vertical value badges
- Crop visual coverage tightly around the measurement overlay
* fix(canvas): cover measurement edge cases
- Draw transformed target outlines and center value pills on guides
- Suppress conflicting hover overlays during measurement mode
- Cover containment, overlap, multi-selection, rotation, and fractional gaps
* fix(canvas): suppress conflicting measurement overlays
- Hide auto-layout hover visuals while distance measurements render
- Assert fractional measurement geometry with numeric tolerance
* fix(core): preserve inline SVG primitives
- Convert parsed circle, ellipse, rect, line, polyline, and polygon children through the shared SVG path pipeline
- Preserve nested presentation attributes, transforms, and JSX-style SVG attribute names
- Cover mixed and primitive-only inline SVG renders
* chore: format merged mobile navigation
* fix(core): disambiguate xmldom document types
- Alias the xmldom Document type so clean CI does not resolve it as the browser DOM type
- Guard the generated SVG document root before collecting paths
* fix(core): preserve inline SVG root styles
- Carry root presentation properties into parsed SVG child conversion
- Apply nested SVG transforms before creating vector networks
- Cover inherited root fills and transformed primitive geometry
* fix(core): scale transformed SVG strokes
- Warn once with the rejected surface dimensions and original CanvasKit error
- Disable repeated cache allocation attempts until the renderer surface is replaced
- Keep direct scene rendering as the visible fallback
- Scale retained margins to a fixed device-pixel budget on large viewports
- Fall back to direct scene rendering when CanvasKit rejects an offscreen surface
- Cover the reported wide HiDPI geometry without coupling rendering to a WebGL context
- Normalize SVG paint sentinels before creating icon vector nodes
- Keep inline outline artwork transparent instead of falling back to black
- Cover fill and stroke none values through the Design JSX renderer
Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
- Resolve Figma path style overrides into format-neutral geometry fills
- Render and export per-path paints across normal and pattern drawing
- Preserve path fills through copying, instance scaling, and derived geometry
- Cover import, serialization, scaling, and CanvasKit output
Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
Enable CanvasKit half-leading for solid and gradient paragraph rendering while preserving the existing font fallback system.\n\nCo-authored-by: jongwong <jongwong@aliyun.com>
Fill the three biggest gaps that blocked authoring real designs in JSX:
- mask prop on any node: <Ellipse mask> / mask="luminance" / mask="vector"
sets isMask + maskType so sibling content clips (Figma's mask model)
- <svg> element rendering inline SVG markup into vector nodes, reusing the
iconify path pipeline (extractPaths + parseSVGPath + scalePathInfos)
- Instance overrides: <Instance of="X" overrides={{ 'childName:prop': v }} />
applies child overrides by name and records them so component sync keeps them
Adds gap tests and the shared scalePathInfos/finishIconRender helpers to
avoid duplicating the iconify pipeline.