* feat: persist AI conversations and add chat history
Store transcripts and attachment previews in IndexedDB, separate conversation history from transport lifetime, and add document-aware switching with shared Storybook coverage. Preserve interrupted activity and guard stale writes and async switches.
* test: group chat history tests by domain
* feat: simplify chat history navigation and diagnostics
Use a compact header with searchable document-scoped history and a correctly anchored conversation menu. Move diagnostic copying into the menu with copy-result feedback, and separate Storybook fixtures from composition.
* fix: address chat history review findings
* test: cover harness shutdown and restored chat scrolling
* fix: preserve Portless proxy port in MCP routes
* docs: clarify UI animation conventions
* feat: configure reasoning display and animate disclosure
* fix: separate transcript following from reasoning disclosure
* fix: restore selected chat after document recovery
* refactor: separate chat history persistence and sessions
* style: format reasoning story imports
- Classify authentication, access, model, rate-limit, network, and credit errors\n- Preserve the original provider error across the no-output stream consequence\n- Add optional toast actions for opening model settings\n- Consume handled chat send rejections to avoid global unhandled errors\n- Localize provider failure guidance and cover the authentication flow
- Replace the shared serial page with isolated configured-chat fixtures\n- Split composer, attachment, Markdown, profile, navigation, and response behavior\n- Use Playwright's structured @real-llm tag with a credential-gated config\n- Keep mock stream construction and page operations in focused helpers
- Keep deterministic chat tests permanently bound to the mock transport\n- Add an explicit @real-llm Playwright project and package script\n- Skip the paid OpenRouter smoke test clearly when its credential is absent
- Move chat app setup and shared locators into the test helper domain\n- Move mock AI stream construction out of the panel specification\n- Keep the panel behavior suite focused on user-facing assertions
* 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
- 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
- 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
* 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(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
- 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
- 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>
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
* 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>
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
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.
- 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%)