Commit graph

1135 commits

Author SHA1 Message Date
Danila Poyarkov cfefc1fb4e fix(kiwi): preserve derived instance layout 2026-04-25 20:30:52 +03:00
Danila Poyarkov 63d3f2e0cb fix(kiwi): improve figma export fidelity 2026-04-25 15:57:15 +03:00
Danila Poyarkov 53a623e466 fix(canvas): clip vector inside stroke geometry 2026-04-25 13:36:39 +03:00
Danila Poyarkov 5f33f26756 fix(canvas): clip inside stroke geometry 2026-04-25 13:32:45 +03:00
Danila Poyarkov 3bccb69759 fix(canvas): use fig stroke geometry for shapes 2026-04-25 13:20:03 +03:00
Danila Poyarkov 3e52f762ff fix(kiwi): preserve flipped vector bounds 2026-04-25 13:14:20 +03:00
Danila Poyarkov 489f273e33 fix(kiwi): scale nested instance contents 2026-04-25 13:08:09 +03:00
Danila Poyarkov d1e659c1db fix(app): prevent browser mod shortcuts 2026-04-25 12:42:46 +03:00
Danila Poyarkov 2b24669949 fix(vue): edit text inside components on double click 2026-04-25 12:38:22 +03:00
Danila Poyarkov d9e72b2ff2 fix(app): fit opened files to viewport 2026-04-25 12:32:12 +03:00
Danila Poyarkov cf416e6ceb fix(io): parse fig files off the main thread 2026-04-25 12:26:39 +03:00
Danila Poyarkov 317b3469df fix(kiwi): resolve Figma variable aliases 2026-04-25 12:21:35 +03:00
Danila Poyarkov 006e143525 fix(canvas): render size badge in screen space so it doesn't scale with zoom
The selection size pill was drawn in local node space after
canvas.concat(worldMatrix * viewMatrix), causing it to scale
with the canvas zoom. Now computed in screen coordinates like
the multi-selection badge.
2026-04-23 20:34:41 +03:00
Danila Poyarkov d5b63682c4 chore: commit Cargo.lock with fix-path-env dependency 2026-04-23 20:07:12 +03:00
Danila Poyarkov 44f24fa72e fix(undo): call requestRender after undo
undoAction was missing requestRender, unlike redoAction. Added
E2E test: 3 duplicates then 3 undos verifies all are reversed.
2026-04-23 20:03:11 +03:00
Danila Poyarkov 668309a557 fix(clipboard): catch invalid kiwi data in parseFigmaClipboard
Non-Figma clipboard content (plain text, images) matched the figmeta
regex but failed at decodeMessage, flooding the console with unhandled
promise rejections on every paste.
2026-04-23 19:51:42 +03:00
Danila Poyarkov 35f3bb7f54 ci: create GitHub releases as drafts
Auto-generated release notes from CHANGELOG.md are often wrong.
Create as draft so the changelog section can be reviewed and
pasted manually before publishing.
2026-04-23 17:16:58 +03:00
Danila Poyarkov ba414e1af7 Release v0.11.8 2026-04-23 16:15:37 +03:00
Danila Poyarkov 8b5e372953 fix(tauri): use navigator.platform for Windows detection in MCP spawn
navigator.userAgent may not contain 'Windows' in all WebView2
contexts. navigator.platform.includes('Win') is more reliable.

Verified working on Windows x64 VM — MCP server spawns via
cmd /c and connects successfully.
2026-04-23 16:01:11 +03:00
Danila Poyarkov 1bf19fff59 fix(app): inline Tauri detection in EditorView
IS_TAURI is evaluated at module load time and may be false if
__TAURI_INTERNALS__ isn't injected yet. This prevented the
automation WebSocket from connecting on Windows and some Linux
configurations.
2026-04-23 14:37:33 +03:00
Danila Poyarkov 8c643298f3 Revert "debug: add MCP spawn logging, revert cmd approach"
This reverts commit 663e1018e7.
2026-04-23 14:05:19 +03:00
Danila Poyarkov efd1ef2ba6 Revert "debug: localStorage MCP debug"
This reverts commit c9a18c9094.
2026-04-23 14:05:19 +03:00
Danila Poyarkov c9a18c9094 debug: localStorage MCP debug 2026-04-23 14:04:09 +03:00
Danila Poyarkov 663e1018e7 debug: add MCP spawn logging, revert cmd approach 2026-04-23 13:58:53 +03:00
Danila Poyarkov 5b50b1f514 fix(automation): inline Tauri detection in spawnMCPIfNeeded
IS_TAURI is evaluated at module load time and can be false if
__TAURI_INTERNALS__ isn't injected yet. Check at call time instead.
2026-04-23 13:36:19 +03:00
Danila Poyarkov fbf582a493 fix(tauri): fix MCP server spawn on Windows and PATH on macOS
- Windows: npm installs .cmd wrappers, not direct executables. Spawn
  via 'cmd /c openpencil-mcp-http' on Windows to resolve .cmd files.
- macOS/Linux: add fix-path-env-rs to inherit shell PATH in GUI apps.
- Add 'cmd' to shell spawn capabilities for Windows.

Fixes #226
2026-04-23 13:20:58 +03:00
Danila Poyarkov 1f073859c9 fix(tauri): fix PATH not inherited by GUI app on macOS/Linux
macOS GUI apps don't inherit shell PATH from .bashrc/.zshrc, so
openpencil-mcp-http installed via bun/npm/nvm couldn't be found.
Added fix-path-env-rs which reads /etc/paths and shell config
to populate PATH before spawning child processes.

Fixes #226
2026-04-23 11:57:38 +03:00
Danila Poyarkov d25d80083b fix(vue): resolve unexported subpath import breaking npm consumers
- Export getAbsolutePositionFull from @open-pencil/core/canvas subpath
- Update vue import from @open-pencil/core/canvas/coordinate to @open-pencil/core/canvas
- Formatting fixes from oxfmt in pen/convert.ts, pen/read.ts, figma-factory.ts
2026-04-22 21:11:53 +03:00
Danila Poyarkov f3dbf2c271 Release v0.11.7 2026-04-22 20:31:33 +03:00
Danila Poyarkov 14325280d9 docs(i18n): translate all missing pages to 6 languages
- 78 SDK pages per language (components, composables, advanced API,
  guides, architecture, getting-started)
- vector-edit.md user guide page (5 languages)
- Update stale ru/programmable/cli/inspecting.md
- Fix YAML frontmatter quoting for colons in descriptions

474 files, 0 missing pages across de/es/fr/it/pl/ru.
VitePress build verified — all 546 SDK pages render.
2026-04-22 18:35:22 +03:00
Danila Poyarkov 51213d014e docs: update README and AGENTS.md for new features
- Update tool count from 90 to 100+
- Document OPENPENCIL_MCP_ROOT env var for file path scoping
- Document MCP-only tools, export path param, and prompt markdown files
  in AGENTS.md
2026-04-22 18:12:15 +03:00
Danila Poyarkov d2d6a89448 fix(lint): resolve all linting warnings and errors
- scene-graph: replace non-null assertion with guard
- figma-api: console.log → console.warn for notify()
- editor/nodes: replace non-null assertion with guard
- automation/server: console.log → console.debug
- mcp/stdio: proper Buffer/ArrayBuffer/Buffer[] UTF-8 decoding
- design-jsx/render: suppress no-implied-eval for Function constructor

0 warnings, 0 errors.
2026-04-22 18:01:52 +03:00
Danila Poyarkov 84c5e0dded docs: update changelog 2026-04-22 17:53:32 +03:00
Danila Poyarkov 1d5558ddc3 feat: JSX reference, multi-root JSX, prompts as markdown files
- Move CODEGEN_PROMPT to codegen.md, loaded via raw-md bundler plugin
- Add JSX_REFERENCE as jsx-reference.md with full prop/tag/example docs
- Copy JSX Reference button (book icon) in Code panel header
- Multi-root JSX: try parsing as-is, wrap in fragment on failure
- Component and Instance tag aliases in JSX renderer
- renderJSX returns RenderResult[] to support fragments
- raw-md plugin for both tsdown and Vite

Co-authored-by: sld0Ant <sld0Ant@users.noreply.github.com>
2026-04-22 17:51:41 +03:00
Danila Poyarkov d692953d62 test: fix pre-existing failures and add coverage for new features
- Fix mcp-stdio.test.ts: defer heavy SDK imports to avoid describe()
  registration race when running full suite
- Fix mcp-server.test.ts: add server.close() to properly clean up
  sessions and pending requests between tests (eliminates 8 unhandled
  errors)
- Add mcp-path-scoping tests: path traversal, sibling dir, root prefix
  trick, valid paths
- Add editor-store-path tests: setPlannedFilePath with Unix/Windows
  paths
- Add MCP server tests: open_file/new_document registered when mcpRoot
  is set, absent when null

0 fail, 0 errors, 1108 pass
2026-04-22 17:20:09 +03:00
Danila Poyarkov b502b43c4d feat(mcp): add open_file, new_document tools and disk export via path param
- open_file: opens a .fig/.pen from disk into a new tab via MCP
- new_document: creates an empty doc with optional save path
- export_image, export_svg, get_jsx: optional path param writes output
  to disk instead of returning base64/string (avoids flooding AI context)
- OPENPENCIL_MCP_ROOT env var scopes all file paths (defaults to cwd)
- setPlannedFilePath + startWatchingCurrentFile on editor store
- Extract openFileFromPath helper in use-menu.ts
- Add fs:allow-mkdir and scoped fs:allow-watch Tauri capabilities
- Better 'app not connected' error in stdio — instructs agent to stop

Co-authored-by: Jais Pedersen <jais@pedersens.net>
2026-04-22 17:05:54 +03:00
Danila Poyarkov a46d63916c fix(chat): fix provider settings popover not appearing
The Tip (Tooltip) wrapper around PopoverTrigger caused floating-ui
to miscalculate the popover position as translate(0px, -200%),
rendering it off-screen. Replaced with a plain title attribute
for the hover hint.

Fixes #218
2026-04-22 16:52:36 +03:00
Danila Poyarkov d0b5c51c5a fix(tools): set_font_range now produces valid style runs
- Use applyStyleToRange from text/style-runs.ts instead of naively
  appending runs (which created overlaps and invalid state)
- Apply fontWeight from style name (e.g. 'Bold' → 700)
- Apply color as fills on the style override
- Previously the color param was accepted but silently ignored

Fixes #214
2026-04-22 16:01:03 +03:00
Danila Poyarkov 5c0262fa80 docs: update changelog 2026-04-22 15:53:07 +03:00
Danila Poyarkov 99c5e7b609 fix(app): show toast when MCP server fails to start
Previously the error was silently caught with console.error, leaving
Windows users with no indication why the automation bridge was missing.
Now shows a visible toast with install instructions.
2026-04-22 15:51:26 +03:00
Danila Poyarkov 3eeea4203c fix(mcp): explicitly list entry points in tsconfig
tsgo with `include: ["src"]` sometimes skips standalone entry points
like stdio.ts that aren't imported by other files. This caused the
published npm package to be missing dist/stdio.js, breaking the
`openpencil-mcp` stdio binary.

Fixes #224 (npm publish issue)
2026-04-22 15:49:18 +03:00
Danila Poyarkov 10921d1b58 feat: JSX absolute positioning + MCP tools/list_changed on app connect
JSX renderer: support position="absolute", top, and left props for
placing children inside auto-layout containers without breaking flow.

MCP server: send notifications/tools/list_changed when the desktop
app connects or disconnects, so MCP clients can refresh their tool
list instead of discovering disconnection per-call.

Addresses #208 (findings 2 and 5)
2026-04-22 15:40:19 +03:00
Danila Poyarkov 7513399fce fix(mcp): coerce string-encoded numbers in tool parameters
MCP clients often serialize numeric arguments as JSON strings.
Previously this caused validation errors on tools like node_move.
Now both the MCP server (zod) and AI adapter (valibot) coerce
string-encoded numbers to numbers, rejecting only genuinely
non-numeric values like 'abc'.

Fixes #207
2026-04-22 14:40:11 +03:00
Danila Poyarkov ab1ff69f99 fix(vector): validate VectorNetwork input in create_vector
create_vector now validates the path JSON before accepting it:
checks vertices have numeric coordinates and segment indices are
in range. Returns a clear error message instead of silently
creating a malformed node that crashes on save.

The scene graph also normalizes vectorNetwork on updateNode as
a safety net for other code paths.
2026-04-22 14:35:25 +03:00
Danila Poyarkov 3727fa19d0 test(vector): add normalizeVectorNetwork tests
Cover missing tangent defaults, partial tangent, and encode/decode
round-trip for segments created without tangent handles.
2026-04-22 14:31:49 +03:00
Danila Poyarkov 66b2e7c487 fix(vector): default missing tangents to {x:0, y:0} in create_vector
Vectors created via MCP with straight-line segments (no tangent
handles) crashed the .fig serializer on save. Segments without
tangentStart/tangentEnd are now normalized to zero vectors at
creation time, producing straight line segments as intended.

Fixes #206
2026-04-22 14:29:25 +03:00
Danila Poyarkov 405a3f2ecf fix(vue): show Create Instance instead of Create Component for components
When right-clicking a component, the context menu now shows
'Create Instance' instead of 'Create Component'. Previously both
were shown simultaneously.
2026-04-22 14:21:18 +03:00
Danila Poyarkov 3a2a3865f4 fix(vue): enable Create Instance menu item for components
The disabled flag was inverted — the manually constructed menu item
used `!commandMenuItem(...).disabled` which made the item disabled
when the command was enabled, and vice versa. Replaced with a direct
commandMenuItem() call which handles label, disabled, and action
correctly.

Fixes #223
2026-04-22 14:16:14 +03:00
Danila Poyarkov e23b3f0ce1 feat(layout): use opentype.js for accurate headless text measurement
estimateTextSize() now tries opentype.js per-glyph advance widths
before falling back to the rough 0.6 × fontSize × charCount heuristic.
Since font data is already cached via loadFont(), this gives accurate
variable-width measurement for any loaded font — no CanvasKit needed.

Parsed opentype.js Font objects are cached per family+style to avoid
reparsing on every layout pass.
2026-04-22 14:11:36 +03:00
Danila Poyarkov 226542cfcf fix(layout): use stored .fig dimensions for headless text measurement
The estimateTextSize() fallback (fontSize × 0.6 × charCount) produced
wildly inaccurate results, causing 26K node mismatches on material3.fig.
Nodes imported from .fig files already carry Figma's computed dimensions —
use those instead. Falls back to the estimate only for newly-created
nodes with the 100×100 default size.

Reduces headless layout mismatches by ~56% (26,299 → ~11,669), with
TEXT node mismatches dropping 92%.

Fixes #212

Co-authored-by: Alexander Bonin <3462438+alex4o@users.noreply.github.com>
2026-04-22 14:00:07 +03:00