2026-02-28 05:29:39 +00:00
|
|
|
/* eslint-disable */
|
|
|
|
|
// @ts-nocheck
|
|
|
|
|
// biome-ignore lint: disable
|
|
|
|
|
// oxlint-disable
|
|
|
|
|
// ------
|
|
|
|
|
// Generated by unplugin-vue-components
|
|
|
|
|
// Read more: https://github.com/vuejs/core/pull/3399
|
|
|
|
|
|
|
|
|
|
export {}
|
|
|
|
|
|
|
|
|
|
/* prettier-ignore */
|
|
|
|
|
declare module 'vue' {
|
|
|
|
|
export interface GlobalComponents {
|
2026-03-01 08:12:37 +00:00
|
|
|
APIKeySetup: typeof import('./src/components/chat/APIKeySetup.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
AppearanceSection: typeof import('./src/components/properties/AppearanceSection.vue')['default']
|
Add app menu bar for browser mode (File, Edit, View, Object, Text, Arrange)
Reka-ui Menubar in the left panel header, hidden when running in Tauri
(native menu handles it). Includes Open, Save, Save As, Export, Undo/Redo,
Zoom, Group/Ungroup, Component creation, Bring to front/Send to back,
Auto layout, Text formatting, Alignment.
2026-03-01 12:09:08 +00:00
|
|
|
AppMenu: typeof import('./src/components/AppMenu.vue')['default']
|
2026-03-02 18:33:04 +00:00
|
|
|
AppSelect: typeof import('./src/components/AppSelect.vue')['default']
|
2026-03-01 15:25:04 +00:00
|
|
|
AppToast: typeof import('./src/components/AppToast.vue')['default']
|
2026-02-28 15:10:26 +00:00
|
|
|
CanvasContextMenu: typeof import('./src/components/CanvasContextMenu.vue')['default']
|
2026-03-01 08:11:46 +00:00
|
|
|
ChatInput: typeof import('./src/components/chat/ChatInput.vue')['default']
|
|
|
|
|
ChatMessage: typeof import('./src/components/chat/ChatMessage.vue')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
ChatPanel: typeof import('./src/components/ChatPanel.vue')['default']
|
2026-03-01 09:12:45 +00:00
|
|
|
CodePanel: typeof import('./src/components/CodePanel.vue')['default']
|
2026-03-01 13:44:37 +00:00
|
|
|
CollabPanel: typeof import('./src/components/CollabPanel.vue')['default']
|
2026-02-28 23:18:23 +00:00
|
|
|
ColorInput: typeof import('./src/components/ColorInput.vue')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
ColorPicker: typeof import('./src/components/ColorPicker.vue')['default']
|
2026-03-01 07:55:52 +00:00
|
|
|
DesignPanel: typeof import('./src/components/DesignPanel.vue')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
EditorCanvas: typeof import('./src/components/EditorCanvas.vue')['default']
|
Add Effects section to properties panel
Support adding, removing, and editing drop shadows, inner shadows,
layer blurs, background blurs, and foreground blurs. Each effect row
has visibility toggle, type dropdown, and remove button. Expanded
view shows offset, blur, spread, and color controls for shadows,
or just blur radius for blur effects.
2026-02-28 17:12:37 +00:00
|
|
|
EffectsSection: typeof import('./src/components/properties/EffectsSection.vue')['default']
|
2026-02-28 21:05:19 +00:00
|
|
|
ExportSection: typeof import('./src/components/properties/ExportSection.vue')['default']
|
2026-02-28 14:26:50 +00:00
|
|
|
FillPicker: typeof import('./src/components/FillPicker.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
FillSection: typeof import('./src/components/properties/FillSection.vue')['default']
|
2026-03-01 00:25:50 +00:00
|
|
|
FontPicker: typeof import('./src/components/FontPicker.vue')['default']
|
2026-03-01 14:15:08 +00:00
|
|
|
HsvColorArea: typeof import('./src/components/HsvColorArea.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
IconLucideALargeSmall: typeof import('~icons/lucide/a-large-small')['default']
|
2026-03-01 15:25:04 +00:00
|
|
|
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
|
2026-02-28 06:01:54 +00:00
|
|
|
IconLucideAlignCenter: typeof import('~icons/lucide/align-center')['default']
|
CSS Grid layout mode (#73)
* Switch to @open-pencil/yoga-layout with CSS Grid support
Use our fork (open-pencil/yoga, grid branch) which cherry-picks the
upstream CSS Grid PRs (#1893–#1898) onto current main. The JS bindings
are ported from the old embind approach to the new wasm_bridge.c API.
npm:@open-pencil/yoga-layout alias keeps all imports as 'yoga-layout'.
Also handle FinalizationRegistry change (upstream #1908) — node.free()
no longer exists, nodes are garbage collected automatically.
* Add CSS Grid layout mode
Scene graph:
- LayoutMode gains 'GRID' option alongside HORIZONTAL/VERTICAL
- GridTrack type (sizing: FIXED/FR/AUTO, value) for track definitions
- GridPosition type (column, row, columnSpan, rowSpan) for children
- New node props: gridTemplateColumns/Rows, gridColumnGap, gridRowGap,
gridPosition
Layout engine:
- buildGridTree() sets Display.Grid and maps GridTrack[] to Yoga's
setGridTemplateColumns/Rows API with FR/Points/Auto track types
- Grid children use gridPosition for column/row placement with span
- Flex layout path unchanged
Store:
- setLayoutMode('GRID') auto-creates NxM track grid based on child
count (sqrt heuristic), defaults to 1fr tracks
UI (LayoutSection.vue):
- Grid button (grid-2x2 icon) added to flow direction row
- Columns/Rows track editors: ScrubInput for value + AppSelect for
sizing mode (Fill fr / Fixed px / Auto), add/remove buttons
- Separate column gap and row gap ScrubInputs
- Wrap button hidden when grid is active
- Flex alignment grid hidden when grid is active
Kiwi serialization skips GRID mode for now (no .fig codec support).
* Add grid layout integration tests
12 new test scenarios covering:
- Basic 2x2 grid, fixed columns, mixed fr/fixed, unequal fr weights
- Column gap, row gap, both gaps combined
- Padding offsets
- Explicit gridPosition placement, column span, row span
- Absolute children skipped
- Hidden children collapsed
- Nested grid inside flex parent (computeAllLayouts)
Also fix: grid frames as children of flex parents now correctly use
Display.Grid via configureChildAsGrid() instead of falling through
to configureChildAsAutoLayout() which set FlexDirection.
* Deduplicate grid yoga configuration
Extract configureAsGrid() and createGridChildNode() shared by
buildGridTree (root-level grid) and configureChildAsGrid (grid
nested inside flex parent).
* Add grid layout to changelog
* Add grid support to JSX and Tailwind CSS export
OpenPencil format: grid → columns/rows/columnGap/rowGap props,
child colStart/rowStart/colSpan/rowSpan.
Tailwind format: grid grid-cols-N grid-rows-N gap-x-* gap-y-*,
mixed tracks use arbitrary values (grid-cols-[200px_1fr_auto]),
child col-start-*/row-start-*/col-span-*/row-span-*.
Both formats: padding emitted for grid frames (shared with flex),
flex-only props (justify/items/wrap/gap) scoped to isFlex.
* Remove duplicate gridTrackToTw, use shared formatTrack
Also drop unused GridTrackSizing import from LayoutSection.vue.
* Fix grid icon: use layout-grid instead of grid-2x2
unplugin-icons can't resolve icon names with digits after hyphens
(grid-2x2 → grid2x2 which doesn't exist).
* Polish layout UI and fix auto-layout behavior
- Replace text labels with compact icons: ↔/↕ for gap, ☐ for uniform padding, T/R/B/L for per-side padding
- Direction-aware gap icon: ↕ for vertical, ↔ for horizontal
- Pin +/− padding toggle button right of gap input
- Fix alignment grid axes for vertical layout (transpose primary/counter)
- Fix grid switch: set FIXED sizing, compute frame size from children
- Remove hardcoded white fill from Shift+A wrap
- Auto-detect horizontal vs vertical from selection bounds
* Add flex-to-grid switch integration test
Verify HUG frame expands and children are placed in 2x2 grid
when switching from vertical flex to grid layout.
* Update changelog for grid layout
2026-03-08 15:23:53 +00:00
|
|
|
IconLucideAlignHorizontalDistributeCenter: typeof import('~icons/lucide/align-horizontal-distribute-center')['default']
|
2026-02-28 06:55:02 +00:00
|
|
|
IconLucideAlignHorizontalJustifyCenter: typeof import('~icons/lucide/align-horizontal-justify-center')['default']
|
|
|
|
|
IconLucideAlignHorizontalJustifyEnd: typeof import('~icons/lucide/align-horizontal-justify-end')['default']
|
|
|
|
|
IconLucideAlignHorizontalJustifyStart: typeof import('~icons/lucide/align-horizontal-justify-start')['default']
|
2026-02-28 06:01:54 +00:00
|
|
|
IconLucideAlignLeft: typeof import('~icons/lucide/align-left')['default']
|
|
|
|
|
IconLucideAlignRight: typeof import('~icons/lucide/align-right')['default']
|
2026-02-28 06:55:02 +00:00
|
|
|
IconLucideAlignVerticalJustifyCenter: typeof import('~icons/lucide/align-vertical-justify-center')['default']
|
|
|
|
|
IconLucideAlignVerticalJustifyEnd: typeof import('~icons/lucide/align-vertical-justify-end')['default']
|
|
|
|
|
IconLucideAlignVerticalJustifyStart: typeof import('~icons/lucide/align-vertical-justify-start')['default']
|
2026-03-07 14:17:48 +00:00
|
|
|
IconLucideArrowDown: typeof import('~icons/lucide/arrow-down')['default']
|
|
|
|
|
IconLucideArrowRight: typeof import('~icons/lucide/arrow-right')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
IconLucideBaseline: typeof import('~icons/lucide/baseline')['default']
|
CSS Grid layout mode (#73)
* Switch to @open-pencil/yoga-layout with CSS Grid support
Use our fork (open-pencil/yoga, grid branch) which cherry-picks the
upstream CSS Grid PRs (#1893–#1898) onto current main. The JS bindings
are ported from the old embind approach to the new wasm_bridge.c API.
npm:@open-pencil/yoga-layout alias keeps all imports as 'yoga-layout'.
Also handle FinalizationRegistry change (upstream #1908) — node.free()
no longer exists, nodes are garbage collected automatically.
* Add CSS Grid layout mode
Scene graph:
- LayoutMode gains 'GRID' option alongside HORIZONTAL/VERTICAL
- GridTrack type (sizing: FIXED/FR/AUTO, value) for track definitions
- GridPosition type (column, row, columnSpan, rowSpan) for children
- New node props: gridTemplateColumns/Rows, gridColumnGap, gridRowGap,
gridPosition
Layout engine:
- buildGridTree() sets Display.Grid and maps GridTrack[] to Yoga's
setGridTemplateColumns/Rows API with FR/Points/Auto track types
- Grid children use gridPosition for column/row placement with span
- Flex layout path unchanged
Store:
- setLayoutMode('GRID') auto-creates NxM track grid based on child
count (sqrt heuristic), defaults to 1fr tracks
UI (LayoutSection.vue):
- Grid button (grid-2x2 icon) added to flow direction row
- Columns/Rows track editors: ScrubInput for value + AppSelect for
sizing mode (Fill fr / Fixed px / Auto), add/remove buttons
- Separate column gap and row gap ScrubInputs
- Wrap button hidden when grid is active
- Flex alignment grid hidden when grid is active
Kiwi serialization skips GRID mode for now (no .fig codec support).
* Add grid layout integration tests
12 new test scenarios covering:
- Basic 2x2 grid, fixed columns, mixed fr/fixed, unequal fr weights
- Column gap, row gap, both gaps combined
- Padding offsets
- Explicit gridPosition placement, column span, row span
- Absolute children skipped
- Hidden children collapsed
- Nested grid inside flex parent (computeAllLayouts)
Also fix: grid frames as children of flex parents now correctly use
Display.Grid via configureChildAsGrid() instead of falling through
to configureChildAsAutoLayout() which set FlexDirection.
* Deduplicate grid yoga configuration
Extract configureAsGrid() and createGridChildNode() shared by
buildGridTree (root-level grid) and configureChildAsGrid (grid
nested inside flex parent).
* Add grid layout to changelog
* Add grid support to JSX and Tailwind CSS export
OpenPencil format: grid → columns/rows/columnGap/rowGap props,
child colStart/rowStart/colSpan/rowSpan.
Tailwind format: grid grid-cols-N grid-rows-N gap-x-* gap-y-*,
mixed tracks use arbitrary values (grid-cols-[200px_1fr_auto]),
child col-start-*/row-start-*/col-span-*/row-span-*.
Both formats: padding emitted for grid frames (shared with flex),
flex-only props (justify/items/wrap/gap) scoped to isFlex.
* Remove duplicate gridTrackToTw, use shared formatTrack
Also drop unused GridTrackSizing import from LayoutSection.vue.
* Fix grid icon: use layout-grid instead of grid-2x2
unplugin-icons can't resolve icon names with digits after hyphens
(grid-2x2 → grid2x2 which doesn't exist).
* Polish layout UI and fix auto-layout behavior
- Replace text labels with compact icons: ↔/↕ for gap, ☐ for uniform padding, T/R/B/L for per-side padding
- Direction-aware gap icon: ↕ for vertical, ↔ for horizontal
- Pin +/− padding toggle button right of gap input
- Fix alignment grid axes for vertical layout (transpose primary/counter)
- Fix grid switch: set FIXED sizing, compute frame size from children
- Remove hardcoded white fill from Shift+A wrap
- Auto-detect horizontal vs vertical from selection bounds
* Add flex-to-grid switch integration test
Verify HUG frame expands and children are placed in 2x2 grid
when switching from vertical flex to grid layout.
* Update changelog for grid layout
2026-03-08 15:23:53 +00:00
|
|
|
IconLucideBetweenHorizontalEnd: typeof import('~icons/lucide/between-horizontal-end')['default']
|
|
|
|
|
IconLucideBetweenHorizontalStart: typeof import('~icons/lucide/between-horizontal-start')['default']
|
|
|
|
|
IconLucideBetweenVerticalEnd: typeof import('~icons/lucide/between-vertical-end')['default']
|
|
|
|
|
IconLucideBetweenVerticalStart: typeof import('~icons/lucide/between-vertical-start')['default']
|
2026-02-28 06:20:31 +00:00
|
|
|
IconLucideBlend: typeof import('~icons/lucide/blend')['default']
|
2026-03-01 06:03:38 +00:00
|
|
|
IconLucideBold: typeof import('~icons/lucide/bold')['default']
|
2026-03-01 08:08:47 +00:00
|
|
|
IconLucideBot: typeof import('~icons/lucide/bot')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
IconLucideCheck: typeof import('~icons/lucide/check')['default']
|
|
|
|
|
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
Event-driven requestRender and component sync via SceneGraph emitter
Move requestRender() and component instance sync from manual call sites
to graph event subscriptions. Removes 79 manual requestRender() calls
(94→22) and all 9 syncIfInsideComponent calls.
Batching: component sync uses queueMicrotask — mutated node IDs
accumulate during a synchronous block, then deduplicate to ancestor
component IDs and call syncInstances once per component. requestRender()
is already rAF-batched (just increments counters checked by animation
frame loop), so multiple event-triggered calls in one sync block
collapse to a single repaint.
Event subscriptions:
- node:updated → invalidate render cache, schedule component sync, requestRender
- node:created/deleted/reparented/reordered → schedule component sync, requestRender
- Re-subscribes when graph instance is replaced (file open/reload)
Also fixes collab sync gap: subscribe to node:reordered in use-collab.ts
(same-parent reorder was never synced to Yjs peers).
Remaining requestRender() calls are for pure UI state changes (pen tool,
viewport pan/zoom, text editing, selection) and methods that bypass
events (detachInstance, bringToFront/sendToBack, restorePageFromSnapshot).
2026-03-09 13:13:03 +00:00
|
|
|
IconLucideClipboardCopy: typeof import('~icons/lucide/clipboard-copy')['default']
|
2026-03-01 09:12:45 +00:00
|
|
|
IconLucideCode: typeof import('~icons/lucide/code')['default']
|
|
|
|
|
IconLucideCopy: typeof import('~icons/lucide/copy')['default']
|
2026-02-28 06:06:04 +00:00
|
|
|
IconLucideEye: typeof import('~icons/lucide/eye')['default']
|
|
|
|
|
IconLucideEyeOff: typeof import('~icons/lucide/eye-off')['default']
|
Pages support: multi-page documents like Figma
Document structure: Document (root) → Pages (CANVAS) → Nodes.
Each page has its own viewport (pan/zoom) and background color.
- CANVAS node type for pages
- SceneGraph: addPage(), getPages(), getAbsolutePosition stops at CANVAS
- Store: currentPageId, switchPage(), addPage(), deletePage(), renamePage()
- All operations (create, select, paste, hit test, render) scoped to current page
- Per-page viewport state saved/restored on page switch
- Pages list in LayersPanel with add/switch/double-click rename
- .fig import creates proper pages from DOCUMENT→CANVAS hierarchy
- Renderer takes pageId to render correct page's children
- Unit test for pages, updated E2E tests for page-scoped graph
2026-02-28 10:12:27 +00:00
|
|
|
IconLucideFile: typeof import('~icons/lucide/file')['default']
|
2026-02-28 06:55:02 +00:00
|
|
|
IconLucideFlipHorizontal: typeof import('~icons/lucide/flip-horizontal')['default']
|
|
|
|
|
IconLucideFlipVertical: typeof import('~icons/lucide/flip-vertical')['default']
|
2026-02-28 22:59:01 +00:00
|
|
|
IconLucideFolderPlus: typeof import('~icons/lucide/folder-plus')['default']
|
Tier 1 rendering parity: gradients, images, effects, strokes, arcs
Data model:
- Gradient fills (LINEAR, RADIAL, ANGULAR, DIAMOND) with stops + transform
- Image fills with hash reference, scale mode, transform
- Blend modes on fills, nodes, effects
- Stroke cap, join, dash pattern, independent border weights
- Arc data for partial ellipses/donuts
- Text: vertical align, auto-resize, case, decoration, max lines
- Constraints: horizontal/vertical
- New node types: ROUNDED_RECTANGLE, COMPONENT, COMPONENT_SET,
INSTANCE, CONNECTOR, SHAPE_WITH_TEXT
Import:
- All paint types imported (not just SOLID)
- Image hashes extracted from .fig ZIP images/ directory
- Font weight mapping (Thin→100 through Black→900)
- Arc data, stroke options, constraints, text properties
Renderer:
- Gradient shaders via CanvasKit (linear, radial, sweep)
- Image fills with FILL/FIT scale modes and shader matrix
- Drop shadow and inner shadow effects
- Arc/donut rendering for ellipses with arcData
- Stroke cap/join/dash pattern support
- Image cache with cleanup on destroy
Export:
- Gradient/image fills round-trip through Kiwi
- New node types mapped to Figma equivalents
UI:
- Fill section shows gradient/image type labels for non-SOLID fills
2026-02-28 13:40:34 +00:00
|
|
|
IconLucideImage: typeof import('~icons/lucide/image')['default']
|
Image drag-and-drop, clipboard paste, and paste-at-cursor (#92)
* Image drag-and-drop and clipboard paste onto canvas
* Paste images at cursor position, track canvas cursor in state
* Paste nodes at cursor position (Figma and internal clipboard)
* Fix review: center images at cursor, fix hasImageFiles, clean up hash/undo
* Paste at viewport center when cursor is outside canvas
* Fix Figma paste positioning: center nodes at cursor after import
* Image support: clipboard, export, drag-and-drop, paste, renderer, tools, UI
- Clipboard: embed image bytes (base64) in OpenPencil clipboard payload so
copy/paste between documents preserves image fills
- Fig export: write images/ folder to .fig zip (both fflate and Tauri paths)
- Tauri: extend build_fig_file to accept image entries
- Renderer: implement CROP (with imageTransform) and TILE (TileMode.Repeat)
scale modes, fix FIT to center the fitted image
- FigmaAPI: add createImage(bytes) with sync FNV-1a hash
- Tools: add set_image_fill tool for AI/MCP
- FillPicker: replace placeholder with file picker, preview, scale mode selector
- Drag-and-drop: new use-image-drop composable creates image nodes from dropped files
- Paste: keyboard paste handler detects image clipboard items
- Shared utils: extract hashImageBytes and getImageDimensions to src/utils/image.ts
* Add image tests and Yjs image sync for collaboration
Tests (18 new):
- FigmaAPI.createImage: deterministic hash, storage, format
- set_image_fill tool: all scale modes, error handling, storage
- Clipboard roundtrip: image bytes preserved, multiple images, children
- Fig export/import: zip contains images/, full round-trip
Collab:
- Add yimages Y.Map to sync graph.images via Yjs
- Observer applies remote image adds/deletes to local graph
- syncNodeToYjs pushes referenced image data alongside node props
- syncAllNodesToYjs bulk-syncs all images on room share
* Fix FillPicker: remove deleted utils/image import, use SHA-1 inline
* Extract storeImage() on editor store, use in FillPicker and placeImageNode
* Unify image hash: use sync FNV-1a everywhere, export computeImageHash
Editor store's hashBytes (async SHA-1) produced different hashes than
FigmaAPI.createImage (sync FNV-1a) for the same bytes. This meant
drag-and-drop images couldn't be deduplicated against AI tool images.
Replace hashBytes with computeImageHash from core. storeImage() is
now sync.
* Skip drawing IMAGE fills when image data is missing
When pasting from Figma, image fills reference a CDN hash but no pixel
data is included in the clipboard. Previously this rendered as a solid
black rectangle because applyImageFill bailed without setting a shader,
leaving stale paint state.
applyFill now returns false when the fill can't be applied, and callers
skip the draw call. The node still exists with the correct imageHash —
if the image data is later provided (e.g. via file re-open), it will
render correctly.
* Warn when Figma paste has missing image data
Show amber warning toast when pasted nodes reference image fills
without available bytes (Figma clipboard limitation).
Add 'warning' toast variant with tailwind-variants, extract toast
styles to src/components/ui/toast.ts.
* Use useFileDialog and useObjectUrl in FillPicker
Replace manual file input ref, click(), createObjectURL/revokeObjectURL
with vueuse composables. Remove hidden <input type=file> from template.
---------
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-11 04:52:04 +00:00
|
|
|
IconLucideImageOff: typeof import('~icons/lucide/image-off')['default']
|
2026-03-01 06:03:38 +00:00
|
|
|
IconLucideItalic: typeof import('~icons/lucide/italic')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
IconLucideKeyRound: typeof import('~icons/lucide/key-round')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
|
CSS Grid layout mode (#73)
* Switch to @open-pencil/yoga-layout with CSS Grid support
Use our fork (open-pencil/yoga, grid branch) which cherry-picks the
upstream CSS Grid PRs (#1893–#1898) onto current main. The JS bindings
are ported from the old embind approach to the new wasm_bridge.c API.
npm:@open-pencil/yoga-layout alias keeps all imports as 'yoga-layout'.
Also handle FinalizationRegistry change (upstream #1908) — node.free()
no longer exists, nodes are garbage collected automatically.
* Add CSS Grid layout mode
Scene graph:
- LayoutMode gains 'GRID' option alongside HORIZONTAL/VERTICAL
- GridTrack type (sizing: FIXED/FR/AUTO, value) for track definitions
- GridPosition type (column, row, columnSpan, rowSpan) for children
- New node props: gridTemplateColumns/Rows, gridColumnGap, gridRowGap,
gridPosition
Layout engine:
- buildGridTree() sets Display.Grid and maps GridTrack[] to Yoga's
setGridTemplateColumns/Rows API with FR/Points/Auto track types
- Grid children use gridPosition for column/row placement with span
- Flex layout path unchanged
Store:
- setLayoutMode('GRID') auto-creates NxM track grid based on child
count (sqrt heuristic), defaults to 1fr tracks
UI (LayoutSection.vue):
- Grid button (grid-2x2 icon) added to flow direction row
- Columns/Rows track editors: ScrubInput for value + AppSelect for
sizing mode (Fill fr / Fixed px / Auto), add/remove buttons
- Separate column gap and row gap ScrubInputs
- Wrap button hidden when grid is active
- Flex alignment grid hidden when grid is active
Kiwi serialization skips GRID mode for now (no .fig codec support).
* Add grid layout integration tests
12 new test scenarios covering:
- Basic 2x2 grid, fixed columns, mixed fr/fixed, unequal fr weights
- Column gap, row gap, both gaps combined
- Padding offsets
- Explicit gridPosition placement, column span, row span
- Absolute children skipped
- Hidden children collapsed
- Nested grid inside flex parent (computeAllLayouts)
Also fix: grid frames as children of flex parents now correctly use
Display.Grid via configureChildAsGrid() instead of falling through
to configureChildAsAutoLayout() which set FlexDirection.
* Deduplicate grid yoga configuration
Extract configureAsGrid() and createGridChildNode() shared by
buildGridTree (root-level grid) and configureChildAsGrid (grid
nested inside flex parent).
* Add grid layout to changelog
* Add grid support to JSX and Tailwind CSS export
OpenPencil format: grid → columns/rows/columnGap/rowGap props,
child colStart/rowStart/colSpan/rowSpan.
Tailwind format: grid grid-cols-N grid-rows-N gap-x-* gap-y-*,
mixed tracks use arbitrary values (grid-cols-[200px_1fr_auto]),
child col-start-*/row-start-*/col-span-*/row-span-*.
Both formats: padding emitted for grid frames (shared with flex),
flex-only props (justify/items/wrap/gap) scoped to isFlex.
* Remove duplicate gridTrackToTw, use shared formatTrack
Also drop unused GridTrackSizing import from LayoutSection.vue.
* Fix grid icon: use layout-grid instead of grid-2x2
unplugin-icons can't resolve icon names with digits after hyphens
(grid-2x2 → grid2x2 which doesn't exist).
* Polish layout UI and fix auto-layout behavior
- Replace text labels with compact icons: ↔/↕ for gap, ☐ for uniform padding, T/R/B/L for per-side padding
- Direction-aware gap icon: ↕ for vertical, ↔ for horizontal
- Pin +/− padding toggle button right of gap input
- Fix alignment grid axes for vertical layout (transpose primary/counter)
- Fix grid switch: set FIXED sizing, compute frame size from children
- Remove hardcoded white fill from Shift+A wrap
- Auto-detect horizontal vs vertical from selection bounds
* Add flex-to-grid switch integration test
Verify HUG frame expands and children are placed in 2x2 grid
when switching from vertical flex to grid layout.
* Update changelog for grid layout
2026-03-08 15:23:53 +00:00
|
|
|
IconLucideLayoutGrid: typeof import('~icons/lucide/layout-grid')['default']
|
2026-02-28 22:59:01 +00:00
|
|
|
IconLucideLink: typeof import('~icons/lucide/link')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
IconLucideLoaderCircle: typeof import('~icons/lucide/loader-circle')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
IconLucideMenu: typeof import('~icons/lucide/menu')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
IconLucideMessageCircle: typeof import('~icons/lucide/message-circle')['default']
|
CSS Grid layout mode (#73)
* Switch to @open-pencil/yoga-layout with CSS Grid support
Use our fork (open-pencil/yoga, grid branch) which cherry-picks the
upstream CSS Grid PRs (#1893–#1898) onto current main. The JS bindings
are ported from the old embind approach to the new wasm_bridge.c API.
npm:@open-pencil/yoga-layout alias keeps all imports as 'yoga-layout'.
Also handle FinalizationRegistry change (upstream #1908) — node.free()
no longer exists, nodes are garbage collected automatically.
* Add CSS Grid layout mode
Scene graph:
- LayoutMode gains 'GRID' option alongside HORIZONTAL/VERTICAL
- GridTrack type (sizing: FIXED/FR/AUTO, value) for track definitions
- GridPosition type (column, row, columnSpan, rowSpan) for children
- New node props: gridTemplateColumns/Rows, gridColumnGap, gridRowGap,
gridPosition
Layout engine:
- buildGridTree() sets Display.Grid and maps GridTrack[] to Yoga's
setGridTemplateColumns/Rows API with FR/Points/Auto track types
- Grid children use gridPosition for column/row placement with span
- Flex layout path unchanged
Store:
- setLayoutMode('GRID') auto-creates NxM track grid based on child
count (sqrt heuristic), defaults to 1fr tracks
UI (LayoutSection.vue):
- Grid button (grid-2x2 icon) added to flow direction row
- Columns/Rows track editors: ScrubInput for value + AppSelect for
sizing mode (Fill fr / Fixed px / Auto), add/remove buttons
- Separate column gap and row gap ScrubInputs
- Wrap button hidden when grid is active
- Flex alignment grid hidden when grid is active
Kiwi serialization skips GRID mode for now (no .fig codec support).
* Add grid layout integration tests
12 new test scenarios covering:
- Basic 2x2 grid, fixed columns, mixed fr/fixed, unequal fr weights
- Column gap, row gap, both gaps combined
- Padding offsets
- Explicit gridPosition placement, column span, row span
- Absolute children skipped
- Hidden children collapsed
- Nested grid inside flex parent (computeAllLayouts)
Also fix: grid frames as children of flex parents now correctly use
Display.Grid via configureChildAsGrid() instead of falling through
to configureChildAsAutoLayout() which set FlexDirection.
* Deduplicate grid yoga configuration
Extract configureAsGrid() and createGridChildNode() shared by
buildGridTree (root-level grid) and configureChildAsGrid (grid
nested inside flex parent).
* Add grid layout to changelog
* Add grid support to JSX and Tailwind CSS export
OpenPencil format: grid → columns/rows/columnGap/rowGap props,
child colStart/rowStart/colSpan/rowSpan.
Tailwind format: grid grid-cols-N grid-rows-N gap-x-* gap-y-*,
mixed tracks use arbitrary values (grid-cols-[200px_1fr_auto]),
child col-start-*/row-start-*/col-span-*/row-span-*.
Both formats: padding emitted for grid frames (shared with flex),
flex-only props (justify/items/wrap/gap) scoped to isFlex.
* Remove duplicate gridTrackToTw, use shared formatTrack
Also drop unused GridTrackSizing import from LayoutSection.vue.
* Fix grid icon: use layout-grid instead of grid-2x2
unplugin-icons can't resolve icon names with digits after hyphens
(grid-2x2 → grid2x2 which doesn't exist).
* Polish layout UI and fix auto-layout behavior
- Replace text labels with compact icons: ↔/↕ for gap, ☐ for uniform padding, T/R/B/L for per-side padding
- Direction-aware gap icon: ↕ for vertical, ↔ for horizontal
- Pin +/− padding toggle button right of gap input
- Fix alignment grid axes for vertical layout (transpose primary/counter)
- Fix grid switch: set FIXED sizing, compute frame size from children
- Remove hardcoded white fill from Shift+A wrap
- Auto-detect horizontal vs vertical from selection bounds
* Add flex-to-grid switch integration test
Verify HUG frame expands and children are placed in 2x2 grid
when switching from vertical flex to grid layout.
* Update changelog for grid layout
2026-03-08 15:23:53 +00:00
|
|
|
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
|
|
|
|
|
IconLucideMoveHorizontal: typeof import('~icons/lucide/move-horizontal')['default']
|
|
|
|
|
IconLucideMoveVertical: typeof import('~icons/lucide/move-vertical')['default']
|
2026-02-28 14:26:50 +00:00
|
|
|
IconLucidePlus: typeof import('~icons/lucide/plus')['default']
|
2026-02-28 06:20:31 +00:00
|
|
|
IconLucideRadius: typeof import('~icons/lucide/radius')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
IconLucideRedo2: typeof import('~icons/lucide/redo2')['default']
|
2026-02-28 06:57:45 +00:00
|
|
|
IconLucideRotateCcw: typeof import('~icons/lucide/rotate-ccw')['default']
|
2026-02-28 06:55:02 +00:00
|
|
|
IconLucideRotateCw: typeof import('~icons/lucide/rotate-cw')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
IconLucideSend: typeof import('~icons/lucide/send')['default']
|
Multi-provider AI, sucrase JSX, Figma zoom shortcuts (#72)
* Add multi-provider AI support (Anthropic, OpenAI, Google AI, OpenAI-compatible)
- Add AI_PROVIDERS registry with per-provider model lists, key placeholders, and URLs
- Refactor use-chat composable: provider factory creates the right AI SDK model
- Per-provider API key storage in localStorage with automatic legacy migration
- New ProviderSetup.vue replaces APIKeySetup.vue with provider selector
- New ProviderSettings.vue popover accessible from gear icon in chat input
- OpenAI-compatible provider with custom base URL and model ID fields
- Install @ai-sdk/anthropic and @ai-sdk/google dependencies
- Update E2E tests for new provider setup flow
* Fix reactivity, key masking, missing model ID field, and chat reset on model change
- Use ref instead of computed for apiKey — computed getter over localStorage
wasn't triggering reactivity when the key was set
- Load stored key for new provider in providerId watcher
- Reset chat on modelId and customModelId change, not just provider switch
- Add custom model ID field to ProviderSetup.vue for OpenAI-compatible
- Simplify ProviderSettings key field — empty input with contextual placeholder
instead of fragile dot-masking that broke on partial edits
- Use data-test-id locator for model selector in E2E test
* Use useLocalStorage from vueuse instead of manual ref + watch + localStorage
Replaces 5 hand-rolled ref/watch/localStorage sync pairs with useLocalStorage.
API key uses a computed storage key that rebinds when the provider changes.
* Use useLocalStorage for collab name persistence
* Review fixes: changelog placement, remove dead class, cursor-pointer
* Extract ProviderSelect component and uiInput helper
- ProviderSelect.vue: shared select dropdown for AI providers
- ui/input.ts: shared input styling (sm/md sizes)
- Remove 7 duplicated input class strings across chat components
* Use uppercase acronyms: keyURL, customBaseURL, supportsCustomBaseURL
* Match select dropdown width to trigger via --reka-select-trigger-width
Move min-w-[var(--reka-select-trigger-width)] into selectContent base
style so all selects get it by default. Remove per-component overrides.
* Uppercase acronyms in variable names, improve setup form layout
- providerID, modelID, customModelID, setAPIKey, AIProviderID
- Keep apiKey lowercase at start (standard JS convention)
- Compact setup form: single column, full-width Connect button
- Fix grammar: 'a OpenRouter' → 'an OpenRouter'
- Shorter promo text
* Allow text selection in chat panel
* Fix chat E2E test: Save → Connect button text
* Disable mermaid in chat markdown renderer
Alias mermaid and beautiful-mermaid to empty shims in Vite config,
preventing vue-stream-markdown from attempting to load them.
* Interleave text and tool calls in chat messages
Render message parts in order instead of grouping all tool calls
first then all text. Use SDK's isToolUIPart/isTextUIPart/getToolName
instead of custom type guards. Fix error state: output-error, not error.
* Implement figma.viewport.scrollAndZoomIntoView()
Figma Plugin API method that centers the viewport on given nodes.
Reuses the same bounding box logic as the viewport_zoom_to_fit tool.
* Catch tool execution errors and return them to the AI
Instead of crashing with an unhandled exception, tool errors are
caught and returned as { error: message } so the AI can retry or
explain the failure. UI detects error outputs and shows them in red
with the error message when expanded.
* Replace esbuild with sucrase for JSX transform
Sucrase is a pure JS transform (201 KB / 46 KB gzip) that works in
both Node/Bun and the browser. Replaces esbuild (13 MB WASM) which
only worked in Node/Bun.
- buildComponent() and renderJSX() are now synchronous
- render tool works in browser AI chat (no more 'esbuild required')
- Handles full JS expressions (map, ternaries, Array.from, etc.)
* Update system prompt to prioritize render tool with JSX
Document available tags, props, layout, text, and sizing options.
Instruct the AI to use full JS expressions in JSX for complex layouts.
* Fix zoom shortcuts to match Figma
Cmd+0: Zoom to 100% (was incorrectly mapped to Zoom to fit)
Cmd+1: Zoom to fit
Cmd+2: Zoom to selection
Shift+1 / Shift+2: same as Cmd+1 / Cmd+2
Add zoomTo100() and zoomToSelection() to editor store.
Refactor zoomToFit() to use shared zoomToBounds() helper.
* Refactor keyboard shortcuts to useMagicKeys
Replace manual keydown handler with VueUse useMagicKeys + whenever
for declarative shortcut registration.
- mod() helper for cross-platform Meta/Control shortcuts
- plain() helper for modifier-free keys
- Proper modifier exclusion (⌘G vs ⌘⇧G no longer conflict)
- Add E2E tests: duplicate, zoom (⌘0/⌘1/⌘2/⇧1/⇧2), auto-layout (⇧A)
- All 26 keyboard shortcut tests pass
* Fix scrollAndZoomIntoView to actually zoom
Match Figma Plugin API behavior (equivalent to Shift-1): compute
zoom level that fits all nodes with padding, capped at 100%.
Previously only set center without adjusting zoom.
2026-03-08 19:05:18 +00:00
|
|
|
IconLucideSettings: typeof import('~icons/lucide/settings')['default']
|
2026-02-28 22:59:01 +00:00
|
|
|
IconLucideSettings2: typeof import('~icons/lucide/settings2')['default']
|
2026-03-01 13:44:37 +00:00
|
|
|
IconLucideShare2: typeof import('~icons/lucide/share2')['default']
|
2026-03-01 17:14:39 +00:00
|
|
|
IconLucideSidebar: typeof import('~icons/lucide/sidebar')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
IconLucideSlidersHorizontal: typeof import('~icons/lucide/sliders-horizontal')['default']
|
CSS Grid layout mode (#73)
* Switch to @open-pencil/yoga-layout with CSS Grid support
Use our fork (open-pencil/yoga, grid branch) which cherry-picks the
upstream CSS Grid PRs (#1893–#1898) onto current main. The JS bindings
are ported from the old embind approach to the new wasm_bridge.c API.
npm:@open-pencil/yoga-layout alias keeps all imports as 'yoga-layout'.
Also handle FinalizationRegistry change (upstream #1908) — node.free()
no longer exists, nodes are garbage collected automatically.
* Add CSS Grid layout mode
Scene graph:
- LayoutMode gains 'GRID' option alongside HORIZONTAL/VERTICAL
- GridTrack type (sizing: FIXED/FR/AUTO, value) for track definitions
- GridPosition type (column, row, columnSpan, rowSpan) for children
- New node props: gridTemplateColumns/Rows, gridColumnGap, gridRowGap,
gridPosition
Layout engine:
- buildGridTree() sets Display.Grid and maps GridTrack[] to Yoga's
setGridTemplateColumns/Rows API with FR/Points/Auto track types
- Grid children use gridPosition for column/row placement with span
- Flex layout path unchanged
Store:
- setLayoutMode('GRID') auto-creates NxM track grid based on child
count (sqrt heuristic), defaults to 1fr tracks
UI (LayoutSection.vue):
- Grid button (grid-2x2 icon) added to flow direction row
- Columns/Rows track editors: ScrubInput for value + AppSelect for
sizing mode (Fill fr / Fixed px / Auto), add/remove buttons
- Separate column gap and row gap ScrubInputs
- Wrap button hidden when grid is active
- Flex alignment grid hidden when grid is active
Kiwi serialization skips GRID mode for now (no .fig codec support).
* Add grid layout integration tests
12 new test scenarios covering:
- Basic 2x2 grid, fixed columns, mixed fr/fixed, unequal fr weights
- Column gap, row gap, both gaps combined
- Padding offsets
- Explicit gridPosition placement, column span, row span
- Absolute children skipped
- Hidden children collapsed
- Nested grid inside flex parent (computeAllLayouts)
Also fix: grid frames as children of flex parents now correctly use
Display.Grid via configureChildAsGrid() instead of falling through
to configureChildAsAutoLayout() which set FlexDirection.
* Deduplicate grid yoga configuration
Extract configureAsGrid() and createGridChildNode() shared by
buildGridTree (root-level grid) and configureChildAsGrid (grid
nested inside flex parent).
* Add grid layout to changelog
* Add grid support to JSX and Tailwind CSS export
OpenPencil format: grid → columns/rows/columnGap/rowGap props,
child colStart/rowStart/colSpan/rowSpan.
Tailwind format: grid grid-cols-N grid-rows-N gap-x-* gap-y-*,
mixed tracks use arbitrary values (grid-cols-[200px_1fr_auto]),
child col-start-*/row-start-*/col-span-*/row-span-*.
Both formats: padding emitted for grid frames (shared with flex),
flex-only props (justify/items/wrap/gap) scoped to isFlex.
* Remove duplicate gridTrackToTw, use shared formatTrack
Also drop unused GridTrackSizing import from LayoutSection.vue.
* Fix grid icon: use layout-grid instead of grid-2x2
unplugin-icons can't resolve icon names with digits after hyphens
(grid-2x2 → grid2x2 which doesn't exist).
* Polish layout UI and fix auto-layout behavior
- Replace text labels with compact icons: ↔/↕ for gap, ☐ for uniform padding, T/R/B/L for per-side padding
- Direction-aware gap icon: ↕ for vertical, ↔ for horizontal
- Pin +/− padding toggle button right of gap input
- Fix alignment grid axes for vertical layout (transpose primary/counter)
- Fix grid switch: set FIXED sizing, compute frame size from children
- Remove hardcoded white fill from Shift+A wrap
- Auto-detect horizontal vs vertical from selection bounds
* Add flex-to-grid switch integration test
Verify HUG frame expands and children are placed in 2x2 grid
when switching from vertical flex to grid layout.
* Update changelog for grid layout
2026-03-08 15:23:53 +00:00
|
|
|
IconLucideSpace: typeof import('~icons/lucide/space')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
IconLucideSparkles: typeof import('~icons/lucide/sparkles')['default']
|
|
|
|
|
IconLucideSquare: typeof import('~icons/lucide/square')['default']
|
2026-03-01 06:03:38 +00:00
|
|
|
IconLucideStrikethrough: typeof import('~icons/lucide/strikethrough')['default']
|
Event-driven requestRender and component sync via SceneGraph emitter
Move requestRender() and component instance sync from manual call sites
to graph event subscriptions. Removes 79 manual requestRender() calls
(94→22) and all 9 syncIfInsideComponent calls.
Batching: component sync uses queueMicrotask — mutated node IDs
accumulate during a synchronous block, then deduplicate to ancestor
component IDs and call syncInstances once per component. requestRender()
is already rAF-batched (just increments counters checked by animation
frame loop), so multiple event-triggered calls in one sync block
collapse to a single repaint.
Event subscriptions:
- node:updated → invalidate render cache, schedule component sync, requestRender
- node:created/deleted/reparented/reordered → schedule component sync, requestRender
- Re-subscribes when graph instance is replaced (file open/reload)
Also fixes collab sync gap: subscribe to node:reordered in use-collab.ts
(same-parent reorder was never synced to Yjs peers).
Remaining requestRender() calls are for pure UI state changes (pen tool,
viewport pan/zoom, text editing, selection) and methods that bypass
events (detachInstance, bringToFront/sendToBack, restorePageFromSnapshot).
2026-03-09 13:13:03 +00:00
|
|
|
IconLucideTrash2: typeof import('~icons/lucide/trash2')['default']
|
2026-03-01 07:54:51 +00:00
|
|
|
IconLucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default']
|
2026-03-01 06:03:38 +00:00
|
|
|
IconLucideUnderline: typeof import('~icons/lucide/underline')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
IconLucideUndo2: typeof import('~icons/lucide/undo2')['default']
|
2026-02-28 22:59:01 +00:00
|
|
|
IconLucideUnlink: typeof import('~icons/lucide/unlink')['default']
|
2026-03-01 14:15:08 +00:00
|
|
|
IconLucideUsers: typeof import('~icons/lucide/users')['default']
|
2026-03-07 14:17:48 +00:00
|
|
|
IconLucideWrapText: typeof import('~icons/lucide/wrap-text')['default']
|
2026-02-28 22:59:01 +00:00
|
|
|
IconLucideX: typeof import('~icons/lucide/x')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
LayersPanel: typeof import('./src/components/LayersPanel.vue')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
LayerTree: typeof import('./src/components/LayerTree.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
LayoutSection: typeof import('./src/components/properties/LayoutSection.vue')['default']
|
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 16:51:51 +00:00
|
|
|
MobileDrawer: typeof import('./src/components/MobileDrawer.vue')['default']
|
|
|
|
|
MobileHud: typeof import('./src/components/MobileHud.vue')['default']
|
2026-03-02 16:42:46 +00:00
|
|
|
NodeContextMenuContent: typeof import('./src/components/NodeContextMenuContent.vue')['default']
|
2026-02-28 08:42:13 +00:00
|
|
|
PageSection: typeof import('./src/components/properties/PageSection.vue')['default']
|
2026-02-28 10:14:03 +00:00
|
|
|
PagesPanel: typeof import('./src/components/PagesPanel.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
PositionSection: typeof import('./src/components/properties/PositionSection.vue')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
PropertiesPanel: typeof import('./src/components/PropertiesPanel.vue')['default']
|
Multi-provider AI, sucrase JSX, Figma zoom shortcuts (#72)
* Add multi-provider AI support (Anthropic, OpenAI, Google AI, OpenAI-compatible)
- Add AI_PROVIDERS registry with per-provider model lists, key placeholders, and URLs
- Refactor use-chat composable: provider factory creates the right AI SDK model
- Per-provider API key storage in localStorage with automatic legacy migration
- New ProviderSetup.vue replaces APIKeySetup.vue with provider selector
- New ProviderSettings.vue popover accessible from gear icon in chat input
- OpenAI-compatible provider with custom base URL and model ID fields
- Install @ai-sdk/anthropic and @ai-sdk/google dependencies
- Update E2E tests for new provider setup flow
* Fix reactivity, key masking, missing model ID field, and chat reset on model change
- Use ref instead of computed for apiKey — computed getter over localStorage
wasn't triggering reactivity when the key was set
- Load stored key for new provider in providerId watcher
- Reset chat on modelId and customModelId change, not just provider switch
- Add custom model ID field to ProviderSetup.vue for OpenAI-compatible
- Simplify ProviderSettings key field — empty input with contextual placeholder
instead of fragile dot-masking that broke on partial edits
- Use data-test-id locator for model selector in E2E test
* Use useLocalStorage from vueuse instead of manual ref + watch + localStorage
Replaces 5 hand-rolled ref/watch/localStorage sync pairs with useLocalStorage.
API key uses a computed storage key that rebinds when the provider changes.
* Use useLocalStorage for collab name persistence
* Review fixes: changelog placement, remove dead class, cursor-pointer
* Extract ProviderSelect component and uiInput helper
- ProviderSelect.vue: shared select dropdown for AI providers
- ui/input.ts: shared input styling (sm/md sizes)
- Remove 7 duplicated input class strings across chat components
* Use uppercase acronyms: keyURL, customBaseURL, supportsCustomBaseURL
* Match select dropdown width to trigger via --reka-select-trigger-width
Move min-w-[var(--reka-select-trigger-width)] into selectContent base
style so all selects get it by default. Remove per-component overrides.
* Uppercase acronyms in variable names, improve setup form layout
- providerID, modelID, customModelID, setAPIKey, AIProviderID
- Keep apiKey lowercase at start (standard JS convention)
- Compact setup form: single column, full-width Connect button
- Fix grammar: 'a OpenRouter' → 'an OpenRouter'
- Shorter promo text
* Allow text selection in chat panel
* Fix chat E2E test: Save → Connect button text
* Disable mermaid in chat markdown renderer
Alias mermaid and beautiful-mermaid to empty shims in Vite config,
preventing vue-stream-markdown from attempting to load them.
* Interleave text and tool calls in chat messages
Render message parts in order instead of grouping all tool calls
first then all text. Use SDK's isToolUIPart/isTextUIPart/getToolName
instead of custom type guards. Fix error state: output-error, not error.
* Implement figma.viewport.scrollAndZoomIntoView()
Figma Plugin API method that centers the viewport on given nodes.
Reuses the same bounding box logic as the viewport_zoom_to_fit tool.
* Catch tool execution errors and return them to the AI
Instead of crashing with an unhandled exception, tool errors are
caught and returned as { error: message } so the AI can retry or
explain the failure. UI detects error outputs and shows them in red
with the error message when expanded.
* Replace esbuild with sucrase for JSX transform
Sucrase is a pure JS transform (201 KB / 46 KB gzip) that works in
both Node/Bun and the browser. Replaces esbuild (13 MB WASM) which
only worked in Node/Bun.
- buildComponent() and renderJSX() are now synchronous
- render tool works in browser AI chat (no more 'esbuild required')
- Handles full JS expressions (map, ternaries, Array.from, etc.)
* Update system prompt to prioritize render tool with JSX
Document available tags, props, layout, text, and sizing options.
Instruct the AI to use full JS expressions in JSX for complex layouts.
* Fix zoom shortcuts to match Figma
Cmd+0: Zoom to 100% (was incorrectly mapped to Zoom to fit)
Cmd+1: Zoom to fit
Cmd+2: Zoom to selection
Shift+1 / Shift+2: same as Cmd+1 / Cmd+2
Add zoomTo100() and zoomToSelection() to editor store.
Refactor zoomToFit() to use shared zoomToBounds() helper.
* Refactor keyboard shortcuts to useMagicKeys
Replace manual keydown handler with VueUse useMagicKeys + whenever
for declarative shortcut registration.
- mod() helper for cross-platform Meta/Control shortcuts
- plain() helper for modifier-free keys
- Proper modifier exclusion (⌘G vs ⌘⇧G no longer conflict)
- Add E2E tests: duplicate, zoom (⌘0/⌘1/⌘2/⇧1/⇧2), auto-layout (⇧A)
- All 26 keyboard shortcut tests pass
* Fix scrollAndZoomIntoView to actually zoom
Match Figma Plugin API behavior (equivalent to Shift-1): compute
zoom level that fits all nodes with padding, capped at 100%.
Previously only set center without adjusting zoom.
2026-03-08 19:05:18 +00:00
|
|
|
ProviderSelect: typeof import('./src/components/chat/ProviderSelect.vue')['default']
|
|
|
|
|
ProviderSettings: typeof import('./src/components/chat/ProviderSettings.vue')['default']
|
|
|
|
|
ProviderSetup: typeof import('./src/components/chat/ProviderSetup.vue')['default']
|
2026-03-01 14:15:08 +00:00
|
|
|
RouterLink: typeof import('vue-router')['RouterLink']
|
|
|
|
|
RouterView: typeof import('vue-router')['RouterView']
|
2026-03-01 12:41:27 +00:00
|
|
|
SafariBanner: typeof import('./src/components/SafariBanner.vue')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
ScrubInput: typeof import('./src/components/ScrubInput.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
StrokeSection: typeof import('./src/components/properties/StrokeSection.vue')['default']
|
2026-03-02 18:07:25 +00:00
|
|
|
TabBar: typeof import('./src/components/TabBar.vue')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
Toolbar: typeof import('./src/components/Toolbar.vue')['default']
|
Split PropertiesPanel into sections, add Typography panel
- PropertiesPanel.vue: 513 → 67 lines (composition only)
- Extracted: PositionSection, LayoutSection, AppearanceSection,
FillSection, StrokeSection, TypographySection
- Shared useNodeProps() composable for update/commit logic
- Typography panel: font family picker with search (queryLocalFonts),
weight select, font size, line height, letter spacing, text alignment
- Font picker shows each family in its own typeface
- Loads selected font into both CanvasKit and browser on pick
2026-02-28 06:00:25 +00:00
|
|
|
TypographySection: typeof import('./src/components/properties/TypographySection.vue')['default']
|
2026-02-28 22:59:01 +00:00
|
|
|
VariablesDialog: typeof import('./src/components/VariablesDialog.vue')['default']
|
|
|
|
|
VariablesSection: typeof import('./src/components/properties/VariablesSection.vue')['default']
|
2026-02-28 05:29:39 +00:00
|
|
|
}
|
|
|
|
|
}
|