Nudge: Arrow keys move selection by 1px, Shift+Arrow by 10px.
Consecutive nudges within 300ms are batched into a single undo
entry so Cmd+Z undoes the entire sequence at once.
Fix duplicate redo creating nodes with new IDs, breaking
subsequent undo entries that reference the original IDs. Now
snapshotSubtree/restoreSubtree preserve node IDs across redo.
Fix delete undo not restoring children — deleteSelected only
snapshotted top-level nodes, so undoing a frame delete lost all
its children. Now snapshots the full subtree.
Fix draw tool not committing resize to undo — shapes created via
draw had their final dimensions lost on redo because handleDrawUp
never recorded the resize. Now uses beginBatch/commitBatch to
combine create+resize into a single undo entry.
Clean up use-keyboard.ts — remove shouldPreventDefault and opaque
PREVENT_* sets, move tool switch and nudge to dedicated keydown
listener, add shift() helper.
Closes#182
- Move clipboard-text-outlines.ts → text/opentype.ts
- Move kiwi/serialize-clipboard-v4.ts → text/clipboard-derived-text.ts
- Delete types-opentype.d.ts in favor of @types/opentype.js
- Add opentype.js to core package dependencies
- Add Vite alias for opentype.js ESM resolution
- Update all imports across core, kiwi, clipboard, and tests
* chore: remove unused pi context file
- Delete .pi/context.md
- Remove an unused local context artifact from the repository
* Add clipboard text v4 export
* fix(clipboard): rebase v4 export branch onto master
- Resolve conflicts after the core text module reorganization
- Fix clipboard text export imports and bundled font asset loading
- Update raw binary expectations for textUserLayoutVersion 4
- Re-run check and unit tests after the rebase
The layout section was gated on node.type === 'FRAME', hiding it for
COMPONENT, COMPONENT_SET, and INSTANCE nodes that also support
auto-layout. Also use copyStyleRuns instead of structuredClone per
project lint rules.
styleRuns were updated on the graph during editing (via syncText) but
commitTextEdit only captured text changes. Now both text and styleRuns
are snapshotted at startTextEditing and restored on undo.
commitTextEdit compared prevText from the scene graph node, but
syncText already updates the node on every keystroke during editing.
This made prevText === newText, so the undo entry was never pushed.
Fix: capture the original text when startTextEditing is called and
use that snapshot for the undo comparison.
* fix(acp): show install errors for missing agent CLIs (#172)
* Use npm for install commands, toasts for errors, tighten error matching
- Replace bun add -g with npm i -g (universal baseline)
- Replace initError inline banner with toast.show() (consistent with rest of app)
- Remove overly broad 'not found' / 'no such file' from isMissingCommandError
* Refactor toast API to toast.info/warning/error
The kiwi binary encoder silently drops layoutWrap (flex-wrap) and
counterAxisSpacing (row-gap) because they were missing from the
schema definition. The serialize/deserialize code already handles
these values correctly — only the schema was incomplete.
Adds:
- StackWrap enum (NO_WRAP, WRAP)
- stackWrap field (476) to NodeChange
- stackCounterSpacing field (477) to NodeChange
- STRETCH value (4) to StackAlign enum
Fixesopen-pencil/open-pencil#165
Adds a new scenario to the pen tool specifications defining the behavior for relocating the active vertex with spacebar during a curve drag, including asserting that the cursor remains a crosshair instead of switching to the hand tool.
Made-with: Cursor
Align selected anchor points relative to each other in vector edit mode. The standard alignment buttons in the position panel now operate on selected vertices when 2 or more are selected, enabling precise vector path editing workflows.
- Add bezier math utilities for VectorNetwork manipulation
- Enable resuming pen drawing from existing open path endpoints
- Allow closing open paths by dragging endpoints together
- Add comprehensive vector editing capabilities for curves and paths
nanostores + @nanostores/i18n + @nanostores/vue (~6 KB total).
English base translations defined inline in messages.ts, other
locales loaded on demand from JSON. Grouped by domain: menu,
commands, tools, panels, pages, dialogs.
useI18n() composable returns reactive translation objects plus
locale controls. Russian locale included as first translation.
Browser language auto-detected, user preference persisted in
localStorage.