Commit Graph
399 Commits
Author SHA1 Message Date
bflatasticandClaude Opus 5.5 b08188e5a5 Toolbar: only tool groups' buttons check for a flyout press, ROADMAP (review of #436)
A press on a single tool's button no longer schedules the repaint that
would open a flyout it doesn't have. The ROADMAP's UI list and Last
updated line name the right and corner press.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:01:25 +07:00
Michael GiesonandClaude Opus 5.5 e80b3543d3 Toolbar: a right press or a corner press opens a tool group's flyout at once
A right press opens the flyout on the press instead of on the release,
including on the active tool's button, and the release leaves it open. A
primary press on the button's corner triangle opens it at once without
picking the tool; a plain click in the middle still picks it. The long
press keeps scheduling the frame where it becomes long.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 13:01:13 +07:00
bflatasticandClaude Opus 5.5 a32aed7e81 Layers: only the trash and New Layer take dropped rows (review of #435)
Illustrator documents two drop targets in the Layers panel's bottom bar:
dragging items to Create New Layer duplicates them, to the Delete icon
deletes them. Dropping rows on New Sublayer or Make/Release Clipping
Mask no longer creates a sublayer or clips the row's layer; those
buttons only act on a click, and the new test checks a drop on New
Sublayer changes nothing. The trash and New Layer share one drop loop,
and the drag ghost paints its colour bar and thumbnail with the row's
own helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:58:21 +07:00
Michael GiesonandClaude Opus 5.5 a216c36779 Layers: drag ghost, drops on the bottom-bar buttons, trash deletes again
Dragging a row shows a dimmed copy of it (colour bar, thumbnail, name)
under the pointer. A row dropped on Delete, New Layer, New Sublayer or
Make/Release Clipping Mask runs that button on the row's layer, as a
click would: Delete removes the row itself and New Layer duplicates it,
as in Illustrator.

Rows dragged onto the trash weren't deleted. The trash took the drop as
a target-circle payload first, and egui's take discards a payload of
another type, so the rows reached the second check as nothing. It now
peeks at the type before taking. The trash is also outlined while rows
or a target circle are over it, like the other drop buttons.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 12:58:04 +07:00
Michael GiesonandClaude Opus 5.5 80f4de1a5e Layers: Layer Options' Color list shows a chip of each colour
Each preset in the Color dropdown has a chip of its colour before its
name; Custom has none, its name aligned with the others.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 12:58:04 +07:00
bflatasticandClaude Opus 5.5 1706e84829 Fix #429: Type on a path brackets, flip and Type on a Path Options that keep the type in place
Type on a path could only be repositioned by Type on a Path Options, whose menu item sent
`{start: 0}`, so OK threw the text to the start of its path; nothing showed or dragged its
brackets. Now, as in Illustrator:

- Type on a path flows between a start and an end bracket (`TextKind::OnPath { end }`, None:
  the end of the path or once round a closed one; `TextKind::path_span`). Flipping
  (`TextObject::flip_on_path`) reverses the path keeping a closed path's first anchor and swaps
  the brackets, so the type keeps its stretch of the path.
- Type on a Path Options gains Align to Path (ascender, descender, center, baseline) and
  Spacing (glyphs spaced as if set that far above the path: closed up round the outside of
  curves, opened up round the inside), laid out by the text engine.
- `type.pathOptions {start?, end?, flip?, effect?, alignToPath?, spacing?, ids?}` sets them in
  one undo step; with none of them it queries. The menu item opens the dialog with the selected
  type's Effect, Flip, Align to Path and Spacing (dropdowns for the choices) and leaves the
  brackets alone, so OK as opened changes nothing.
- The Selection and Direct Selection tools show selected type on a path's path and its start,
  centre and end brackets (`tools::pathtype`, with a bracket cursor): dragging the start or end
  bracket sets where the type runs, dragging the centre bracket slides it along the path and,
  across the path, flips it (Cmd/Ctrl held: it only slides). Each drag is one undo step.
- Arc-length measuring moved to `vectorcraft_geom::ArcPath` (layout, the tools and
  `text.createInPath` share it). SVG export outlines path type whose end bracket, alignment or
  spacing `<textPath>` can't express.
- docs/mcp.md documents the command and the drags; ROADMAP updated; dialog strings translated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:50:25 +07:00
bflatasticandClaude Opus 5.5 b86d19feed Appearance: the popover closes when its anchor isn't shown (review of #430)
The popover kept its open flag while its anchor was hidden (panel closed, selection changed), so
it came back open the next time the row was drawn. It now remembers the last frame it was open in
and counts as closed after a frame without it, as egui's remembered popups do. ROADMAP: the
Appearance panel's Opacity popup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:27:26 +07:00
Michael GiesonandClaude Opus 5.5 e6914861f1 Appearance: the Opacity popup's blend list and slider work
egui remembers one open popup at a time, so opening the blend-mode
dropdown or the opacity slider inside the Opacity popup closed the popup,
and the dropdown or slider with it. A new widgets::popover keeps its own
open state and closes only on Escape, its anchor, or a click outside it
and the popups it opened. The Opacity popup and the Stroke popover (which
holds dropdowns too) use it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 12:27:06 +07:00
bflatasticandClaude Opus 5.5 f155bc5514 Stroke: keep the pt ladder, fix the fallback units and per-unit decimals (review of #426)
- The pt ladder stays 0.25 … 10, 20, 30 … 100 pt (the dropdown's documented 20/30/40 pt steps).
- Feet, yards and meters returned the pt ladder converted as if it were in that unit (a 100 m
  stroke); they now get the pt ladder in points.
- weight_presets returns a fixed array (no allocation per frame); the pica ladder is stored in
  points; 0.325 in is 0.375 in (the eighths ladder).
- Fields keep three decimals in points, pixels and picas (595.276 pt) and show four in mm, cm,
  inches and larger units (8.2677 in, 0.0078 in).
- ROADMAP: the Stroke panel's per-unit presets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:27:06 +07:00
raytheengineerandClaude Opus 4.7 60439d88b9 Stroke: per-unit weight presets so mm/cm/in dropdowns show clean numbers
The Stroke panel weight dropdown converted one pt-based preset list into
whatever Units > Stroke was set to, which produced ugly rounded values like
0.088 mm, 0.265 mm, 10.583 mm from the pt ladder. Each unit now has its own
native ladder (observed from Illustrator's running dropdowns and typed in),
converted to pt once at use. Points, Millimeters, Centimeters, Inches,
Pixels and Picas each get Illustrator's actual values; the exotic Unit
variants (Feet & Inches, Meters, Yards, Feet) fall back to the pt ladder.

Picas round-trip to exact pt weights (1 pt, 12 pt, 60 pt …) but display as
decimal picas until Unit::number learns 0p1 / 1p3 notation; that is a
system-wide formatter change left for a separate PR.

Also bumps Unit::number precision from 3 to 4 decimals so Illustrator's
sub-eighth-inch strokes (0.0078, 0.0156, 0.0313, 0.0625 in) show their
authored values instead of rounding to 0.008 / 0.016 / 0.031 / 0.063.
Trailing zeros are still trimmed, so 1 pt stays "1".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-08 12:26:47 +07:00
bflatasticandClaude Opus 5.5 b71d877a0a Fix #400: Scrub numeric fields by dragging their label
Dragging a numeric field's label left or right steps the field's value,
as Photoshop's scrubby labels do (beyond Illustrator): one unit every
2 points, ten with Shift, a tenth with Ctrl/Cmd (counts stay whole), in
the field's unit and precision. The panels apply each value as they
apply a typed one, so the art follows live and the caller's limits
hold (dragging back from a limit changes the value at once).

- scrub.rs: the label zone over the label noted just before a field
  (dim_label, label_row, widgets::field_label, the Character and
  Paragraph panels' icons, the Transform and Properties rotate/shear
  icons), the ↔ cursor on hover, a click on the label focuses the
  field. The text box itself never scrubs; a field without a label
  doesn't. num_field, plain_field, mixed_field (and the spin fields)
  and opt_field take part.
- Engine: undo groups (Session::begin_undo_group / end_undo_group). The
  edits made in a group, commands and committed live drags alike, are
  one undo step, journaled one by one; a cancelled group is undone and
  dropped from the journal. The app opens one while a label is dragged,
  so a scrub is one undo step whichever path the panel applies values
  by (app.run, session.execute, live_run), and Escape cancels it
  (shortcuts leave Escape to the drag, so it doesn't close a dialog or
  deselect).
- Preferences > General > Scrub Numeric Fields by Dragging
  (`scrubNumericFields`, on by default), translated in ja, es, zh-hans,
  zh-hant and pt-br.
- Tests: label drags with Shift/Ctrl, limits, counts and units, Escape,
  the preference, the cursor, click-to-focus, no scrub on the box or
  without a label, and in the app one undo step for a W drag.
- ROADMAP and docs/mcp.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:33:57 +07:00
bflatasticandClaude Opus 5.5 e04385e950 Fix #423: Movable dialogs and Offset Path's Joins dropdown
The modal dialogs were anchored to the window's centre, so none of them
could be moved. They now share one modal frame (dialogs/modal.rs): the
band from the window's top edge down to its heading moves it, as a title
bar would, with a grab cursor; it stays on screen and keeps where it was
moved (per dialog, for the session). The shared dialog frame, New
Document (and More Settings), Preferences, Keyboard Shortcuts,
Workspaces and Find Font all use it; About moves by its title bar.

The form dialogs drew every string field as a text box, so Offset
Path's Joins had to be typed. Their choice fields are now dropdowns
(Joins: Miter, Round, Bevel; Average's axis; Shear's and Reflect's
axis), the fields follow the dialog's order (Offset, Joins, Miter Limit;
Rows, Columns, Gutter) instead of alphabetical, and Offset Path has a
Preview that Cancel rolls back.

The axis labels are translated in a new "axis" catalog context: the
plain Horizontal and Vertical rows mean writing direction in Japanese.
ja, es, zh-hans and zh-hant get the axis rows (Horizontal, Vertical,
Both) and Joins, Miter, Bevel, Offset and Axis.

Headless tests drag the heading of four dialog windows (moves, a drag
below the heading doesn't, kept on screen, reopens where it was left),
open the Joins dropdown and choose Round (same result as the command
with round joins), and check the axis dropdowns and the preview.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:52:05 +07:00
bflatasticandClaude Opus 5.5 8e3dbd5369 Part of #394: Preferences for snapping, handles, rubber bands, pattern tiles, tints and type
Fourteen more Preferences options that were stored but read by nothing now
act, for the mouse and for agents (prefs.set, then pointer_gesture,
press_key or the commands):

- Selection & Anchor Display: Snap to Point and its distance (with Smart
  Guides off, View > Snap to Point lands a dragged selection's grabbed
  point, drawn points and reference points on anchors and ruler guides);
  Move Locked and Hidden Artwork with Artboard (artboard.move, the Artboard
  tool and Rearrange; off, hidden art now stays too); Enable Rubber Band for
  the Pen and Curvature tools; Handles (solid, hollow, large);
  Show handles when multiple anchors are selected (canvas and Direct
  Selection's handle picking); Hide Corner Widget for angles greater than.
- General: Transform Pattern Tiles is the default of a new `patterns`
  param on object.transform/move/rotate/scale/reflect/shear/transformEach
  (journalled when the art uses a pattern), and the transform dialogs get a
  Transform Patterns checkbox; Select Same Tint % (off, Select > Same
  Fill/Stroke Color takes every tint of a global or spot swatch).
- Type: new type.step {attribute, by} steps size, leading, tracking, kerning
  and baseline shift by the Size/Leading, Tracking and Baseline Shift
  increments; Increase/Decrease Font Size (Cmd+Shift+. and Cmd+Shift+,);
  the Type tool's Alt+arrows (kerning at a caret or tracking, leading,
  Shift for baseline shift, Cmd/Ctrl for five steps). Enable Missing Glyph
  Protection keeps characters a new font lacks in the font that had them
  (text.setStyle, text.setRangeStyle). Fill New Type Objects With
  Placeholder Text: type the Type tools place starts with placeholder text,
  selected (text.create / text.createInPath take `placeholder`).

Units > General, Stroke and Type were already the single source of truth
(units.rs). Auto Size New Area Type, Show Font Names in English and
Highlight Alternate Glyphs need subsystems the app doesn't have yet and
stay open with the rest of #394.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:43:12 +07:00
bflatasticandClaude Opus 5.5 9a5429a325 MCP: inspect_document slices too, stricter find filters (review of #416)
- The inspect_document tool (and the control channel's document.inspect)
  take {depth?, childLimit?}, so MCP clients get the skeleton without
  going through run_command; the tool description points at the
  drill-down path.
- document.find rejects a filter of the wrong type instead of dropping
  it (which widened the search); one opt_u64 helper parses limit, depth
  and childLimit.
- docs/mcp.md spells out find's matching rules; control-protocol.md
  and ROADMAP updated; MCP and engine tests cover the new paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:17:06 +07:00
Fahri Meral e278ccf44d MCP: skeleton reads and find for large documents
document.inspect takes the same {depth?, childLimit?} slicing as
document.node (a depth-0 read is the layer skeleton), and document.find
{name?|kind?|text?, limit?} locates nodes across the tree with their
ancestor paths and a total. docs/mcp.md documents the drill-down recipe;
the CLI stdio sweep covers both.
2026-10-08 09:17:06 +07:00
bflatasticandClaude Opus 5.5 2f710d3acc Fix #414: select, move and delete ruler guides with the selection tools
Guides dragged out of the rulers could not be touched again: no tool hit-tested
them, so a click on a guide went to the art under it.

- The Selection, Direct Selection and Group Selection tools pick a ruler guide
  within the selection tolerance (over the art; Direct Selection takes an
  anchor on it first) through a shared tools::rulerguide::GuideEdit. A drag
  moves the selected guides in one undo step (Alt copies them, Shift snaps the
  dragged one to the ruler's ticks, otherwise it snaps to pixels, the grid or,
  with Smart Guides, the art's edges, centres and anchors); dropped off the
  canvas onto its ruler, the guide is deleted. Hidden or locked guides can't be
  picked; the pointer shows which way a guide moves.
- Selection::guides holds the selected guides (on their own, like slices); the
  canvas draws them in the selection colour. Delete/Backspace (edit.clear)
  delete them, the arrow keys (object.nudge) nudge them, Lock Guides and Hide
  Guides deselect them.
- Commands for agents: guide.list, guide.select {indexes, toggle?},
  guide.move {dx?, dy?, copy?} (besides {index, pos}) and guide.remove {index?};
  guide positions out of range are refused. Documented in docs/mcp.md.
- Shift while dragging a new guide out of a ruler snaps it to the ticks too
  (Unit::ruler_step, shared with the rulers' drawing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:55:51 +07:00
bflatasticandClaude Opus 5.5 47bed05ff4 Part of #394: Preferences that act on selection, the wheel, tool tips and the grid
Fifteen Preferences options were stored but read by nothing. They now act,
through the existing plumbing (ToolContext for the tools, the prefs read by the
canvas for the view), and reach agents through prefs.set:

- General: Show Tool Tips (off: no widget's tool tip comes due), Double Click
  To Isolate, Zoom with Mouse Wheel (the wheel zooms about the pointer,
  Shift-wheel scrolls up and down, Cmd/Ctrl-wheel sideways), Use Precise
  Cursors (Pen, Eyedropper, Slice and Blend pointers become a crosshair).
- Selection & Anchor Display: Tolerance (objects, anchors and handles, in
  place of the fixed 3 and 4 px), Object Selection by Path Only (compound
  paths too), Command Click to Select Objects Behind (cycles down the objects
  under the pointer; not when Cmd only borrows the selection tool), Zoom to
  Selection (Zoom In/Out centre the selection), anchor Size, Highlight anchors
  on mouse over (Direct Selection).
- Type: Number of Recent Fonts (up to 15 Recent Fonts slots).
- Guides & Grid: guide and grid Color and Style (lines or dots), Grids In Back.

The canvas's context menu, panel drops and hover highlight pick objects like
the selection tools. The control channel gets ui.wheel (with modifiers held
like ui.click) so agents can turn the wheel. The rest of #394 stays open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:22:24 +07:00
Brandon ThomasandClaude Opus 5.5 f7a03160d6 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#411)
* About: contributors compiled in from contributors.json (grab bag + table, sortable, name toggle)

Help > About VectorCraft gains Contributors and Models tabs next to the existing About
content, following the craftrules standard (standards/contributors.md):
contributors/contributors.json is generated by craftrules/scripts/contributors.py and baked
into the binary by crates/ui-egui/build.rs as static tables (credits.rs). Each contributor
line: @username, opt-in display/real name, PRs, commits, lines +/-/delta, binary assets +/-,
first/last commit. Sortable by every column; alphabetical ignores case and the leading @.
Labels are translated (ja, zh-hant, pt-br catalog rows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors docs: craftrules is two levels up (../../craftrules)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section

build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only
for entries the generator verified as written by that person (craftrules standards/contributors.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n: credits labels for catalogs added on main since this branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: regenerate after rebase onto main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:22:04 -04:00
bflatasticandClaude Opus 5.5 4767ec7113 Fix #398: Linux/Wayland bitmap paste, copied files paste as art
Under Wayland the window is native but arboard was built without its
data-control backend, so it read the X11 clipboard through XWayland,
which only holds what X11 apps copied: Paste found nothing and reported
an empty clipboard (egui's text paste into fields uses smithay-clipboard,
which is why that worked). arboard now has `wayland-data-control`, the
same as PhotoCraft; it falls back to X11 where the compositor lacks the
protocol (GNOME's Mutter).

The reporter's clipboard also held text/uri-list: with a file copied in a
file manager, Paste would have taken the path as text before the bitmap.
Copied files now paste first, as the first one that is art (SVG, PDF,
EMF/WMF or a bitmap), told from its first 4 KiB by the engine's format
detection (`clipboard::file_flavour`): text/uri-list through arboard on
Linux and macOS (a trailing CR from CRLF lists trimmed), CF_HDROP on
Windows, read with the clipboard closed again.

Dropping files on the window can't work under Wayland yet: winit 0.30,
which eframe 0.36 runs on, has no Wayland drag and drop (winit#1881,
added in 0.31), so no dropped_files arrive. docs/development.md documents
it and the workarounds (copy and paste the files, File > Place, or run
under XWayland with `WAYLAND_DISPLAY=`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:44:23 +07:00
bflatasticandClaude Opus 5.5 d324138bc1 Fonts: drop the plain font dropdown now that no picker uses it (review of #397)
Find Font and the Glyphs panel were widgets::font_dropdown's last
callers; the font menu's own tests cover searching and Enter. ROADMAP:
the Glyphs panel and Find Font's Replace With use the font menu, without
its live preview.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:32:12 +07:00
DWAA1660andClaude Opus 5.5 1d5e9cb239 Find Font and Glyphs: pick fonts from the font menu (#358 follow-up)
Both switch from widgets::font_dropdown to font_menu::font_menu, so they
get the same samples, favourites and filters as the Character and
Properties panels. They only pick a font: the new font_menu::picked
returns the choice and toggles stars, and never previews on the document.

Follow-up to #339 as suggested in #364.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BRE8ZD3p8QgYctENhnJDz3
2026-10-08 07:32:11 +07:00
bflatasticandClaude Opus 5.5 18cbe007b8 Character Alignment: on a character range too, with the East Asian options (review of #389)
text.setRangeStyle takes charAlign, so the panel menu works on the
characters selected with the Type tool (it routes there while text is
edited, and charAlign alone was answered with "nothing to change").
One char_align_param parses it for both commands. The menu shows with
Preferences › Type › Show East Asian Options, like Mojikumi Set and
Top-to-Top Leading. Rows in the Simplified Chinese, Spanish and
Brazilian Portuguese catalogs; the Japanese Em Box Center names the em
box. ROADMAP: the gating, the range command, and ICF top/bottom still
missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:32:11 +07:00
imachusandClaude Opus 5.5 178ca03eba Type: Character Alignment — where smaller characters line up with a line's largest
Characters of different sizes on one line all sat on the Roman baseline.
Japanese layout usually lines them up by their ideographic em boxes
instead (a 16 pt 第一回 next to 32 pt 雅楽 centred on it, not sitting on
its baseline). The Character panel menu now has Character Alignment:

- `CharStyle.char_align`: Roman Baseline (as before, the default), or the
  em box Top/Right, Center or Bottom/Left: a character smaller than the
  line's largest moves so that its em box top, centre or bottom lines up
  with the largest's (the em box from the font's vertical metrics, else
  0.12 em below the baseline to 0.88 em above it). Vertical type the
  same across the column (top → right). Written only when not Roman
  Baseline.
- `text.setFormat {charAlign: "romanBaseline"|"emBoxTop"|"emBoxCenter"|
  "emBoxBottom"}`; the menu shows the selection's setting; catalog rows
  in ja and zh-hant.
- Not yet: aligning on the ICF (average character face) top and bottom.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:32:10 +07:00
bflatasticandClaude Opus 5.5 505f3bd7ae Freehand Tool Options: Accurate/Smooth ends on the Fidelity slider, Spanish rows (review of #390)
The Fidelity slider names its ends, Accurate and Smooth, through a
shared form::slider_ends that the Color Guide Options' Less/More now use
too. The dialog's strings get Spanish rows (complete catalog), and
Accurate rows in every catalog. ROADMAP: the Drawing tools row lists
what the freehand Tool Options still lack (Keep selected, Alt to the
Smooth tool, Blob Brush Merge Only with Selection, brush angle and
roundness).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:32:09 +07:00
theironchefandClaude Sonnet 5.5 02582f318d Fix #388: Pencil, Paintbrush, Smooth, Blob Brush and Eraser Tool Options on double-click
The five freehand tools keep options (Fidelity, fill, the two
tolerances, the brush size) that last across tool switches and are
saved with the preferences, but nothing in the window showed or changed
them: a double-click on the tool's button answered "the pencil tool has
no options", which the toolbar drops. It now opens a Tool Options dialog
with the options the tool has, and OK sets them with tool.setOption. The
dialog's strings are in the Japanese, Chinese (both scripts) and
Brazilian Portuguese catalogs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 07:32:09 +07:00
bflatasticandClaude Opus 5.5 0916534db9 Fix #392: Cmd+A selects a focused field's text or the type being edited
Select All (Cmd/Ctrl+A) now follows the keyboard, as in Illustrator:

- macOS menu bar: the system menu takes Cmd+A/X/C/V before the window
  sees them, so a focused panel, dialog or search field got the document
  command. `menus::invoke_from_system_menu` (used by `NativeMenu::poll`)
  hands Select All, Cut, Copy and Paste to a focused text field as the
  events its keys would send; clicks on those menu items follow the same
  rule. Other commands, and these with no field focused, run as before.
- Type tool: `select.all` itself selects all of the text being edited
  while the Type tool edits (returns {editing, start, end}), so the menu,
  the shortcut, the control channel and headless MCP (`press_key`) agree.
  The UI's own Select All interception and the Type tool's hard-wired
  Cmd+A are gone; Cmd+A reaches it through the Select All shortcut.
- Windows, Linux and the web already left Cmd+A to a focused field; now
  covered by headless app-frame tests (field, Type tool, canvas).

Docs: docs/mcp.md (Select All while editing type), ROADMAP. Not yet:
Select All across threaded frames (one frame's text is selected).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:18:52 +07:00
bflatasticandClaude Opus 5.5 649656b092 Fix #369: restart the web build's graphics after a lost device or context
When the browser dropped the page's WebGPU device or WebGL context, the
editor kept running but nothing it drew reached the screen: Chrome and
WebKit showed a frozen, stale canvas and Firefox a blank page, while
edits and Save went on.

- ui-egui: new `graphics` module. `GraphicsLoss` carries a loss report
  from any thread (wgpu's device-lost callback, the canvas's
  `webglcontextlost` event) to the UI thread. `VectorcraftApp::
  graphics_lost` writes Data Recovery copies of the modified documents at
  once. The app now notices when the host hands it a new egui context and
  uploads everything again (fonts and theme, the canvas, place cursor
  thumbnails); every per-thread texture cache (layer, symbol, swatch,
  style, asset and glyph thumbnails, previews, font samples, Navigator,
  Flattener Preview) is a `TexCache` that empties for a new context.
  Without this, the moved app drew stale or wrong textures and egui
  panicked on its missing font families.
- web: the shell watches both loss signals; on a loss it puts a fresh
  canvas in place of the dead one and starts the same `WebRunner` again,
  handing over the same app (documents, undo, panels), with services made
  for the new context and a status-bar note. If no new graphics can be had
  (or after three losses) it removes the stale canvas, lets go of the
  recovery copies and says to reload.
- engine: `recovery::leave` lets go of a session's area but keeps its
  copies, so the reloaded page offers them at once instead of after the
  heartbeat goes stale.
- desktop: eframe can't give a window a new device, so a lost device
  (driver reset) is logged, the copies are written and the status bar says
  to save and restart; closing then skips the Save Changes question it
  can't show.
- Tests: loss reporting across threads, `TexCache` emptying, the app
  uploading the canvas, a cached preview, theme and fonts again in a new
  context (fails without the fix), copies written on loss, and
  `recovery::leave` offering copies at once with both lock and heartbeat
  stores. docs/development.md (web section) and ROADMAP updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:18:32 +07:00
bflatasticandClaude Opus 5.5 b05c14f8eb Fix #381: round single corners with Direct Selection (per-corner Live Corners)
A live rectangle stored four corner radii but drew them all with the largest,
and the corner widgets always rounded all four corners, so one corner could not
be rounded on its own.

- geom: rectangle_with_corners draws each corner with its own radius and kind
  (Round, Inverted Round, Chamfer); a sharp corner is one anchor, a cut one two.
  Uniform corners keep the exact rounded-rectangle anchors.
- doc: LiveShape::Rectangle gains `kinds` (saved only when a corner isn't round;
  older files read as round), plus helpers mapping anchors to corners.
- engine: object.setLiveShape takes `corners` and `kind`; without `corners` it
  edits the Direct-Selected corners, else all four, and keeps the selected
  corners selected as their anchors change.
- tools: with Direct Selection, only the selected corners show widgets and a
  drag rounds just those; Alt-click cycles the corner kind; double-click opens
  the new Corners dialog (also `ui.corners`).
- ui: Properties/Transform radius fields show and set the selected corners;
  CSS export keeps per-corner radii and skips cut-in corners.
- docs: mcp.md (Live Corners), control-protocol.md (Corners dialog), ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:56:32 +07:00
bflatasticandClaude Opus 5.5 048cbc51b1 Fix #367: keep a paused browser tab's recovery copies
A heartbeat alone can't tell a crashed tab from one whose timers stopped in
the background, so another tab could discard (or restore) the copies of a
still-live document once its heartbeat went stale.

- Recovery stores without locks can now announce an area for as long as the
  app runs (RecoveryStore::announce/announced). The web shell implements it
  with the browser's Web Locks (navigator.locks through js_sys::Reflect): a
  tab holds a lock named after its area, which the browser releases only when
  the tab is gone. Lock snapshots are refreshed every 10 s (once before the
  first frame); a snapshot over a minute old counts every area as held.
- An area is gone only when no announcement holds it and its heartbeat is
  stale. Where the store can't tell (no Web Locks), the owner repairs: each
  heartbeat (also its first frame after a pause) and each recovery save write
  again the copies missing from its area, and a heartbeat newer than the one
  another tab wrote when taking the area over marks it running again.
- MemoryStore::announcing models Web Locks in tests; engine and UI tests cover
  the issue's sequence. Native folder locks are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:25:39 +07:00
bflatasticandClaude Opus 5.5 5865e4cb51 Type: Top-to-Top Leading follow-ups (review of #385)
- The em box top that Top-to-Top Leading asks of every glyph's line metrics parsed the font's
  cmap and vertical metrics per glyph on every layout: each face now reads it once.
- The Paragraph panel menu shows Top-to-Top / Bottom-to-Bottom Leading with the East Asian
  options (Preferences › Type), as the Mojikumi Set does, and Reset Panel goes back to
  Bottom-to-Bottom Leading, the default.
- Saved as `leading_model`, like the other paragraph attributes in files.
- Simplified Chinese (landed since) translates the two menu items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:25:18 +07:00
imachusandClaude Opus 5.5 46dc8b1bde Type: Top-to-Top Leading — leading measured from em box top to em box top
Japanese layout measures leading from the top of one line's ideographic
em box to the next (the right side, in vertical type), not from baseline
to baseline. The Paragraph panel menu now offers both:

- `ParaStyle.leading_model`: RomanBaseline (as before, the default) or
  EmBoxTop. With EmBoxTop a line's leading is the space below it — the
  next line's em box top is this line's plus its leading, and the next
  baseline hangs from there by that line's tallest em box — and area
  type's first line touches the top of the frame (its em box top on the
  frame's top, at least Area Type Options' minimum). With baseline
  leading a line's leading stays the space above it.
- Lines of one size are exactly as far apart either way; the models
  differ for mixed sizes and for the first line of area type (fonts
  whose ascent isn't the em box top: Hiragino's nearly is).
- The em box top comes from the font's vertical metrics (#343), else
  0.88 em. Written only when EmBoxTop.
- `text.setFormat {leadingModel: "romanBaseline"|"emBoxTop"}`; menu items
  Top-to-Top Leading / Bottom-to-Bottom Leading (checked as set); catalog
  rows in ja and zh-hant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:24:41 +07:00
theironchefandClaude Sonnet 5.5 5883b6c1fd Enter opens the tool's dialog; the selection tools open Move
The Rotate, Scale, Reflect and Shear tools answer Enter with their
dialog, but the shortcut handler only applied what a tool asks of the
UI for the keys it claims, so the request was dropped and Enter did
nothing. It now applies it for Enter, and consumes the key when a
dialog opened, since a dialog takes Enter as its OK in the same frame.

The Selection, Direct Selection and Group Selection tools open the
Move dialog the same way, on Enter and on a double-click on their
toolbar button (`tool.options`), as Object > Transform > Move does.
With nothing selected neither opens anything.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 06:24:24 +07:00
bflatasticandClaude Opus 5.5 a3fec5486d Fix #362: Spanish interface (es)
Adds a Spanish catalog (crates/ui-egui/src/i18n/es.tsv) covering the whole
interface like Japanese and Traditional Chinese: every menu string, every
tl! literal, the hint-bar fragments, plurals and the runtime strings the
other complete catalogs carry. It is written from the meaning of the
English labels in neutral, international Spanish (Latin American usage)
with the vocabulary users of vector illustration software know (Mesa de
trabajo, Muestras, Trazo, Relleno, Trazado, Pluma, Selección directa,
Buscatrazos, Motivo, Preajuste); no other application's localisation was
copied. Like Japanese it keeps the product, OpenType, the built-in
workspace names and the perspective grid preset names in English.

The language is registered with complete_menus (so the completeness
tests enforce it) and the one/other plural rule; every es-* system locale
(es-ES, es-MX, es-AR, es-419 ...) resolves to it.

Tests: es locale tags, Spanish vocabulary and plurals, Spanish letters in
the bundled UI fonts, and a new check that catalogs written with spaces
keep the leading/trailing spaces of joined fragments. ROADMAP, README and
docs/development.md list the language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:03:37 +07:00
theironchefandClaude Sonnet 5.5 746a271aeb Tab no longer leaves the keyboard focus on a hidden field
Tab is the Panels shortcut (show/hide all panels), but egui's own
focus navigation read the same Tab press at the start of the frame and
moved the keyboard focus on to a field. The shortcut handler returns
early while a field has the keyboard, so after one Tab a second Tab,
tool letters and Ctrl chords did nothing until a click or Esc, and in
the Type tool the letter typed after a Tab was lost. When no field has
the keyboard, the handler now cancels the focus move for that frame;
in dialogs, the command palette and panel fields, Tab still moves
between fields.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 05:35:54 +07:00
bflatasticandClaude Opus 5.5 256e938fff Number fields: arrows step plain fields too, keep the value selected; ROADMAP (review of #357)
The Up/Down stepping of #357 now serves every numeric field through one
helper: num_field (lengths, in their unit) and plain/mixed fields
(degrees, percent, counts, at their precision: a count ignores the
Ctrl/Cmd tenth), as in Illustrator's panels and dialogs. After a step
the new value is selected, so typing replaces it. The select-all code
the dialog focus and focus/double-click already duplicated becomes one
helper, and the tests move to tests_numfields.rs with its field harness
(which can now run with the window unfocused). ROADMAP.md notes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:35:54 +07:00
theironchefandClaude Opus 5.5 85dafc78d7 Fix #352: Up/Down arrows step the value of panel number fields
Panel number fields (num_field: X, Y, W, H, corner radius, stroke
weight, font size...) only parsed their text on Enter or focus loss,
so the arrow keys did nothing. With the field focused, Up/Down now
step the value by one of the field's unit (Shift: ten, Ctrl/Cmd: a
tenth), from the value shown, and apply it at once, as in the
reference app.

The field's focus is read from egui's memory, as its highlight and its
typing do: Response::has_focus also needs the window to report
keyboard focus, which the running app's window doesn't always do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 05:35:54 +07:00
bflatasticandClaude Opus 5.5 accfec7146 Fix #359: web drops land in the document they were dropped on
The web reads dropped files asynchronously, and the place inbox only kept
the drop point, so a file dropped on document A was placed into whatever
document was active when its bytes arrived. A drop on a canvas now carries
place::DropAt (the document's uid, the point, embed), and drop_files makes
that document active again before placing; when it has closed meanwhile,
nothing is placed and the status bar says so. The native drop path is
synchronous and goes through the same code, so it is unchanged in effect.

Test: a drop pending while another document opens lands in the first one
at the drop point; one whose document closes is not placed anywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:28:50 +07:00
bflatasticandClaude Opus 5.5 d218e60168 i18n: Simplified Chinese rows for the strings that landed alongside the catalog
Simplified Chinese is a complete catalog, so the strings added the same day need rows: Delete
Anchor Points, Edit Selection and the 25 Saved Selection slots, Paragraph Direction and the
Mojikumi Set. Remove Anchor Points reads 移除锚点, apart from Delete Anchor Points (删除锚点), as in
Traditional Chinese.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:28:50 +07:00
bflatasticandClaude Opus 5.5 27e8c695c1 i18n: Simplified Chinese reads as mainland Chinese, with rows for the strings added since (review of #303)
- Taiwan vocabulary left over from the zh-hant rows #303 started from is
  replaced with the mainland's, about 560 rows: 档案 → 文件, a Taiwan 文件
  (document) → 文档, 工作区域 → 画板, 笔画 → 描边, 色票 → 色板, 遮色片 → 蒙版,
  渐层 → 渐变, 偏好设定 → 首选项, 快速键 → 快捷键, 按一下 → 单击, 描述档 → 配置文件,
  品质 → 质量, 汇出/汇入 → 导出/导入, 连结 → 链接, 点阵 → 栅格, a row 列 → 行,
  「」 → “”, half-width parentheses; tool names that collided or were wrong
  are fixed (Selection Tool 选择工具, Scale Tool 比例缩放工具, Warp 变形,
  Puppet Warp 操控变形, Divide 分割, Exclude 差集, the 直排 type tools).
- 42 rows for strings that landed after #303 (Layers panel, graphics
  processor preference, panel collapse, the font menu's filters).
- A catalog header documenting the format and the vocabulary, and a test
  that zh-hans has no Traditional characters or Taiwan terms.

rows were the better base (hand-adjusted mainland terms in the menus and
dialogs), so no rows were taken from #316.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:28:49 +07:00
Pengjingyu1992 1e4db64362 i18n: Simplified Chinese (zh-hans) interface catalog
The zh-hans catalog, registered as 简体中文: zh-CN, zh-SG, zh-Hans-* and
a bare zh locale resolve to it (tested), and it claims complete menus.
NOTICE lists the craft-fonts faces of the current manifest (Noto Sans CJK
SC and Noto Sans Arabic were missing).

Landed from #303 (catalog, registration, locale tests and the NOTICE
line only). Its horizontal CJK composition, UI font fallback by weight,
dock, New Document, Brushes panel and preference-migration changes are
left for separate pull requests.
2026-10-08 05:28:49 +07:00
bflatasticandClaude Opus 5.5 0687d2d127 Type: Paragraph Direction instead of an Automatic alignment toggle; bidi only where needed (review of #300)
Illustrator sets a paragraph's direction (Left-to-Right / Right-to-Left
Paragraph Direction in the Paragraph panel, with the Middle Eastern
options) and keeps its alignment buttons physical; it has no automatic
alignment toggle. So:

- ParaStyle.direction (None: from each paragraph's first strong
  character, as before; written only when set), the Paragraph panel's two
  direction buttons with Show Indic Options (original icons), and
  text.setFormat {direction: "auto"|"leftToRight"|"rightToLeft"}. The
  layout uses it as the base direction.
- The Automatic alignment check box is gone. Justify::Auto stays as the
  start of the paragraph's direction, set on new type only (text.create,
  the Type tools, with the mojikumi default in one new_type_para()):
  TextObject::point is Left again, so importers (PDF, SVG, DXF, EMF) keep
  their own alignment. The alignment buttons show what Auto resolves to.
- Bidi only where there is something right to left: plain paragraphs skip
  the algorithm (no per-layout allocation), it runs once per paragraph
  (shaping takes the paragraph's levels instead of recomputing them; path
  type computed it four times), and the shaper keeps Han, Hiragana,
  Katakana and Bopomofo in one segment (splitting at every change of
  script cost Japanese text a shaper call per kana-kanji switch).
- Vertical type keeps its logical order, so its glyphs and lines are not
  marked right to left (the caret no longer runs backwards down a column).
- PDF import puts Hebrew and Arabic, which PDFs (and VectorCraft's own
  export) draw in visual order, back in logical order with the direction
  that shows it as drawn; with bidi layout it came back mirrored.
- SVG import pins the direction SVG implies (left to right unless
  direction: rtl); SVG export also outlines right-to-left paragraphs.
- Docs (development.md), ROADMAP (folded into the Type entry, Type core
  row) and README updated; catalog rows for the new labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:25:01 +07:00
Daniel Efrat edc7c931dc Type: Hebrew and Arabic text laid out right to left (bidi), aligned by its direction
- Unicode bidi (unicode-bidi, unicode-script, unicode-general-category):
  each paragraph's direction from its first strong character, Hebrew and
  Arabic shaped right to left, embedded Latin and numbers keep theirs,
  lines wrapped in logical order and reordered per line by whole
  clusters; source text, undo and style ranges stay logical.
- Caret, hit testing, selection and the arrow keys follow the visual
  order.
- New text aligns automatically: right-to-left paragraphs right, others
  left (Justify::Auto, text.setStyle {justify: "auto"}); the Paragraph
  panel highlights the resolved alignment and has an Automatic alignment
  toggle. Explicit alignments, and older documents', are kept.
- SVG export outlines bidirectional text (with a warning) until it can
  be written as directional live text.
2026-10-08 05:24:44 +07:00
bflatasticandClaude Opus 5.5 249b34fc7c Select: saved selections checked when a file opens, names capped, docs (review of #318)
A file's saved selections are untrusted: on load at most 25 are kept, names
are trimmed, capped at 255 characters and unique, and ids the document
doesn't have are dropped (after fix_next_id such an id would select whatever
object is drawn next). Save and Edit Selection cap names the same way. The
limit, the name cleanup and the default "Selection N" live on
SavedSelection, shared by the engine, the Save Selection dialog and the
Select menu's slot table. docs/mcp.md documents the select.save /
savedList / recall / editSaved commands; ROADMAP counts 13 stubbed menu items
and names what's still missing (Update Selection, the Layers panel menu).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:24:44 +07:00
bflatasticandClaude Opus 5.5 87d1acd589 MCP: panel names from the dock's own tables, object-read errors kept apart, docs (review of #328)
normalize_panel walks DockTab::ALL and ICON_PANELS instead of repeating the
dock tab ids. vectorcraft://object/{id} turns only a missing object into
-32602; no document or an unreachable app stay backend errors, as before.
docs/mcp.md documents document.node's summary flag and the panel names
open_panel takes; control-protocol.md the window.panel names. The ROADMAP
states the measured speed-up (a leaf read on 2,000 objects: ~20 ms to ~0.06 ms).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:24:44 +07:00
bflatasticandClaude Opus 5.5 cea2ee6a54 Type: font menu previews end safely, rows and samples cost less per frame (review of #339)
- A preview whose menu has gone (the panel isn't drawn: text deselected,
  panel closed) or is closing (Escape, a press outside the menu) is undone
  at the start of the frame, before the canvas sees the input: a canvas
  drag no longer runs inside the preview's interaction and gets rolled
  back with it. The menu also ends the preview in the frame it closes.
- The list fits the popup (it was taller than the popup, which then
  scrolled too), so the current family is centred in view on opening;
  ↑/↓ scroll only as far as needed.
- Type › Font (in-window menu bar): each item keeps room for its sample
  after the name, so long family names no longer run under it.
- Rows are listed again only when the query, filters, expanded families,
  favourites or font list change, not every frame.
- Samples: one budget per frame across every menu (Type › Font gave each
  item its own), at most 8 rendering at once, the worker asks for a
  repaint when it is done instead of polling, the cache holds 512, and
  one function draws a sample for both menus.
- Font menus use menu_family_list (the hidden-family rule lives there);
  FontClass::label is gone (the menu's translated labels are the only
  ones).
- Tests: a preview left by a menu that is gone is undone; a press
  outside the open menu ends the preview in that frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:24:44 +07:00
bflatasticandClaude Opus 5.5 8f7949c30b i18n: Japanese rows for the Layers panel and GPU preference, consistency fixes (review of #317)
- 28 rows for strings that landed on main after #317 was written (Layers
  panel menu and options, Release to Layers, the Graphics Processor
  preference), so the Japanese catalog stays complete.
- Fixes: Live Paint Bucket is ライブペイントツール (was the selection
  tool's name), the File Info rating stars read {n} つ星 (were "rays"),
  Flare Tool Options and Symbol Sprayer match their tool names, Rectangular
  Grid matches its tool (長方形グリッド), recovery data is 復帰用データ
  throughout, (今後対応予定) for every roadmap note.
- Consistent spelling: フォルダー, 切り替え, Web サイト, 英語 (米国), and
  a half-width colon after every field label, as in the other 200 rows.
- tests: KEEPS_MENU_NAMES gets its own doc comment instead of taking
  MENU_KEEP_AS_IS's.
- ROADMAP and docs/development.md: Japanese ships complete, Czech covers
  the menus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:24:44 +07:00
bflatasticandClaude Opus 5.5 079c46dafb Type: the Mojikumi Set menu shows with the East Asian options, under its feature name (review of #350)
The Paragraph panel's mojikumi control is one of the panel's options
(hidden with Hide Options) and appears when Preferences > Type > Show
East Asian Options is on, as Illustrator shows its Mojikumi and Kinsoku
sets; it is labelled Mojikumi Set. ROADMAP notes where it lives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:24:13 +07:00
Brandon ThomasandClaude Opus 5.5 5196337ca8 i18n pt-br: drop three Layers strings main no longer shows (#384 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:04:30 -04:00
Brandon Thomas 0cf2879d06 Merge pull request #384 from RicSchonfelder/i18n-pt-br
i18n: add Brazilian Portuguese (pt-br) UI translation

# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/i18n/tests.rs
2026-10-07 18:01:27 -04:00
Brandon ThomasandClaude Opus 5.5 4f159b98fb Merge pull request #376 from Haven-King/feat/artboard-snapping
Add Snapping to the Artboard Tool

Merged with the Artboard tool's Alt-drag duplicate (#348): a copy snaps too, and to the artboard
and art it leaves behind (a move skips them).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 17:50:15 -04:00