Conflicts with #214 (fonts by PostScript name): this branch's name index (FaceKeys,
aliases) stays, and #214's `by_postscript_name` (used by PDF import) reads a PostScript
map filled from it, keyed by the same normalized names.
Removed: scanning ~/Library/Application Support/Adobe (CoreSync/plugins/livetype and
Fonts), and the preference that switched it (Type › Include Fonts of Other Design Apps'
Folders). Fonts activated through another vendor's subscription are licensed for use
through that vendor's own sync; reading their files from VectorCraft would go around that
licence, and the project's clean-room rules keep VectorCraft out of other vendors' files.
Fonts the user has installed in the system or user font folders are still found by every
name, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Right-clicking the canvas now opens a context menu, as the reference app
does. The object under the pointer is selected first unless it already is.
The menu then lists what applies to the selection: Undo and Redo, Cut, Copy,
Paste, Isolate Selected Group or Exit Isolation Mode, Group and Ungroup,
Join and Average, Make or Release Clipping Mask and Compound Path, Make
Guides, the Transform, Arrange and Select submenus, and Export Selection.
On empty canvas it lists Paste, the zoom commands, the ruler, grid and guide
toggles and Select All. Commands that can't run now are left out (Undo and
Redo stay, greyed out), and a press on the canvas only closes the menu. Agents list the same menu with the
`ui.contextMenu.list` control method.
Root cause: the canvas ignored the secondary button except in the toolbar's
tool flyouts; there was no context menu at all. The menu reuses the menu
bar's items, rendering and dispatch; Object > Transform and Arrange are now
shared lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choosing an Effect > Pathfinder item (Add, Intersect, Exclude, Subtract...)
with several loose objects selected now groups them and applies the live
effect to the group, in one undo step, so the result shows at once and stays
editable. A selection that already holds a group, or a single object, works
as before, and agents get `grouped: true` back when it happens.
Root cause: Pathfinder effects combine the contents of a group or layer.
Applied to loose objects, each one got an effect with nothing to combine, so
nothing visibly changed, while the Pathfinder panel's commands (found by the
command search) act on the selection directly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pressing with Shift on an object that was already selected toggled it
out of the selection at once and went idle, so the drag that followed
moved nothing. The toggle now waits for the release: a Shift-click
still takes the object out of the selection, while a Shift-drag moves
the selection constrained, as it already did for unselected objects
and when Shift was pressed mid-drag.
Fixes#224.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U9TrG4w49oSRYbP3cqxA3D
DXF text justified Fit now keeps its height and stretches or squeezes
horizontally to run exactly from its first point to its second; Aligned
text scales as a whole, height included, to do the same, its width factor
kept. Attributes justified the same way follow suit. Without a second point,
or with both points the same, the text keeps its size as before.
Root cause: the importer took the angle of the two points and anchored the
text at the first, but kept the text's height and width factor, ignoring the
distance between the points. The text now records that span and, when it
becomes a text object, is measured in the font it is set in, then given the
horizontal scale (Fit) or size (Aligned) that makes it end at the second
point, kept between 1% and 10,000% of its natural length. The import's
bounds estimate uses the same scale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Live Paint Bucket now fills areas enclosed by any paths. Crossing
lines enclose faces it can fill, a line across a shape splits that shape's
face in two, and an area enclosed by paths becomes a face even when nothing
fills it (it starts unfilled). Edges are the paths cut wherever another path
meets them, keeping their stroke, so Shift-click paints each piece. Closed
shapes give the same faces as before.
Root cause: Live Paint built its faces from the Pathfinder regions, which
divide the filled areas of closed shapes. An open path counted only as the
area it would fill if closed, which for a straight line is nothing, so lines
divided nothing and the bucket found no face to fill. A new planar map in
vectorcraft-pathops (live_paint) sweeps every path as an edge: an open path
goes in traced there and back, with crossing counts that never cancel; faces
are walked on the resulting graph (holes included), and their fills and the
edges' sources are found geometrically. The sweep runs in an exactly sheared
frame, because it mishandles a horizontal line traced there and back.
Make, Merge, the bucket and Live Paint Selection all use it;
interior_point moves to vectorcraft-pathops with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shippori Mincho (assets/fonts, from #145) moves to storytold/craft-fonts. crates/text/build.rs
embeds the fonts of a craft-fonts checkout when CRAFT_FONTS_DIR is set (web: BIZ UDPGothic
Regular only) as vectorcraft_text::CRAFT_FONTS; unset, it is empty and Japanese falls back to the
installed fonts. Document text gets the Japanese faces after the bundled fonts (Mincho first); the
UI adds them at the end of every egui family (BIZ UDPGothic first), before ui_fonts' system-font
discovery. Release and FreeBSD CI builds check craft-fonts out at a pinned commit; packages carry
the fonts' OFL texts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the vertical type from #145 (same data, commands and tools).
- Kinsoku (the strict set, horizontal Japanese too): no line starts with a
closing bracket, 、。, a middle dot, colon, !?, the long vowel mark,
iteration marks or small kana; no line ends with an opening bracket.
Hyphenation breaks obey it too.
- Tate-chu-yoko: a run of two or three half-width digits (10月, 第100回)
stands upright across the column as one block one em long, squeezed to
the em when wider; longer numbers stay on their side.
- Upright or on its side by Unicode's Vertical_Orientation: CJK and
symbols upright, marks that need a vertical form on their side when the
font has none; upright glyphs advance at least one em, centred, and turn
about the ideographic em box centre so they sit on the column's centre
line. Point type keeps its anchor on the first column's centre line.
- `vrt2` is no longer requested: its glyphs are already on their side, so
with the layout turning Latin and digits too they came out upside down
(and tate-chu-yoko blocks broke) in fonts that have it, e.g. Hiragino.
- The line→text mapping is one affine (`TextLayout::line_xf`, an exact
quarter turn plus the point-type offset); `logical_point` and
`physical_point` use it. Pixel snapping skips vertical type.
- Tests: kinsoku in both directions, tate-chu-yoko blocks, upright vs
sideways, anchor on the centre line, arrows along columns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Documents made elsewhere often name a font differently from the family
name VectorCraft lists: a localized family name (ヒラギノ角ゴシック), a
legacy family/style pair (Hiragino Sans + W3) or a PostScript name
(HiraginoSans-W3). FontDb now indexes every name in a face's name table
and resolves a request to the right face, telling the caller whether it
matched exactly, by another name, by substitution or not at all
(FontMatch).
- text: FaceKeys/aliases, FontDb::resolve, canonical, find_postscript;
fix a race where a face loaded by another thread was reported missing.
- engine: text.fonts reports status, the resolved face and missing
glyphs; Replace Font resolves the same way.
- render: substituted-font highlighting uses resolve, so a font found
by another name is no longer shown as missing.
- Find Font notes how each font was found.
- Preferences › Type › Include Fonts of Other Design Apps' Folders
(macOS, on by default).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Typing with an input method (the macOS Japanese IME and the like) used to
insert the raw keys (romaji came in as ASCII): egui's Ime events weren't
handled.
- Tools gain `ime_preedit` / `composing` / `ime_caret`; the Type tool lays
the marked text out in place inside its typing session, underlined (the
clause being converted thicker), and commits it through `text_input`.
Character ranges from the IME convert to byte ranges in one place. A
cancelled composition leaves no empty undo step; an empty clear with
nothing marked never deletes the selection.
- The UI sends Text and Ime events to the tool in order, sends IMEOutput
every frame while text is edited (what lets egui-winit enable the IME
and place its candidate window at the caret, or the converting clause),
and holds back keys, Enter/Escape, clipboard, single-key shortcuts and
menu commands (Undo, Cut, Select All… also from the native menu) while
composing. A click, a tool switch or a style change ends the
composition with the marked text kept as typed; on macOS the IME is
told to discard it (NSTextInputContext.discardMarkedText) so it doesn't
come back in the next input.
- Undo during a typing session also ends the Type tool's session, so its
caret and marked text follow the document.
- Tests: tools (char → byte ranges, underlines and candidate position),
engine (compose → commit is one step, cancel leaves no step, a click
commits, Undo mid-composition is safe), ui-egui (synthetic Ime events:
compose, commit, ⌘Z held back, IME output, click and app switch).
Checked by hand on macOS 27 with the Japanese IME (romaji input, convert,
commit, cancel, Undo/Redo after commit, click away, Latin mode, app
switch). Not checked: Windows and Linux IMEs, the web build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VectorCraft ▸ Language gains Čeština (`app.language {lang: "cs"}`,
persisted as "cs"). `Language::tr` now picks a table per language.
The Czech table (crates/ui-egui/src/i18n/czech.rs) translates every
menu title, submenu, item and header in the menu tree, plus the labels
toggling items switch to (Show/Hide Guides, Lock/Unlock Grid, Outline/
Preview, Edit Contents/Edit Envelope…). It was written clean-room from the
meaning of the English labels: commands in the infinitive, menus as nouns.
Left as-is on purpose: the product name, language names, OpenType, the
built-in workspace names and the perspective grid preset names.
Tests:
- every Czech (and Japanese) entry is a menu label, toggled labels included;
- Czech covers every menu label except fonts, sizes, plug-in names and
an explicit KEEP_AS_IS list;
- ellipses match their source label, entries are unique;
- the Language menu lists every language; `cs` validates and persists;
- each UI font family's own first font has the Czech letters and
punctuation (no system fonts needed).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
47 of the 83 entries were labels from other apps ("New blank PDF", "Comments panel",
"Read mode"…) that never appear here. A test now walks the menu tree and fails on any
entry that isn't one of VectorCraft's labels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A document names its fonts by PostScript name, which the importer split at the
first hyphen into family and style. A family with hyphens in its name was never
found: "Rounded-X-Mplus-1c-black" became family "rounded", style
"x-mplus-1c-black", and the text fell back to another font. Common Japanese
families have hyphens in their names.
The system font scan now also indexes each installed face's PostScript name
(read from the same name table it already reads). The importer asks for the face
with that exact name first (`FontDb::by_postscript_name`) and splits the name only
when no installed face has it.
Test: installed faces are found by PostScript name, in any case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Edit Contents: while an envelope's contents are edited, clicks hit the
content where it sits undistorted and the Selection tool selects it; the
menu item reads Edit Envelope then. No default shortcut: the reference
app's (Shift+Cmd+V) is Paste in Place here.
- A selected envelope shows its mesh (a warp's surface grid, a mesh's own,
a top object's outline) instead of its content's outlines.
- Mesh envelope points get bezier handles (append-only; none stored is the
smooth Catmull-Rom mesh, which the first handle edit fills in exactly
along the grid lines). setMeshPoint takes `handle`; object.mesh.movePoint,
addLine and deletePoint edit mesh envelopes too. The Mesh tool and Direct
Selection drag points and the clicked point's handles (shared MeshEdit);
the Mesh tool adds rows and columns inside mesh envelopes. Reset with
Mesh keeps the shape along the grid with handles; Release returns the
exact mesh.
- Control bar: Edit Envelope / Edit Contents, warp style, orientation,
bend and distortions or mesh rows and columns, Reset, Envelope Options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Perspective Grid tool's left and right ground-level points move the whole
grid; the left and right extents are separate (extentRight; Alt drags both,
Shift goes by whole cells); a cell size widget sits up the line where the planes
meet; Shift keeps a vanishing point on the horizon and the ground points and
origin on one axis. The Plane Switching Widget stays put in a corner of the
document window (ViewInfo/ToolContext carry the window as a ScreenFrame), takes
its clicks with any tool while the grid shows (the Perspective Selection tool
too while it's hidden), and 1-4 pick the left, horizontal, right and no plane.
Double-clicking the Perspective Grid tool opens Perspective Grid Options
(show the widget, its corner; perspective.widget.options, kept with the
preferences).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Type and symbol instances attached to a plane keep their art and carry a projection
(Node.perspective.projection, append-only); the canvas and every export draw their outlines
through it (reshape/bake), so they are foreshortened and stay type and symbol instances. Bounds,
hit testing and transforms follow the projection. Object › Perspective › Edit Text
(perspective.editText, or double-clicking with the Perspective Selection tool) shows the type flat
where it is drawn in isolation mode; exiting isolation projects it again. Release with
Perspective keeps their look. Copies, pastes, duplicates and Selection-tool moves keep the
attachment (the art moves as dragged). Flare, spiral and polar grid draw on the active plane, and
so do the click-to-size shape dialogs (perspective.draw `at`: sizes in plane units).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Images distort as a raster mesh warp: cut by Fidelity into triangles,
each a clipped image under the affine map of its corners (pieces overlap
slightly, no seams; the renderer paints them without clip layers; SVG
writes the pixels once and <use>s them).
- Symbol instances distort as their symbol's art (the renderer's cache
follows edits to the symbol).
- Distort Appearance bakes strokes (filled outlines) and geometry effects
before bending; Distort Linear Gradients bends linear gradients; Distort
Pattern Fills bends the tiles. Opacity masks of the content bend too.
- Bug fix: a rotated, sheared or reflected warp envelope re-warped along
the page axes. Envelopes now keep their own frame (append-only model
field), so the warp turns with the art; move and scale keep it square.
- Live evaluation shares one set of hooks (vectorcraft_doc::live::Hooks,
vectorcraft_effects::{expand_live, expanded_live_group,
expand_live_deep}) between the canvas, Expand and every exporter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closed shapes start where they twist least (unless the Blend tool
picked the anchors); dashes, miter limit, arrowhead scale and width
profiles interpolate (caps, joins and brushes switch halfway);
gradients with different stop counts resample to common stops; groups
with different member counts grow the extra members out of a point;
compound steps stay compound paths; type, symbol instances and images
interpolate their transforms, type its colours and sizes run by run.
New blends are knockout groups and the canvas draws them knocked out
as the exports do. Expand and Release keep the blend's name,
transparency, opacity mask and appearance, and Object > Expand expands
blends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A compound path's stroke lives on the compound: the tool edits it through
any member, and stroke.widthPoint.* / stroke.widthProfile.set given a member
edit the compound's profile.
- Width points show on every subpath the stroke runs along (not just the
first) for the hovered and the selected stroke.
- Cursors: the Width tool's own glyphs off a stroke, over a stroke (a drag
adds a point) and over a width point or handle (a drag moves or widens it).
- Hint-bar text for the tool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Holding the brush still keeps Twirl, Pucker and Bloat applying, scaled by the
time held: a new Tool::tick (time supplied by the host: canvas frames, or
`holdMs` on ui.pointer / pointer_gesture events) repeats the last point every
0.1 s, one more dab there. Liquify never touches graphs, meshes, type, symbols,
images, or envelopes, repeats and blends with their contents, nor guides in
selection mode; those under the brush come back in `skipped` with a `warning`
that the status bar shows (UiRequest::Status). A drag applies only the dabs
each new sample adds (the stroke's state is kept between previews), giving the
same result as the whole stroke, so journal replay still reproduces it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
View > Perspective Grid gains Show/Hide Rulers, Snap to Grid (on by default),
Lock/Unlock Grid and Lock Station Point, with flipping labels and checkmarks (and
Show/Hide Grid's label). Lock Grid stops the grid widgets from dragging; Lock
Station Point swings the other vanishing point around the station point; Snap to
Grid lands drawn corners and moved edges on gridlines within a quarter cell
(perspective.draw/move take snap?); Show Rulers draws a ruler up the true height
line in the grid's units. Gridlines draw in the Define Grid colours and opacity.
New commands perspective.grid.lock/lockStation/snap/rulers (view state).
Existing tests that measure exact perspective moves (two in tests_distort.rs,
the persp_session helper of tests_persp_select.rs) turn snapping off first (it is on by
default and would move their exact corners onto gridlines).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The grid keeps per-plane offsets (leftOffset, rightOffset, groundOffset). Each plane has a widget
(a diamond in its colour) that the Perspective Grid and Perspective Selection tools drag: plain
moves the plane, Shift moves the objects on it too, Alt copies them (perspective.plane.move).
Double-clicking a widget, or ui.perspectivePlane, opens the plane's options dialog (location; Do
Not Move / Move All Objects / Copy All Objects). Object › Perspective › Move Plane to Match Object
(perspective.plane.matchObject). New objects attach where the plane is. Grid edits leave
attached art in place as the reference app does; perspective.grid.set {reproject: true} moves it
with the grid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pins show as soon as the tool has art selected: automatic ones at the
centre and the middle of each limb's end (at least three), until pins are
placed. They stay across tool switches while the selection is unchanged and
a new selection starts afresh.
- The pins live in the document (Document.puppet, never saved) with the rest
shape they started from: every warp starts from it, so warps don't stack and
dragging a pin back restores the original; Undo/Redo take the pins back with
the art. Adding and deleting pins are undo steps too.
- object.puppetWarp gains rest: true (the tool's pins); the new query
object.puppetWarp.pins reads them. A click off the mesh adds nothing and
says so (not-allowed cursor, canvas label); the command refuses such pins.
- The mesh grid covers the expanded art, so tiny art with a wide Expand no
longer allocates millions of cells.
- Tests on groups, compound paths and open strokes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A selected blend shows its spine. Direct Selection drags spine points
and handles, the Pen tool adds and deletes points on a selected
blend's spine (object.blend.spine.moveAnchor/addAnchor/removeAnchor).
The straight spine becomes a path on the first edit, its points pinned
to the key objects (append-only BlendSpec.key_anchors): moving a key
moves its end of the spine and the other way round; spines from older
files and Replace Spine still spread the keys evenly. Direct and Group
Selection pick blend keys on the canvas, edits redraw the blend live,
and Release leaves the spine as a path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blend Options is a dedicated dialog (Spacing dropdown with its value,
Align to Page / Align to Path buttons, live preview) opened on the
selected blend's own options, so OK no longer turns Smooth Color into
5 steps; with nothing selected it sets what new blends start with
(object.blend.info reads them). The Blend tool blends from clicked
anchor points (blend spec `starts`), adds keys to an existing blend
instead of nesting it, opens Blend Options on double-click, Alt-click
and its toolbar button, and has its own cursors and hint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Perspective Selection tool shows the selection's bounding box in perspective; its handles
scale in plane space (perspective.transform; Shift proportional, Alt from the centre). Alt-drag
moves copies that stay attached, pressing 5 while dragging moves along the plane's normal
(ToolKey::Digit, delivered by the canvas and MCP press_key), and the arrow keys nudge in
perspective (perspective.nudge). Transform Again repeats the last perspective move or scale in
plane space. Attachments (plane and depth) now live on the objects (Node.perspective), so copies,
duplicates and pastes keep them; grids from older files hand theirs over. Homography moves to
vectorcraft-geom. Hint-bar text for the tool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
enveloped_type_exports_distorted_in_every_format compared two exports
byte for byte, and EPS dates its file to the second (%%CreationDate), so
it failed whenever the clock ticked between them. The comparison now drops
the CreationDate/ModDate lines and still checks repeatability and that the
bend shows in every format.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
View > Perspective Grid > One/Two/Three Point Perspective list the built-in views
([1P/2P/3P-Normal View], plus Low and High views generated in code) and the saved
presets of each type; Save Grid as Preset... and Define Grid's Preset menu and Save
Preset... save the fields. Edit > Perspective Grid Presets... (previously a
mislabelled reset to the two-point grid) manages them: New, Edit, Delete, Import,
Export (.vcperspective), stored with the preferences. New commands
perspective.presets.list/save/delete/export/import; perspective.grid.preset takes
a preset name. Preset import is fuzzed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Double-clicking a Liquify tool opens its Tool Options (dialog liquifyOptions):
the shared Global Brush Dimensions (Width, Height, Angle, Intensity, Use
Pressure Pen), the tool's options (Detail, Simplify with its checkbox, Twirl
Rate, Complexity, Brush Affects, Horizontal/Vertical) and Show Brush Size.
The canvas captures pen pressure from pen/touch force, ui.pointer and MCP
pointer events take `pressure`, and object.liquify takes [x, y, pressure].
Fixes: LiquifyParams::to_json now sends affectAnchors/In/Out; Alt-drag sizes
the brush from its current size (Shift keeps proportions) instead of
collapsing it. Hint-bar text for the seven tools. Registers the MCP Liquify
tests file added in M8.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Alt-dragging near (not on) a selected pin turns the art around it; the pin
keeps that turn through later warps. arap::Pin gains an angle constraint and
object.puppetWarp takes angles: [deg|null...].
- Shift-click adds pins to the selection or takes them out; dragging a pin
moves every selected pin. Select All Pins selects them all (it used to
deselect). Alt-click no longer deletes a pin; Delete removes the selection.
- Control bar: Expand (0 allowed), Show Mesh (on by default), Select All
Pins. Hint-bar text for the tool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every exporter (SVG, PDF, EPS, EMF/WMF, DXF) evaluated live objects with no
text outliner, so type inside an envelope was written undistorted. They now
share vectorcraft_effects::expand_live_deep, which outlines type per run in
each run's paint, as the canvas now does too (it used the first run's paint
for the whole line).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Make with Warp and Make with Mesh open dedicated dialogs (style dropdown,
orientation, bend and distortion sliders; rows and columns) with live
preview; while an envelope is selected the menu shows Reset with Warp /
Reset with Mesh instead, which switch the envelope's kind and keep its
content (Maintain Envelope Shape samples the current surface). Envelope
Options is a full dialog prefilled from the selection (Anti-Alias,
Preserve Shape, Fidelity, Distort Appearance, Distort Linear Gradients,
Distort Pattern Fills); with nothing selected it sets the defaults for new
envelopes. object.envelope.info reads the settings. Release returns the
warp or mesh shape as a gradient mesh and keeps opacity, blend mode and
mask; Expand keeps name, knockout, isolation and mask; Object > Expand
expands envelopes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
View > Perspective Grid > Define Grid... opens a dedicated `perspectiveGrid` dialog
(ui.perspectiveGridDialog) prefilled from the current grid instead of a generic
form with fixed defaults that forced kind 2. The grid model gains (append-only)
name, viewing angle (the station point), units, scale, plane colours and opacity;
grids with a viewing angle foreshorten each axis as a true camera. New commands:
perspective.grid.get (read-only) and perspective.grid.define (one undo step,
only changed fields apply).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Width and Puppet Warp tools now claim Delete and Backspace while they
have width points or pins selected, so the shortcut routing (desktop and MCP
press_key) removes those instead of clearing the artwork. With nothing of the
tool's selected the keys still clear the selected objects. Headless Backspace
clears the selection like the desktop app does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each tool keeps its options (a table in vectorcraft_tools::settings) when another
tool is chosen, across document switches, and in the saved preferences
(Prefs::tool_settings). The 7 Liquify tools share one set of Global Brush
Dimensions; the Symbolism tools share their brush. tool.setOption takes
{values} and a `tool` (another tool's options are stored for later), returns
the options, and works headless (MCP host command).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>