Commit Graph

269 Commits

Author SHA1 Message Date
bflatastic aa4fa48751 Merge #548: Part of #516: the in-window menus' Select All, Cut, Copy and Paste act on a focused field
# Conflicts:
#	ROADMAP.md
2026-10-09 00:58:46 +07:00
bflatastic 165db95dd4 Merge #533: Control bar: Transform link opens the Transform panel; reference point locator redrawn 2026-10-09 00:58:40 +07:00
bflatastic 8e753d746e Part of #516: the in-window menus' Select All, Cut, Copy and Paste act on a focused field
The clipboard keys already went to a focused text field, but on Windows,
Linux and the web the press that opens a menu takes the keyboard from the
field, so Edit > Copy (and Cut, Paste, Select > All) acted on the art.
The menu bar now keeps the field that had the keyboard, and its
selection, while the menus are open; those four commands go to it as the
events their keys send, the frame after the click that chose them, and
give it the keyboard back. Everything else, and those commands with no
field, run as before. The macOS menu bar shares the same helpers.

Tests: Copy, Cut and Paste events in the W field and the Color panel's
hex field edit the text, never the art; Select > All, Edit > Copy and
Edit > Paste clicked in the in-window menus act on the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:51:02 +07:00
bflatastic 971b0bb95e Merge #529: Fix #506: drawing tools snap to Smart Guides as the Selection tool does
# Conflicts:
#	ROADMAP.md
2026-10-09 00:44:46 +07:00
bflatastic 7248c6aeec Merge #538: Fix #511: Live Corners on any path's corners (stars, polygons, pen paths)
# Conflicts:
#	ROADMAP.md
2026-10-09 00:44:29 +07:00
bflatastic 7a270c8deb Merge #527: Type (#432): new type in a Japanese interface starts top-to-top with em box centre alignment
# Conflicts:
#	ROADMAP.md
2026-10-09 00:44:10 +07:00
bflatastic d9544d8f87 UI: the ruler unit menu in the ROADMAP; its test names the PR
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:43:50 +07:00
bflatastic ab65474044 macOS: the menus live in the system menu bar, settings in the VectorCraft menu
On macOS the menus are in the system menu bar, laid out as a Mac app's (Illustrator on the Mac as
the reference), and the app settings sit in the VectorCraft menu: About, Settings ▸ (one item per
Preferences page, General… ⌘K), Language ▸, Appearance ▸ (UI brightness), Services, Hide
VectorCraft ⌃⌘H (⌘H stays View › Hide Edges), Hide Others ⌥⌘H, Show All and Quit ⌘Q (the app's own
quit, so unsaved documents are asked about); they leave Edit and Help. Window gets Minimize ⌃⌘M,
Zoom and Bring All to Front; Help is the system's Help menu. Windows, Linux and the web keep the
in-window menu bar unchanged; --in-window-menus or VECTORCRAFT_IN_WINDOW_MENUS keeps it on macOS
too, and so does a menu bar AppKit refuses to build.

One menu model: the in-window menus and the native bar draw the same rows (menus::entry), and the
platform-free native_menu module lays them out, decides when to update (a cheap state hash; labels,
enabled and checked in place, a rebuild only when the structure changes) and hands key equivalents
back to egui as the key presses they were, so shortcuts, user overrides and focused text fields
behave as in the window and nothing fires twice. The muda backend (pinned =0.21.1, macOS only, no
unsafe) is apps/vectorcraft/src/mac_menu.rs, mirroring PhotoCraft's. ui.inspect reports
nativeMenuBar. Translations for the new labels in ja, es, zh-hans, zh-hant, cs and pt-br.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:45:31 +07:00
bflatastic b43b15a6e8 Part of #505: restore puts dictionaries back as they were at the save
The PostScript Language Reference (3rd ed., §3.7.3 "Save and Restore")
has `restore` reset composite objects in local VM to their values at the
`save`. The reader only restored the graphics state, so dictionary
entries changed between `save` and `restore` stayed changed: a program
that counts its open saves in a dictionary and refuses to go past a depth
stopped after that many saves in sequence, and the file fell back to its
preview. EPS files with many images, each drawn between `save` and
`restore`, are a likely case of #505; its reporter hasn't sent a file, so
this is part of it.

- While a `save` is open, `def`, `store`, `put`, `undef` and dictionary
  `copy` keep each entry's old value in a journal (counted against the
  memory budget); `restore` undoes it, newest change first, down to its
  save. A save restored already, itself or through an outer one, is no
  longer valid (`invalidrestore`, §3.7.3).
- `cachestatus` reports a device's cache sizes (chapter 8) instead of
  zeros: its last value, the most bytes one cached glyph may take, is what
  programs divide by.
- Tests (programs written from the reference): 100 saves counted in a
  dictionary under a depth limit, each dictionary change undone, saves
  restored twice, `cachestatus`'s glyph limit. Mutation-checked: without
  the journal or with a zero `cachestatus` the test fails. The import fuzz
  vocabulary has saves and restores around dictionary changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:43:51 +07:00
bflatastic 1d265951df Fix #498: EPS images with several data sources read them in turn
An image can take one data source per colour component
(MultipleDataSources, or `colorimage` with multi true). The PostScript
Language Reference (3rd ed., §4.10 "Images" and `image`/`colorimage` in
chapter 8) has the operator read the sources in turn, the same amount
from each. The reader drained each source on its own instead: when the
sources are procedures reading one file a row at a time, the first plane
took the rows of every component and the image came out in sheared
horizontal bands, as in #498.

- Data sources are read in turn by one reader, `read_sources`: a procedure
  once a round, a file or string a row a round, until each plane is full or
  its source ends. Single sources, procedure filters and masked images use
  it too; a string source resumes where it left off.
- Tests (`tests_images.rs`, programs written from the reference):
  procedures reading one file in the dictionary and `colorimage` operand
  forms (fails without the fix), separate string sources, rows padded to a
  byte at 1, 2, 4, 12 and 16 bits with an odd width (one and several
  sources), Flate PNG predictors with every row filter at an odd width.
  The import fuzz vocabulary has several sources reading one file and
  sources of uneven lengths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:43:49 +07:00
bflatastic eb255ef379 EPS tests: interpreter programs written from the PostScript Language Reference
Two EPS import tests become programs written from the PostScript Language
Reference (3rd ed.), with their own names and structure, covering the
interpreter features the importer relies on:
- `level_2_and_3_probes_read_as_vectors`: a resource category made from
  Generic, resources defined and found in it (§3.9), local and global VM
  (`currentglobal`, `setglobal`, `gcheck`, §3.7.2), `internaldict` probed
  through `stopped`, a halftone dictionary set and restored, `cachestatus`,
  `rootfont` and `currentcolorrendering`, drawing with its own procedures.
- `executable_strings_and_integer_keys`: shading dictionaries closed by an
  executable string (`(>>) cvx`, or a Level 1 builder with `counttomark`),
  procedures kept under integer keys run with `load` through `stopped`,
  `cvx`/`cvlit`/`xcheck` on strings.
Each was mutation-checked: making any of findresource, defineresource,
currentglobal, setglobal, gcheck, internaldict, sethalftone,
currenthalftone, currentcolorrendering, cachestatus, rootfont, stopped,
cvx, load, counttomark, cvlit or xcheck unknown fails a test, and so do
strings that `cvx` leaves literal, `load` refusing integer keys and
`gcheck` answering true.

The ROADMAP's EPS paragraph, file-formats row and last-updated line state
the PostScript features the reader supports; the legacy `.ai` comments in
the import module and the interpreter cite that format's published
specification (PRONOM fmt/423) for the `%AI` header comments and `u`/`U`
group operators the reader keys on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:43:48 +07:00
bflatastic 4eac7b505f Fix #506: drawing tools snap to Smart Guides as the Selection tool does
Every drawing tool now asks one snapper (guides::DrawSnap) for the points it
places: the Pen's and Curvature tool's anchors, the Pencil's ends, the shape
tools' start and dragged corner or radius (Rectangle, Rounded Rectangle,
Ellipse, Polygon, Star, Line Segment, Arc, Spiral, the grids), the Type tool's
click and frame and a new artboard's corners. Points land on other objects'
anchors, centres and paths ("anchor", "center", "path") or line up with their
anchors, edges and centres ("align"); hovering shows it before the press, the
Pen's guides end its rubber band, and the Pen lands on the path being drawn
too. Shift keeps a point to its 45-degree step (from Constrain Angle) or a
box's diagonal and slides it along that way into line with a target.
Preferences > Smart Guides > Construction Guides and Angles now act: a drawn
point near a guide through the point it leaves from lands on it.

Before, the Pen snapped only its press and showed nothing, and the shape tools
re-gathered targets on every drag step from the document showing their own
preview, so the dragged corner stuck to where the last step left it.

Performance: targets are gathered once per document state (keyed on the
document's uid and revision, now in ToolContext), from the art in the window,
and sorted, so the anchors, centres and lines near the pointer are found by
bisection. Leaving objects out of the targets no longer looks up each node's
ancestry (quadratic in the document's size): one pruned walk does it, which
also speeds up the Selection tool's moves and resizes.

The direction handles' HandleSnap (#494) is now a DrawSnap holding its own
targets (Targets::for_handle) for the drag: one cache, one lookup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:20:27 +07:00
bflatastic 1d14f337db Fix #511: Live Corners on any path's corners (stars, polygons, pen paths)
Corner widgets only ever showed on live rectangles: CornerWidgets::of bailed
out unless the selection's live shape was LiveShape::Rectangle, and
object.setLiveShape only edited rectangle radii. A star is a plain path, so it
had no widgets with either selection tool.

- geom::corners: the corners of any path (anchors without handles between two
  straight sides) and the path with them cut round, inverted round or
  chamfered, each corner a circle tangent to both sides, its radius no larger
  than takes the cut halfway along its shorter side. Rectangles now cut their
  corners with it (bit-identical anchors and layout).
- doc::corners::LiveCorners/set_corners: one model for a live rectangle's,
  a live polygon's (new radii/kinds) and any other path's corners (new
  LiveShape::Path keeping the uncut outline, plain again once nothing is cut);
  radii scale with transforms (Scale Corners) and stay circular.
- object.setLiveShape takes anchor indices for `corners` on any path; polygons
  keep a shared radius as their sides change.
- Widgets: Direct Selection on any path, Selection on live rectangles and
  polygons; placed on each corner's bisector, the drag follows it, the red
  limit outline marks corners at their own limit; the angle preference and
  View > Hide Corner Widget apply. Corners dialog and the Properties and
  Transform panels' Corner Radius (now on polygons too) work on them.
- Tests for the star, the polygon and a pen path in geom, doc, tools, engine
  and ui-egui; docs and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:05:41 +07:00
bflatastic 7c5799aec6 i18n: Italian rows for strings added since the PR's base, consistency and fit fixes (review of #515)
- Italian rows for the strings main gained after the PR's base: the
  floating panels of #513 (Float Panel, Dock Panel, the drag hints, two
  window.float messages) and the GPU fallback status message of #519, so
  the completeness tests pass on current main.
- File > Document Setup... reads "Imposta documento..." like the dialog
  title and the Properties button, instead of "Impostazione documento...".
- The Properties panel's quick action "Arrange: Bring to Front" reads
  "Porta in primo piano": "Disponi: Porta in primo piano" was clipped at
  both ends of its fixed-width button.
- OpenType figure style "Proportional Oldstyle" reads "Proporzionali stile
  antico", matching "Tabulari stile antico".
- Blank line between the Italian test and the next item; ROADMAP's
  "Last updated" lists the Italian interface (#514).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:04:36 +07:00
callmeBalloch 7c7ae55625 i18n: Italian interface (it)
Adds an Italian catalog (crates/ui-egui/src/i18n/it.tsv, 3,549 rows in
es.tsv's order) covering every menu string, every tl! literal, the
plurals and every status/error message, at the same scope as Spanish.
Standard Italian in sentence case, written clean-room from the meaning
of the English labels; the product, workspace, perspective preset and
language names stay in English as in es and ja.

Registered as "Italiano" with the one/other plural rule and
complete_menus; every it-* locale resolves to it. Tests: it joins
KEEPS_MENU_NAMES and COMPLETE_MESSAGES, italian_reads_as_italian checks
vocabulary and plurals, tags_map_to_languages the locale tags, and the
bundled-font glyph test covers Italian accents and guillemets. README,
ROADMAP and docs/development.md list the language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iayep93edeimiAE8oSfaT
2026-10-08 23:04:31 +07:00
David Panusch b5759662c1 Control bar: Transform link opens the Transform panel; reference point locator redrawn
The Control bar's underlined Transform link opens the Transform panel as a
popover under it (reference point, X/Y/W/H, Rotate and Shear with their icons
and angle presets, flips, shape properties, Scale Corners, Scale Strokes &
Effects), as the Stroke link does the Stroke panel. X/Y/W/H stay inline while
the bar has room.

The reference point locator is now nine squares with the outer eight joined
around the box; the old outline was inset off the squares' centres.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:38:18 +02:00
bflatastic e63606826a Fix #508: PDF/.ai import keeps type strokes, wrapped lines and layer order
- Type left unfinished when a layer's marked content ends, and a clip that closes in a later layer,
  stay in their own layer (no stray "Page N" layer, no art moved to the next layer).
- Top-level layers stack as the pages mark them, so a layer whose art starts on a later page keeps its
  place; layers marked with no art come in empty.
- A stroke written as each glyph's outline stroked, right after (or before) the type, becomes the type
  object's stroke above (or below) its characters: the type stays one editable object.
- Spaces read from gaps wider than a space get their own tracking where even tracking would put the
  glyphs after them more than a tenth of an em off (barcode digit groups); lines are measured to their
  last visible glyph, so a wrapped line's trailing space no longer spreads into the tracking.
- Lines wrapped in a frame (each wrapped line ending in a space, one left edge, font, size and leading,
  with an optional paragraph spacing) come back as one area type object, left aligned or justified,
  only where laying it out breaks the lines exactly where the file does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:37:37 +07:00
bflatastic 71371b9d58 Linux clipboard probe: look only while Paste depends on it, stop at once (review of #510)
- The worker reads the clipboard only while nothing is copied in the app
  and a document is open (as the in-line check did before), and idles on
  a channel otherwise; it sends a verdict and wakes the UI only when the
  answer changes.
- The in-line check (Windows, macOS, web) keeps its short-circuit: it
  doesn't open the system clipboard after an in-app copy or without a
  document, as before the PR.
- A dead worker hands the check back to the UI thread instead of
  freezing the last verdict.
- Dropping the app wakes the worker and waits (250 ms at most) until it
  has let go of its clipboard handle: on X11 the last arboard handle
  alive hands what the app copied to the clipboard manager, and a
  detached worker holding it would lose that handover at exit.
- The factory stays in `Services` until the first frame takes it (no
  extra app fields); main.rs installs it with `cfg!(target_os = "linux")`.
- Tests trimmed from five to three that share tests_sysclip's helpers:
  the frame never waits on a slow clipboard and only the probe thread
  reads it (nothing read without a document), the probe rests after an
  in-app copy and lets go of its handle when the app drops, and a dead
  probe falls back to the in-line check. The in-line case is already
  covered by tests_clipboard.
- ROADMAP (Last updated, Platforms, Shipped so far) and
  docs/development.md (Linux: clipboard owners that never answer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:36:44 +07:00
imachus fba610284c Type (#432): new type in a Japanese interface starts top-to-top with em box centre alignment
As decided in #432 (option 2): while the interface is in Japanese, new type starts with em box
top-to-top leading and em box centre character alignment; other languages keep the Roman
baseline, so Latin area type doesn't move.

- The UI tells the engine the language it is drawn in each frame (`Session::ui_language`); without
  a UI, an explicit `interfaceLanguage` preference counts (`Session::japanese_interface`).
- `text.create` and `text.createInPath` (the Type tools' clicks and drags) apply it, and take
  `leadingModel` / `charAlign` to choose; the values used are journaled, so a replay does the same
  in any language.
- `paraStyle.new` / `charStyle.new` made from nothing (no `attrs`, no text selected) carry them.
- Imported text (PDF, SVG, DXF…) keeps the Roman baseline. Saved documents keep what they say.
- docs/mcp.md (New type in a Japanese interface) and the ROADMAP note it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:08:03 +09:00
bflatastic ac63002df9 Fix #504: Convert corner/smooth for a path selected as a whole with the anchor tools
Reported: Alt-click with the Pen should toggle an anchor between corner and smooth, and the
Control bar had no way to make a corner anchor smooth.

v0.5.0 already has Pen + Alt as the Anchor Point tool (#473) and the Control bar's and the
Properties panel's Convert buttons for direct-selected anchors, the path being drawn included (its
last anchor is selected). The remaining gap: with Direct Selection, the Pen, the Curvature or the
anchor tools, a path selected as a whole shows all its anchors selected, yet the Convert buttons
only appeared once anchors were direct-selected.

- chrome::anchor_controls decides what the Control bar and the Properties panel show: Convert and
  Anchors for direct-selected anchors (as before), Convert alone for paths selected as a whole
  with a tool that edits anchors (path.convertAnchors already converts every anchor of those);
  Remove and Cut still wait for anchors direct-selected, as they act on those only.
- Tests: the Control bar and the Properties panel offer Convert for a whole rectangle with Direct
  Selection and the Pen, not with the Selection tool, and smooth converts every corner; Pen + Alt
  on the path being drawn makes a smooth anchor a corner (click) and a corner smooth (drag), and
  drawing goes on. ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:03:17 +07:00
bflatastic f6cd4588ee Fix #501: a Pen drag out of the first anchor always closes the path
Reported: closing a shape by pressing on the first anchor and dragging out a curve flickered, and
releasing at the wrong moment left the shape open.

The press decided to close (path.close preview), but every move asked again which path was being
drawn: the closing preview leaves the path closed, so no longer one the Pen extends, and the move
fell through to "first anchor of a new path" (a path.create preview), the next move back to a
close of a path that no longer existed in the preview... The release committed whichever was last.

- The press now records what it placed (Place: the anchor, the path it goes on, none for a new
  path, and whether it closes it), and the drag only shapes that anchor's handles: a close stays a
  close, symmetric by default and Alt breaking the handles, and the release closes the path wherever
  it ends. The first anchor of a new path no longer depends on the selection either.
- Tests: tools (the drag sees the closed path), engine (closed throughout a 12-move drag, symmetric
  handles at the release), MCP headless pointer_gesture. ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:00:24 +07:00
bflatastic aefd961bbd Fix #494: Pen + Cmd edits the path being drawn; Pen + Alt reshapes segments; handle drags fixed and fast
Reported: with the Pen there was no way to edit curves in place as Ctrl does in Illustrator on
Windows (Cmd on the Mac), segments couldn't be grabbed, and the curve-editing tools felt slow and
sometimes didn't take a handle.

- Cmd/Ctrl held at a press with any tool but a selection tool now borrows the selection tool in the
  engine (Session::pointer), not in the canvas: the selection tool chosen last, or Direct Selection
  for the Pen, Curvature and anchor tools until one is chosen. The tool lent to isn't deactivated,
  so the Pen goes on drawing its path after the release while that path stays selected; it no
  longer draws on another open path selected meanwhile. Agents reach it with pointer_gesture
  mods.cmd. With Cmd held the pointer is the borrowed tool's. Choosing a tool or switching
  documents mid-gesture gives the tool back first.
- Pen + Alt over a segment of a selected path reshapes it, as the Anchor Point tool does.
- The handles shown are the handles that drag (one helper for Direct Selection, the Anchor Point
  tool and Pen + Alt): Direct Selection now drags the handles of a path selected as a whole (as
  the Pen leaves it, so Cmd + drag of a handle while drawing works), the Anchor Point tool no
  longer grabs handles that Show handles when multiple anchors are selected hides, the nearest
  handle wins, and a handle shorter than the tolerance leaves its anchor to be picked. The anchor
  tools pick within Selection & Anchor Display > Tolerance instead of a fixed 4 px.
- path.reshapeSegment keeps a smooth anchor at either end smooth (its other handle turns with the
  moved one) instead of kinking it.
- A dragged handle gathered its smart guide targets from the whole document on every pointer move
  (21 ms per move on 20,000 paths): HandleSnap now gathers them once per drag (1 ms).
  vectorcraft-cli perf measures handle and segment drags on a 5,000-anchor path.
- catalog::is_selection_tool and catalog::edits_anchors replace the UI's own lists.
- Tests in tools, engine and MCP (headless pointer_gesture); docs/mcp.md, control-protocol.md,
  ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:00:13 +07:00
bflatastic d12d5195da Fix #502: no startup crash on dual-GPU Linux desktops under Wayland
0.5.0 asked wgpu for the low-power adapter everywhere (#306). On a Linux
desktop whose KDE Wayland compositor runs on an NVIDIA GPU, wgpu sorted the
Ryzen's integrated RADV GPU first: it reports that it can present to the
window, but KWin can't import the buffers it allocates ("importing the
supplied dmabufs failed"), which is a fatal Wayland protocol error. The next
surface configuration then got ERROR_SURFACE_LOST_KHR and egui-wgpu panicked
("The surface isn't supported by this adapter", winit.rs:129).

- Preferences > Performance > Graphics Processor gains Automatic, the new
  default: power saving on Windows and macOS as #306 wanted, and on Linux the
  system's order, where Mesa's device-select layer puts the GPU the desktop
  runs on first. Values are now automatic / lowPower / highPerformance; 0.5.0
  saved powerSaving for everyone, which reads as automatic.
- The app picks the window's adapter itself (apps/vectorcraft/src/gpu.rs,
  eframe's native_adapter_selector): only adapters that present to the
  surface, WGPU_ADAPTER_NAME first, hardware before software, native backends
  before OpenGL, then the power preference. Every adapter and the pick are
  logged.
- run_native runs under the engine guard. When the graphics fail during the
  first frames (a wgpu error, or a panic in wgpu, egui-wgpu or naga), the app
  starts again without that adapter (exec on Unix, so an AppImage stays
  mounted; a new process on Windows), passing the failed adapters in
  VECTORCRAFT_GPU_SKIP, and says so in the status bar. When none is left it
  logs why and exits with an error, never a panic.
- docs/development.md, README and ROADMAP: the new default, the restart and
  the escape hatches (WGPU_POWER_PREF, WGPU_ADAPTER_NAME, WGPU_BACKEND,
  MESA_VK_DEVICE_SELECT, DRI_PRIME).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:59:55 +07:00
bflatastic ea1993c848 Fix #495: panels, panel groups and the Tools panel undock into floating panels
Nothing could be undocked: the dock's tabs only switched panels, the icon
column only popped its panels out next to it, and only tool flyouts
(#437) could float.

- A dock tab, a panel icon or a popped-out panel's title dragged out of
  the dock floats that panel inside the app window; the strip right of
  the dock's tabs floats the whole Properties | Layers | Libraries group.
- A floating group moves by its title bar (or the strip right of its
  tabs); a tab dragged out of it floats on its own; dropped on another
  group's title bar or tabs it stacks with that group; dropped on the
  dock, which lights up under the pointer, its panels go back where they
  live in the dock (tabbed group or icon column). Its x does the same.
- The Tools panel floats by its title bar or grip and docks again on the
  window's left edge, which lights up the same way.
- window.panel.float {panel, x?, y?, onto?, group?} and window.panel.dock
  {panel, group?} (panel "tools" for the Tools panel) for the control
  channel and MCP; every panel menu ends with Float Panel / Dock Panel;
  window.panel on a floating panel shows its tab.
- Floating groups and the Tools panel's position are kept with the
  preferences and in user workspaces (built-ins dock everything),
  sanitized on load, and clamped into a smaller window when drawn.
- One move state in egui temp memory drives every drag, so a tab torn off
  keeps following the pointer; per frame the work is a lookup per panel.
- Icon buttons can be drawn for a caller's response (paint_icon_button),
  so the column's icons keep their ids when one above them floats.
- Translations (ja, es, zh-hans, zh-hant; cs and pt-br for the commands),
  es @msg rows, docs/control-protocol.md and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:14:31 +07:00
bflatastic d0a99faf1e macOS DMG: generate DS_Store and TIFF from a script, attribute the assets (review of #493)
- packaging/macos/dmg/generate.py writes background.tiff and DS_Store on any OS (resvg with
  the repo's fonts; ds_store and mac_alias, as dmgbuild uses). Both are byte-reproducible and
  carry nothing from the machine that made them.
- DS_Store: same window, icon size, positions and background alias as before, without the
  bookmark (pBBk/pBB0) and the alias's disk-image record, which held the author's boot volume
  UUID and name, its creation date and temporary paths.
- background.tiff: the same pixels as before (checked), now opaque RGB with Deflate (1.8 MB to
  1.0 MB) and LittleCMS's built-in sRGB profile instead of an HP profile with no open licence;
  no file names or software tags.
- background.svg: drop an unused pattern and the xlink namespace (renders the same).
- ASSETS.md rows for the three files; README, docs/releasing.md (volume name) and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:36:43 +07:00
bflatastic d11daabdc7 Contextual Task Bar: drag it by its handle, pin and reset its position
The grip at the bar's left end now drags the bar anywhere on the canvas
(grab cursor), which always holds it whole, also when the window shrinks.
Unpinned, the bar keeps that offset as it follows the selection (in the
document it was moved in); the "…" button opens a More Options menu with
Hide Bar, Pin Bar Position (checked while pinned: the bar stays where it
is across selection changes and the handle still moves it; unpinned it
follows the selection again from there), Reset Bar Position (unpinned,
back under the selection) and Show Properties Panel, as in Illustrator.
As there, the position and the pin last for the session only.

- UI commands window.taskBar.pin {pinned?} and window.taskBar.reset, so
  the control channel and MCP reach them; ui.inspect reports taskBar
  {pinned, rect}.
- One opt_bool helper reads the {key?: bool} params of the toggling window
  commands (toolbarColumns, floatTools, collapseDock, taskBar.pin).
- New strings in ja, es, zh-hans, zh-hant, pt-br and cs; the dropped
  "Hide Contextual Task Bar" tooltip and per-key es messages removed.
- Headless egui test: dragging the grip moves the bar, the offset follows
  the selection, pin holds it across selection changes and a shrinking
  window, unpin follows from there, reset restores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:23:15 +07:00
wstock 948f696f52 Part of #394: Auto-Collapse Iconic Panels
Preferences › User Interface › Auto-Collapse Iconic Panels was stored
and never read: a panel popped out of the icon column stayed until its
» or its icon. On, a press away from the flyout now puts it away. A
press on the icon column is left to the icons (they swap or close the
flyout on release, so acting on the press would reopen the one being
closed), and a press in a popup the flyout opened (a panel menu, a
dropdown) keeps it. Off by default, as in Illustrator.

Developed with AI assistance (Claude Code); reviewed and tested locally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 20:22:14 +07:00
bflatastic 077e4f34ec Numbers Without Units Are Points: picas fields, on by default (review of #486)
The option tells points from picas, where a bare `12` could be either: the
reference app dims it unless a unit is Picas, and it starts on. A number typed
with no unit into a field in picas is now read in points with it on (the
default again, as before #486), and in picas with it off; fields in any other
unit read it in their unit either way, as there. The Preferences dialog dims
the checkbox unless one of the Units is Picas.

The fields learn the preference when it changes, through
`prefs_dialog::apply_runtime` as Scrub Numeric Fields does, rather than from a
write into the egui context every frame. Tests for picas and other fields, the
wiring and the dimming; docs/mcp.md and ROADMAP say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:22:11 +07:00
wstock b1aac0cc1b Part of #394: Numbers Without Units Are Points
Preferences › Units › Numbers Without Units Are Points was stored and
never read. On, a number typed with no unit into any length field
(`num_field`, `opt_field`, and so the spin fields built on them) is read
in points whatever the field's unit; a typed unit (`12 mm`, `1in`)
still wins, and the field keeps showing its value in its own unit. The
app sets the frame's rule from the preference in `logic`, the fields
read it through the egui context (`widgets::typed_unit`), so none of
the 45 callers change.

The default flips from true to false, which is Illustrator's default
and the behaviour the fields had while the option was unread.

Developed with AI assistance (Claude Code); reviewed and tested locally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 20:22:10 +07:00
wstock f1935596ef Part of #394: Show The Home Screen When No Documents Are Open
Preferences › General › Show The Home Screen When No Documents Are Open
was stored and never read: the Home screen always replaced the canvas
while no document was open. Off, the window is now empty while no
document is open (as when Illustrator's option is off), and the Home
button (`app.home`) still shows the screen; closing the last document
returns to the empty window. `menus::home_showing` is the one place
that decides, for the canvas and the Home button's lit state.

Developed with AI assistance (Claude Code); reviewed and tested locally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 20:21:42 +07:00
bflatastic 517b166722 Fix #485: the mouse wheel steps the focused numeric field
The wheel did nothing over numeric fields. As in Illustrator, once a numeric
field has the focus, turning the wheel over it steps its value like Up/Down:
a notch a step, Shift ten, Ctrl/Cmd a tenth, applied at once with the new value
selected.

- widgets::step_value (was step_with_arrows) reads the arrows and the wheel
  through one path for num_field, plain/mixed fields and the spin fields built
  on them (panels, the Control bar, dialogs, the Font Size and Leading combo
  boxes), and now opt_field (Stroke dash/gap) too. Trackpad and fine-wheel
  points add up to notches; the rest carries to the next turn.
- While the pointer is over the focused field the panel under it doesn't
  scroll; an unfocused field, or the pointer anywhere else, leaves the wheel to
  the panel and the canvas as before.
- The dialogs' and preferences' egui drag values become the same recessed
  number fields (widgets::range_field: clamped to their range, rounded to their
  precision), so they type, step and scroll like every other numeric field.
- Headless tests for the wheel (units, modifiers, points, focus and pointer),
  and for the panel scrolling or not; docs/control-protocol.md and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:21:39 +07:00
bflatastic 377f236687 Fix #483: Shift-drag marquees toggle the selection; the Lasso's Alt keeps the rest
A Shift-drag marquee only ever added to the selection, so there was no way to
take some of the selected objects out with it. As in Illustrator, Shift-drag
now toggles what the marquee reaches: the selected ones are deselected, the
others selected, the rest of the selection stays.

- Selection tool: the Shift marquee runs select.toggle, which now also takes
  {ids} (each one toggles).
- Direct Selection and Group Selection: the Shift marquee toggles the anchors
  inside it (select.anchorsMany mode "toggle"); a path left with none leaves
  the selection, one with all of them is selected whole again.
- select.anchorsMany takes mode "set"|"add"|"toggle"|"subtract" (add: true is
  still mode add). Adding anchors of a path selected whole keeps it whole, and
  anchor references outside the path are ignored.
- Lasso: Shift adds and Alt subtracts through the same command, so an Alt lasso
  no longer drops selected type, groups and other non-path objects.
- Tests at the tool, engine (Session::pointer) and MCP (pointer_gesture) levels,
  command sweep cases for the new modes; docs/mcp.md and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:21:36 +07:00
bflatastic f7c88af1de Fix #482: Smart Guides land resize handles on anchors and snap the transform tools' drags
Bounding-box resizes with the Selection and Free Transform tools already lined
their edges up with the other objects (#355, after v0.4.0), but a handle never
landed on another object's anchor point, nothing was labelled, and the Rotate,
Scale, Shear and Reflect tools didn't snap their drags at all.

- Targets::snap_scale: the dragged corner (free, or along its diagonal with
  Shift) or side lands on the nearest anchor or centre within the snapping
  tolerance, labelled "anchor"/"center"; alignment snaps keep their line and
  now say "align", as an artboard's resize handle does. Alt (from the centre)
  and rotated boxes work the same way.
- The transform tools start a drag from the anchor or centre they grabbed (the
  point a click would set) and snap the pointer as drawing tools do (Smart
  Guides, the grid, pixels or Snap to Point), so a corner dragged onto another
  object's anchor scales exactly onto it.
- Factor the nearest anchor/centre lookup (Targets::nearest_point) and the
  transform tools' drag teardown (end_drag).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:21:31 +07:00
bflatastic 744edddaea Part of #491: pen taps click where they tap; Linux note for tablet pens on Wayland
Pens on KDE Plasma 6.3+ under Wayland don't reach the app: winit 0.30 binds no
Wayland tablet protocol (zwp_tablet_v2), and since Plasma 6.3 KWin no longer
emulates pointer input for such clients (kwin!6336), so the pen moves KWin's own
cursor while the window gets no motion or presses. The fix is upstream (winit
0.31's tablet input, winit#4318, once eframe moves to it). docs/development.md
and the README give the workarounds: run under XWayland (WAYLAND_DISPLAY=), or
set KWIN_WAYLAND_EMULATE_TABLET=1 for the session.

On our side, a pen or finger (winit Touch, emulated as a mouse by egui-winit)
lifts with an egui PointerGone in the same frame. A tap whose press and lift
arrived in one frame had no hover position, so the canvas ignored it, and a
drag ending without a pointer position ended at the canvas's centre. The press
and double-tap now fall back to the interact position, and the release to the
last position seen during the drag. Headless frame tests replay the events a
pen produces: a stroke with pressure reaching Use Pressure Pen, and a one-frame
tap that selects without moving (fails without the fix).

ROADMAP: Last updated, the Platforms row, the hardening gap and the graphics
processor/input bullet (pen pressure reaches the tools on Windows only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:14:18 +07:00
bflatastic 7578787392 Canvas grid: turn with a rotated view, no per-frame vectors (review of #478)
The pixel grid draws through the document grid's `grid()`, which assumed an
unrotated view: with Rotate View its lines stayed upright on screen, in the
wrong places, or vanished. It now spans the box round the canvas's corners in
document space and draws each line through the view, so the pixel grid and
the document grid (lines and dots) turn with the art. The lines are iterated
rather than collected, capped, and computed by multiplication instead of
accumulating a step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:00:03 +07:00
wstock aa8e77d09a Part of #394: Show Pixel Grid (Above 600% Zoom)
Preferences › Guides & Grid › Show Pixel Grid (Above 600% Zoom) was stored and
never read. On (the default), in View › Pixel Preview at 600% zoom and above the
canvas draws a translucent line at every document pixel over the art, so the
pixels the art rasterizes to can be seen; nothing below 600%, out of Pixel
Preview, or with the option off. Drawn by the same `grid()` as the document
grid, at a 1 pt spacing.

A headless frame test counts the pixel grid's lines at 800%, 500% and 600%, in
and out of Pixel Preview and with the option off, and checks they are drawn
over the art.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 19:00:03 +07:00
wstock 349e1eac3c Part of #394: Anti-aliased Artwork
Preferences › General › Anti-aliased Artwork was stored and never read. Off, the
canvas renders the art with `AntiAlias::None` — a pixel is painted when the art
covers at least half of it, raster effects and pattern tiles staying smooth — and
on (the default) with `AntiAlias::Art`, as before. The preference is part of the
canvas's cache key, so changing it re-renders. Exports keep their own
Anti-aliasing option.

A headless frame test reads the art's raster back out of the frame's texture
upload: edge pixels partly covered with the option on, none off, and smooth
again once it is back on.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 18:59:51 +07:00
bflatastic afb438367d Type Object Selection by Path Only: every baseline, Type tool and Eyedropper (review of #475)
- Type is picked on the baseline of any of its lines (point type and area
  type, besides area type's frame), as in Illustrator, not only the first
  one: the layout's baselines are cached with its bounds
  (`TextObject::cached_baselines`, from `TextLayout::baselines`), with the
  first one across the bounds as the fallback. Vertical type's runs down
  each column's centre line.
- Type in perspective is picked where it is drawn (through its projection).
- The bounds rule type out before its path is built, so hovering stays
  free of allocations away from type.
- The Type tools still edit type clicked among its characters (they made
  new type there with the preference on), and the Eyedropper still samples
  it there, as it does inside fills with Object Selection by Path Only.
- Tests for all of it; docs/mcp.md and ROADMAP say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:59:47 +07:00
wstock beea693ee1 Part of #394: Type Object Selection by Path Only
Preferences › Type › Type Object Selection by Path Only was stored and never read.
On, the Selection tools pick type on its type path only — point type's baseline
(the line the selection marks under it, across its layout's width), area type's
frame, type on a path's path — within the selection tolerance; a click among the
glyphs or inside the frame selects nothing. Off (the default), anywhere in the
type's bounds selects it, as before.

`HitOptions` gains `type_path_only`, set by the tools' context (`hit_options()`)
and the canvas's `hit_at` from the preference, so the Selection tools, Cmd-click
behind, object highlighting and the context menu agree. Tests at the hit-test
level (point, area and on-path type) and through the Selection tool with
`prefs.set`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 18:59:47 +07:00
bflatastic 6dc6b8600b Fix #487: Keyboard Shortcuts records chords with modifiers
egui reports a modifier's own key press (ShiftLeft, ControlLeft…) as a key event, and the
recorder took the first key press it saw, so pressing Shift+A saved `ShiftLeft` as the chord: a
shortcut that can never fire. The recorder now skips modifier key presses, so the chord completes
on the first other key with the modifiers held (Esc still cancels). A modifier is never a chord's
key in the shortcut parser either, so `shortcuts.set` refuses one, and overrides saved that way by
older versions are dropped on start, giving the default back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:55:02 +07:00
bflatastic 4827780f03 Fix #474: Illustrator 8 EPS gradients open (executable strings, `load` by integer keys)
Stock EPS files saved in the Illustrator 8 format failed with "typecheck in `a
dictionary`": its gradient procset builds shading dictionaries with `bd` …
`ed`, where `ed` is `(>>) cvx` (or a Level 1 dictionary builder as a
string), and `cvx` left a string literal, so `shfill` got a string. Its
`discard` procedure also `load`s procedures by integer keys, which `load`
refused.

- Executable strings: `cvx` makes a string executable (`xcheck` true,
  `cvlit` back, still a `stringtype` for `length`/`get`/`forall`/
  `getinterval`); run by name or met in a procedure, it runs its text as a
  program, every time (an executable file shares the same runner).
- `load` takes any key (integers, booleans), as `where` already did.
- Test: an Illustrator 8-style gradient procset with both `ed` forms and
  `discard`; the import fuzz vocabulary has executable strings and integer
  keys. An Illustrator 2026 file saved in the Illustrator 8 format (made
  here, not committed) now opens as vectors with its radial gradient.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:24:05 +07:00
bflatastic 9f3b992ecc EPS import: other apps' EPS open as vectors, a preview that stands in says why
Discord feedback: "EPS files are always rasterized: they open as bitmaps, in
other programs as vectors". Every EPS saved by Illustrator 2026 fell back to
its TIFF preview (`gcheck` was unknown), and cairo/Inkscape files stopped at
their first image (`status`), dropped masked images (ImageType 3) and failed
on mesh gradients; the warning didn't say where the program failed.

Fallback never silent:
- The warning names the PostScript error, the operator that raised it and
  the named procedures it ran in, innermost first ("a PostScript error
  (typecheck in `add`), in `inner` in `outer`"); errors caught by `stopped`
  aren't reported. It reaches the status bar, document.open, app.open and
  file.open (which now return document.open's result, so MCP's open_file over
  the control channel gets it), and `vectorcraft-cli info` ("warnings").
- A file whose PostScript can't be read opens as its TIFF preview, else its
  Windows metafile preview (as vectors, scaled to the bounding box), else its
  EPSI bitmap. es @msg rows for the new messages.

Interpreter:
- Operators programs probe or use: gcheck/scheck, rootfont,
  currentcolorrendering, currenthsbcolor, status, token, read,
  bytesavailable, fileposition/setfileposition, resetfile, write*, pdfmark
  (marks dropped, as Ghostscript does), execform, nulldevice, strokepath,
  pathforall, user paths (ufill/ueofill/ustroke/uappend/upath/setbbox, the
  encoded form too), setcachedevice2, findcolorrendering, cache, device and
  VM queries, revision; (%stdout) (w) file gives a sink.
- `//name` is replaced by its value when read, inside procedures too.
- Tiling patterns become pattern swatches (their PaintProc drawn in pattern
  space, one cell; uncoloured ones in the colour under them; makepattern
  copies share one swatch); nested patterns stop at 8 levels.
- Type 3 fonts draw their glyphs (BuildGlyph/BuildChar, widths from
  setcachedevice/setcharwidth) as outlines grouped per string, spaced by
  ashow/widthshow/awidthshow/xshow/yshow/xyshow/kshow; glyphshow and
  stringwidth/cshow use them.
- Shadings of types 1 and 4-7 become gradient meshes (data from arrays,
  strings or files, packed or not, flags sharing edges), as fills through a
  shading pattern too; sampled functions (type 0) are evaluated;
  GradientMesh::coons/triangle are shared with PDF import.
- ImageType 3 masked images, interleaved by sample, by row or in separate
  sources; ReusableStreamDecode reads its data when made; Flate/LZW PNG and
  TIFF predictors.
- Bounded: DeviceN at 32 inks, sampled functions at 2^24 samples and 32
  outputs, 4096 patches a shading, strokepath ignores dashes finer than 1e5
  pieces, Type 3 glyphs nest 8 deep (a glyph that shows itself overflowed the
  stack before the limit), 64-bit index maths for 32-bit targets.

Tests: a synthetic program per generator (cairo/Inkscape, its Type 3 fonts,
matplotlib, CorelDRAW, Affinity, LibreOffice, Ghostscript eps2write,
Illustrator procsets), meshes and function shadings, masked images, fault
messages, probing operators, metafile/EPSI previews, `//name`, user paths,
hostile patterns/glyphs/shadings (run against main, 12 of the 14 generator
and feature tests fail; CorelDRAW- and LibreOffice-style programs already
read there). The import fuzz vocabulary covers the new operators. Checked
against real files made here with Inkscape 1.4 (cairo) and Illustrator 2026
(its own format and the CS-to-CC compatibility formats; all fell back or
stopped early on main and open as vectors now; its Illustrator 8 format
needs the next commit); those files aren't committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:23:36 +07:00
bflatastic 13d5645052 Fix #479: Add/Delete Anchor Point tool icons drawn from the Pen tool's
The two tools had hand-drawn nibs that didn't match the Pen tool's icon and read as distorted
next to it. They now use Lucide's pen-tool (the Pen tool's own icon) with a plus or minus badge,
so the Pen group reads as one family; the Remove Anchor Points buttons pick up the minus one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:17:59 +07:00
bflatastic e98a880e75 Part of #472: Escape leaves Presentation Mode; dropped documents open as tabs; .ai off-artboard note
Presentation Mode hides the menus and panels, had no Escape and was saved with the
preferences, so it came back on every restart with no way out. Escape now leaves it (as in
Illustrator), F from it goes back to Normal Screen Mode, and the screen mode is no longer
saved: the app always starts in Normal Screen Mode, and preferences saved in Presentation
Mode by older versions open normally. ui.inspect reports `screenMode` on its own.

Desktop file drags carry no pointer position or modifiers (winit 0.30 on Windows, macOS
and X11), so every drop on a window with a document open was taken for the middle of the
canvas and placed. Where the platform doesn't say, a dropped document (native, SVG, PDF,
.ai, EPS, DXF, EMF/WMF) now opens as a tab of its own and a picture or text is placed in
the middle of the view; pictures dropped together with documents are placed first, so the
last document opened is shown. The web, whose drag events give the position, keeps
Illustrator's choice: placed on the canvas, opened off it (the tab bar).

Illustrator's PDF-compatible part leaves out art outside the artboards (it lives in the
private data alone, which isn't read), so it can't open; importing a file with that
private data now says so in a note. Off-page art a PDF does draw already opens on the
pasteboard; a test keeps it so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:01:12 +07:00
bflatastic 10a87311d3 Type: burasagari labels Regular/Force, full-width commas and full stops hang, MCP docs (review of #463)
- The Paragraph panel menu's Burasagari submenu reads None / Regular / Force, as the reference
  app's help names them (`text.setFormat` keeps "none"/"standard"/"forced", its scripting names);
  Reset Panel goes back to new type's Regular.
- The full-width ,.and half-width 、。 hang too; Latin commas and full stops never do, so Latin
  text keeps its breaks and composer with new type's default (tested). The spaces after a hanging
  mark stay on its line.
- docs/mcp.md: a Burasagari section. ROADMAP: wording, Last updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:34:05 +07:00
imachus eb38894759 Type: new type takes Standard burasagari, as in Illustrator; Latin paragraphs keep the every-line composer
- New type (the Type tools, `text.create`, area type and type on a path) takes Standard
  burasagari, Illustrator's default (observed in its Paragraph panel menu). Documents from before
  it and imported text keep None.
- Only a paragraph with a Japanese comma or full stop is composed line by line, so Latin text keeps
  the every-line composer with the new default (tested).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:34:05 +07:00
imachus 08e7a4f713 Type: burasagari — a Japanese comma or full stop ending a line may hang outside it
Paragraph panel menu › Burasagari › None / Standard / Forced (with the East Asian options) and
`text.setFormat {burasagari}`:

- None: a comma or full stop that doesn't fit goes to the next line with the character before it.
- Standard: one that doesn't fit hangs outside the line; one that fits stays inside.
- Forced: one ending an area type line always hangs, and the rest of the line fills the measure.

Only 、 and 。 hang (closing brackets don't), in the width their mojikumi gives them. Horizontal
and vertical. Saved only when not None; new type keeps None. Paragraphs with burasagari are
composed line by line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:34:05 +07:00
bflatastic 253249717b Pen and anchors: Alt edits one handle, cut and convert selected anchors from the Control bar
Discord feedback: Alt with the Pen didn't move a direction handle on its own, and there was no
way to break a path at an anchor and pull the ends apart.

- Pen: Alt held over a handle end or an anchor of a selected path (the one being drawn too) hands
  the gesture to the Anchor Point tool, with the convert cursor: dragging a handle moves it alone,
  clicking a smooth anchor makes it a corner, dragging an anchor pulls out symmetric handles.
  Drawing goes on afterwards. Handles hidden by a partial anchor selection aren't grabbed.
- path.cutAtAnchors cuts at the direct-selected anchors in one undo step: a closed path opens
  there, an open one becomes one path per piece, and one anchor of each cut stays selected so a
  drag pulls the path apart. It answers {ids} and refuses (no undo step) when only open ends are
  selected. path.convertAnchors checks `to`, skips stale anchor refs instead of indexing past
  them, and keeps a smooth anchor's handles when converting to smooth.
- SubPath::cut_at and SubPath::smooth_anchor (geom) now back path.split, path.cutAtAnchors,
  path.convertAnchor and path.convertAnchors, replacing three copies of that logic.
- Control bar and Properties panel: "Convert:" corner/smooth and "Anchors:" remove, connect
  (Join), cut buttons when anchors are selected; Cut Path on the task bar and the context menu.
  New original icon dc-anchor-smooth.svg (ASSETS.md row).
- ja, zh-hans, zh-hant and es strings (cs for the context menu item), es @msg rows; docs/mcp.md
  section; ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:16:12 +07:00
bflatastic d6f8cc879f Control bar: image controls (Image Trace, Embed, Crop, Mask) when an image is selected
Discord feedback: Image Trace and the other image options were hidden when an image was
selected; copy, paste, Image Trace should work from the Control bar as in Illustrator.

- Control bar, one image selected: Linked File/Embedded, file name, colour mode and PPI, then
  Embed (+ Edit Original) or Unembed…, an Image Trace split button (click: Default preset;
  arrow: trace with any preset), Mask and Crop Image, in place of Fill and Stroke.
- Control bar, one Image Trace object selected: "Image Tracing", its Preset (choosing another
  traces it again), the Image Trace panel and Expand.
- Properties panel: Image Trace (split), Crop Image and Mask in Quick Actions for an image; an
  Image Trace section (Preset, Expand, Release) for an Image Trace object. Embed/Unembed buttons
  are shared with the Control bar (place::link_buttons).
- New engine command object.maskImage ("Mask"): clips the selected image with a clipping path
  on its outline (rotated with it) and selects that path so its handles crop; one undo step.
- widgets::split_button (flat button with a menu arrow).
- Translations for the new strings (es, ja, zh-hans, zh-hant; "Mask" also pt-br, cs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:15:48 +07:00
bflatastic 20ed00784f Desktop: report an unusable log folder through the logger, ROADMAP (review of #465)
The "no log file" message went through eprintln!, which panics when
standard error is a broken pipe; log::warn! reaches standard error the
same way (the file sink has already given up) and never panics.
ROADMAP: the log file in "Shipped so far" and "Last updated".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:07:24 +07:00