Commit graph

1233 commits

Author SHA1 Message Date
Danila Poyarkov 7a02ee2c62
feat(editor): model split canvas panes (#519)
* 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

* feat(editor): model split canvas panes

- Add pure recursive split-tree operations with validated sizes

- Add explicit pane registry and independent view-state cloning

- Cap visible panes and cover close and split behavior

* 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

* feat(editor): model split canvas panes

- Add pure recursive split-tree operations with validated sizes

- Add explicit pane registry and independent view-state cloning

- Cap visible panes and cover close and split behavior

* feat(editor): add split canvas views

- Render recursive pane layouts with Reka UI splitters and pane-local headers
- Route canvas input, selection, viewport state, and close actions to the active pane
- Repaint every canvas during shared document previews and cover split lifecycle in tests

* fix(editor): clean up inactive pane interactions

- Cancel drag, padding preview, and text editing state when pane focus changes
- Remove duplicate changelog entries introduced while updating master
2026-08-15 10:31:52 +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 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 f781c48eaa
fix(canvas): pan horizontally with Shift+wheel (#523)
- Convert primarily vertical Shift+wheel input to horizontal panning
- Preserve native horizontal trackpad deltas instead of swapping both axes
- Keep Ctrl and Meta wheel gestures on the existing zoom path
2026-08-14 20:58:28 +03:00
Danila Poyarkov d67415c793
docs: explain component library workflows (#520)
Documents component libraries and localized workflows added by #512.
2026-08-14 20:39:47 +03:00
Danila Poyarkov a29f609dd2
fix(settings): separate MCP connections from models (#522)
- Add a dedicated MCP connections destination to Settings navigation
- Keep ModelsPanel focused on model profiles and assignments
- Update documentation, changelog, and browser coverage for the new location
2026-08-14 20:39:29 +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 b93ffea073
feat: add Figma-style variants and component libraries (#512)
Closes #239
2026-08-14 19:31:41 +03:00
Danila Poyarkov c2103ef722
feat(canvas): measure distances between layers (#515)
* feat(canvas): measure distances between layers

- Show temporary Figma-style guides while Option or Alt is held

- Support deep nested targets with Command or Control plus Alt

- Measure transformed world bounds and container padding

* fix(canvas): anchor diagonal measurement guides

- Join diagonal distances at the nearest object corners

- Separate horizontal and vertical value badges

- Crop visual coverage tightly around the measurement overlay

* fix(canvas): cover measurement edge cases

- Draw transformed target outlines and center value pills on guides

- Suppress conflicting hover overlays during measurement mode

- Cover containment, overlap, multi-selection, rotation, and fractional gaps

* fix(canvas): suppress conflicting measurement overlays

- Hide auto-layout hover visuals while distance measurements render

- Assert fractional measurement geometry with numeric tolerance
2026-08-14 18:18:55 +03:00
Danila Poyarkov e19ed6adce Merge remote-tracking branch 'origin/master' into fix-svg-clip-path-import 2026-08-14 13:37:46 +03:00
Danila Poyarkov bcf361f45d fix(io): handle bounded and nested SVG clips
- Carry inherited clip metadata through use expansions
- Map objectBoundingBox clips per painted path bounds
- Apply nested clip regions as ordered vector mask frames
2026-08-14 13:21:46 +03:00
Danila Poyarkov 742522a692 fix(io): preserve SVG clip paths
- Resolve clipPath geometry referenced through use elements
- Import clipped paint runs as editable mask groups
- Cover clipped multicolor SVG imports with a regression test
2026-08-14 12:58:44 +03:00
Danila Poyarkov 3ee3708d71
perf(clipboard): index pasted node children (#511)
- Build parent-to-child indexes once while decoding Figma clipboard data

- Reuse the index for hierarchy traversal and internal component discovery

- Avoid quadratic scans that blocked large flat pastes
2026-08-14 12:37:05 +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 8294e32f33 feat(ai): add isolated visual inspection
- Route bounded selection renders through the optional Vision model

- Return compact text findings without retaining image data in Design chat history

- Bound MCP and AI image exports by their longest pixel edge
2026-08-13 21:15:44 +03:00
Danila Poyarkov eb4f7c7ed8
Merge pull request #506 from open-pencil/adapt-pr-397-current
fix: adapt FIG parsing and package hardening from PR 397
2026-08-13 21:12:13 +03:00
Danila Poyarkov 3572eeec5e docs: remove untranslated localized placeholders
- Route copied English MCP, CLI, technical reference, shortcut, and useNumberField pages to their canonical English sources
- Repair remaining mixed-language user-guide and SDK labels
- Detect cross-locale placeholder copies and substantial English content in localized Markdown during documentation integrity checks
2026-08-13 20:40:41 +03:00
Danila Poyarkov 2c064c5a79 chore: align adapted checks with current conventions
- Preserve uppercase acronym casing in package tooling and CSS helpers

- Keep selector escaping without restoring removed test ID prop APIs

Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
2026-08-13 20:33:10 +03:00
Danila Poyarkov 15ea98d8fb fix(clipboard): report image fetch attempts accurately
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
2026-08-13 20:33:10 +03:00
Danila Poyarkov 6a888ea692 fix(vue): escape test selectors outside browsers
- Fall back to standards-compatible CSS identifier escaping when CSS.escape is unavailable

Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
2026-08-13 20:33:10 +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 9676bfe31b
Merge pull request #504 from open-pencil/fig-export-memory
perf(fig): reduce export graph cloning
2026-08-13 20:32:21 +03:00
Danila Poyarkov 3c385ebe15 docs: explain font substitution diagnostics
- Document the missing-font banner, active substitutions, retry flow, and font source behavior in every maintained user guide
- Add list_available_fonts and get_font_status to the MCP tool reference and replace stale exact tool counts with a durable 100+ description
2026-08-13 20:26:25 +03:00
Danila Poyarkov 1eb1f38c78 feat(text): surface missing font substitutions
- Track loaded font provenance and aggregate page-level face fidelity, including synthesized styles and active Inter substitutions
- Add a localized warning banner with retry and affected-layer actions
- Expose get_font_status through the Figma API and MCP tool registry
2026-08-13 18:33:10 +03:00
Danila Poyarkov 87d921f75e docs: link portable font roadmap issues 2026-08-13 18:02:49 +03:00
Danila Poyarkov 6bd319881e perf(fig): reduce export graph cloning
- Clone mutable node shells without duplicating imported binary resources

- Preserve live graph isolation during lazy population
2026-08-13 17:56:37 +03:00
Danila Poyarkov 7b8e5fbfbe fix(app): harden document recovery lifecycle
- Serialize recovery cleanup with active snapshot writes

- Preserve version ordering across autosave, restore, and cleanup

- Fall back to memory when IndexedDB operations fail
2026-08-13 17:22:59 +03:00
Danila Poyarkov 37912d92a7 feat(app): recover unsaved documents
- Persist debounced FIG snapshots for source-less documents in IndexedDB

- Restore or discard orphaned snapshots from the editor startup dialog

- Clear recovery data after successful saves and explicit tab closure
2026-08-13 17:22:59 +03:00
Danila Poyarkov 3a2c8afc3e fix(ai): harden failure diagnostics
- Redact and bound provider error details in copied logs

- Avoid repeat toasts after locale changes

- Show reasoning effort only for supported providers
2026-08-13 15:03:12 +03:00
Danila Poyarkov 9e93884e7f fix(ai): surface silent provider failures
- Report credit and output-limit failures through localized toasts and debug logs

- Persist optional provider-specific reasoning effort on model profiles

- Classify insufficient-credit connection tests and cover failure diagnostics
2026-08-13 15:03:12 +03:00
Danila Poyarkov 3ef554ab2d docs: reorganize documentation and improve integrity
- Replace the ambiguous Guide section with explicit overview, reference, and development routes while preserving legacy URLs with redirects
- Route missing localized content to maintained canonical pages and emit SEO alternates only for real translations
- Add parser-backed documentation integrity checks and make the optimized local build the default while retaining a complete production build
2026-08-13 14:19:12 +03:00
Danila Poyarkov fece63b5b5
Merge pull request #498 from open-pencil/issue-102-multifixture-audit
fix(fig): preserve nested text overrides
2026-08-12 20:58:22 +03:00
Danila Poyarkov a8cf8eb412
fix(tauri): prevent Windows font loading crashes (#497)
* fix(tauri): prevent Windows font loading crashes

- Return native font files over binary Tauri IPC instead of JSON byte arrays

- Resolve desktop script fallbacks without parsing large system fonts in JavaScript

* fix(core): restore quality checks

- Use the shared Vector primitive for render-bound offsets

- Remove unsupported SLICE handling from SceneGraph rescaling

- Drop an unused generated-text test binding

* chore: address font fix review

- Place the release note under the Fixed heading

- Name fallback resolution options and use the shared Tauri constant
2026-08-12 20:09:04 +03:00
Danila Poyarkov cf72b8d1c4 fix(clipboard): preserve pasted instance links
- Share exported GUID assignments across clipboard roots

- Preserve source metadata while applying pasted instance overrides
2026-08-12 20:08:06 +03:00
Danila Poyarkov 38e98095b2 chore(fig): format instance override test 2026-08-12 19:44:32 +03:00
Danila Poyarkov 7d0f9746ee fix(fig): preserve nested text overrides
- Propagate effective text through structurally protected clone chains

- Preserve explicit text overrides and add focused regression coverage
2026-08-12 19:36:37 +03:00
Danila Poyarkov 3102a60f91
fix(vector): preserve open segments with regions (#496)
- 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
2026-08-12 19:28:37 +03:00
Danila Poyarkov 71bb3b20a6 fix(figma-api): implement layout compatibility gaps
- 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
2026-08-12 18:50:25 +03:00
Danila Poyarkov 3c016e9573
fix(mcp): close orphaned servers after app disconnects (#494)
* 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>
2026-08-12 18:44:31 +03:00
Danila Poyarkov 0ed6697946
docs: refresh product roadmap after v0.14.0 (#493)
* docs: refresh product roadmap after v0.14.0

- Record delivered editor, storage, automation, and reusable package foundations
- Add Cloud, self-hosting, BYOK, reliability, international text, and design verification priorities
- Incorporate durable feature-request themes for IDE integration, multimodal agents, import and code workflows, fonts, motion, tables, and inspection
- Track Figma Slides as a lower-priority interoperability exploration and correct stale compatibility entries

* docs: keep roadmap commitments outcome-focused

- Describe Cloud and self-hosting through supported user capabilities rather than implementation choices
- Keep BYOK and workspace portability as explicit product promises
- Track Figma Slides without coupling the published roadmap to an active PR
2026-08-12 16:05:17 +03:00
Danila Poyarkov 6f73b28f3d
fix(core): preserve inline SVG primitives (#485)
* 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
2026-08-12 16:02:45 +03:00
Danila Poyarkov 8bba716db0 Merge origin/master into fix-issue-385-instance-index 2026-08-12 14:57:42 +03:00
Danila Poyarkov 51ab21571a fix(i18n): translate remaining app surfaces
- Localize import, collaboration, settings, color, font, and accessibility text across supported locales

- Synchronize the document language through reactive Unhead attributes

- Translate connection failures, browser fallbacks, notifications, and save prompts
2026-08-11 22:19:40 +03:00
Danila Poyarkov 05a36e9672 fix(fig): preserve imported instance links
- 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
2026-08-11 18:54:29 +03:00
Danila Poyarkov a6842fde47 fix(vue): discard stale workspace results 2026-08-10 22:47:00 +03:00
Danila Poyarkov b21509cc8d fix(storage): isolate workspace refresh state
- Discard stale provider refreshes and queued previews

- Guard malformed deflate data and listener failures

- Start periodic refresh without an immediate callback
2026-08-10 22:30:54 +03:00
Danila Poyarkov c2bcb21fcf fix(vue): preserve workspace item types 2026-08-10 21:18:36 +03:00
Danila Poyarkov 6d9aeea28b refactor(vue): split workspace preview lifecycle 2026-08-10 20:42:56 +03:00
Danila Poyarkov 4fe27dd1e6 fix(storage): harden workspace previews
- Validate ranged thumbnail payloads and S3 bounds

- Invalidate stale previews and expose loading errors

- Document the public document workspace composable
2026-08-10 20:02:58 +03:00