Commit graph

89 commits

Author SHA1 Message Date
Danila Poyarkov 38e80a4567
Merge branch 'master' into mcp-v2-webmcp 2026-09-15 19:55:49 +03:00
Danila Poyarkov a91647b852 fix(tools): guard checkpoint identities and share input schemas
Reject replacement of captured node and variable objects, preserving references on rollback. Reuse traversal and comparison schemas and the node-not-found helper to remove the duplication failures without changing tool inputs.
2026-09-15 19:45:30 +03:00
Danila Poyarkov f25e3f72a2 fix(tools): preserve atomic property state and validate inputs
Record property presence for exact undo/redo, including explicit undefined values, and reject instance index mutations. Tighten numeric and operand schemas to match execution contracts.
2026-09-15 16:47:37 +03:00
Danila Poyarkov 4a9bad5cec refactor(tools)!: centralize schemas and execution contracts
Define native Valibot inputs and execution/exposure metadata on each tool. Derive effects and default capabilities, consume upstream Standard Schema conversion, and validate finite numeric inputs consistently across adapters.

Move atomic execution to Core and restore failures from Scene Graph checkpoints without relying on a property diff. Preserve topology, collections, indexes and surviving object identities during rollback.

BREAKING CHANGE: custom tools use input schemas and execution metadata instead of params, ParamDef and independently declared mutation flags. Direct tool execution validates inputs before invoking the handler.
2026-09-15 10:55:27 +03:00
Danila Poyarkov 3ea63ad09f fix: refresh Undo and Redo command availability
Publish committed history changes independently of scene mutations so menus observe history recorded after the final draw. Align the assets regression with the documented top-left default.
2026-09-14 00:25:36 +03:00
Danila Poyarkov f8f5f0fc4e fix(instances): preserve ambiguous imported extras 2026-09-11 10:57:18 +03:00
Danila Poyarkov ff1c157a86 refactor(fig): clarify imported instance reconciliation 2026-09-11 10:30:03 +03:00
Joseph Cumines a4350396a8 fix: prevent unbounded duplication of Figma-imported instance children
Imported instance children arrive with componentId null, so every component edit re-cloned them and compounded per keystroke into runaway memory. Also fixes sibling inversion, extras jumping to front on sync, and stack overflow on self-referential components.

Sync: match unmatched children by name+type forward instead of cloning, guard self/cyclic sync via isDescendant, and keep unmapped extras at the end.

Import and paste: link children positionally at import time so renamed children stay linked. Clipboard paste is scoped to new instances only so existing instances are untouched.
2026-09-11 17:17:10 +10:00
Danila Poyarkov edb608e992 refactor(editor): centralize mutation layout reconciliation 2026-09-02 01:58:50 +03:00
Danila Poyarkov 9bb9478833
refactor: replace complex conditional object spreads
- 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
2026-09-01 19:49:57 +03:00
Danila Poyarkov 379ef8b62d
perf(canvas): rebuild navigation rendering (#591)
* 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.
2026-08-31 13:39:40 +03:00
Danila Poyarkov 6f5638380a
feat(app): prepare documents atomically per tab (#592)
* feat(app): show atomic document loading progress

- Preserve the existing full-canvas pencil loader while adding phase, detail, accessible status, and honest determinate progress
- Keep one generation-safe load owner across FIG decoding, graph preparation, page population, fonts, fallbacks, layout, viewport fitting, and first-render fade
- Prevent nested page setup and viewport cleanup from revealing partially prepared documents
- Cover obsolete sessions, font-resolution ownership, and staged loader UI

* refactor(app): scope editor preparation per tab

- Replace the shared loading boolean with one reactive preparation snapshot and one imperative controller per editor store
- Keep Core page work progress-only and inject canvas suspension from the app boundary
- Route FIG, storage, recovery, DOM import, and page switching through reusable tab-local preparation handles
- Abort only the closing tab's operation and cover generation safety, multi-tab isolation, progress UI, and disposal

* fix(editor): commit prepared pages atomically

- Prepare population, fonts, fallbacks, and layout without changing the visible page
- Reject cancelled and stale prepared pages before committing viewport, selection, and page events
- Keep the preparation overlay until the committed scene version is presented
- Cover call order, cancellation, stale generations, and presentation acknowledgement

* fix(app): stage imported documents before commit

- Prepare imported graphs in an isolated Core editor before replacing the live document
- Share font loading while keeping live selection, graph, renderers, and history untouched during staging
- Preserve the previous graph when staging is cancelled or fails and remove the duplicate pre-font layout pass

* refactor(app): namespace preparation UI

- Move canvas and tab preparation presentations into focused subfolders with concise component names
- Share progress and phase presentation helpers across preparation surfaces
- Show tab-local preparation status without covering the active canvas for background work

* fix(app): cancel preparation work at source

- Publish typed per-store preparation lifecycle events with explicit completion, cancellation, and failure outcomes
- Propagate tab-local AbortSignals through FIG parsing, population workers, and browser font downloads
- Keep cancellable font requests outside shared in-flight caches while retaining globally completed font registrations
- Stop FIG manifest previews from replacing the live graph before atomic document commit

* fix(app): cancel storage and DOM preparation

- Propagate preparation signals through S3 downloads, byte progress, local-cache boundaries, and DOM/CSS conversion checkpoints
- Reuse merged diagnostics and localized toasts for document, storage, and presentation failures
- Replace manual font concurrency and presentation timers with es-toolkit limitAsync and withTimeout
- Guard stalled first presentation and fix the merged recovery dialog title bindings

* fix(app): stage reload and font retry

- Prepare reload graphs in isolation and preserve the current document on read, decode, font, or layout failure
- Restore page and viewport state only after atomic graph commit with cancellable reload reads
- Run font Retry as a tab-local preparation with cache reset, final layout, picture invalidation, and presentation acknowledgement
- Keep completed document pixels visible while Retry reports activity in the tab

* fix(app): enforce exclusive preparation outcomes

- Complete document, storage, recovery, and DOM preparations only after successful commit
- Keep failed and cancelled handles terminal so lifecycle events cannot report contradictory outcomes
- Preserve external AbortError identity across storage timeouts and cancel streamed readers without returning partial bytes
- Cover credential-free pre-abort, mid-stream cancellation, progress cutoff, and terminal outcome exclusivity

* feat(diagnostics): record preparation outcomes

- Persist completed, cancelled, and failed preparation lifecycles through the validated diagnostics recorder
- Store only operation kind, outcome, cancellation or failure category, terminal phase, and coarse duration bucket
- Exclude document subjects, font families, storage identities, URLs, raw durations, messages, and stack traces

* chore(app): keep browser font tests with typography split

- Remove the browser font transport test inherited from a mixed cancellation commit; the source and coverage remain on the typography branch and safety snapshot

* test(vue): assert injected render suspension

- Exercise shouldSuspendRender instead of removed Core loading state\n- Preserve the contract that rendering resumes without a version change

* test(app): complete atomic preparation contracts

- Acknowledge first presentation in headless file-open tests\n- Assert the cancellable font-loading signature at the Tauri fallback boundary

* fix(app): preserve preparation cancellation

- Stage imported graphs before mutating live tabs and propagate aborts through page, DOM, font, and storage work\n- Use the accessible progress primitive and clamp determinate values\n- Cover fallback-font cancellation and yield pending-open test polling to the task queue

* test(text): await fallback font request cancellation

Start the mocked remote font request before aborting so the test proves that the active request receives the preparation signal.
2026-08-30 12:21:49 +03:00
Danila Poyarkov f892cb583c feat(editor): add ruler guide authoring foundation
- Preserve page and frame guide GUIDs through .fig conversion
- Add undoable guide add, move, transfer, and remove actions
- Start page/frame guide previews from CanvasKit rulers
- Render warm guide previews with frame-scoped solid lines and dotted continuation
2026-08-19 18:08:48 +03:00
rcoenen be272dc4dc
feat(fig): support text on path 2026-08-17 12:00:01 +03:00
Danila Poyarkov d8c968c04a
fix(editor): place nested component instances in world space (#524)
- Derive default instance placement from transformed component bounds
- Convert the target point into the destination parent coordinate space
- Preserve explicit drag-and-drop coordinates and undo snapshots
2026-08-14 21:24:20 +03:00
Danila Poyarkov 29ed07bcc4
refactor(editor): separate canvas view state (#518)
* refactor(editor): separate canvas view state

- Classify shared and view-local editor state explicitly

- Let canvas surfaces render supplied view state and report their viewport

- Preserve the existing one-canvas behavior by default

* fix(canvas): preserve loading render state

* refactor(editor): group state ownership modules

- Move shared and view state into the editor state namespace

- Model the partition with explicit interfaces and default factories

- Derive runtime view keys from the default view object
2026-08-14 21:24:01 +03:00
Danila Poyarkov b93ffea073
feat: add Figma-style variants and component libraries (#512)
Closes #239
2026-08-14 19:31:41 +03:00
Danila Poyarkov 7f91594d0c refactor: standardize acronym casing
- Rename first-party API, RPC, JSON, CORS, SVG, JSX, and related identifiers to preserve acronym casing
- Keep upstream and serialized boundary names unchanged
- Add a lint guardrail and migration notes for exported APIs
2026-08-10 15:48:16 +03:00
Danila Poyarkov de8578c435
feat(app): expand Figma menu and selection parity (#459)
- Add Figma-style object, component, selection, view, settings, and browser Move to Page actions
- Add undoable z-order, transformed distribution, inverse selection, and single/bulk rename behavior
- Keep browser and native menus, shortcuts, generated schema, localization, and documentation aligned
2026-08-03 00:23:05 +03:00
Danila Poyarkov 7e8feb998a fix(canvas): import dropped SVG files
- Parse dropped SVGs through the shared XML-backed import pipeline
- Preserve transforms, gradients, viewport alignment, and reusable shapes
- Place mixed image drops in one ordered undoable transaction
- Cover file filtering, atomic placement, undo, and browser drops

Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
2026-07-29 15:20:06 +03:00
Danila Poyarkov cb2c4aa3d3 fix(vector): edit vectors from opened files
- Resolve the active graph at action time and map edit geometry through full world transforms
- Regenerate live path fills and discard stale imported stroke outlines after edits
- Preserve fill and stroke geometry through resize previews, undo, and redo
- Cover graph replacement, nested transforms, edit history, resizing, and CanvasKit output

Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
2026-07-28 12:30:21 +03:00
Danila Poyarkov a6940bcab8 feat(app): convert image layers to vectors
- Add secure Recraft and fal.ai clients with centralized credentials and Media settings

- Replace image rectangles with editable vector frames in one undoable operation

- Bound desktop proxy responses, timeouts, and redirects for provider downloads

- Cover provider parsing, placement, settings, context-menu visibility, and undo

Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
2026-07-27 16:22:05 +03:00
Luca Candela 18899ff13c
feat(editor): add Figma-style frame presets (#418)
Add frame preset creation and resizing with constraint-aware layout, undo/redo, toolbar flyouts, localization, documentation, and regression coverage.
2026-07-26 00:05:57 +03:00
Danila Poyarkov e0d8efb2a4
Merge pull request #411 from open-pencil/figma-paste-images
fix(clipboard): fetch pasted Figma images on desktop
2026-07-18 05:04:49 +03:00
Danila Poyarkov 798a3c2b2c fix(editor): harden opacity shortcuts
- Match numeric keypad input by generated key so NumLock-off navigation remains available
- Coalesce buffered opacity updates into one undo interaction
- Add end-to-end coverage for digit composition, modifiers, and undo
2026-07-18 04:22:38 +03:00
Danila Poyarkov b08e199c85 fix(clipboard): finalize paste before image fetch
- Complete placement and undo registration before awaiting image hydration
- Bound Figma image requests with timeouts and use shared Tauri detection
- Localize missing-image notifications and cover stalled requests
2026-07-18 04:22:38 +03:00
Danila Poyarkov c8bbdbbb46 Merge branch 'master' into review-pr-411-current 2026-07-18 04:00:57 +03:00
sadkodev a5cffcc1ed
fix(editor): reject partially numeric opacity buffers
Add /^\d+$/ guard to opacityFromBuffer so strings like '12x', '5abc',
or '3.5' return 100% instead of silently parsing the numeric prefix.
Add unit tests for partial, decimal, scientific, negative, and
whitespace-prefixed buffers.
2026-07-17 15:34:00 -04:00
sadkodev d1284cd01b
fix(editor): guard setOpacity against non-finite values
Add Number.isFinite guard to editor.setOpacity so NaN, Infinity, and
-Infinity are rejected silently instead of corrupting node opacity.
Tighten opacityFromBuffer to use Number.isFinite for the same reason.
Add 3 unit tests covering NaN, Infinity, and -Infinity rejection.
2026-07-17 15:34:00 -04:00
Sadko 1151383de0
Merge branch 'open-pencil:master' into master 2026-07-17 15:09:35 -04:00
sadkodev d58cf0b9e9
test(keyboard): add unit tests for opacity buffer logic
Extract opacityFromBuffer as a pure function in core/editor and test
it directly. Covers Figma semantics: 0→100%, 5→50%, 28→28%, 00→0%,
100→100%, clamp at 100%, NaN guard. Refactor the keyboard handler to
reuse the shared function instead of inlining the buffer math.
2026-07-17 15:00:10 -04:00
Danila Poyarkov 23645d5a2d feat(editor): add component property controls
- Model typed component definitions, references, assignments, and preferred swap values

- Add SDK-owned variant, text, boolean, and nested instance-swap controls with mixed-selection undo

- Preserve assignments across variant changes and component synchronization

- Round-trip component property metadata through .fig import and export
2026-07-17 21:59:49 +03:00
sadkodev 0300cb521b
refactor(keyboard): address review feedback on opacity shortcuts
- Move setOpacity to core editor action (packages/core/src/editor/nodes.ts)
  so it is testable directly without duplicating logic in tests
- Replace external window listener with tinykeys bindings in
  registerKeyboardShortcuts, reusing shared input/focus guards
- Add 20 bindings for Digit0-9 and Numpad0-9 with modifier guards
- Implement Figma-style multi-digit buffer: 2→20%, 28→28%, 00→0%, 100→100%
  with 400ms timeout and auto-apply at 3 digits
- Simplify selection.setOpacity command to delegate to editor.setOpacity
- Rewrite unit tests to call editor.setOpacity directly
- Add ofetch to dependencies (was imported but missing from package.json)
- Update CHANGELOG
2026-07-17 14:09:46 -04:00
Danila Poyarkov fc85589438 fix(clipboard): fetch pasted Figma images on desktop
- Resolve Figma image hashes through the existing Tauri HTTP bridge
- Verify downloaded image bytes and report partial failures through typed editor events
- Warn web users and surface actionable desktop fetch errors
2026-07-17 18:03:00 +03:00
sadkodev e91ea4b061
feat(keyboard): add Figma-style opacity shortcuts 1-9/0
Add opacity keyboard shortcuts matching Figma: pressing Digit1-Digit9
sets the selected layers to 10%-90% opacity, and Digit0 sets 100%.

- Add 'selection.setOpacity' EditorCommandId with registry metadata
  (shortcut display '1-9, 0') so it appears in the command cheatsheet
- Implement the command in selection.ts using editor.undo.runBatch +
  updateNodeWithUndo, following the toggleVisibility/setBlendMode pattern
- Add setOpacityTarget closure in useEditorCommands so the external
  keyboard handler can inject the digit value before runCommand
- Add canSetOpacity capability (enabled when there is a selection)
- Add bindOpacityKeys handler in src/app/shell/keyboard/opacity.ts,
  following the nudging.ts external-handler pattern since the opacity
  value comes from event.code and cannot be expressed as a fixed
  keybinding in the registry
- Guard against text editing, number field focus, and modifier keys
- Add i18n label 'setOpacity' across all 8 locales
- Add unit tests for single/multi selection, undo batch, and no-op
2026-07-16 22:12:50 -04:00
Danila Poyarkov 2c69e0949b fix(text): preserve Figma auto-size text imports
- Normalize auto-layout text imported from Figma when fixed bounds match derived text layout
- Recompute auto-size text bounds on typography metric changes
- Clear stale Figma derived text layout caches when text sizing changes
- Cover FIG import text sizing and line-height resize regressions
2026-07-02 17:30:15 +03:00
Danila Poyarkov 13314d6b6d fix(editor): relayout after component sync 2026-07-02 10:35:10 +03:00
Danila Poyarkov ddb46af32f test(layout): place auto-layout reflow cases by domain 2026-07-02 10:16:08 +03:00
Danila Poyarkov d522dc0529 fix(layout): match Figma auto-layout reflow 2026-07-02 08:53:48 +03:00
Danila Poyarkov ef0271f040 fix(vue): import export types from scene graph package 2026-06-30 11:18:19 +03:00
Danila Poyarkov 898431e646 feat: split SceneGraph and Pen packages 2026-06-30 10:54:32 +03:00
Danila Poyarkov 7d27cac5ab refactor(kiwi): remove redundant core shims 2026-06-30 10:50:30 +03:00
Danila Poyarkov 7303a40964
fix(editor): separate preview updates from graph mutations
Separate preview-only scene graph updates from committed mutation events and fix vector resize undo.
2026-06-30 10:28:02 +03:00
Joseph Cumines 4bc1698e93
fix(scene-graph): deep-copy cloned nodes and validate bindings 2026-06-10 17:10:25 +03:00
rcoenen 27cfcbb932
fix(resize): scale group and boolean children
* fix(resize): scale children when resizing GROUP or BOOLEAN_OPERATION

Resizing a group (or boolean operation) only moved the bounding box and
rescaled the selection outline — the rendered fill stayed at its original
size, so the blue outline and the filled content visibly diverged.

Two root causes, both in the resize preview path:

1. During drag, children were scaled in the preview graph via
   updateNodePreview(), which mutates nodes without emitting a node:updated
   event. The renderer's vector/geometry caches (vectorPathCache,
   fillGeometryCache, strokeGeometryCache) were therefore never invalidated,
   so fills kept drawing from stale, unscaled cached paths while outlines
   drew live from the new dimensions. Fixed by calling
   renderer.invalidateVectorPath(childId) after each child preview update.

2. On commit, the final scaled values were written with updateNode() but no
   repaint was requested, leaving the canvas frozen on the last mismatched
   drag frame until the next interaction. Fixed by calling requestRepaint()
   after commitGroupResize().

collectDescendants() now also collects children for BOOLEAN_OPERATION nodes,
which render purely from child geometry (no node-level scale), so scaling the
children is the only way to scale the result — same mechanism as GROUP, no
double-transform. Group resize verified visually; boolean resize verified in
-browser via the editor automation API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(editor): preserve vector undo during group resize

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-06-06 17:05:09 +03:00
Danila Poyarkov 5c09ca0299 test(core): stabilize engine unit tests
- Update EffectType schema assertion for modern Figma enum values
- Treat unsupported OpenType font buffers as unavailable
- Skip CJK outline fallback coverage when the LFS font fixture is not materialized
2026-05-25 01:41:59 +03:00
Danila Poyarkov abd795bd9a chore: format codebase 2026-05-24 12:15:29 +03:00
Danila Poyarkov 213db6373a refactor(kiwi): group fig format internals
- Move codec, NodeChange conversion, and instance overrides under kiwi/fig
- Flatten the codec entrypoint to kiwi/fig/codec
- Keep schema-runtime as the generic Kiwi parser/compiler boundary
2026-05-21 14:54:13 +03:00
Danila Poyarkov f3649550f2 chore: enable stricter oxlint rules 2026-05-18 18:58:30 +03:00
Danila Poyarkov b1ad6d41e9 fix(editor): enable outlining grouped strokes 2026-05-17 20:27:45 +03:00