Commit graph

74 commits

Author SHA1 Message Date
Danila Poyarkov bb8c5c18d9
fix(editor): commit vector drags on pointer release (#586)
* fix(editor): commit vector drags on pointer release

- Commit vertex and Bézier handle edits immediately on release\n- Rebase active vector-edit state after graph commits\n- Use vector-specific input paths and cover Pen-created Bézier drags

* test: reduce visual snapshot scope

- Replace behavior screenshots with scene assertions\n- Capture renderer fixtures from bounded canvas regions\n- Remove obsolete full-viewport snapshots

* fix(vector): harden edit cancellation and endpoint remapping

- Restore temporary vector geometry on cancelled pointer drags\n- Commit Bézier handle releases consistently and guard stale handle state\n- Preserve valid endpoint indices when connecting vector vertices

* fix(vector): cancel bend drags and satisfy formatting

* fix(vector): guard stale handle references

* fix(vector): guard stale overlay handle indices

* fix(vector): validate overlay handle keys

* refactor(vector): use typed handle selection ids

* test(vector): align typed handle selection coverage
2026-08-24 16:02:27 +03:00
Danila Poyarkov 4a8714377e refactor(mcp): model tool effects and capabilities
- Remove duplicated document-access declarations from core tools

- Replace the MCP catalog with typed descriptors, capabilities, and policy

- Emit standard MCP annotations through type-safe registration
2026-08-20 15:55:48 +03:00
Victor Wads 6a78da9eb0
refactor(mcp): derive catalog from registration
- Collect tool metadata through the existing registration wrapper
- Expose the runtime catalog to Settings without a parallel MCP-only list
- Keep disabled tools discoverable so they can be re-enabled
2026-08-20 00:27:13 -03:00
rcoenen be272dc4dc
feat(fig): support text on path 2026-08-17 12:00:01 +03:00
Danila Poyarkov d7613f34ec
perf(scene-graph): accelerate axis-aligned hit tests (#534)
* perf(scene-graph): accelerate axis-aligned hit tests

- Reuse cached world positions for untransformed node chains
- Preserve exact matrix hit testing beneath rotated and flipped ancestors
- Profile pointer, hit-test, cached, overlay, and volatile rendering at 500 and 2,000 nodes

* fix(vue): restore layer drop indicators

- Forward reactive drag instructions through virtualized layer item slots
- Render above, below, and child feedback before the drop completes
- Add browser regressions for reorder lines and container highlights

* fix(scene-graph): harden hit-test performance coverage

- Cache transformed ancestry within each hit-test traversal
- Invalidate absolute positions after preview flips and reparenting
- Attach scale-relative browser profiles and correct auto-layout fixture geometry

* fix(scene-graph): reject cyclic layer reorder

- Prevent reorderChild from parenting a node beneath its descendant
- Cover graph preservation after a rejected cyclic reorder
2026-08-16 15:43:25 +03:00
Danila Poyarkov 780221b006
test(tauri): add native WebView interaction harness (#532) 2026-08-15 14:17:05 +03:00
Danila Poyarkov b0ebe91ed6
perf(collab): skip unchanged Yjs node fields (#529)
* perf(collab): skip unchanged Yjs node fields

- Compare encoded node fields before writing to Y.Map
- Cover unchanged sync, repeated drag-like updates, echo behavior, and concurrent convergence
- Reduce 100 unchanged syncs from 13,700 updates to zero

* test(collab): simulate queued Yjs convergence

- Use the official Yjs TestConnector for deterministic queued delivery
- Exercise offline concurrent edits, reconnect, random message flushing, and convergence
2026-08-15 11:40:27 +03:00
Danila Poyarkov 6193b6da7e
feat(code): add live JSX and HTML/CSS editing (#525)
* 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
2026-08-15 09:48:42 +03:00
Danila Poyarkov 39bc4d2eb1
feat(acp): add reusable MCP connections (#516)
* feat(acp): add reusable MCP connections

- Store named Streamable HTTP connections separately from model providers
- Keep bearer tokens in the credential manager and resolve them per ACP session
- Add localized settings, validation, documentation, and focused coverage

* test(acp): harden MCP connection workflow

- Label credential inputs and confirm destructive connection removal
- Exercise MCP server delivery through an in-memory ACP session
- Extend the browser smoke test to cover accessible input and confirmation flows

* fix(acp): validate MCP connection lifecycle

- Keep non-browser storage initialization in memory and restore the German model copy
- Reject persisted name collisions and invalid draft IDs
- Preserve connections on credential failures and require credentials before enablement
2026-08-14 20:08:45 +03:00
Danila Poyarkov 5190a64017
fix(tauri): transfer FIG exports over binary IPC (#509)
- Return native FIG archives as raw IPC responses instead of JSON byte arrays

- Decode the response as an ArrayBuffer and cover the binary contract

- Prevent large desktop exports from multiplying memory use during save
2026-08-13 22:23:37 +03:00
Danila Poyarkov c7b944d103 fix(kiwi): harden FIG containers and DOM imports
- Decode zstd FIG data and reject invalid compressed payloads

- Compose caller CSS with Tailwind defaults during DOM import

- Slice pooled fixture buffers to their exact byte range

Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
2026-08-13 20:33:10 +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 9c9e4fae08 refactor(mcp): align transport domain structure
- Group server, stdio, and transport implementation by ownership

- Mirror those domains in engine tests and centralize shared MCP fixtures
2026-07-25 22:59:59 +03:00
Danila Poyarkov 2b32a8c7d3 feat(mcp): add local transport discovery
- Prefer private Unix sockets with TCP fallback for local MCP clients

- Unify HTTP and WebSocket lifecycle, authentication, and cleanup

- Discover transport details from an owner-only runtime file
2026-07-25 21:29:03 +03:00
Danila Poyarkov f3fbc94abf feat(text): create text boxes by dragging 2026-07-20 21:22:30 +03:00
Danila Poyarkov 86d6a63d0f fix(fig): restore strict parse validation
- Reject corrupt compressed Kiwi chunks instead of accepting raw garbage\n- Tolerate legacy source metadata without editedFields in renderer fallbacks\n- Align font, derived glyph, shared style, renderer mock, and ownership tests with current contracts\n- Restore clean quick and heavy unit quality gates
2026-07-18 03:58:18 +03:00
Danila Poyarkov 8b9be17a56 perf(fig): remove large fixture bottlenecks
- Index component property definitions and variable asset references once per export or override context\n- Skip converged instance fill and text propagation and use native binary hex encoding\n- Add a non-populating parse mode for raw fixture validation\n- Reduce the Material 3 round trip from minutes to about twenty seconds
2026-07-18 03:37:58 +03:00
Danila Poyarkov 5371ed11b8 feat(app): migrate paint effect and export panels
- Share compact item-row and paint-field anatomy across Fill, Stroke, Effects, and Export
- Add COLOR binding transactions with picker rollback and one-step undo
- Replace bespoke panel test hooks with semantic selectors and visual coverage
2026-07-14 17:06:28 +03:00
Danila Poyarkov fcdf7f1d4c feat(app): migrate layout size controls
- Replace duplicated width and height binding branches with shared semantic fields

- Switch edited Hug and Fill dimensions to Fixed in one reversible transaction

- Add typed LayoutControls actions, documentation, and visual coverage
2026-07-13 15:34:25 +03:00
Danila Poyarkov 1d60d3a407 feat(ui): add binding field skins
- Show one variable picker affordance with a quiet identity pill and accessible combobox

- Keep bound NumberField focus non-destructive until the first value mutation

- Prefer accessible and semantic test selectors over compound test IDs
2026-07-13 14:04:13 +03:00
Danila Poyarkov 5d9730ae04 feat(vue): finalize NumberField API and docs
- Remove ScrubInput aliases, app wrappers, test hooks, and translated legacy references

- Share a Tailwind NumberField demo between Storybook and VitePress

- Generate component API tables from Vue metadata and type-check examples with Twoslash

- Upgrade the docs stack to VitePress 2
2026-07-13 10:19:24 +03:00
Danila Poyarkov 8b1c452cc3 refactor(cli): replace DOM command with import and HTML export 2026-07-04 13:34:24 +03:00
rcoenen d2d816b688
fix(collab): replicate document content to joiners
Squashed PR #340 with maintainer follow-up fixes and local validation.
2026-06-30 11:56:58 +03:00
Danila Poyarkov 3a5b6a5c37 test: share scene variable helper 2026-06-30 10:55:05 +03:00
Danila Poyarkov 898431e646 feat: split SceneGraph and Pen packages 2026-06-30 10:54:32 +03:00
Danila Poyarkov 346d13c405 feat(dom-css): map image aspect styles 2026-06-30 10:51:37 +03:00
Danila Poyarkov 3f1a5ce779 feat(dom-css): map browser text casing 2026-06-30 10:51:05 +03:00
Danila Poyarkov 5b61c1bf46 test(dom-css): expand fixture matrix 2026-06-30 10:47:26 +03:00
Danila Poyarkov 101a047e4d test(dom-css): share CSS fixture suite 2026-06-30 10:47:25 +03:00
Danila Poyarkov 568cdf91ef Merge remote-tracking branch 'joey/feature-overlapping-element-analysis-tool' into pr352-local-merge
# Conflicts:
#	tests/engine/clipboard/fixtures/gold-preview-roundtrip.test.ts
#	tests/engine/io/fig/import/group-reclassify.test.ts
#	tests/engine/io/fig/roundtrip/glyph-blob.test.ts
#	tests/engine/layout/auto-layout/text/measurement.test.ts
2026-06-30 09:47:07 +03:00
Joseph Cumines dc9638ac3e
fix(export): prevent GUID collisions and file corruption on .fig round-trip (#333)
* fix(export): prevent GUID collisions and file corruption on .fig round-trip

Nodes sharing the same imported source.id (component instance children,
cloned subtrees) silently overwrote each other on export because the
GUID assignment reused imported GUID values without checking for
duplicates.  This caused data loss on reimport — only the last node
with a given GUID survived.

- Track all assigned GUID values in a Set for O(1) collision detection.
- Scan imported source.ids for both sessionID 0 and 1 before assigning
  any new GUIDs, so the counter starts past every imported value.
- Fall back to counter-based GUIDs when source.id collides with an
  already-assigned value.

Additional fixes in the same change set:

- cloneTree now deep-copies source.fig via structuredClone, preventing
  mutations on a clone from corrupting the original node's kiwi payload.
- Removed decompressFigKiwiData sync wrapper (zero callers) and the
  silent try/catch fallback in parseFigKiwiContainer that masked
  corrupt data as raw bytes.
- buildFigKiwi uses Bun.zstdCompressSync when available, matching the
  zstd decompression path already used on import.
- Fixed setSavedVersion ordering in read.ts — must run after
  requestRender to capture the post-bump version, preventing spurious
  dirty-state immediately after file reload.

Tests: GUID collision (2 and 3 node), clone isolation, parse failure,
text export zstd compatibility, gold-preview round-trip.

* fix(export): handle EXCLUDE boolean operation and BOOLEAN_OPERATION node type

The internal representation uses EXCLUDE for exclude boolean operations,
but Figma's kiwi schema uses XOR. Map EXCLUDE back to XOR on export so
round-trips through .fig files don't fail. Also add BOOLEAN_OPERATION to
VALID_NODE_TYPES and increase timeout for heavy material3 fixture test.

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

* style: format export-node.ts

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

* test: add type guard after null assertion in guid-collision test

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

* fix(clipboard): detect zstd-compressed data before zlib inflate

fflate's inflateSync silently accepts zstd-compressed data and returns
garbage instead of throwing. Check for the zstd magic bytes (28 b5 2f
fd) before attempting zlib decompression. Also revert the EXCLUDE enum
addition to the kiwi schema since the export-node.ts mapping is
sufficient.

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

* fix(clipboard): add length guard before zstd magic byte check

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

* test(scene-graph): group clone regression coverage

* ci: retrigger CI checks

* test: increase timeouts for heavy .fig fixture tests on slow CI runners

Gold-preview.fig and material3.fig parsing/export tests consistently
exceed the 5s bun:test default timeout on GitHub Actions runners.
Increase to 30s for: beforeAll codec init, clipboard roundtrip,
glyph blob roundtrip, group reclassification, and text measurement.

* test: add individual test timeouts for heavy .fig fixture tests

The beforeAll timeout helped but individual test() calls also need
explicit 30s timeouts since bun:test applies the 5s default per-test.
Fixes remaining CI flakes in glyph-blob roundtrip and clipboard
roundtrip tests.

* test: increase beforeAll timeout for render cache test

The canvas/render cache test loads gold-preview.fig AND initializes
CanvasKit (Skia WASM), which is much slower than the other fixture
tests. Use 60s timeout to account for slow CI runners.

* fix(export): reserve document GUID to prevent 0:0 namespace collision

- Add docGuid (0:0) to assignedGuidValues before processing imported
  node source.ids, preventing an imported node with source.id "0:0"
  from reusing the document's GUID slot
- Add regression test using session-0 source.ids to verify nodes
  survive roundtrip without document GUID collision
- Remove unnecessary async keyword from synchronous component
  metadata test

* fix(export): guard canvas GUID reuse with assignedGuidValues check

- Mirror getOrCreateNodeGuid() collision logic in buildCanvasEntries():
  if an imported page's source.id maps to a GUID already in
  assignedGuidValues, generate a fresh counter-based GUID instead
- Prevents canvas-level last-write-wins when multiple pages share
  the same source.id or a page uses 0:0

* fix(test): use explicit little-endian writes and fix misleading test title

- Replace host-endian Uint32Array writes with DataView.setUint32(offset, value, true) in parse-failures.test.ts to ensure platform-independent fig-kiwi container assembly
- Rename test title from "clone clears source.id from the original" to "clone clears source.id from the clone" to accurately reflect what the assertions verify

* test(io): use file-level timeout for heavy fixture

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-06-23 18:05:23 +03:00
Joseph Cumines fa5c072cad test: extend timeout for slow gold-preview fixture tests
Port the timeout fixes from PR #351 that resolved the unit-test CI
timeouts. Add HEAVY_TEST_TIMEOUT_MS (30s) in tests/helpers/test-utils.ts
and apply it to the slow gold-preview.fig fixture tests/hooks:
- clipboard roundtrip
- group reclassification
- glyph blob preservation
- auto-layout text measurement
- render/canvas/cache
2026-06-19 11:05:25 +10:00
Joseph Cumines f862beb08d Add heuristic overlap detection tool and CLI
Implement the analyze overlaps CLI command, RPC endpoint, and ToolDef
to enable heuristic detection of visual overlaps, parent overflows, and
overlay patterns. Geometry computation now uses world-matrix visual
bounds to handle nested ancestor rotations and clipping frames correctly.

Update file-backed CLI commands to use Node fs/promises instead of Bun
runtime file APIs. This is required for the published CLI to function
when installed and executed in a standard Node environment. Add the
package.json subpath export to the core package for metadata consumers.
2026-06-19 09:54:27 +10:00
Danila Poyarkov abd795bd9a chore: format codebase 2026-05-24 12:15:29 +03:00
Danila Poyarkov a48b23129f fix(fig): stabilize engine test suite 2026-05-21 14:54:17 +03:00
Danila Poyarkov f3649550f2 chore: enable stricter oxlint rules 2026-05-18 18:58:30 +03:00
Danila Poyarkov 11e38b626a test(e2e): extract shared editor setup fixture
- useEditorSetup() / useEditorSetupWithClear() in tests/e2e/fixtures.ts
- Migrated 17 specs to shared fixture, removing ~300 lines of boilerplate
- Moved getSelectedNode/getSelectedNodes to shared store helpers
- Replaced inline getSelectedNode in 4 specs with shared import
- Test duplication: 329 → 308 clones (10.12% → 9.08%)
2026-05-16 16:19:03 +03:00
Danila Poyarkov cb51fc73fc test(tauri): clean up font cache mocks 2026-05-16 13:55:09 +03:00
Danila Poyarkov 692ddf9a3f refactor(toolbar): reuse test id helpers 2026-05-15 13:13:24 +03:00
Danila Poyarkov 698bcde00b refactor(vue): share generated test id helpers 2026-05-15 13:10:44 +03:00
Danila Poyarkov 6b406002dd test(e2e): remove remaining raw test id strings 2026-05-15 13:02:41 +03:00
Danila Poyarkov 2f94ce6510 test(e2e): use Playwright test id locators 2026-05-15 12:49:49 +03:00
Danila Poyarkov 541c9bd13c test: move tauri helpers into domain folder
- Move tests/helpers/tauri-mocks.ts → tests/helpers/tauri/mocks.ts
- Move tests/helpers/tauri-fig-export-fixture.ts → tests/helpers/tauri/fig-export-fixture.ts
- Update all imports
2026-05-06 16:16:08 +03:00
Danila Poyarkov 432f945da6 chore(tests): narrow no-empty-function exceptions
- Replace simple no-op test callbacks with explicit undefined returns
- Remove no-empty-function exceptions for autosave, stroke picker, font settings, pen, tauri, AI adapter, and undo helper tests
- Validate check plus affected engine and E2E tests
2026-05-06 12:32:54 +03:00
Danila Poyarkov bbf122a5c5 chore(tests): route store access through browser bridge
- Expose window.openPencil.getStore() instead of a direct store property
- Update E2E helpers and specs to use the bridge getter
- Add lint coverage preventing direct window.openPencil.store access
2026-05-06 12:19:39 +03:00
Danila Poyarkov 8ec70330b4 chore(lint): centralize OpenPencil window API
- Route browser globals through src/app/window-api.ts instead of private __OPEN_PENCIL fields
- Move E2E tests to the public window.openPencil test API
- Add an oxlint rule banning direct window.__OPEN_PENCIL* access
2026-05-06 10:38:03 +03:00
Danila Poyarkov 39675b2969 chore(tests): remove easy non-null assertions
- Replace browser store non-null assertions with explicit initialization guards
- Use expectDefined for optional test resources and tool results
- Clean low-count non-null assertions in font, icon, snap, OKHCL, and visual tests
2026-05-06 02:59:56 +03:00
Danila Poyarkov a63ac93a3d chore(lint): enforce type imports in tests
- Remove the broad test exception for consistent type imports
- Mark test-only core and helper imports as type imports where appropriate
- Keep targeted validation for affected engine tests
2026-05-06 02:35:57 +03:00
Danila Poyarkov c3f5189f8f style: tighten import grouping
- Configure oxfmt custom import groups for workspace, app, package, and test aliases
- Keep type imports grouped with their matching source category instead of one global tail group
- Expand the format script to cover formatter config, Vite files, and scripts
2026-05-06 02:22:08 +03:00
Danila Poyarkov e42dffa003 chore(lint): consolidate oxlint config
- Merge structural lint rules into oxlint.json and remove the second config file
- Add #tests/* path aliases and rewrite deep test helper imports
- Ban new deep parent-relative imports through the shared lint config
2026-05-06 02:05:02 +03:00