- 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
- Reject combine_as_variants inputs that span different parents\n- Preserve all source components when validation fails\n- Cover the tool-level error path
- Match the Figma parent and insertion-index API contract\n- Reject duplicate component references before graph mutation\n- Share slash-name variant derivation with editor actions\n- Add structural regression coverage and changelog entry
figma.combineAsVariants() was unimplemented in the scripting/automation
layer (figma-api/proxy.ts's compatibility surface), even though the editor
UI already has a full equivalent — createComponentSetFromComponents()
(editor/components.ts) plus wrapSelectionInContainer()
(editor/structure/container-wrap.ts) — wired to the app's own "Create
component set" menu action.
This ports that logic down to FigmaAPI (the class MCP tools, the CLI, and
AI chat scripting all run against): wraps selected COMPONENT nodes sharing
a parent into a COMPONENT_SET, and derives variant property definitions from
"Category/Value" node-name segments, matching the editor behavior exactly.
Drops the undo-stack push and selection-state writes the editor path has,
since neither concept exists at the scripting layer — everything else is
the same primitives (SceneGraph.createNode/insertChildAt/reparentNode),
which FigmaAPI already had direct access to.
Also adds a matching combine_as_variants tool (packages/core/src/tools/
create/components.ts) registered in EXTENDED_TOOLS, so it's exposed
through MCP, the CLI, and AI chat alongside the existing create_component/
create_instance tools.
* Revert "feat(ai): add HarnessAgent sidecar foundation (#560)"
This reverts commit 83a5ea1b42.
* Revert "Revert "feat(ai): add HarnessAgent sidecar foundation (#560)""
This reverts commit 0d8c03515888c62dc47186d4a3b0b7b04e78f8af.
* refactor(ai): ship Harness as optional companion
* fix(ai): support Harness companion on Windows
* test(ai): restore navigator after Harness Windows test
- Limit text reads and existence checks to platform discovery files
- Drop unused create and open capabilities
- Document restored desktop automation reconnection
- Preserve rounded auto-layout preview displacement through drop commits
- Bound imported layout-guide counts and tiny grid steps
- Normalize missing guide arrays in direct graph hydration paths
- Add regression coverage for auto-layout movement and guide limits
* 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
* ci: parallelize quality and trim shard builds
- Split source, package, repository, and Storybook checks into independent jobs
- Build only Core before quick unit shards instead of all public packages
- Keep full package and dist validation in the dedicated package-quality job
- Disable duplicate Storybook component metadata extraction
* fix(ci): build declarations before source checks
* fix(ci): apply review security and docgen settings
- Pin checkout and disable credential persistence
- Declare read-only workflow permissions
- Explicitly disable Storybook Vue docgen
* ci: clarify check and step names
* fix(ci): cap workflow job runtimes
- Limit the WebView dependency install to three minutes
- Limit native contracts to eight minutes
- Limit all other CI jobs to ten minutes
* ci: retry native contracts on a fresh runner
* ci: move native contracts to Ubuntu 24
- Leave the deprecated Ubuntu 22 hosted image
- Retry WebKit dependency provisioning on the current runner image
* ci: prebuild native contract dependencies
* ci: pin native contracts image digest
* fix(ci): authenticate native image pulls
* fix(ci): include Bun archive tooling
* ci: pin updated native contracts image
* fix(tools): register CI image tooling
- Preserve parent-local movement during Control bypass
- Route native snapping menu actions through persisted preferences
- Harden guide alignment, lifecycle rendering, and transformed resize behavior
- Update preference descriptions and fractional position display
- Upgrade direct model chat providers and transports to AI SDK 7.\n- Keep the local ACP execution path unchanged.\n- Preserve chat, tool, vision, and provider behavior across the adapter boundary.
- 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
- Keep only the active assistant response in streaming Markdown mode.\n- Remount completed responses in static mode to release incremental AST caches.\n- Preserve final Markdown, code rendering, and theme behavior.
- Open multiple selected design files in separate tabs.\n- Support desktop, File System Access, and fallback pickers.\n- Continue opening later selections when one file fails.
parseGuidOrNull requires the strict Figma GUID shape (sessionID:localID),
but every OpenPencil code path that creates a component property definition
uses `prop:<hex>` IDs, which never match. applyComponentMetadata silently
dropped componentPropDefs/componentPropRefs/componentPropAssignments/
variantPropSpecs whenever any entry failed that check, so any component
property created by the app (as opposed to imported straight from a real
Figma file) vanished on the very next save.
Mint a stable synthetic GUID for non-Figma-shaped property IDs instead of
dropping them, memoized per export so defs/refs/assignments/variantPropSpecs
pointing at the same property stay consistent. Also fix INSTANCE_SWAP
default/preferred/assignment values, which reference target node IDs and
had the same GUID-shape assumption, and were never being resolved back to
real node IDs on import in the first place (remapComponentIds only handled
instance.componentId, not these fields).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The default capability only granted read_file/write_file/mkdir/remove/watch,
but the app also calls exists(), readTextFile(), create(), and open() from
@tauri-apps/plugin-fs. Each fs-plugin command requires its own explicit
permission identifier in Tauri v2 — these were never added.
The most visible symptom: EditorView's automation startup calls
discoveryFileExists()/readDiscoveryToken() (spawn.ts) to reuse an already-
running MCP server. With allow-exists and allow-read-text-file missing, both
calls silently fail (the fs-plugin IPC rejection is caught and treated as
"file not found"), so the app always falls through to spawning a redundant
MCP server process and registers with a freshly generated auth token instead
of the real one — which the already-running server then rejects, leaving
automation permanently disconnected from any already-running MCP server.