Commit graph

1295 commits

Author SHA1 Message Date
Danila Poyarkov 2f39ecaa4d fix(canvas): cull nested nodes in world space 2026-05-13 03:56:04 +03:00
Danila Poyarkov 66f562b52f fix(fonts): bundle Inter bold 2026-05-13 03:50:35 +03:00
Danila Poyarkov ccc2b0a52c chore(release): polish asset labels and loaders 2026-05-13 03:44:11 +03:00
Danila Poyarkov f25e5cfe0a fix(scene-graph): avoid id collisions after import 2026-05-13 03:36:28 +03:00
Danila Poyarkov bfd364909e docs: move development notes into docs package
- Move variables roadmap into the VitePress development docs
- Replace the stale renderer profiler plan with current usage notes
- Refresh eval scripting docs with the supported API surface
2026-05-13 02:48:09 +03:00
Danila Poyarkov 858a8a2566 docs: move roadmap notes out of root
- Remove the stale root PLAN document
- Move the variables UI roadmap under docs/development
- Update contributor guidance to point at the development roadmap
2026-05-13 02:41:01 +03:00
Danila Poyarkov 9daf260471 feat(components): render asset detail previews
- Generate PNG previews for the selected component/default variant in the details dialog
- Revoke stale preview URLs and refresh on scene changes
- Assert the rendered preview image in assets e2e coverage
2026-05-12 22:24:16 +03:00
Danila Poyarkov 0a53c91a46 feat(components): add asset details dialog
- Add a Figma-style component details dialog from local Assets
- Show preview, description, library key, documentation link, variant properties, and insert action
- Cover details metadata in the Assets panel e2e test
2026-05-12 22:16:07 +03:00
Danila Poyarkov 880fcf3d5b feat(components): surface library metadata in assets
- Show imported library badges in the local Assets panel
- Display component descriptions and documentation links from Figma metadata
- Cover library metadata rendering in the Assets panel test
2026-05-12 21:56:07 +03:00
Danila Poyarkov 66f91de825 test(kiwi): cover component metadata serialization
- Assert verified component library metadata is emitted to NodeChange fields
- Cover component property definitions and variant property specs serialization
2026-05-12 21:52:23 +03:00
Danila Poyarkov 866cabd623 feat(kiwi): preserve component library metadata
- Import Figma component keys, library keys, publish/version IDs, descriptions, links, and variant specs
- Export verified component metadata back into NodeChange fields
- Use variantPropSpecs to derive component property values after component set imports
- Cover large fixture metadata import as heavy tests
2026-05-12 21:48:42 +03:00
Danila Poyarkov 4bf161b983 fix(components): sync instances when switching variants
- Swap instance contents when changing variants instead of only updating componentId
- Recreate instance redo state without stale child IDs
- Insert assets into entered containers using local coordinates
- Fall back to Windows paths when URL parsing fails
2026-05-12 18:43:11 +03:00
Danila Poyarkov 24d3ea748f fix(components): respect default variants in assets
- Insert component set assets using explicit default variant values
- Report duplicate variant value combinations in the assets panel
- Cover default insertion and conflict warnings in Playwright
2026-05-12 18:16:51 +03:00
Danila Poyarkov 1ba9b3e697 docs: update changelog for fallback fonts 2026-05-12 17:47:26 +03:00
Danila Poyarkov 57e6ed46cb fix(demo): clean up component assets showcase
- Move the button variant set into a dedicated clean area
- Keep variant labels from overlapping button contents
- Space the components section to avoid collisions
2026-05-12 17:39:02 +03:00
Danila Poyarkov 1ceabf7938 feat(components): add local assets panel
- Add a left-sidebar Assets tab for local components and component sets
- Insert component set assets as instances of the default variant
- Showcase local component set insertion in the demo
- Cover search, empty state, standalone insert, and variant switching with Playwright
2026-05-12 17:30:35 +03:00
Danila Poyarkov 23039e0b99 fix(canvas): wait for CJK fallback fonts
- Skip CJK and Arabic text until fallback font families are ready
- Re-render after async fallback font loading completes
- Add Playwright coverage for delayed CJK fallback loading
2026-05-12 16:54:55 +03:00
Danila Poyarkov ddf415ec30 feat(tauri): support desktop file associations
- Register .fig and .pen files with desktop bundles
- Open associated files on startup and via single-instance handoff
- Reuse the existing Tauri file open flow without broad filesystem scopes
2026-05-12 16:12:16 +03:00
Tem-man 3fb2cfc7b1
update id and name of deepseek as deepseek-v4-flash and deepseek-v4-pro (#253)
Update DeepSeek model IDs to the new V4 Flash/Pro names, set V4 Flash as the default, and bump @ai-sdk/deepseek.
2026-05-12 15:23:53 +03:00
Danila Poyarkov ac5db0dc9f docs: tighten domain folder convention in AGENTS.md 2026-05-11 12:20:53 +03:00
Danila Poyarkov 5787e45117 refactor(tools): move eval modules into domain folder 2026-05-11 12:19:46 +03:00
Danila Poyarkov a1339e845b refactor(tools): use acorn AST parser for eval wrapping
Replace hand-rolled regex heuristic with proper acorn JS parsing.
The AST correctly identifies ExpressionStatement as the last node
regardless of trailing semicolons, comments, or complex syntax.

Fixes edge cases like '42;' which the regex wrongly treated as a
statement.

Co-authored-by: stiff <v.stiff@gmail.com>
Co-authored-by: Jais Pedersen <jais@pedersens.net>
2026-05-11 12:15:38 +03:00
Danila Poyarkov 9e013e60e7 fix(mcp): fix stdio handshake and eval return values
Stdio handshake:
Server now sends { type: register, token } to WebSocket clients on
connection, so the stdio bridge receives the token immediately instead
of deadlocking while both sides wait to receive.

Eval return values (REPL-style):
Add wrapEvalCode() that detects when the last non-empty line is a value
expression and promotes it to a return statement. Bare expressions like
JSON.stringify(result) now return the value instead of undefined.
Single implementation in packages/core/src/tools/analyze/eval-wrap.ts
used by both the tool and the automation bridge.

Co-authored-by: stiff <v.stiff@gmail.com>
Co-authored-by: Jais Pedersen <jais@pedersens.net>
2026-05-11 12:05:43 +03:00
Danila Poyarkov 74e550adb2 chore(i18n): sync all locale files and add CI check
- Add scripts/check-locales.ts to detect missing/extra locale keys
- Wire as 'check:i18n' in the check pipeline
- Add missing translations for de, es, fr, it, pl, ru, zh-CN:
  layout size limits, gap controls, variable modes, variants
2026-05-07 21:42:34 +03:00
Danila Poyarkov c79c9106fb feat(components): add variant inspector UI
Show variant property dropdowns in the design panel when an instance
of a component set is selected. Switching a dropdown value finds the
matching variant component and updates the instance's componentId.

- VariantSection.vue with property dropdowns using AppSelect
- Bridge variant actions to the editor return object
- i18n for en, ru, zh-CN

Ref #239
2026-05-07 19:48:59 +03:00
Danila Poyarkov fee44ebdb7 feat(components): add variant property data model and core actions
Data model:
- Add ComponentPropertyDefinition and ComponentPropertyType to SceneNode
- Add componentPropertyValues for per-variant property values
- Populate from .fig import via componentPropDefs kiwi field
- Parse variant name=value syntax from component names on import

Core actions (packages/core/src/editor/components/variants.ts):
- Add/remove/rename variant property definitions with undo
- Collect variant options from component set children
- Find variant by property values
- Switch instance variant by changing a property value
- Parse and build variant name strings

Combine as variants:
- Parse slash-naming (Button/Primary/Large) into variant properties
- Auto-detect property columns from consistent slash counts
- Populate componentPropertyValues on each variant component

Ref #239
2026-05-07 16:12:46 +03:00
Danila Poyarkov ad88364562 feat(variables): bind min/max size constraints to variables
Wire VariableScrubInput for minWidth, maxWidth, minHeight, maxHeight
in the size controls panel. Uses the after-variable slot to preserve
the existing select dropdown suffix alongside the variable binding UI.
2026-05-07 14:32:36 +03:00
Danila Poyarkov 72614fac4c fix(editor): paste into selected container
Paste now targets the selected frame/group/component/section instead of
always placing nodes on the page root, matching Figma's behavior:

- Single container selected: paste as child
- Non-container child selected: paste as sibling in its parent
- Entered container (double-click): paste inside it
- Nothing or multiple nodes selected: paste on the page

Also applies to image paste via placeImageFiles.

Fixes #79
2026-05-07 13:56:30 +03:00
Danila Poyarkov 98ce9a70ac docs: update changelog with variables and refactoring work 2026-05-07 11:41:52 +03:00
Danila Poyarkov 99d4cb8c3b docs: update changelog with variables and refactoring work 2026-05-07 10:38:37 +03:00
Danila Poyarkov 4e702799fc refactor: reuse blurTarget in inline rename 2026-05-07 10:30:27 +03:00
Danila Poyarkov a6da185e54 refactor: eliminate HTMLInputElement template casts
- Add shared DOM event helpers to Vue SDK (inputValue, inputNumberValue,
  blurTarget, selectTarget) in packages/vue/src/shared/dom-events.ts
- Export from @open-pencil/vue for app consumption
- Replace all template `as HTMLInputElement` casts with helper calls:
  ColorPickerPanel fields, PickerSlider, GradientEditor, ColorInput,
  CollabPanel, AppMenu, ScrubInputRoot
- Fix useInlineRename to use instanceof guards instead of casts
- Fix document name rename to accept Event directly
2026-05-07 10:24:46 +03:00
Danila Poyarkov 0a268f0a65 feat(variables): add inspector bindings for typography properties
- Add lineHeight, letterSpacing, fontWeight, paragraphSpacing,
  paragraphIndent to NumberBindingPath
- Wire lineHeight and letterSpacing inputs in TypographySection
  to VariableScrubInput with variable binding support
2026-05-07 09:50:52 +03:00
Danila Poyarkov 506b5f81e1 refactor(variables): reuse useInlineRename for dialog renaming
- Reuse useInlineRename composable for collection and mode renaming
- Accept Event in useInlineRename.commit to eliminate template casts
- Remove HTMLInputElement casts from PagesPanel and LayerTree blur handlers
- Remove hand-rolled rename state from dialog composable
2026-05-07 00:02:13 +03:00
Danila Poyarkov 107e9f3d51 feat(variables): add mode management and collection deletion
Core:
- Add addMode, removeMode, renameMode, setDefaultMode to SceneGraph
- Add editor actions with full undo: addMode, removeMode, renameMode,
  setDefaultMode, duplicateMode, setActiveMode

Vue SDK:
- Wire all mode and collection actions through SDK helpers
- Add mode rename state to dialog composable
- Remove HTMLInputElement casts from commit handlers

UI:
- Mode management lives in the table column headers (Figma-style):
  double-click to rename, right-click for context menu (rename,
  duplicate, set as default, delete), + button after last mode column
- Collection ⋯ dropdown menu with rename and delete
- Use shared menu UI styles throughout
- i18n for en, ru, zh-CN
2026-05-06 21:36:04 +03:00
Danila Poyarkov 2147aeb327 fix(core): resolve bundled fonts from package root
Replace fragile ../../assets relative path with createRequire-based
package.json resolution. The old path broke if the file moved or after
bundler output restructuring. The new approach resolves the package root
via require.resolve('@open-pencil/core/package.json') which works from
source, dist, and node_modules.
2026-05-06 17:07:31 +03:00
Danila Poyarkov c2afb8ddca chore(lint): enforce type safety in scripts
- Fix non-null assertions in visual-compare script
- Fix missing radix parameter
- Replace ImageMagick rgba() color with hex notation
- Remove no-non-null-assertion, no-hand-rolled-color, and radix script overrides
- Keep only no-console override for CLI scripts
2026-05-06 16:41:40 +03:00
Danila Poyarkov 541c9bd13c test: move tauri helpers into domain folder
- Move tests/helpers/tauri-mocks.ts → tests/helpers/tauri/mocks.ts
- Move tests/helpers/tauri-fig-export-fixture.ts → tests/helpers/tauri/fig-export-fixture.ts
- Update all imports
2026-05-06 16:16:08 +03:00
Danila Poyarkov 738d199e8a refactor: remove ugly type intersection casts
- Add optional nodeEditState, cursorCanvas, and pen resume fields to core EditorState
- Add parent field to LintNode instead of repeated intersection casts
- Use typed LintPathNode for node path traversal
- Replace autosave useDebounceFn cancel hack with watchDebounced
- Simplify AppEditorState to extend EditorState without Omit+intersection
- Remove PenState intersection type alias
- Use in-operator check for optional setViewportSize
2026-05-06 16:08:36 +03:00
Danila Poyarkov 7dce333200 docs: document event bus and wire app consumers
- Update CHANGELOG.md with event bus, test restructuring, and lint hardening
- Document EditorEvents table and conventions in AGENTS.md
- Wire collab graph sync to editor event bus instead of SceneGraph.onNodeEvents
- Wire collab awareness zoom to viewport:changed instead of Vue watcher
- Wire collab selection broadcasting to selection:changed instead of Vue watcher
2026-05-06 15:38:45 +03:00
Danila Poyarkov 1835903e56 feat(editor): add event bus for editor lifecycle events
Wire nanoevents-based event bus to the editor core:

- EditorEvents type with render, graph, selection, tool, page, viewport events
- All node mutations (create/update/delete/reparent/reorder) forwarded from SceneGraph
- Selection changes routed through setSelectedIds() with diff detection
- Tool changes routed through setActiveTool() with change detection
- Page switches emit page:changed
- Viewport pan/zoom/fit/zoomTo emit viewport:changed
- Graph replacement emits graph:replaced
- Typed onEditorEvent() subscription on the Editor return object
- useEditorEvent() Vue composable with automatic scope cleanup
- Replace all direct state.selectedIds/state.activeTool mutations in core, app, and SDK
2026-05-06 15:25:27 +03:00
Danila Poyarkov dfa9505805 chore(lint): enforce domain prefix folders project-wide
- Generalize sibling domain prefix linting beyond tests
- Move create icon tools under the existing create domain folder
- Keep create.ts exporting the icon tools through the domain folder
2026-05-06 14:33:17 +03:00
Danila Poyarkov aad26b535e test: move prefixed splits into domain folders
- Move newly split tests under explicit domain subfolders
- Update nested helper imports after the moves
- Add a lint guard against repeating existing sibling domains as filename prefixes
2026-05-06 14:27:21 +03:00
Danila Poyarkov b9da0dcb76 chore(lint): enforce test complexity limits
- Extract color picker fill comparison helpers
- Guard derived text assertions without optional chains
- Split resize math into smaller helper functions
- Remove the remaining complexity test exceptions
2026-05-06 14:18:29 +03:00
Danila Poyarkov 13a3475026 test(layout): split auto-layout coverage
- Move auto-layout tests into focused files by layout behavior
- Preserve sizing, wrapping, text measurement, and positioning coverage
- Remove the last max-lines test exception
2026-05-06 14:10:20 +03:00
Danila Poyarkov 5e7c042352 test(scene-graph): split basic coverage
- Move scene graph behavior tests into focused files
- Share basic scene graph helper builders from basic/helpers.ts
- Remove scene graph basic from max-lines exceptions
2026-05-06 14:05:58 +03:00
Danila Poyarkov c589818a71 test(clipboard): split node import coverage
- Move clipboard node import tests into focused files by behavior
- Preserve filtering, hierarchy, styling, component, and undo coverage
- Remove import-nodes from max-lines exceptions
2026-05-06 14:01:18 +03:00
Danila Poyarkov 9ebc61d78f test(figma): split API coverage
- Move FigmaAPI tests into focused files by feature area
- Share API construction from figma/api/helpers.ts
- Remove FigmaAPI coverage from max-lines exceptions
2026-05-06 13:37:15 +03:00
Danila Poyarkov 54ccc35e3e test(fig): split legacy import coverage
- Move legacy fig import tests into focused domain files
- Share NodeChange fixture builders from legacy/helpers.ts
- Remove legacy import from max-lines exceptions
2026-05-06 13:33:42 +03:00
Danila Poyarkov 42d346ba47 test(kiwi): split serialization regression coverage
- Move Kiwi serialization fix tests into one file per regression area
- Share codec setup and Kiwi helpers from serialize-fixes/helpers.ts
- Remove serialize fixes from max-lines exceptions
2026-05-06 13:12:53 +03:00