- Render segments not referenced by any region loop alongside filled paths\n- Add focused path construction coverage and a canvas visual snapshot\n- Resolve Plugin API type-aware lint findings exposed by the full quality gate
- Match Figma rescale behavior across geometry, typography, effects, and descendants\n- Expose page backgrounds through preserved FIG metadata\n- Return nullable visual render bounds with stroke, effect, and clipping support\n- Cover the Plugin API behavior against live Figma probes
* fix(mcp): close orphaned servers that no app ever claims
- Add ServerOptions.appAttachTimeoutMs: if no app registers within this
window after startup, the server closes itself and removes its
discovery file, instead of squatting the port indefinitely.
- Wire it through the openpencil-mcp-http CLI as
OPENPENCIL_MCP_APP_TIMEOUT_MS (opt-in, unset/0 disables it — a bare
CLI invocation for manual testing should not self-terminate).
- The desktop app opts in with a 30s timeout when it spawns the server.
Without this, a server that outlives its spawning app (renderer crash,
forced reload) keeps holding its port with a stale discovery file. The
app's liveness check only asks whether something answers /health, not
whether an app has ever registered (see /health's no_app status) — so
every later launch finds the orphan already listening and defers to
it, and MCP tool calls fail with "app is not connected" until someone
manually kills the orphaned process. Closing self-caused orphans at
the source means the next launch finds no discovery file and takes
the normal fresh-spawn path.
Fixes#488
* fix(mcp): clean up servers after app disconnects
- Re-arm the orphan watchdog when the registered app disconnects
- Cancel pending shutdown when the app reconnects within the grace period
- Reject timeout values that overflow the runtime timer range
* test(mcp): make watchdog reconnect coverage deterministic
- Wait for the disconnected health state before reconnecting
- Report distinct safe-integer and timer-range validation errors
---------
Co-authored-by: swe-sanad <sanad.arousi@export119.com>
* fix(storage): polish workspace and connection feedback
- Move Refresh into the Storage Workspace header and keep shell theme actions available outside the editor route
- Split native shell and editor menu listeners behind the app shell
- Replace permanent CORS controls and inline connection results with standard toasts and clearer browser guidance
* fix(app): harden native menu routing
- Dispose menu listeners that finish registering after their Vue scope closes
- Mark shell-owned actions in the shared menu schema instead of duplicating IDs
- Cover route-neutral shell action classification
* 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
- Use macOS predefined copy, cut, and paste menu items so WebKit routes accelerators to focused editing controls\n- Keep canvas clipboard events handled by OpenPencil and recognize composed contenteditable targets\n- Cover editable input and nested contenteditable event paths
- Remap component IDs through SceneGraph updates so the instance index stays consistent
- Cover imported instances resolving through the remapped source ID
- Restore source component synchronization for the reporter's .fig file
- Load Prism JSX only after exposing the Prism runtime
- Treat unavailable MCP automation as optional during startup
- Connect the desktop automation bridge only after MCP is ready
- Discard stale provider refreshes and queued previews
- Guard malformed deflate data and listener failures
- Start periodic refresh without an immediate callback
- Validate ranged thumbnail payloads and S3 bounds
- Invalidate stale previews and expose loading errors
- Document the public document workspace composable
- Load embedded Figma thumbnails through bounded S3 byte-range requests
- Add a headless Vue workspace composable with lazy previews and refresh lifecycle
- Cache local previews and refresh the workspace after saves and synchronization
- Cover UI identifiers in the acronym guardrail
- Preserve FIG thumbnail and metadata values through archive parsing
- Use Vue-compatible acronym prop attributes
- 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
- Separate model, connection, and advanced settings
- Resolve capabilities and output limits from provider catalogs
- Keep custom model compatibility declarations explicit
- Populate subsequent lazy pages in a development-only persistent worker
- Journal created, updated, and deleted graph state as field deltas
- Invalidate stale worker replicas after authoritative graph mutations
- Fall back to synchronous population when the worker is unavailable
- Ignore undefined values before cache invalidation and preview mutation\n- Treat text direction as a glyph-shaping input\n- Cover the corrected invalidation contract
Keep authoritative derived glyphs through layout, alignment, fill, and decoration changes while still invalidating them for actual shaping edits.
Add scene-graph invalidation coverage and a Gold Preview fixture regression test.
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- 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
- Recompute Yoga positions after shaped text changes effective geometry
- Resolve inherited stretch and skip hidden or absolute children
- Share and normalize Plugin API transform matrices
- Centralize design-profile eligibility for the selector, role assignment, fallback, and tests
- Keep built-in provider model selection intact while switching saved custom profiles
- Add localized accessible labeling and constrain long profile names
The chat model chip is a dropdown for built-in providers but a static label
for OpenAI-compatible and custom-model setups, so anyone using a custom
endpoint had to open Settings to change models -- even with several models
already configured.
Add a profile switcher that reassigns the design role from the chat panel.
It lists saved profiles that can use tools, shows the profile name rather
than the raw model ID, and badges the vision-capable ones. It writes the
same assignments.design field the Settings dropdown does, so the two stay in
sync.
Only renders when more than one tools-capable profile exists; a single
profile keeps the existing label. Built-in providers keep ProviderModelSelect
unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Return rotation and reflection matrices for relative and absolute transforms
- Preserve authoritative imported FIG transforms until geometry is edited
- Cover nested rotation and reflected vector behavior
- Reconcile stale generated text widths with CanvasKit measurements
- Propagate effective widths through HUG containers while preserving fixed geometry
- Cover component-lineage and stretched-text safeguards
- Preserve Figma min/max constraints, scalar variable bindings, and direct text bounds
- Reflow only authoritative generated instance geometry and pinned descendants
- Avoid double-applying auto spacing and unsafe lone-child normalization
- Replace the full-width editor header action with a shared browser and native menu command
- Localize the destination and cover menu schema and browser navigation
- Support vectorPaths, vectorNetwork, and async network assignment
- Normalize geometry bounds and preserve winding and region paint semantics
- Validate network topology and expose handle mirroring only on vectors
Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
- Include stroke color and rounded weight in node and child summaries
- Select the first visible stroke when hidden paints precede it
- Cover both describe output paths with mixed stroke visibility
Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
- Validate inline SVG roots against their supported root props
- Leave descendant SVG markup to the SVG renderer instead of Design JSX validation
- Keep warnings for unsupported root props and cover both paths
Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
- Name every create_shape node type in the tool description
- Direct arbitrary geometry requests to SVG-backed create_vector input
- Keep the prose synchronized with the declared type enum
Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
- 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>