Commit Graph
1261 Commits
Author SHA1 Message Date
dotCooCoo 2ecc488111 Variables: Import and Export Data Sets… show file dialogs on macOS and keep the window running
The Variables panel menu called import_datasets and export_datasets
directly, so their file dialogs were asked for outside an entry and
shown in line. macOS shows no dialog in line, so the two items did
nothing there. On Linux the dialog held the window until it closed.

Both items now go through picks::button, as the Libraries panel
menu's Import Library… and Export Library… do. When the dialog
answers, the item runs again with the picked path. Errors still go
to the status bar.

variables_panel_data_sets_ask_off_the_ui_thread clicks both items
with dialogs shown as sheets, as on macOS, imports a CSV and exports the
data sets to the path the dialog answers.
2026-10-19 22:19:47 +07:00
NetterSilverandClaude Haiku 5.5 3eed3be8e6 Stop locked guide objects from blocking marquee selection
Fixes #3036

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:19:42 +07:00
dotCooCoo 219253f163 Objects on Path: attach objects to a path and spread them along it
VectorCraft had no Objects on Path. A click with the Objects on Path
tool attaches the selected objects to the path clicked. The tool is in
Edit Toolbar's Modify section and the Blend tool's flyout, and Object ›
Objects on Path › Attach and the context menu with two or more objects
selected choose it. The objects and the path go into an Objects on
Path group with the path at its bottom.

- object.objectsOnPath.attach orders the objects column by column from
  left to right and top to bottom in each column. The first one sits
  on the path's first anchor, the last one sits on the end of an open
  path, and the rest are spread evenly by length; a closed path takes
  them all the way round. Each object's attachment point (Pivot, one
  of nine, default the center of its bounds without strokes) sits on
  the path, and the object is turned to the path there. An object that
  was rotated by an angle other than a multiple of 90° is first turned
  to the nearest multiple of 90°. Rotate tilts every object by that
  many degrees counter-clockwise. Symbol instances are measured by
  their art. Guides, compound paths, type, paths without length and
  graphs are refused with a reason.
- After every edit the objects go back on the path: editing the path
  moves them, an object moved on its own goes back, and objects moved
  into or out of the group in the Layers panel join the end of the
  path or leave it. Scaling, shearing or reflecting the whole group
  keeps each object's scale, shear or reflection, and the objects are
  spread evenly along the transformed path again. Undo takes the path
  and the objects back in one step, and a copied group stays live on
  its own path.
- object.objectsOnPath.options changes Pivot and Rotate, and
  object.objectsOnPath.expand leaves a plain group. Both act on the
  groups that `ids` names, or on the groups of the selection when
  `ids` is not given.
- The native file keeps the group live; SVG, PDF and the other exports
  write plain art. The tool highlights the path under the pointer, has
  a cursor entry, and Escape during a press cancels the click.

Part of #2545

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 21:51:32 +07:00
dotCooCoo e6c8ce1759 Live Paint: Live Paint Selection Tool Options set what a click selects and the highlight
Double-clicking the Live Paint Selection tool did nothing, and the tool
always selected faces and edges and highlighted them in red at 4 px.
It now opens Live Paint Selection Tool Options with Select Fills,
Select Strokes, Color and Width, the options the reference app's help
page on selecting objects lists.

- With Select Fills off, a click selects the edge nearest the pointer,
  as Alt-click does. With Select Strokes off, Alt-click selects the
  face under the pointer, as a click does. With both off, a click
  selects no face or edge.
- Color (a layer color or a custom color) and Width (1 to 20 px) set
  the highlight on the face or edge under the pointer.
- The defaults are the earlier behavior: both selections on, red
  (#ff2a2a) and 4 px. The options are tool options (tool.setOption
  {tool: "livePaintSelection"}): they last across tool switches and
  are saved with the preferences.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 21:51:31 +07:00
dotCooCoo e1761eb489 Layers: the panel options command describes when Locate Selected Objects opens nothing
The params text of ui.layersPanelOptions and the doc comment on
PanelOptions::locate_selected said that, with the option on, the panel
opens the rows around any object selected outside it and scrolls to its
row. When Show Layers Only, a search or the filter leaves the object
without a row, the panel opens no rows for it and scrolls as with the
option off. Both texts now describe that case, as docs/mcp.md does.
2026-10-19 21:51:29 +07:00
dotCooCoo 7f01abd687 Layers: Locate Selected Objects skips a selection recalled from the panel menu
With Locate Selected Objects on, choosing a saved selection in the panel
menu's Save Selection submenu opened the rows around the recalled object
and scrolled to it. A selection changed with the panel's own menu is not
located, and the submenu's recall now follows that rule as the other
panel menu items do.
2026-10-19 21:51:29 +07:00
dotCooCoo 729582dc78 Layers: Locate Selected Objects opens nothing for an object the panel does not list
With Locate Selected Objects on, an object selected on the canvas had the
layers and groups around it opened even when Show Layers Only, a search or
the filter left it without a row. The panel did not scroll to it, but the
rows stayed open, so they showed once the panel listed every row again.

The panel now opens the rows around the object only when the object has a
row once they are open. Otherwise it opens nothing and scrolls as with the
option off. The Layers panel entries in docs/mcp.md and
docs/target-app-parity.md describe this.
2026-10-19 21:51:29 +07:00
dotCooCoo 7c64ecd998 Layers: with Locate Selected Objects on, the panel shows the row of art selected on the canvas
By default the Layers panel opens the layers around a row and scrolls
the row into view when its highlighted rows change, for example after
a click in the panel, a new layer or Locate Object. When no row is
highlighted and a selection makes another layer or sublayer the
active one, the panel does the same for that layer's row. Selecting
art on the canvas or by a command highlights no row, so by default
the panel does not open the layer or sublayer that holds the art or
the groups around it, and does not scroll to the art's row.

Panel Options gets Locate Selected Objects, off by default and kept
with the UI state. With it on, when the last selected object changes
on the canvas or by a command, the panel opens the layers and groups
around it and scrolls to its row, as after Locate Object, but does not
highlight the row. When the selection also changes the active layer,
the panel scrolls to the object's row instead of the layer's row.
When Show Layers Only, a search or the filter leaves the object
without a row, the panel scrolls as it does with the option off. A change made with the panel's own
rows, buttons or menu is not located, and a change made during a drag
is located when the drag ends. Locate Object is unchanged.

On a layer of 5,000 groups of 10 paths with every row open, the first
panel frame after a selection change took a median of 1.6 ms with the
option on and took a median of 1.4 ms with it off, in a release build.

Docs: docs/mcp.md (locateSelected), docs/target-app-parity.md.
2026-10-19 21:51:29 +07:00
dotCooCoo 55f98095f3 Tabs: Snap to Unit, or Shift held, puts a tab stop on the ruler's nearest tick
A tab stop placed or dragged on the Tabs panel's ruler always landed
at a fractional position, and the ruler was numbered in inches with a
tick every 18 pt whatever the document's units. The reference app
snaps stops to the ruler units with Snap To Unit in the panel menu or
with Shift held during a drag, and numbers its tab ruler in the
document's units.

The ruler is now numbered in the General unit with the canvas rulers'
label step at the ruler's scale, and has a tick every tenth of a step.
The panel menu has Snap to Unit, checked while it is on. With it on,
or with Shift held, a stop placed by a click or dragged on the ruler
lands on the nearest tick, through Unit::snap_to_ruler_tick as a
Shift-dragged ruler guide does. A snapped click that lands on an
existing stop's tick selects that stop and records no undo step. The
stops still change through text.tabs.set. The reference app's page
names Shift for a drag only; Shift also snapping a click, and a
snapped click onto a stop's tick selecting that stop, are VectorCraft's
own rules.

Docs: docs/target-app-parity.md.
2026-10-19 21:51:27 +07:00
dotCooCoo c3a6aa0a29 Variables: a data set with no value for any bound variable shows a status message
Choosing a data set in the Variables panel, stepping through data sets
with the panel's arrows, and running `dataset.select`, `dataset.next`
and `dataset.prev` applied nothing and showed no message when no object
was bound to a variable that the data set has a value for. This
happens, for example, after Import Data Sets… defines new variables
without binding them.

The app now shows "nothing in data set A was applied: no object is
bound to a variable it has a value for" in the status bar, and the three
commands add it to the reply as `warning`. A data set with a value for
a bound variable gives no warning even when every object bound to it is
locked; `skipped` counts those objects. What applying a data set does
is unchanged.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 21:51:26 +07:00
dotCooCoo 1d214956f9 Smart Guides: object highlighting stays off with Snap to Grid or Pixel Preview
The reference app's help says Smart Guides cannot be used while Snap to
Grid or Pixel Preview is on, even with the menu command selected. The
Selection tool's move already leaves Smart Guides out under Snap to
Grid, but the canvas still drew the Object Highlighting outline under
the pointer with either view on.

The hover outline now draws only with Smart Guides and Object
Highlighting on and with Snap to Grid and Pixel Preview off.
2026-10-19 21:51:25 +07:00
Hamza 1c80b23745 Exports: SOURCE_DATE_EPOCH stands in for the clock, so output repeats
PNG, PDF, EPS, legacy `.ai`, packaged zips and saved `.vectorcraft` files
record the wall-clock time they were made in, so the same commands run twice
gave different bytes although the pixels matched (#2880):

  PDF   /CreationDate and /ModDate   D:20261018155816 vs D:20261018155817
  PNG   the `Creation Time` tEXt entry
  .vectorcraft  one byte of the stored date
  SVG   carries no time, and already repeated

`metadata::output_time` is the time a file records: `SOURCE_DATE_EPOCH` when it
holds a non-negative whole number of seconds, else the clock, as the
reproducible-builds specification defines it. A date a command is given
(`file.new {created}`, a save's `modified`) still wins over the variable, and a
blank, negative or non-numeric value is ignored so that an export is never
refused over the environment.

`metadata::now_unix` keeps the real clock and now has exactly two callers, both
of which must not be pinnable from the environment: the recovery heartbeat, and
the seed PDF encryption draws on.
2026-10-19 00:37:55 +07:00
bflatastic c9a2491ef7 Rotate View: a dial shows the angle while a drag rotates the view
Squashed from #2677 (its merge of main dropped).
2026-10-19 00:37:53 +07:00
bflatastic b529f356cc Tests: the dock-width test builds its UiState with the width, as clippy asks (#2953) 2026-10-19 00:28:48 +07:00
nrq6993-a11yandCursor 57ddb71d97 #1753: saved workspaces restore dock panel width
Persist the main dock column width in workspace snapshots and reapply it when switching layouts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 00:27:40 +07:00
bflatastic 048875cc4e fmt (#2952) 2026-10-19 00:27:40 +07:00
nrq6993-a11yandCursor 49f7f5d7d8 G1: busy cursor during background open, save and export
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 00:26:27 +07:00
bflatastic 278fba490a i18n(ka): the pdfInfo thumbnail page error (#2914) 2026-10-19 00:16:42 +07:00
bflatastic d807236bf2 i18n(zh-hant): the shared parameter-check errors, now that Traditional Chinese translates every message (#2930, #2784)
#2784 made zh-hant a language that translates every status and error message while #2930 added the shared parameter-check errors to the others; main's shared_param_check_errors_are_translated failed on zh-hant.
2026-10-19 00:15:35 +07:00
dotCooCoo ab01a1a927 Save for Web: each browser preview is written to its own folder
Preview in Browser wrote every page to one folder,
VectorCraft Save for Web in the system's temporary folder. A second
preview of a document with the same title, from the same app or from
another one running, overwrote the first preview's page, and any image
file of the same name (PNG-8 and PNG-24 both write .png), before the
browser read them. The same overwrite made
dialogs::tests_save_for_web::preview_in_browser_opens_the_page fail now
and then, when another Save for Web test in the same binary, or a test
run in another checkout on the machine, previewed Untitled-1 at the
same time.

Each preview now goes to a new folder inside that one, named after the
process id and a count of the process's previews. A name that an
earlier process with the same id left there is skipped for the next
count. The page keeps its name and its images folder. A preview first
removes the entries of VectorCraft Save for Web that were last changed
an hour ago or earlier, which also clears the files the single folder
held before.

Docs: docs/target-app-parity.md.
2026-10-19 00:03:53 +07:00
dotCooCoo 9fe0f03de2 i18n: number, object id and date parameter errors are translated
The errors of the parameter checks that many commands share had no
@msg row in any catalog: "`{key}` must be a number, not {v}" from
check_numbers (object.move, object.rotate, object.setBounds and the
other callers), "missing integer `{key}`" from index_param in
cmd/appearance.rs (appearance, effect and freeform gradient commands),
the three object id errors of checked_ids_param and checked_id, and
the Unix seconds error of date_param. A template
matches only the whole text, so the row for "`{key}` must be a
number" did not match the longer check_numbers message. These errors
showed in English in the status bar, and "no such object id {value}"
matched the general "no such object {0}" row and showed half in
English. The scan in complete_languages_translate_every_message does
not read messages built with bad(), so that test did not report them.

Each of the 6 messages now has a row in the 18 catalogs that translate
every status and error message and in Bulgarian, Danish, Indonesian and
Dutch, and "missing number `{key}`" has a row in Danish and Indonesian.
No English string changes.

Docs: docs/target-app-parity.md.
2026-10-19 00:03:48 +07:00
dotCooCoo 05264b2801 MCP: document.pdfInfo answers one page for an EPS, a .ps file or a PostScript .ai
document.pdfInfo refused PostScript (an EPS, a .ps file, or a .ai in
PostScript form), mostly with "this is a PostScript file ..., not a
PDF", although document.open and file.place read the same file
through the EPS reader as one page.

It now answers {pages: 1, needsPassword: false, pageInfo: [{width,
height, rotation: 0, boxes: {}}]}. The page is the file's bounding box
(%%HiResBoundingBox when it has one, a US Letter page without one),
read from its comments without running its program. thumbnail: 1 is a
PNG of what that page prints, read by the EPS reader without any
editing data the file carries. A program whose header is only %! is
read as PostScript, as clipboard.importEps reads it.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-19 00:03:45 +07:00
dotCooCoo 841fc0cd51 i18n: Traditional Chinese translates status-bar and error messages
Squashed from #2784 (its merge of main dropped).
2026-10-18 23:50:58 +07:00
MortenHusted d25289a3be Select: select.color picks objects by fill and stroke colour, no object needed
Scripts had to know an object id before they could select art by colour
(select.same.* and select.magicWand start from one). select.color
{fill?, stroke?} takes colours as paint.setFill does and selects the
objects whose fill and stroke read as them in #rrggbb, as
document.inspect reports colours, with Select > Same's selection rules.

The label is translated in each of the 23 catalogs, which
complete_languages_translate_every_menu_string requires.
2026-10-18 23:50:57 +07:00
dotCooCoo 09a6fbe470 Type: a click away from the text being edited ends editing without making new text
While a Type tool edited text, a click on blank canvas ended the
editing and also placed new point type at the click. Area Type and
Type on a Path did the same with a click on a path.

Such a click now only ends the editing. The typing session is
committed, empty point type that a click placed is discarded as before,
and the Type tool in use stays active; the next click makes new type.
The same applies when the editing began with a double-click from a
selection tool. A click on other type still edits it at once, and a
drag that draws an area-type frame still makes the frame.
2026-10-18 23:50:57 +07:00
dotCooCoo b30d80e056 Fields: arrow keys and the wheel step to the next whole unit, and Ctrl/Cmd steps to the next tenth
In number fields, Up/Down and a wheel notch added a whole step and kept
the fraction, so 5.1 mm stepped up to 6.1 mm. A plain step now goes to
the next whole unit in its direction (5.1 goes up to 6 and down to 5; 5
goes up to 6 and down to 4), and a Ctrl/Cmd step goes to the next tenth,
in the field's own unit. Repeated presses or notches of one size add or
take one step each, and presses and notches are applied in the order
they came. A Shift step still adds or takes ten and keeps the fraction.

The number fields that widgets.rs draws for the panels, the Control bar
and the dialogs (unit, percent, degree and count fields) all use this
step (widgets::step_value).
2026-10-18 23:50:57 +07:00
bflatastic 12d107cc56 fmt (#2928) 2026-10-18 23:50:57 +07:00
nrq6993-a11yandCursor 2025c357d9 Fix #2719: honor Display Print Size for status-bar 100% zoom
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 23:50:57 +07:00
bflatastic 88494319da fmt (#2911) 2026-10-18 23:50:57 +07:00
nrq6993-a11yandCursor bf19ada5e1 Fix #2646: keep undocked shared-dock panels opaque
Floating groups on the canvas now always get a panel-colour backdrop from layout geometry when egui has not registered the area yet, and panel bodies paint their own fill so empty regions do not show through.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 23:48:57 +07:00
Davit MatchakhelidzeandClaude Opus 5.5 4675a1633e i18n(ka): Letter and Word Spacing as in the other Craft apps; "weren't read" in the catalog's usual form
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-18 23:48:55 +07:00
ElPaule d99b04e594 i18n(de): add missing PSD error messages and effect Soft entry 2026-10-18 23:48:54 +07:00
bflatastic 5f37fccdce New Document: Recent and Saved cards keep the orientation they were made in, so one A3 card is chosen, not two (#2860)
The cards turned to the chosen orientation in every category, so a portrait and a landscape A3 in Recent looked the same and both read as chosen. Only the built-in presets' cards turn now.
2026-10-18 20:12:47 +07:00
bflatastic bb14031aa3 Canvas: selected point type's baseline marker runs under each line, centred and right-aligned type too (#2879)
The marker ran from the anchor to the right edge of the bounds, so centred type showed half a line and right-aligned type none, and rotated type a level line. It now draws each line's baseline in text space (TextObject::baselines, the lines a click selects the type on), turned with the type.
2026-10-18 19:02:52 +07:00
AJ Richardson 411f5f7680 prioritize bounding-box handles over mirror axis 2026-10-18 19:02:44 +07:00
AJ Richardson 63cebd392a Add interactive Mirror Repeat axis controls 2026-10-18 19:02:44 +07:00
bflatastic 5906f53ca5 Fonts: the timed look for activated fonts doesn't wake an idle app; the next frame reads it (#2884) 2026-10-18 19:02:44 +07:00
nrq6993-a11yandCursor 640c87b537 Fix #2576: poll for fonts font managers activate while the app is in front
Font services (NexusFont, Adobe Fonts) register faces with the OS without
touching font-folder mtimes; only re-asking the platform lister sees them.
Check every two seconds as well as on window focus, and test that path churn
is detected on the system font scan.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 18:46:03 +07:00
ham9zah 561db27fef G1: test that a group of clipped images shows only its masks and their bounds
A group of four clip groups, each a placed image larger than its
rounded-square mask: one Selection click selects the group, which
shows the four masks' outlines and no image rectangle, with a bounding
box and bounds equal to the union of the masks. The behaviour landed
in #2650 (part of #2422); this covers the grouped case.
2026-10-18 18:46:01 +07:00
Tomasz LewandowskiandClaude Opus 5.5 cc1a496947 i18n(pl): Artboard is "obszar kompozycji", Guides "linie pomocnicze"
Three new Polish rows broke the catalog's own vocabulary:

- "By Artboard or Item" and "Artboard or Item Name" (Export for Screens)
  read "obszar roboczy", which this catalog uses for Workspace; Artboard is
  "obszar kompozycji" everywhere else (72 rows).
- "Enable Guides for 9-Slice Scaling" read "prowadnice"; Guides are
  "linie pomocnicze" in the other 29 rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DukHNvUY45rjRnvtz91x33
2026-10-18 18:46:00 +07:00
bflatastic 37b90eb050 i18n: Georgian rows for the strings added since #2045 last merged main
Cancel Export, Clear History, Justification…, the .ait template and editing-data notes, the variable-library note, and the style undo templates.
2026-10-18 18:29:55 +07:00
Davit Matchakhelidze 7cf8dd4764 i18n: Georgian (ka) interface language
Squashed from #2045 (merge commits from main dropped).
2026-10-18 18:09:14 +07:00
bflatastic 1e3ab306d4 Tests: the dropdown keyboard test clears the texture updates it doesn't apply (#2824) 2026-10-18 13:26:54 +07:00
bflatastic 1b4c421bc4 .ai import: the variable-library note reads its counts after a colon and is translated in every complete language (#2842) 2026-10-18 13:26:54 +07:00
Louise FoxandClaude Opus 5.5 0842941f8d Dialogs: a plug-in's parameters scroll when they don't fit the window
A plug-in's dialog (Effect › Plug-ins, Object › Plug-ins) lays out each
parameter of its manifest as a row, and a manifest may declare up to 64.
With many of them the dialog ran off the top and the bottom of the
window, its heading and its OK and Cancel buttons off screen. A plug-in
of 64 number parameters got a dialog 2,329 px tall, its top at y = -755
in a 900 px window.

modal::scrolled_rows() puts rows in a vertical scroll area as tall as
the screen less the rest of the modal window and its edge gaps.
modal::show_at() measures that rest once it has drawn the window, for
the next frame, and asks for that frame at once if the rest changed.
The dialog then fits without waiting for input. schema_fields() puts the
parameter grid in it, for filters and effects alike. A dialog of a few
parameters is as tall as before.

a_plug_in_dialog_of_many_settings_fits_the_window opens an effect's and
a filter's dialog of 64 parameters in a 1280 × 900 window, and one of 4.
Before the change it failed with "effect: the dialog is at
[[457.0 -755.0] - [823.0 1574.0]] in the [1280.0 900.0] window".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-18 12:41:25 +07:00
Louise FoxandClaude Opus 5.5 dbb6b470b3 Menus: leave installed plug-ins' names out of the strings to translate
menu_strings() lists the strings a complete language catalog has to
cover, and it leaves out names that are user data: fonts, recent files,
saved views and libraries. An installed plug-in's name stayed in.
Plug-ins are installed for the whole process, and a test that installs
one before complete_languages_translate_every_menu_string runs made that
test fail with the plug-in's name as an untranslated menu string.

lists_plugin() tells an Object › Plug-ins filter or an Effect ›
Plug-ins effect by its command and params. Those are part of the menu
item itself and stay right while another test installs or removes
plug-ins. menu_strings() uses it, and so does
menu_catalogs_translate_every_menu_label in place of its own copy of the
rule.

plugins_are_listed_in_the_object_and_effect_menus checks it. Before the
change it failed with "Desaturate… is a menu string to translate".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-18 12:41:25 +07:00
MarekSpodBiedry df1bf3fe32 Close a dropdown list after an option is picked with the keyboard (#2789) 2026-10-18 12:41:25 +07:00
Fabian GrajalesandClaude Opus 5.5 ec410552cf Export: a progress dialog with Cancel for background exports
While an export runs in the background (Background Export), a dialog shows
what is being exported and how far it has got: artboard by artboard for a
raster export of several artboards, busy otherwise. Cancel (also Esc) stops
it and writes no file; Continue in Background leaves it to the status bar.
It opens after 0.4 s and never over another dialog.

Built on the shared vectorcraft_doc::cancel (#2546): fileio::progress::Progress
wraps its CancelToken and a progress sink, so a cancelled export stops
drawing and keeps no half-drawn pattern tile or effect raster.

- ui: background jobs carry their Progress; export_progress draws the
  dialog; UI commands file.cancelExport and file.exportInBackground.
- i18n: the strings in every language with complete menus, Danish, Dutch
  and Indonesian included; "{n} more exports waiting" is a plural (trn).
- docs: ui-parity, gaps (G12) and ROADMAP rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dt49z1ubK9x1bPq51APgz3
2026-10-18 12:40:20 +07:00
VictorandClaude Opus 5.5 a275496ef4 G4: Paragraph panel menu › Justification… (word and letter spacing, glyph scaling, auto leading, single word justification)
The Paragraph panel menu's Justification… was a disabled stub, and justified
lines stretched word spaces between fixed limits (80% to 150%).

Each paragraph now has Justification options: Word Spacing, Letter Spacing
and Glyph Scaling (Minimum, Desired, Maximum, in percent), Auto Leading and
Single Word Justification. Desired applies to every line; a justified line
fills its measure with word spacing first, then letter spacing, then glyph
scaling, each within its range, and past them its word spaces take the
rest. The Every-line Composer scores lines by that flexibility. Auto
Leading sets the leading of characters whose leading is Auto. A justified
line holding one word is spread between its letters (Full Justify) or
aligned left, centre or right.

The defaults are the documented ones (80/100/133, 0/0/0, 100/100/100, 120,
Full Justify), saved only when they differ. Because the default maximum
word spacing is 133%, justified paragraphs break differently than before;
the Every-line Composer's second pass is now unbounded for justified text
(it used to give up past four times the stretch and fall back to the
Single-line Composer, which the narrower default range would make common),
and lines that can't stretch at all are still refused.

text.setFormat takes wordSpacing, letterSpacing, glyphScaling ([min,
desired, max] or {min?, desired?, max?}), autoLeading and
singleWordJustification; Minimum ≤ Desired ≤ Maximum must hold or nothing
changes. Paragraph styles carry the options (New and Redefine capture
them), and Reset Panel resets them. Justification… opens a dialog
(ui.justification) with Preview; OK is disabled while a row is out of
order. Live SVG text places characters the options move, and a glyph that
Glyph Scaling scales is stretched to its width; PDF uses the glyphs'
transforms. Vertical type isn't glyph scaled; type on a path doesn't use
the options.

Docs: docs/mcp.md, docs/type-parity.md, docs/gaps.md.

Closes #2755.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:40:14 +07:00
dotCooCoo 1caa957531 History: the panel menu clears the history
The History panel menu had no Clear History, and VectorCraft had no
command that removes the undo and redo steps. In the reference app,
Clear History in the History panel menu removes the states after the
selected state, and it removes the states before it while their
changes remain in the document.

edit.clearHistory (Clear History) removes every step to undo and to
redo in the active document and returns how many it removed. The
document and the selection stay as they are, and clearing is not an
undo step. Typing or a drag still in progress becomes a step first, as
Undo makes it one, so its change stays in the document. The command is
disabled when there is no step to clear, and it waits while an input
method composes, as Undo does.

The History panel menu has Clear History after Step Forward. It runs
the command and is disabled when there is no step to clear.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-18 12:39:07 +07:00