Merge main into feat/plugin-importers

This commit is contained in:
dotCooCoo
2026-11-07 23:49:11 -08:00
287 changed files with 24708 additions and 1142 deletions
+10 -1
View File
@@ -685,7 +685,16 @@ art too large at that scale) keeps the dialog open.
Placement Options: the Links panel's flyout (or `ui.placementOptionsDialog {ids?}` for agents) opens the
`placementOptions` dialog for the selected images (fields `ids`, `preserve`: `transforms`/`bounds`/`fileDimensions`/
`fit`/`fill`, `align`: `topLeft` … `bottomRight`, `clip`). `ui.dialog.confirm` runs `links.placementOptions` with them as
one undo step. The Links panel (`window.panel {panel: "links"}`) shows `links.list` and the first selected image's
one undo step.
Relink: the Links panel's Relink… button and flyout item (or `ui.relinkDialog {ids?}` for agents) open the `relink`
dialog for the selected images and placed documents (fields `ids`, `links`: `[{name, ids}]`, one row per linked file,
`scope`: `each`/`selected`/`all`/`missingInFolder`, default `selected`, `path` and `paths`). With nothing selected and
no `ids`, it fails with "select the images to relink, or pass ids". `ui.dialog.set {field: "path", value}` then `ui.dialog.confirm` runs
`links.relink {ids, path, scope}` as one undo step; without `path` a file is picked. With `scope: "each"`, each confirm
takes `path` (or picks a file) for the next row of `links`, adds it to `paths` and keeps the dialog open, returning
`{next}` (the next row's name); the last row's confirm runs `links.relink {scope: "each", paths}` for every row as one
undo step. The Links panel (`window.panel {panel: "links"}`) shows `links.list` and the first selected image's
`links.info`; `links.editOriginal` and `links.reveal` (Edit › Edit Original, Show in Folder) hand the linked file to
the system and return `{path}`. Edit Original opens only a regular file whose extension is a format `document.open`
reads, and answers a control request with an error unless the file is a placed document (which opens in a tab).
+1 -1
View File
@@ -180,7 +180,7 @@ VectorCraft choices; an original approximation, never compared with another appl
|---|---|
| Revolve | initial: live, editable profile, rotation and light gizmos, default-on Cap with continuous curved rims, smooth vector shading and round seam joins, SVG/PDF output, Expand Appearance ([revolve.md](revolve.md), #846) |
| Extrude & Bevel, Inflate, Rotate | initial: live, depth, twist, taper, solid or hollow caps, five original bevel shapes, Inflate volume, both sides, twist, taper and cap, Rotate of every fill and stroke, groups and layers turned about one shared axis (#4506), Position presets, rotation and light gizmos, SVG/PDF output, Expand Appearance ([three-d.md](three-d.md)) |
| Materials panel, lighting, ray-traced render | one to ten directional lights with per-light Color (opaque RGB, white by default), Intensity, Rotation, Height, Softness and neutral Ambient Light, Revolve and colored Inflate use smooth vector shading; Extrude & Bevel and Rotate retain planar shading; original Standard, Diffuse, Top Left and Right lighting rigs; no materials, shadows or ray tracing |
| Materials panel, lighting, ray-traced render | Window › 3D and Materials panel with the object types (Plane, Extrude, Revolve, Inflate) and the Object, Rotation and Lighting settings of the applied effect ([three-d.md](three-d.md#3d-and-materials-panel)); one to ten directional lights with per-light Color (opaque RGB, white by default), Intensity, Rotation, Height, Softness and neutral Ambient Light, Revolve and colored Inflate use smooth vector shading; Extrude & Bevel and Rotate retain planar shading; original Standard, Diffuse, Top Left and Right lighting rigs; no materials, shadows or ray tracing |
| 3D (Classic): Extrude & Bevel, Revolve, Rotate | none |
| Turntable (30.0 beta) | none |
| Export 3D objects (OBJ, USDA, glTF) | none |
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -203,7 +203,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
of 14), open speed on large files (#758), live effects, Scatter, Art, Pattern and Bristle brushes on paths, a
path's own Calligraphic brush settings, and symbols as live objects (#637; plain art styles read Drop Shadow, Outer Glow and Feather as live effects; pattern fills and global colours are read since #1025, placed (linked) files since #1264). A PDF-compatible `.ai` carries an editing copy of the art in the form
VectorCraft's reader reads (layers, groups, paths, colors, gradients, patterns, images, transparency, opacity masks,
clipping and type as text), with live effects left out and symbols and live objects written as their art
clipping, type as text and symbols), with live effects kept only on paths, compound paths and groups that
qualify (at most a fill and a stroke, a non-zero fill rule when filled, no large canvas) and whose appearance and effects the art-style writer supports, other objects' effects other than geometry effects left out, and live objects written as their art
([file-format-parity.md](file-format-parity.md)). Save As
`.ai` › Version: Legacy (version 7) writes the PostScript form from the published version 7 specification,
keeping layers (names, order, visibility, lock), groups, compound paths, clipping groups, gradients and
+142 -14
View File
@@ -380,8 +380,14 @@ explicit `ids`, which stand in for the selection, so they work with nothing sele
`ids?|id?` (or `id?, ids?: … (instead of id)`), `id` is `ids` of one object: `{"id": 7}` does what `{"ids": [7]}`
does, with or without a selection, and an `id` or `ids` that is malformed (not a non-negative integer) or names no
object is refused under the command's name, never replaced by the selection (`effect.apply {"id": "warp.arc"}`
without `effect` names the effect). A command that leaves out an object selected inside a selected group also
leaves it out when `ids` list it together with a group that holds it.
without `effect` names the effect). Other commands that act on `id` or else the selection, the highlighted layer row
or a new object (`css.export`, `object.isolate`, `layer.locate`, `layer.releaseToLayers`, `graphicStyle.new`,
`graphicStyle.redefine`, `charStyle.new`, `charStyle.apply`, `charStyle.redefine` and their `paraStyle.*`
counterparts, `image.info`, `links.info`, `links.goTo`, `links.unembed`, `shaper.release`, `shaper.expand`,
`transparency.editOpacityMask`, `transparency.viewOpacityMask`, `symbol.edit`, the `object.blend.spine.*` commands
and `path.curvature`) refuse an `id` that is not a non-negative integer (`"abc"`, `"5"`, `true`) with an error and
change nothing; an omitted or null `id` keeps the default. A command that leaves out an object selected inside a
selected group also leaves it out when `ids` list it together with a group that holds it.
Paint values (`fill`, `stroke`) accept `"#rrggbb"`, `"none"`, `[r,g,b]` (0..1), `{"c","m","y","k"}`, `{"gray"}`,
or a full `paint.setFill` params object (`{"gradient": …}`, `{"swatch": "name"}`).
@@ -410,7 +416,7 @@ Lines start with no fill and a visible stroke.
The Fill/Stroke proxy commands run through `run_command`: `paint.invert` and `paint.complement` recolor the active
proxy, including a grayscale image's colorized fill, while keeping each color's model. `paint.lastColor` /
`paint.lastGradient` re-apply the last solid color or gradient, and `paint.recent` returns the recent colors that every
paint command (and the eyedropper) feeds.
paint command (and the eyedropper) feeds: up to 30, newest first and each once, kept across documents for the session.
`paint.proxies` returns what the proxies show: the fill and stroke, which one is active, and whether the selected
objects' fills or strokes differ (`fillMixed` / `strokeMixed`, drawn as a "?" proxy).
@@ -446,7 +452,7 @@ opens the picker through `run_command` with the remote backend.
| `transform` | `{ids?, dx?, dy?, rotate?, scale?, scaleX?, scaleY?, reflect?, shear?, origin?, copy?}` | Runs move, rotate, scale, reflect, shear in that order. With `copy`, the first step duplicates. |
| `create_graph` | `{type?, x, y, width, height, csv? \| series?, categories?, rows?}` | The nine Illustrator graph types: column, stacked column, bar, stacked bar, line, area, scatter, pie and radar. An empty CSV cell or a `null` in `rows` is a blank value; a number in straight quotes is a label. Edit later with `graph.setData` / `graph.setType` via `run_command`. `graph.column {design, columnType: vertical\|uniform\|repeating\|sliding, each, fractions: chop\|scale}` draws the columns or bars of the series picked with Group Selection (or `seriesIndexes`, or all of them) with a graph design saved by `graph.design`; with no `design` it returns the series' current design and options. Type in the design that holds a `%` followed by two digits shows each column's value, with at least the first digit's places before the decimal point and the second digit's places after it. |
| `text_wrap` | `{ids?, offset?, invert?, release?}` | Area type below the objects (same layer) flows around them. |
| `undo` / `redo` | `{}` | Many calls that change nothing record no undo step, for example `object.align` with nothing to move, `object.move` by 0 without `copy` and `object.showAll` with nothing hidden. `undo` (`edit.undo`) after such a call undoes the step before it. `inspect_document` lists the undo steps as `history`. `run_command` with `edit.clearHistory` removes every undo and redo step of the active document and returns `{cleared}`, the number of steps removed; the document stays as it is, and the command records no undo step. While the Type tool edits type, `undo` and `redo` put its caret, with no characters selected, at the end of the text the step changed: after the text it puts back or retypes, or where the text it takes out was. A step that changes no characters, such as a formatting change, leaves the caret and the selection where they are. |
| `undo` / `redo` | `{}` | Many calls that change nothing record no undo step, for example `object.align` with nothing to move, `object.move` by 0 without `copy` and `object.showAll` with nothing hidden. `undo` (`edit.undo`) after such a call undoes the step before it. `inspect_document` lists the undo steps as `history`. `run_command` with `edit.clearHistory` removes every undo and redo step of the active document and returns `{cleared}`, the number of steps removed; the document stays as it is, and the command records no undo step. `run_command` with `edit.newDocumentFromState` and `{index?, created?}` makes a new document holding the art as it was at that state and makes it the active document, and returns `{index, title}`. `index` is a row of the History panel, with 0 the oldest state kept, and an omitted `index` uses the current state. An `index` that names no state is an error and changes nothing. `created` is File Info's created date in Unix seconds or null, and defaults to now. The new document takes the next `Untitled-N` name, is marked unsaved, has nothing selected and has no steps to undo or redo. It is out of symbol, opacity-mask and pattern editing, and the art drawn in a pattern's tile is stored in the pattern, as `object.pattern.done` stores it. The source document and its steps stay as they are, except that typing or a drag in progress is first kept as a step. While the Type tool edits type, `undo` and `redo` put its caret, with no characters selected, at the end of the text the step changed: after the text it puts back or retypes, or where the text it takes out was. A step that changes no characters, such as a formatting change, leaves the caret and the selection where they are. |
For example, create editable Japanese area type with the engine's vertical writing mode:
@@ -662,6 +668,10 @@ them. `grays: "black"` puts RGB greys with R = G = B on the black plate instead,
Convert to Grayscale before the switch would give); other colors convert as usual. The reply's `changed` counts the
colors that changed in the art, symbols, graph designs and pattern tiles; it does not count the colors of graphic
styles and brushes.
`edit.colorSettings`, `file.documentColorMode`, `color.convert`, `color.gamutCheck`, `color.loadProfile`,
`color.removeProfile`, `view.proofSetup`, `view.proofColors`, `view.overprintPreview`, `view.separationsPreview` and
`swatch.setSpot` reject a param of the wrong type, such as `"bpc": "no"`, `"on": 1`, `"to": 5` or `"colors": "#fff"`,
and change nothing. Omitting a param or giving null keeps its default.
SVG import keeps what the canvas edits live. `<pattern>` becomes a pattern swatch. A `<symbol>` with `<use>` becomes a
symbol (named after its `data-name`, else its id) with an instance per `<use>`; a `<use>` that shows it differently
@@ -836,6 +846,9 @@ empty `boxes`, and `thumbnail: 1` shows what that page prints. A page whose `Use
opens at its size in points, the file's numbers times the `UserUnit` (one above 75,000 counts as 75,000), and
`document.pdfInfo` gives its size and boxes in points too. A `.ai` whose editing data has its artboards in the units of
such a page opens from that data at the page's size.
`document.open`, `file.place` and `document.pdfInfo` reject a `cropTo`, `crop`, `password`, `colorMode` or `textAs`
that is not a string, and `document.pdfInfo` rejects a `thumbnailSize` that is not a number. The error names the param,
and nothing opens or is placed. Leaving a param out or giving null keeps its default.
Imported colours keep their model:
DeviceCMYK and CMYK ICC colours stay CMYK (a file painted mostly in CMYK opens as a CMYK document), DeviceGray is Gray,
and Separation and DeviceN inks become spot swatches the art links to at its tint (gradient stops too). CMYK images
@@ -1227,6 +1240,14 @@ in `object.envelope.resetWithMesh`; `antiAlias`, `distortAppearance`, `distortLi
`distortPatternFills` in `object.envelope.options`; and `editing` in `object.envelope.editContents`. A null is read as
a key left out.
`object.envelope.makeWithWarp`, `resetWithWarp` and `options` refuse a `style` that is not a string with
`` `style` must be a string `` and a name that is no warp style with `` unknown warp style `foo` ``, and an
`orientation` other than `horizontal` or `vertical`, a number or `true` included, with
`` `orientation` must be one of horizontal, vertical ``. `object.envelope.options` refuses a `preserveShape` that is
not a string with `` `preserveShape` must be a string ``, and checks the warp keys when no envelope or only mesh
envelopes are selected, too. Each leaves the document, its undo history and the options new envelopes start with as
they were. A null is read as a key left out.
These commands refuse a string, a number or another value that is not true or false with
`` `closed` must be true or false ``, naming the key, and leave the document and its undo history as they were:
`clockwise` in `shape.spiral`; `closed` in `shape.arc` and `path.create`; `vertical` and `placeholder` in `text.create`
@@ -1717,6 +1738,13 @@ already walks Image Trace. Short agent entry points:
`symbol.selectInstances {name}`, `symbol.delete {name | names, expandInstances?}` (`names` deletes several in one
undo step). `symbol.new` and `symbol.duplicate {name, newName?}` trim the white space around the
name they are given, as `symbol.options` trims `newName`; a blank one gives "New Symbol" or "<name> copy".
- Reset Transformation (the Symbols panel menu, and Reset on the Control bar while symbol instances are selected):
`symbol.instance.resetTransform {ids?}` gives the selected symbol instances, in groups too, their symbol's own
scale again, without rotation, shear or reflection, as one undo step. VectorCraft's own rules: each instance keeps
the center of its bounding box in place; other objects, instances in perspective and instances already at their
symbol's scale are left as they are, and no undo step is added when nothing changes; an id that names no object, or
objects without a symbol instance, is an error. It returns `{ids}`, the instances it changed. The menu item and the
button are enabled only while a selected instance would change.
- Dynamic symbols: an instance of a symbol whose `type` is `dynamic` changes the appearance of objects in the symbol's
art for itself only and stays linked. `symbol.instance.setAppearance {id?, path, fill?, stroke?, opacity?, blend?}`
names the object by `path`, child indices in paint order from the art's root down through its groups (`[]` is the
@@ -1760,7 +1788,9 @@ already walks Image Trace. Short agent entry points:
what that kind of corner draws when the brush has no `outer_corner` or `inner_corner` art for it: `none` draws no
corner tile, and `autoCentered`, `autoBetween`, `autoSliced` and `autoOverlap` make one from the side tile. Without
a source, the corner draws the other kind of corner's art. `brush.options {params: {outer_corner: null}}` removes
the art. Pattern Brush Options also accepts `side_tile`, `outer_corner_tile`, `inner_corner_tile`,
the art. Outer and inner corner art are both drawn as the corner that joins a side tile on its right to one below
it. The brush turns each corner tile so that those two arms meet the sides at the corner: 90° from one corner to
the next on a rectangle, and halfway between the two sides at other angles. Pattern Brush Options also accepts `side_tile`, `outer_corner_tile`, `inner_corner_tile`,
`start_tile` and `end_tile` in `params`. Each takes `none`, `original` or the name of a pattern
swatch in the active document, and `{"pattern": "<name>"}` names a swatch even when its name is one of those
words. Corner tiles also take `autoCentered`, `autoBetween`, `autoSliced` and `autoOverlap`. A swatch choice copies its whole art and tile frame into the brush. `original`
@@ -2199,6 +2229,21 @@ The document stores their numeric settings; manual edits remain possible through
{"name":"run_command","arguments":{"command":"effect.lightingPreset","params":{"index":0,"item":null,"preset":"topLeft"}}}
```
## 3D object type
`effect.setParams {index: int, threeDType: "plane" | "extrude" | "revolve" | "inflate", params?, item?, ids?, aligned?}`
changes an applied 3D effect into Rotate, Extrude & Bevel, Revolve or Inflate in place, as the object types of
**Window › 3D and Materials** do ([3D and Materials panel](three-d.md#3d-and-materials-panel)). The settings both
effects take keep their values (rotation angles are wrapped into -180..180 when the new type is not Revolve), the
settings only the new effect takes start at its defaults, and `params` then merge into the new effect's settings,
checked as that effect's. It is one undo step. Any other `threeDType` value, or an effect at `index` that is not a
3D effect, is an error and changes nothing. The type the effect already has, with no `params`, `visible` or
`warpStyle`, changes nothing, records no undo step and returns `{ids: []}`.
```json
{"name":"run_command","arguments":{"command":"effect.setParams","params":{"index":0,"item":null,"threeDType":"inflate","params":{"volume":70}}}}
```
## Extrude & Bevel, Inflate and Rotate
`effect.apply {effect: "threeD.extrude" | "threeD.inflate" | "threeD.rotate", params: {...}, ids?, item?}` applies
@@ -3592,7 +3637,11 @@ control channel's `ui.drag` scrubs), `showHomeScreen` (on by default: the Home s
off, an empty window, and the Home button or `app.home` still shows the screen), `autoCollapseIconPanels` (off by
default: on, a click away from a panel popped out of the icon column puts it away), `displayPrintSize` (off by
default: View › Actual Size / `view.actualSize` is one document point per screen point; on, one document inch fills
96 screen points, an inch at the system's reference density whatever the display scaling). `highlightAlternateGlyphs` (off by default: on, the canvas paints a pink cell behind glyphs OpenType features
96 screen points, an inch at the system's reference density whatever the display scaling). The tool shortcut
preference `holdToolKeys` also needs the desktop app. It is on by default: a single-key tool shortcut held for 0.25 s
or more, or held while its tool is used, chooses the tool until the key is let go and then returns to the tool active
before, after any drag in progress; a quicker press chooses the tool for good. With it off, every press and every key
repeat chooses the tool. `highlightAlternateGlyphs` (off by default: on, the canvas paints a pink cell behind glyphs OpenType features
substituted — ligatures, contextual forms — on screen; Document Setup's Highlight Substituted Glyphs stays
separate). `highlightSubstitutedFonts` (on by default: the canvas paints pink behind text drawn in a substitute for a
missing font, in every document). `blackOnScreen`
@@ -3650,6 +3699,11 @@ Guides change where a snapped point lands:
object under the pointer (the gap between their facing edges on an axis on which they lie apart, else the offsets
of their two edges on it) or, over no object, to the left, right, top and bottom edges of the artboard under the
pointer: measured lines labelled in the General unit. Off, Alt-hovering shows none.
- `snapToLastLocation` (Snap to Last Location, off by default): with Smart Guides on, a selection moved by dragging
(Selection, Group Selection or Direct Selection tool / `pointer_gesture` with `selection`, Shift too) goes back to
where the move began while the move is no longer than `snappingTolerance`, labeled "last location"; farther, it
snaps as before. An Alt copy is not pulled back onto its original, and with View › Snap to Grid or Snap to Pixel on
the move snaps to the grid or to whole pixels instead. A value that is not a boolean is refused.
The Snap to Glyph options (the desktop app's Character panel menu › Show Snap to Glyph Options; View › Snap to Glyph
is the desktop app's): `view.snapToGlyph.options {baseline?, xHeight?, bounds?, proximity?, angular?, anchors?}` (or
@@ -3860,6 +3914,12 @@ first; no spaces), stored back as the object name.
`typeSizeIncrement`, `baselineShiftIncrement`) with `measure: "general"|"type"`; they are kept in points and also take
a string with a unit. Unit names: `Points`, `Picas`, `Inches`, `Millimeters`, `Centimeters`, `Pixels`, `Feet & Inches`,
`Meters`, `Yards`, `Feet` (the preferences use `points`, `picas`, … `feetInches`, `meters`, `yards`, `feet`).
`edit.preferences {category?}` opens the Preferences dialog (`preferences`) with its search field focused. Its
`__search` field (`ui.dialog.set`) holds the search text: while it has text, the category list is replaced by the
settings whose label, section or category contains it, ignoring case, in the interface language or in English, or by
"No settings found". Choosing a setting sets `__category` to its category, sets `__found` to its key (the row is
highlighted until another category is shown) and clears `__search`. Cmd/Ctrl+F focuses the field again; Escape in the
field with text clears it, and Escape in the empty field closes the dialog.
```json
{"name":"run_command","arguments":{"command":"prefs.set","params":{"key":"unitsGeneral","value":"millimeters"}}}
@@ -3937,6 +3997,12 @@ with an `index` it closes the document and all its views. `window.nextDocument`
through the views in tab order and add `view` to their reply for a document with more than one view. `ui.inspect`
reports `windows` (every window as `[document index, view number]`, in tab order), `currentWindow` and each pane's
`view`. Views are not saved.
`window.control.dock {edge}` (desktop app) docks the Control bar to the top of the window, under the menu bar
(`edge: "top"`, the default), or to its bottom (`"bottom"`), and returns `{edge}`. The bar's panel menu runs it with
Dock to Top and Dock to Bottom, the edge in use checked, and so does a drag of the bar's gripper, which docks the bar at
the edge the pointer is released nearer and shows a band along that edge while it moves. The edge is kept with the
workspace, and a workspace saved without it docks the bar at the top. Another `edge`, or none, fails with "`edge` must
be one of top, bottom" and leaves the bar where it is.
```json
{"name":"run_command","arguments":{"command":"file.new","params":{"preset":"A4","orientation":"landscape","artboards":4,"artboardLayout":{"columns":2},"bleed":9}}}
@@ -4178,6 +4244,16 @@ is the width of one video pixel over its height, which `view.videoRulers` counts
the native format keep both (written only when on or not 1), and `inspect_document` adds `showVideoSafeAreas: true` and
`videoRulerPixelAspectRatio` to an artboard that has them.
Artboard Options' Global checkboxes are application preferences, not artboard properties: `fadeRegionOutsideArtboard`
(Fade Region Outside Artboard) and `artboardUpdateWhileDragging` (Update While Dragging), both on by default
(`prefs.set {values: {fadeRegionOutsideArtboard: false}}`; also Edit › Preferences › General › Artboard Options). With
the first on, the canvas draws everything outside the artboards darker while the Artboard tool is active, the pasteboard
and the art on it: each color channel at 60% of its value, VectorCraft's own amount. With the second off, that area
draws as usual while the Artboard tool moves or resizes an artboard and darker again when the drag ends. When crossing
artboards would leave more than 4,096 separate rectangles in view, only the area outside the box around all the
artboards draws darker. Presentation Mode, exports and print do not change. The user guide describes both checkboxes under Artboard options
(https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf).
```json
{"name":"run_command","arguments":{"command":"artboard.copy","params":{"index":0}}}
{"name":"run_command","arguments":{"command":"edit.paste","params":{}}}
@@ -4289,9 +4365,11 @@ is mixed with middle gray by its alpha.
A placed image linked to its file (`file.place {path}`, Link on) records the file's absolute path, its size,
modification time and hash, and keeps a low-resolution preview (at most 256 px a side): a `.vectorcraft` save writes
the preview instead of the pixels, plus the path relative to the saved file. `document.open` reads
the preview instead of the pixels, plus the path relative to the saved file. Links inside symbols, graph designs,
patterns and opacity masks are saved with their relative paths too. `document.open` reads
the linked files again, looking for each at its path, then at its relative path and by name in the document's folder
(so a folder moved with its links still opens). Only files on this computer, or on the network share the document is
(so a folder moved with its links still opens), and each link, in the layers or inside that art, takes the path where
its file was found. Only files on this computer, or on the network share the document is
on, are looked at: a path to another computer, a device or a URL counts as missing. On Windows, a document opened from
a mapped drive (`Z:\…`) is on the share the drive is mapped to, and links on that share (`\\server\share\…`) are
looked at. A link on a drive mapped to another share (`Y:\…`) counts as missing, from a document on this computer too.
@@ -4301,7 +4379,19 @@ The result lists `missingLinks`
path isn't followed) when it isn't. The desktop app also checks the active document's links when its window
comes back to the front, following the same preference. `links.check` reports every link's `status` (`ok`, `modified`,
`missing`), `links.update {ids?}` reads modified files again and `links.relink {ids?, path | folder}` points images at
another file (or each at the file of its name in a folder); images keep their bounds, one undo step each. A PNG, JPEG,
another file (or each at the file of its name in a folder); images keep their bounds, one undo step each.
`links.relink`'s `scope` picks which links take the new file (Relink's Replace choice): `selected` (the default) relinks `ids`; `all` also relinks every other object in the layers linked to the same file as
one of them; `missingInFolder` relinks `ids` to `path`, then every other missing link to the file of its name in
`path`'s folder, and lists the names not there in `notFound` (with `copy`, only missing images are looked for); `each` takes `paths: [{ids, path}]` in place of `ids`,
`path` and `folder` and relinks each link to its own file. An unknown scope, `paths` without `each`, `each` with `ids`,
`path` or `folder`, an entry without `path` or `ids`, an id in two entries and `missingInFolder` with `folder` are
errors, and nothing is relinked. Each call is one undo step. The Links panel's Relink… opens the `relink` dialog
(`ui.relinkDialog`) with a Replace menu of these four choices. A PNG, JPEG,
another file (or each at the file of its name in a folder); images keep their bounds, one undo step each.
`links.update`, and `document.open` when it reads a modified file, also read the file again for the images and placed
documents inside symbols, graph designs, patterns and opacity masks that show the same content (by default also for
those that only that art shows); they have no ids, so the results do not list them. Saving a document also reads it
again for the placed documents of it inside that art in the other open documents. A PNG, JPEG,
GIF or WebP file whose pixels can't be read is not shown: `links.relink` fails with "the image's pixels can't be
read", and `links.update`, or `document.open` when it reads the file, lists it as missing, its images keeping what they
showed. Without a file system (the web), linked images show their previews. A link whose path has a colon and no `/`
@@ -5126,7 +5216,11 @@ turn it with the object while its path geometry is still baked, so after a rotat
stay square to it, as in the reference app. `object.rotate {angle, absolute: true}` turns the selection to an angle
(the Transform and Properties panels' Rotate field), and `object.setBounds` measures width, height and the reference
point along the turned box (x, y stay page coordinates). Objects turned together share the box. A new group starts
square to the page. `object.resetBoundingBox` squares the box again without moving the art.
square to the page. `object.resetBoundingBox` squares the box again without moving the art. It also squares every
object inside the selected ones: the children of groups, compound paths, clip groups, blends, envelopes, repeats and
intertwines, and the art of linked opacity masks (an unlinked mask's art keeps its angle). Ungroup, Release Clipping
Mask, Release Compound Path, Blend Expand, Release Envelope, Release Repeat, Release Intertwine and Release Opacity Mask
then bring back boxes square to the page.
```json
{"name":"run_command","arguments":{"command":"object.rotate","params":{"angle":45,"absolute":true}}}
@@ -5435,8 +5529,8 @@ Force) of the selected type (or `ids`): an East Asian comma or full stop ending
、。) hangs outside the frame, Regular only when it doesn't fit, Force always (the rest of the line then fills the
measure). Closing brackets and Latin punctuation don't hang; point type, right-to-left and hyphenated lines are left as
they are. The hanging mark is as wide as its punctuation spacing makes it (`mojikumi`). New type (the Type tools,
`text.create`) takes `standard`; documents from before it and imported text keep `none`, and `none` isn't saved. One
undo step.
`text.create`) takes `standard`; documents from before it and imported text keep `none`, except legacy `.ai` type
with binding punctuation (`Tg`) on, which opens as `forced`. `none` isn't saved. One undo step.
```json
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"burasagari":"forced"}}}
@@ -5872,7 +5966,18 @@ paragraph at `start`; `paraStyle.list` counts paragraphs as uses, and `charStyle
the layers, opacity masks, symbols, graph designs, pattern tiles and the art of brushes. `charStyle.unused` and
`paraStyle.unused` (Select All Unused in the Character Styles and Paragraph Styles panel menus) list the styles with no
uses, in panel order and never the Normal style, and the panel highlights them; `charStyle.delete` and
`paraStyle.delete` take `{names}` to delete several styles in one undo step. A Return the Type tool types (or `text.editRange`
`paraStyle.delete` take `{names}` to delete several styles in one undo step. `charStyle.load` and `paraStyle.load`
(Load Character Styles… and Load Paragraph Styles… in the panel menus, where `ui.loadCharStyles` and
`ui.loadParaStyles` pick the file) copy the styles of that kind from another document VectorCraft opens (`path`, `data`
or `dataBase64`), and `textStyle.loadAll` (Load All Styles…, `ui.loadAllStyles`) copies both kinds, each in one undo
step. The swatches and patterns the styles paint with, the symbols and brushes those patterns use, and the styles of
either kind that type in those patterns and symbols is set in, come along as Paste brings those of pasted type; a global or spot color whose name the document gives another color
is the document's. A loaded style that is the same as the style of
its name, or of its name with a number (`Body 2`), is not added again; any other is added under a free name. The
document's styles and the type that uses them keep their attributes, and the file's Normal styles are not loaded. The
reply lists the names `added` and `existing` in the document and the names `skipped` in the file (a style whose
attributes do not read or are out of the range `charStyle.new` takes, or whose name is empty); `textStyle.loadAll`
replies `{charStyles, paraStyles}`, and `charStyle.load` and `paraStyle.load` list only the styles of their own kind. A Return the Type tool types (or `text.editRange`
inserting `\n`) continues the style of the paragraph it splits; deleting a break keeps the first paragraph's style;
`text.setText` gives every paragraph the first one's attributes. Threaded text keeps each paragraph's attributes as
the story re-flows (a paragraph split between frames has them in both).
@@ -6004,6 +6109,10 @@ the entry's `hash` as `hash` so that a module that changed since is refused. Wit
an error naming the permissions needed. The grant is kept with the module's hash; a module
with the same id that lists no new permission installs without `grant`, and `plugin.remove` forgets the grant. A
filter without `doc.write` or `state` that returns output needing them gets an error and the document is unchanged.
A plug-in of API level 2 (`api: 2` in its entry) that lists `progress` may report its progress, see Cancel and write
log lines: `notifications/cancelled` makes its `vc_cancelled` return 1, and its log lines go to the server's log
under its id. A module that imports anything else, or imports these without `api` 2 and `progress`, is refused by
`plugin.install` and `plugin.inspect` with an error naming the import.
`plugin.install` rejects a `replace` or `allowHeavy` value that is not a boolean, a `grant` that is not a list of
permission names, and a `hash` that is not the module's SHA-256. `prefs.set` returns an error and
changes nothing when a new `pluginsFolder` is missing, is not a folder or can't be listed. It does not check an empty
@@ -6302,6 +6411,13 @@ envelopes, the path for a top-object envelope); the content keeps the envelope's
opacity mask.
Type inside an envelope distorts as its glyph outlines, run by run in each run's paint, on the canvas and in every
export (SVG, PDF, EPS, EMF/WMF, DXF).
An envelope maps type from the bounds of its glyph outlines, underlines and strikethroughs instead of its laid-out
box (ascent to descent, or an area's frame) when the type is on its own, in groups and layers, or used as a clipping
path. Inline graphics in the type count by their boxes. Rotated type and type in perspective map from the bounds of
their outlines as they are drawn. `object.envelope.makeWithMesh`, `object.envelope.makeWithTopObject` and
`object.envelope.resetWithMesh` with `maintainShape: false` use those bounds. Type that draws nothing keeps its
laid-out box, and so does type inside any other object, such as a blend, a repeat or a symbol instance. This is
VectorCraft's own rule, and envelopes of type in saved files redraw by it.
Everything inside bends with the envelope: images as a raster mesh warp (cut into pieces by Fidelity, each a clipped
image under its own affine map; SVG shares the pixels between the pieces), symbol instances as their symbol's art,
and with Distort Appearance strokes (as their filled outlines) and geometry effects; with it, Distort Linear Gradients
@@ -6423,7 +6539,19 @@ shows, and so do the click-to-size dialogs (`perspective.draw` with `at`: the cl
`object.blend.make` blends the selected objects (or `ids`) into a live blend. Spacing and orientation default to
what `object.blend.options` set with no blend selected (`object.blend.info` reads them: `target: "defaults"`),
else Smooth Color and Align to Page. `starts` gives, per object, the anchor of its first subpath the blend starts
else Smooth Color and Align to Page. Smooth Color counts the steps between each pair of keys on its own, from the
keys' colors and visible bounds rather than along the spine. The counts below are VectorCraft's own values. The
colors compared are each key's solid fill and stroke, the fills and strokes of two text objects' runs at the same fraction of their characters, and
those of two groups' members paired in order. When they differ, the count is 255 times the largest change of one
channel as a fraction of its range, a half step rounding down, from 1 to 254. An RGB channel's range is 256 values,
so pure red to pure blue makes 254 steps. CMYK and gray count by their percentages, two tints of the same swatch by
their tints, and colors in different spaces (or Lab) by their RGB values over 255. The count is then at most eight
per point of the largest of the keys' widths and heights and the distance between their centers along either axis,
and when a key has no bounds it is the color count alone. When the colors match, a solid paint meets no paint, or a
key holds a gradient or a pattern, the count is the largest distance between the keys' corresponding visible
bounding-box edges divided by their mean width (left and right edges) or height (top and bottom), rounded, less one,
from 1 to 1000. Keys with no width or no height are measured by their other side, keys with neither in points, and
keys without bounds make 1 step. `starts` gives, per object, the anchor of its first subpath the blend starts
from (as the Blend tool's clicks on anchor points do; an open path's last anchor runs it the other way). With the
Blend tool, `press_key` Escape drops the object clicked first and stops the next click adding to the blend
just made; the document and the selection stay as they are. A blend
+48 -15
View File
@@ -26,7 +26,7 @@ This page is the contract for plug-in authors and the reference for the host (`c
| Command | Params | What it does |
|---|---|---|
| `plugin.list` | `{}` | `{"plugins": [{id, name, version, kind, author, description, params, paramsSchema, effect?, format?, extensions?, budget?, size, source, permissions, hash}], "folder": report}` (`budget: "heavy"` for a [heavy filter](#heavy-filters); `permissions` lists what it [may do](#permissions), `hash` is the module's SHA-256) |
| `plugin.list` | `{}` | `{"plugins": [{id, name, version, kind, author, description, params, paramsSchema, effect?, format?, extensions?, budget?, size, source, api, permissions, hash}], "folder": report}` (`api` is the manifest's [plug-in API level](#permissions); `budget: "heavy"` for a [heavy filter](#heavy-filters); `permissions` lists what it [may do](#permissions), `hash` is the module's SHA-256) |
| `plugin.info` | `{"id"}` | One plug-in: the `plugin.list` entry plus `manifest`, `defaults` and `lastError` (a live effect's last failure). |
| `plugin.run` | `{"id", "params"?: {…}, "ids"?: [...]}` (plug-in params may also be top-level keys) | Runs an object filter on the selection (or `ids`). One undo step; the result is selected, except the objects it locked or made guides. |
| `plugin.install` | `{"path": "/x/y.wasm"}` (desktop) or `{"dataBase64": "…", "name"?}`, `"replace": bool = true`, `"allowHeavy": bool = false`, `"grant"?: [permission]`, `"hash"?: "sha256 hex"` | Validates and installs a module; returns its `plugin.list` entry. The desktop app and `vectorcraft-cli` keep it in the `Plug-ins` folder next to the preferences, so it is installed again after a restart (#2541). A [heavy filter](#heavy-filters) installs only with `allowHeavy: true`, once the user agreed. A plug-in whose manifest lists [permissions](#permissions) installs only when `grant` lists every one of them, once the user agreed. With `hash`, a module whose SHA-256 differs is refused. |
@@ -83,7 +83,7 @@ The host is [`wasmi`](https://github.com/wasmi-labs/wasmi), a pure-Rust WebAssem
| Limit | Default | On violation |
|---|---|---|
| Host imports | none: no WASI, file system, network, clock or randomness | module rejected at install |
| Host imports | none at API level 1; at level 2 only the three [host imports](#host-imports-api-level-2): no WASI, file system, network, clock or randomness | module rejected at install, naming the import |
| Module size | 32 MiB | rejected |
| Linear memory | 512 MiB per instance (input and parameters: at most half) | `memory.grow` returns -1; oversized input or initial memory fails |
| Output | 64 MiB of JSON | `Limit("output size")` |
@@ -134,6 +134,7 @@ capability model proposed for PhotoCraft plug-ins
| `effect.render` | effect | Change how the objects its effect is applied to are drawn. |
| `export.write` | exporter | Write the document as a file in its own format. |
| `import.read` | importer | Read files you open in its format and make documents from them. |
| `progress` | all | Show its progress, see Cancel and write log lines through the [host imports](#host-imports-api-level-2). Needs `api` 2. |
- A manifest without `permissions` gets the ones its kind implies, as plug-ins did before the key existed: a filter
`doc.read`, `doc.write` and `state`; an effect `doc.read` and `effect.render`; an exporter `doc.read` and
@@ -141,9 +142,9 @@ capability model proposed for PhotoCraft plug-ins
A filter whose list is empty may do none of these, and also installs without a prompt.
- An effect, an exporter or an importer that lists permissions lists all of its implied ones. A filter may list any of
its three.
An unknown name, a name listed twice, a name that doesn't apply to the kind, more than 16 names, or one of the names
kept for a later plug-in API (`progress`, `ui.panel`, `ui.tool`) is refused when the module is installed, and the
error names it.
Any kind may add `progress`, which no kind implies. An unknown name, a name listed twice, a name that doesn't apply
to the kind, more than 16 names, `progress` with an `api` below 2, or one of the names kept for a later plug-in API
(`ui.panel`, `ui.tool`) is refused when the module is installed, and the error names it.
- **Installing asks first.** Install Plug-in… and File › Open show the plug-in's name, id, version, the SHA-256 of
the file and each permission in plain words, and install it once the user clicks OK, if the file still has the
SHA-256 shown; Cancel installs nothing. The
@@ -153,15 +154,18 @@ capability model proposed for PhotoCraft plug-ins
`plugin.inspect` entry's `hash` so that the module installed is the one the user saw.
- The grant is recorded with the plug-in's id, the module's hash and the permissions, in `grants.json` in the
`Plug-ins` folder. Installing a module with the same id that lists no new permission needs no new approval; one that
lists more asks again. `plugin.remove` forgets the grant. When a `Plug-ins` folder is in use, only the grants kept
lists more asks again. `plugin.remove` forgets the grant. An entry of `grants.json` that this version can't use (a
permission it doesn't know, an invalid id) is skipped and stays in the file until its plug-in is granted again or
removed. A `grants.json` that is not a JSON list is never overwritten, and the file is never written larger than
1 MiB, the most that is read. When a `Plug-ins` folder is in use, only the grants kept
in it count for `plugin.install`, `plugin.inspect`, `plugin.reload` and the Additional Plug-ins Folder. At startup,
a stored plug-in whose permissions are not granted is skipped. The Additional Plug-ins Folder and `plugin.reload`
install only plug-ins whose permissions were granted, and report the others.
- The host enforces the grant on a filter run: without `doc.read` the filter is given no objects; without `state` its
`documentData` is `null` and an output that sets it is refused; without `doc.write` an output that changes the
document is refused. A refused output leaves the document as it was.
- `api` is the plug-in API level the manifest is written for: `1` (the default) is the only level this host
implements, and a higher one is refused.
- `api` is the plug-in API level the manifest is written for: `1` (the default) or `2`, which adds the [host
imports](#host-imports-api-level-2). A higher one is refused. `plugin.list` and `plugin.info` give it as `api`.
- Parameter declarations are unchanged by permissions (`params`, below).
Every run gets a **fresh instance**, so no state survives between calls. Traps, exhausted budgets, malformed
@@ -184,8 +188,9 @@ geometry: a plug-in runs again only when one of them changes.
## ABI v1
A plug-in is a core WebAssembly module (MVP + the usual post-MVP features: bulk memory, sign extension, multi-value,
saturating conversions; no SIMD, no memory64) with **no imports** and these exports. Integers are `i32` unless noted;
pointers are offsets into the module's memory.
saturating conversions; no SIMD, no memory64) with these exports and no imports other than the [host
imports](#host-imports-api-level-2) of API level 2. Integers are `i32` unless noted; pointers are offsets into the
module's memory.
| Export | Signature | Meaning |
|---|---|---|
@@ -195,6 +200,34 @@ pointers are offsets into the module's memory.
| `vc_alloc` | `(size) -> ptr` | A block of `size` bytes the host may write, or `0` on failure. Never freed: each run gets a fresh instance. |
| `vc_run` | `(input, input_len, params, params_len) -> i64` | Runs the plug-in on the input JSON with the parameters JSON; returns `(len << 32) \| ptr` of the output JSON, or a negative error code. |
### Host imports (API level 2)
A plug-in whose manifest gives `"api": 2` and lists the `progress` [permission](#permissions) may import these
functions from module `env` (the module a Rust or C toolchain gives `extern` functions by default). `vc_abi_version`
still returns `1`.
| Import | Signature | Meaning |
|---|---|---|
| `vc_progress` | `(done: i64, total: i64)` | `done` of `total` steps are finished; `total` 0 or less when not known. Negative values count as 0, and `done` is clamped to `total`. |
| `vc_cancelled` | `() -> i32` | `1` once the user cancelled the run or its time budget ran out, else `0`. |
| `vc_log` | `(level, ptr, len) -> i32` | Writes the UTF-8 text at `ptr` as a log line of `level` (0 error, 1 warning, 2 information, 3 debug). Returns `0` when written, `1` when the run's log budget is spent and the line was dropped, `-1` when the text doesn't lie inside the module's memory, `-2` when it is not UTF-8, `-3` for another level. |
- The host checks the imports when the module is installed: an import it doesn't offer, one with another signature,
and one the manifest doesn't allow (an `api` below 2, or no `progress` permission) refuse the module, and the error
names the import.
- A filter's or an exporter's progress is shown as a share: a heavy filter's progress window and the export's progress
window draw a bar with a percentage, and Cancel there makes `vc_cancelled` return `1`. A plug-in that checks it can
stop early, and the host discards what it returns after the Cancel or after its time budget ran out; one that
ignores it is still stopped by the instruction and time budgets. With a known total, a report is passed on only when
it reaches another tenth of a percent; with an unknown total, each change of `done` is passed on. After 2,000 reports
in a run, one change in 1,000 is passed on, and so is the first report after the total becomes known or unknown. A
live effect's progress is not shown, since it is drawn as part of something else.
- Log lines go to the app's log (target `vectorcraft_plugins`) as `<plug-in id>: <text>`, with control and
bidirectional characters escaped. The whole text must be UTF-8. A line shows its first 1,024 bytes, as at most 1,024
characters with the escapes, and ends with `…` when it is cut. A run writes at most 200 lines and 32 KiB of shown
text, and the host reads at most 1 MiB of log text in a run; past any of these, one "log truncated" line is written
and the rest are dropped.
### Manifest
```json
@@ -400,12 +433,12 @@ crates/plugins/example/build.sh # cargo build --release --target wasm32-unk
The script also refreshes `crates/plugins/tests/fixtures/desaturate.wasm`, the test fixture built from that source.
The crate is not a workspace member. Any language that targets core WebAssembly works (C, Zig, AssemblyScript,
WAT…), as long as the module has no imports: in Rust, `std` on `wasm32-unknown-unknown` is fine as long as it never
WAT…), as long as the module imports nothing but the host imports: in Rust, `std` on `wasm32-unknown-unknown` is fine as long as it never
touches WASI. `vectorcraft_plugins::wat::module` is the skeleton of a plug-in in WebAssembly text, which the tests
build their plug-ins from; `wat::echo_exporter` is an exporter whose file is its input or its parameters (with a
base64 encoder written out in WebAssembly text), which the tests read back what the host hands a plug-in with, and
`wat::when_given` a plug-in whose output depends on whether its input or parameters hold a given text, which shows in a
live effect's geometry.
live effect's geometry. `wat::reporting` and `wat::cooperating` build level 2 plug-ins that import the host functions.
## Performance
@@ -415,9 +448,9 @@ per change of their input, parameters or paints (results are cached), not per fr
## Not in v1
- Tool plug-ins (pointer events on the canvas, a guide line or rectangle, geometry back), panel plug-ins, host
callbacks (a plug-in reporting progress or asking whether it was cancelled), and importers that place files
(File › Place reads only the built-in formats) or make type, images, layers or artboards.
- Tool plug-ins (pointer events on the canvas, a guide line or rectangle, geometry back), panel plug-ins, and
importers that place files (File › Place reads only the built-in formats) or make type, images, layers or
artboards.
- Live effects that change paint (they reshape geometry; an object filter recolours), and filters on type, images or
symbols.
- Apply Last Effect and Last Effect work for plug-in effects; there is no "last filter" for object filters yet.
+1 -1
View File
@@ -89,7 +89,7 @@ Checked 2026-10-10 from the code, tests and open issues; no workflow fails, so V
| Set type: point and area type, Character and Paragraph styles, threading, outlines | yes | type core 80%; styles, threading, Create Outlines and font matching tested ([type-parity.md](type-parity.md)); justification options missing (not blocking) | 0 |
| Organise layers and artboards and export assets (Export for Screens, SVG, PNG, PDF per artboard) | yes | Layers panel rework, Artboards panel and tool, Export for Screens and Asset Export done; Art Optimized supersample closes ghost borders (#983 / #1037) | 0 |
| Print production: CMYK and spot colour, bleed and marks, PDF/X out, separations preview | yes | PDF/X-1a/3/4, presets, marks and bleed, ICC output intent, Separations Preview, Print with PostScript output | 0 |
| Exchange files with Illustrator users: open their `.ai`/EPS/PDF, hand back a file they can open | partial (doesn't block alpha) | `.ai`, EPS and PDF open as editable art, but real files can get the wrong layer structure (#951); Save As `.ai` is PDF-compatible with an editing copy of the art (layers, groups, paths, colors, gradients, patterns, images, transparency, opacity masks, clipping and type as text, with live effects left out and symbols and live objects written as their art; #868), or legacy version 7, which keeps layers, groups and type as text, one artboard per file, and waits for users' testing. EPS legacy mesh corners, vector opacity masks and raster print-page comparison now preserve editable art under synthetic regressions. This is the beta main-format gap ([gaps.md](gaps.md) G9) | 10–20 (for beta) |
| Exchange files with Illustrator users: open their `.ai`/EPS/PDF, hand back a file they can open | partial (doesn't block alpha) | `.ai`, EPS and PDF open as editable art, but real files can get the wrong layer structure (#951); Save As `.ai` is PDF-compatible with an editing copy of the art (layers, groups, paths, colors, gradients, patterns, images, transparency, opacity masks, clipping, type as text and symbols, with live effects kept only on paths, compound paths and groups that qualify (at most a fill and a stroke, a non-zero fill rule when filled, no large canvas) and whose appearance and effects the art-style writer supports ([file-format-parity.md](file-format-parity.md)), other objects' effects other than geometry effects left out, and live objects written as their art; #868), or legacy version 7, which keeps layers, groups and type as text, one artboard per file, and waits for users' testing. EPS legacy mesh corners, vector opacity masks and raster print-page comparison now preserve editable art under synthetic regressions. This is the beta main-format gap ([gaps.md](gaps.md) G9) | 10–20 (for beta) |
Hindi interface follow-up (2026-11-06): the catalog and zero/one plural rule are implemented;
native wording review, layout checks and a web Devanagari font remain. [Notes](localization-hi.md).
+50 -8
View File
@@ -202,7 +202,7 @@ dominate; 3D, graphs and automation are specialist). They sum to 105. Feature de
| Layers, artboards, document setup | 5 | 78% | Selected-artboard deletion is consistent across panel actions (all-selected retains artboard 1); history prunes removed indices while preserving valid multiselection; remaining: 30.0's artboard locking and colours and managing artboards from the canvas, Layers panel row context menus and Collect for Export (click-to-fit objects is done, #1227) | 5–8 | estimated (lowered from 82%: target moved); Artboard deletion keys keep artwork and the first of an all-selected set; Artboard Options' Show Center Mark and Show Cross Hairs draw on the canvas only (exports and print leave them out), regression-tested; Artboard Options' Show Video Safe Areas and Video Ruler Pixel Aspect Ratio (per artboard, native format) are done ([video display options](#evidence-what-ships-today-by-area)), score unchanged |
| View & navigation | 3 | 70% | multiple windows and the rest of Arrange (Cascade, Float in Window, Float All in Windows) | 9–14 | estimated; Window › Arrange › Tile and Consolidate All Windows done (score/hours unchanged); document-tab artwork hover/copy and lossless transfer tested (2026-10-22; score/hours unchanged); cached canvas reprojection preserves scale during resize, rotation and DPI changes (regression and contributor original-file evidence below; scores/hours unchanged); cached canvas dimensions match the renderer limit before cache-key creation, view construction and worker submission (regression evidence below) |
| File formats | 6 | 75% | `.ai` files open with a flattened or wrong layer structure in real cases (#951), no writing of Illustrator's editing data (#868), FreeHand/CorelDRAW/DWG/HEIC import, 3D export; see [file-format-parity.md](file-format-parity.md); EPS legacy mesh/opacity-mask and raster comparison follow-up verified | 10–20 | estimated (lowered from 85%: open issues); DXF control-point SPLINE import closure regression-tested (#1299 follow-up after #1320); PDF writer metadata, reader streaming and immutable backend checks in `crates/pdf/src/tests.rs`, `crates/pdf/src/tests/reader_streaming.rs` and `xtask/src/pdf_deps.rs`; PDF Place and clipboard paste retain live text (synthetic regressions); EPS ordinary Save retains the page frame and editable document (2026-10-26); `.ai`/EPS editing data checked against a user's files (2026-10-30): case setting (`ate::tests`, `tests_native::type_from_the_text_document_keeps_its_case_setting`), placed-file placements (`tests_native::a_small_placed_file_goes_where_it_draws_closer_to_the_page`), pages cropped to the art (`tests_native::type_on_the_copies_of_a_page_cropped_to_the_art_is_read_once`), mesh corner weights and state (`ai::mesh::tests::corners_with_handle_weights_of_zero_are_read`, `ai::tests::a_gradient_mesh_takes_the_transparency_written_after_it`), type on a path spacing (`tests_native::type_round_a_circle_is_spaced_as_the_file_lays_it_out`; the distance from the path under Align to Path comes from VectorCraft's font metrics, and the fit under Gravity does not reproduce the file's layout), blend state (`ai::tests::a_blends_transparency_follows_its_record`); scores and estimates unchanged. |
| File formats | 6 | 75% | `.ai` files open with a flattened or wrong layer structure in real cases (#951), a saved `.ai`'s editing copy without live effects and with symbols and live objects as their art (#868), FreeHand/CorelDRAW/DWG/HEIC import, 3D export; see [file-format-parity.md](file-format-parity.md); EPS legacy mesh/opacity-mask and raster comparison follow-up verified | 10–20 | estimated (lowered from 85%: open issues); DXF control-point SPLINE import closure regression-tested (#1299 follow-up after #1320); PDF writer metadata, reader streaming and immutable backend checks in `crates/pdf/src/tests.rs`, `crates/pdf/src/tests/reader_streaming.rs` and `xtask/src/pdf_deps.rs`; PDF Place and clipboard paste retain live text (synthetic regressions); EPS ordinary Save retains the page frame and editable document (2026-10-26); `.ai`/EPS editing data checked against a user's files (2026-10-30): case setting (`ate::tests`, `tests_native::type_from_the_text_document_keeps_its_case_setting`), placed-file placements (`tests_native::a_small_placed_file_goes_where_it_draws_closer_to_the_page`), pages cropped to the art (`tests_native::type_on_the_copies_of_a_page_cropped_to_the_art_is_read_once`), mesh corner weights and state (`ai::mesh::tests::corners_with_handle_weights_of_zero_are_read`, `ai::tests::a_gradient_mesh_takes_the_transparency_written_after_it`), type on a path spacing (`tests_native::type_round_a_circle_is_spaced_as_the_file_lays_it_out`), blend state (`ai::tests::a_blends_transparency_follows_its_record`); scores and estimates unchanged. |
| File formats | 6 | 75% | `.ai` files open with a flattened or wrong layer structure in real cases (#951), a saved `.ai`'s editing copy without live effects on objects other than paths, compound paths and groups that qualify (at most a fill and a stroke, a non-zero fill rule when filled, no large canvas) and whose appearance and effects the art-style writer supports, and with live objects as their art (#868), FreeHand/CorelDRAW/DWG/HEIC import, 3D export; see [file-format-parity.md](file-format-parity.md); EPS legacy mesh/opacity-mask and raster comparison follow-up verified | 10–20 | estimated (lowered from 85%: open issues); DXF control-point SPLINE import closure regression-tested (#1299 follow-up after #1320); PDF writer metadata, reader streaming and immutable backend checks in `crates/pdf/src/tests.rs`, `crates/pdf/src/tests/reader_streaming.rs` and `xtask/src/pdf_deps.rs`; PDF Place and clipboard paste retain live text (synthetic regressions); EPS ordinary Save retains the page frame and editable document (2026-10-26); `.ai`/EPS editing data checked against a user's files (2026-10-30): case setting (`ate::tests`, `tests_native::type_from_the_text_document_keeps_its_case_setting`), placed-file placements (`tests_native::a_small_placed_file_goes_where_it_draws_closer_to_the_page`), pages cropped to the art (`tests_native::type_on_the_copies_of_a_page_cropped_to_the_art_is_read_once`), mesh corner weights and state (`ai::mesh::tests::corners_with_handle_weights_of_zero_are_read`, `ai::tests::a_gradient_mesh_takes_the_transparency_written_after_it`), type on a path spacing (`tests_native::type_round_a_circle_is_spaced_as_the_file_lays_it_out`), blend state (`ai::tests::a_blends_transparency_follows_its_record`); scores and estimates unchanged. |
| View & navigation | 3 | 70% | multiple windows and the rest of Arrange (Cascade, Float in Window, Float All in Windows) | 9–14 | estimated; Window › Arrange › Tile and Consolidate All Windows done (score/hours unchanged); document-tab artwork hover/copy and lossless transfer tested (2026-10-22; score/hours unchanged); cached canvas reprojection preserves scale during resize, rotation and DPI changes (regression and contributor original-file evidence below; scores/hours unchanged) |
| View & navigation | 3 | 70% | multiple windows and the rest of Arrange (Cascade, Float in Window, Float All in Windows) | 9–14 | estimated; Window › Arrange › Tile and Consolidate All Windows done (score/hours unchanged) |
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; video rulers (View › Rulers › Show Video Rulers, #2849) done, score unchanged |
@@ -283,8 +283,9 @@ The discounts, with the evidence:
ones are about structure, not missing art: layers flattened or multiplied (#951: 109 layers instead of 14;
#868), guides lost (#779), slower opening (#758), live effects, brushes and symbols as their drawn look
(#637). VectorCraft's PDF-compatible `.ai` carries an editing copy of the art (layers, groups, paths, colors,
gradients, patterns, images, transparency, opacity masks, clipping and type as text), with live effects left out
and symbols and live objects written as their art; the legacy `.ai` of version 7 it also writes keeps layers, groups and type as text (one
gradients, patterns, images, transparency, opacity masks, clipping, type as text and symbols), with live effects kept only
on paths, compound paths and groups that qualify (at most a fill and a stroke, a non-zero fill rule when filled, no large canvas) and whose appearance and effects the art-style writer supports, other objects' effects other than geometry
effects left out, and live objects written as their art; the legacy `.ai` of version 7 it also writes keeps layers, groups and type as text (one
artboard per file, no transparency) but hasn't been tested by users yet (#868). That blocks beta (the main-format rule) for people who hand files back to Illustrator users,
but it doesn't stop someone opening and reworking their own archive.
- **Interaction fidelity (×0.9).** Pen, Direct Selection, smart guides and snapping are deep
@@ -518,6 +519,7 @@ since 2026-10-01.
## Evidence: what ships today, by area
- 3D lighting presets: `crates/effects/src/lighting_presets.rs`, `crates/engine/src/cmd/effectcmd/lighting_presets_tests.rs` and the pointer tests in `crates/ui-egui/src/dialogs/revolve/tests.rs` cover recognition, atomic targeting, native round trips, preview, Cancel, reset and Undo.
- Window › 3D and Materials panel: `crates/ui-egui/src/panels/three_d.rs` applies a 3D effect by object type (Plane, Extrude, Revolve, Inflate), changes an applied one in place (`effect.setParams` `threeDType`) and edits it with the options dialog's Object, Rotation and Lighting controls, one undo step per change; `crates/engine/src/cmd/effectcmd/three_d_type_tests.rs` and `crates/ui-egui/src/panels/tests_three_d.rs` cover it. No Materials tab.
- **Status-bar pointer readout (2026-10-18):** uses the same effective ruler origin as the position fields, then formats the relative X/Y in General units. The absolute pointer position, document, selection, Undo/Redo and saved state are preserved. Three `chrome::tests::status_coordinates_*` painted-text regressions cover zero/positive/negative origins, unit conversion, global/artboard mode, active artboard, reset and missing pointer/document. Original headless status-bar captures were reviewed before and after the change. Scores and hours are unchanged.
@@ -546,6 +548,7 @@ since 2026-10-01.
- **Global Edit Options in the Control bar:** while Select › Start Global Edit can run, the Control bar shows a Global Edit Options dropdown that opens the same options as the Properties panel's (Match: Appearance, Size; On Artboards; Include Objects On Canvas; Result(s)); both set the same options. The dropdown is off while Global Edit is on; it sits after Recolor Artwork, ahead of the X/Y/W/H fields ([Edit similar objects globally](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-similar-objects.html)). Regression test: `the_control_bar_shows_the_global_edit_options` in `crates/ui-egui/src/chrome/tests.rs`.
- **Global Edit's enabled check on large paths:** the check that decides whether Select › Start Global Edit can run on the selected path, compound path or symbol instance (which the Control bar makes every frame) measures the box of its anchors and handles instead of building and normalizing a copy of every anchor, with the same answer. On a 200,000-anchor path the check takes 0.7 ms instead of 2.2 to 3.0 ms. Regression tests: `support_*` in `crates/engine/src/cmd/globaledit.rs`.
- **Control bar's inline X, Y, W and H fields fit the bar:** the Control bar shows the inline X, Y, W and H fields only when all four, their labels as drawn in the current language and the W/H link fit inside the bar's margin; otherwise it shows the Transform link alone. This is VectorCraft's own layout rule. Regression test: `the_control_bar_shows_the_inline_transform_fields_only_when_they_fit` in `crates/ui-egui/src/chrome/tests.rs`.
- **Control bar docks to the top or the bottom:** the Control bar's panel menu, at the bar's right end, has Dock to Top and Dock to Bottom, the edge in use checked, and dragging the bar's gripper docks it at the top or the bottom edge of the window, whichever the pointer is released nearer, with a band along that edge while it moves (`window.control.dock {edge}`), as the help page describes ([Control panel overview](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/control-panel-overview.html)). Keeping the edge with the workspace, with older workspaces docking the bar at the top, is VectorCraft's own rule. Not done: dragging the gripper away from both edges to float the bar. Regression tests: `the_control_bar_panel_menu_docks_it_to_the_top_or_the_bottom`, `dragging_the_control_bar_gripper_docks_it_at_the_nearer_edge`, `the_control_bar_dock_command_takes_top_or_bottom` and `menus_stop_above_a_control_bar_docked_at_the_bottom` in `crates/ui-egui/src/chrome/tests.rs`, `saved_workspaces_restore_the_control_bar_edge` in `crates/ui-egui/src/workspaces.rs`.
- **Pattern-only backquote drags:** With Selection move, bounding-box scale or rotate, or the Rotate, Reflect, Scale or Shear tool, backquote held at mouse release applies the drag matrix to selected pattern paint while object geometry stays in place. The key may be pressed after mouse press; if released before the mouse button, the drag commits as an ordinary transform. Objects without pattern paint stay still; stroke-only patterns and patterned objects in mixed selections take the same matrix. Regression tests: `backquote_move_uses_release_state_and_one_commit` and `backquote_box_scale_and_rotate_use_pattern_matrix_at_release` in `select/tests.rs`, `backquote_drag_previews_and_commits_pattern_only_for_each_transform_tool` in `xform/transform.rs`, `selection_backquote_drag_changes_pattern_fill_and_stroke_without_moving_mixed_art` and `selection_backquote_drag_changes_stroke_only_pattern` in `tests/pattern.rs`, `egui_backtick_drag_uses_key_state_at_release` in `canvas/tests.rs`, and `synthetic_pointer_backquote_keeps_solid_object_in_place` in `control/tests.rs`. Scores and hours unchanged.
- **Existing-guide Escape cancellation (G1):** Escape clears a pending guide press or drag through Selection and Direct/Group Selection, cancelling a begun preview. Eight app-input regressions cover both axes, move/copy, temporary selection-tool borrowing from idle Pen, preselected-guide document/interaction-start selection and Undo/Redo/saved-state preservation, later on-canvas motion/release, Shift-press cancellation, and ordinary-release/Enter/idle controls. Historical focused checks on `8ab574b7` recorded five expected RED failures with three passing controls, then all eight GREEN tests and the Direct Selection Escape control passed. All nine also passed in current full CI. A full CI retry on the five-unit combined source at `a0671777` passed all seven stages with 6,613 test outcomes on 2026-10-14. An earlier full-CI run on the identical source panicked in the randomized `ai_mutated_save_never_panics` test; the cause remains unresolved. This five-fix batch does not claim to repair that failure. The original 1280×960 headless app capture from `b076025a` was reviewed for one vertical Selection guide after Escape; source review found its scene unchanged on `a0671777`. Scores and hours unchanged.
- **Escape in more tool drags:** Escape during an Anchor Point drag, a Mesh tool drag, a Direct Selection drag of a mesh point, a corner widget or the brackets of type on a path, or a Shape Builder drag cancels it: the art goes back to how the press found it, no undo step is added, the release does nothing, and the tool stays chosen. Escape during a Measure tool drag brings back the measurement before it. Regression tests: `escape_cancels_*` in `draw2/anchor/tests.rs`, `meshedit.rs`, `direct/tests.rs`, `builder/tests.rs` and `xform/measure.rs`, and `escape_cancels_an_anchor_point_mesh_shape_builder_or_measure_drag` in `tests/toolescape.rs`.
@@ -592,6 +595,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Rulers (#1212):** drag the intersection to preview and set a document zero point, or double-click it to reset to the active artboard's top-left. Transform, Properties and Control X/Y fields measure from it and convert entered positions back to absolute command coordinates. The origin survives every native format version and compression; geometry, guides and patterns stay in place. Public reference: [Use rulers](https://helpx.adobe.com/ca/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html).
- **Global and artboard rulers:** View › Rulers › Change to Global / Artboard Rulers (Cmd+Alt+R, `view.globalRulers`). A new document has artboard rulers: the rulers and the Transform, Properties, Control and Info X/Y count from the active artboard's top-left corner, or the zero point dragged out for that artboard, which moves with it; with global rulers one document zero point serves every artboard. Guide readouts and Shift ruler-tick snapping follow the same zero point. The mode is saved with the document; files from before it open with global rulers, as they measured. Regression tests: `engine/tests/it/ruler_origin.rs`, `ui-egui/src/tests/ruler_origin.rs`, `tools/src/rulerguide.rs`. Public reference: [About rulers](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/about-rulers.html).
- **Video display options (#2849):** Artboard Options' Show Video Safe Areas and Video Ruler Pixel Aspect Ratio, and View › Rulers › Show Video Rulers ([Set video display options for artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/set-video-display-options-for-artboards.html)). Each artboard has its own (`artboard.setProps`/`artboard.new` `showVideoSafeAreas`, `videoRulerPixelAspectRatio` 0.1–10, default 1), set in one undo step and written to the native format only when on or not 1, so other documents save byte for byte as before. The help page names the safe areas without numbers; our choice is [EBU R 95](https://tech.ebu.ch/docs/r/r095.pdf)'s (note 5): the canvas outlines the action-safe area 3.5% and the title-safe (graphics-safe) area 5% in from each edge, 1 point wide and turned with the view; exports and print leave them out. Show Video Rulers (`view.videoRulers`) shows the rulers counting the active artboard in pixels from its top-left corner, whatever the ruler origin: a pixel is as many points wide as the ratio and one point tall, so a 720 × 480 pt artboard at 0.9 reads 800 × 480. Hide Video Rulers returns them to the General unit; Shift ruler-tick snapping of new guides keeps the General unit. Regression tests: `tests/panelcmds.rs` (`artboard_video_display_options_*`, `artboard_display_marks_stay_out_of_exports`), `tests/docsetup.rs` (`an_artboard_shows_its_video_safe_areas`), `canvas.rs` (`video_rulers_count_the_artboard_in_pixels_of_its_aspect_ratio`), `tests/ruler_origin.rs` (`show_video_rulers_counts_the_active_artboard_in_pixels`), `dialogs/tests.rs` (`artboard_options_show_video_display_options`).
- **Fade Region Outside Artboard:** Artboard Options' Global checkboxes Fade Region Outside Artboard and Update While Dragging ([user guide, Artboard options](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf)) are application preferences (`fadeRegionOutsideArtboard`, `artboardUpdateWhileDragging`), both on by default. While the Artboard tool is active the canvas draws everything outside the artboards, the pasteboard and the art on it, with each color channel at 60% of its value (VectorCraft's own amount), or only outside the box around all the artboards when crossing artboards would leave more than 4,096 rectangles in view; with Update While Dragging off it draws as usual during an artboard move or resize. Presentation Mode, exports and print do not change. Regression tests: `canvas::tests` (`the_artboard_tool_fades_the_pasteboard`, `presentation_mode_draws_no_fade`, `the_fade_darkens_art_on_the_pasteboard_and_not_on_the_artboard`, `outside_rects_cover_the_bounds_minus_the_boards`, `outside_rects_of_a_row_of_boards_stay_few`, `the_fade_of_crossing_boards_stays_bounded`, `update_while_dragging_keeps_or_drops_the_fade_during_an_artboard_drag`, `the_pasteboard_fade_leaves_exports_unchanged`), `dialogs/tests.rs` (`artboard_options_global_checkboxes_set_the_preferences`, `artboard_options_error_leaves_the_global_preferences_unchanged`).
- **Saved views (G7):** View › New View… and Edit Views… ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html): up to 25 views per document, saved with it, renamed or deleted in Edit Views, chosen from the bottom of the View menu). Each view keeps the zoom, centre and rotation; the View menu lists all 25 (`view.goto1`–`view.goto25`), and Edit Views… lists them to rename or delete, applied together as one undo step. A file's views are checked when it opens (at most 25, unique names up to 255 characters, a finite centre and a positive zoom). Tests: `saved_views_round_trip_and_from_a_file_are_tidied` (`crates/format/tests/prop_format.rs`), `crates/engine/src/cmd/views.rs`, `crates/ui-egui/src/dialogs/edit_views.rs` and `every_saved_view_is_listed_at_the_bottom_of_the_view_menu` (`crates/ui-egui/src/menus.rs`).
- **Window › Arrange › Tile (G7):** Tile (`window.arrange.tile`) shows every open document at once, edge to edge in the document area ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html)), one pane each in a grid of as many columns as the square root of the number of documents, rounded up, each fitted to its pane. The current document's pane is the canvas; the other panes draw their documents with their artboards, guides and selected objects, and a press in one makes its document the current one. Consolidate All Windows (`window.arrange.consolidateAllWindows`) shows one document under its tab again. The grid and when the two items are enabled (Tile with two documents or more, Consolidate All Windows while they are tiled) are VectorCraft's own. Tests: `crates/ui-egui/src/canvas/panes/tests.rs`.
- **Window › Arrange › Tile (G7):** Tile (`window.arrange.tile`) shows every open document at once, edge to edge in the document area ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html)), one pane each in a grid of as many columns as the square root of the number of documents, rounded up, each fitted to its pane. The current document's pane is the canvas; the other panes draw their documents with their artboards, guides and selected objects, and a press in one makes its document the current one. Consolidate All Windows (`window.arrange.consolidateAllWindows`) shows one document under its tab again. The grid and when the two items are enabled (Tile with two documents or more, Consolidate All Windows while they are tiled) are VectorCraft's own. Tests: `crates/ui-egui/src/canvas/panes/tests.rs`.
@@ -599,6 +603,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Dragging a document tab out and a document window back (G7):** a document tab dragged more than 24 pt above or below the tab strip floats its document in a window under the pointer, and a document window dragged onto the tab strip goes back under the tabs ([public source](https://helpx.adobe.com/illustrator/using/customizing-workspace.html)). A window inside the app's window is dragged by its title bar; an OS window by the document's tab at its top, and the window follows the pointer. The 24 pt distance and the tab at the top of an OS window are VectorCraft's own. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
- **Reordering document tabs (G7, #5663):** a document tab dragged along the tab strip moves its document among the others ([public source](https://helpx.adobe.com/illustrator/using/customizing-workspace.html)); a line marks the gap it goes in. The current document stays current, each document keeps its own zoom, centre and artboard, the views Window › New Window opened move with their document, and Window › Next/Previous Document follow the new order. `document.move` (`{from?, to}`) does the same from MCP, the control channel and recordings. `crates/ui-egui/src/tests/tab_reorder.rs`, `crates/engine/src/tests/document_move.rs`.
- **Preferences › User Interface › Open Documents As Tabs (G7, #394):** checked by default, documents open under tabs in one document window ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/workspace-overview.html)). With the box cleared, each document opened or created afterwards opens in a floating window of its own, at the next cascade place, as Float in Window floats it; documents already open stay where they are, and checking the box again leaves floating documents in their windows. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
- **Preferences search:** a Search Preferences field above the category list has the keyboard focus when the dialog opens, and Cmd/Ctrl+F gives it back; typing lists the settings it finds, and choosing one shows its category, as the [help page](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/set-app-preferences.html) describes. VectorCraft's own rules: the field matches a setting's label, section or category, ignoring case, in the interface language or in English; the list replaces the categories while the field has text and shows every match, or "No settings found"; the chosen setting's row is highlighted and scrolled into view; Escape in the field with text clears it, and Escape in the empty field closes the dialog. The dialog's `__search` field (`ui.dialog.set`) holds the text. Tests: `crates/ui-egui/src/prefs_dialog/tests.rs`.
- **Window › New Window (G7, #4116):** New Window (`window.newWindow`) opens another view of the current document ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html) describes showing one document in several windows at once), with its own zoom, center, rotation, artboard and Outline or Preview, as a tab of its own; an edit in one view shows in every view. Tile, Cascade, Float in Window, Float All in Windows and Consolidate All Windows arrange each view as they arrange a document, and Next and Previous Document step through the views. Closing a view leaves the document open; closing its last view closes the document, asking to save it first. The view's number after the name in its tab ("Name:2", while the document has more than one view) is VectorCraft's own. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
- **Document Setup modal handoff:** Document Setup → Edit Artboards applies valid settings, closes the dialog and activates the Artboard tool. Pointer-click tests cover successful handoff, invalid settings, OK, Cancel and Undo in `dialogs/document_setup.rs`.
@@ -657,6 +662,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- MCP protocol: five prompts (`prompts/list` / `prompts/get`: poster, icon set, recolour, trace-and-style, export set), `completion/complete` for prompt arguments and template variables read from the live catalogues (formats, effect ids, command ids, trace presets, swatch and object ids), four resource templates (`vectorcraft://object/{id}`, `//command/{id}`, `//effect/{id}`, `//swatch/{name}`) so an agent reads one thing instead of the whole document, and `logging/setLevel` with `notifications/message`. `window.panel` ids are case-insensitive (display labels work too), `vectorcraft://object/{id}` reads through `document.node {summary: true}` (the same reply bytes from one lookup instead of summarizing the whole document: a leaf read on a 2,000-object document drops from ~20 ms to ~0.06 ms), large containers slice with `document.node {depth?, childLimit?}` (truncated levels report `childCount`), `document.inspect` and the `inspect_document` tool slice the same way so a depth-0 read is the layer skeleton (~9.7 KB instead of ~2 MB on a 2,000-object document), `document.find` locates nodes by name/kind/text with ancestor paths, and the CLI sweeps the protocol surface over stdio against the compiled binary. `docs/mcp.md` has a Protocol section covering the capability table and what is deliberately absent (subscriptions, progress, cancellation, sampling, elicitation, Streamable HTTP — all of which need the transport to interleave reads).
- Headless CLI (`vectorcraft-cli run`, `convert`, `info`, `bench`, `perf`, `mcp`). A `run` step, like a `command_batch` step over MCP, can use an earlier step's result (`"$1.id"`, #785); `run --commands FILE` (or `-` for standard input) reads the steps from a JSON array or JSON Lines, past the Windows command-line limit (#3147, `apps/vectorcraft-cli/tests/it/cli.rs` `run_reads_*`, `run_refuses_malformed_commands_naming_the_step`).
- File › Scripts › Check Script… reads a script written for Illustrator (`.jsx`, `.jsxinc` or a compiled `.jsxbin`) without running any of it and reports what it needs (the document model, files, dialogs, menu commands, features only Illustrator has), what it would do outside the document, the menu commands and document-model members it uses, its directives and included files, and the problems that would stop it loading (`script.check`, which also reads `.js` files); File › Open and drag-and-drop show the report for `.jsx`, `.jsxinc` and `.jsxbin` files.
- `vectorcraft-js` parses JavaScript as ECMAScript 5.1 defines it, with the legacy octal forms, block functions and lenient regular expressions of Annex B, into a syntax tree with source spans; it reads the `#target`, `#include` and other preprocessor lines as directives without running them, and a script that breaks the grammar or an early-error rule gives a `SyntaxError` with its line and column. Nesting past the parser's depth limit (`Options::max_depth`, 200 by default) gives a `SyntaxError` instead of exhausting the stack. `cargo xtask test262` fetches the conformance suite at a pinned commit and parses its ES5 tests: 5,411 of 5,418 runs pass (the 7 others use `let` and `const`). Nothing runs scripts yet. Tests: `crates/js/src/tests.rs`, `crates/engine/tests/it/js_lexer_vs_scriptcheck.rs`.
- **UI:** Illustrator 2026 layout restyled to measured values:
- Medium Dark theme, categorized and Advanced toolbars (a tool group's flyout opens on a right press, a long press or a press on its corner triangle), 35 pt document tabs, 33 pt panel tabs.
- Hint bar, contextual task bar, 19 dock panels with ≡ menus.
@@ -693,9 +699,11 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
The status bar's messages for documents opened or saved with notes and for discarded or restored recovered documents, and the Global Edit options' Results line, do the same (`open_save_and_recovery_messages_take_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`); the warnings for linked images and placed documents output as their previews and the errors for a page past a placed file's last page or artboard name their counts in the singular or the plural in English.
- Edit › Undo and Edit › Redo, in the in-window and native menus, name the step in the interface language as the History panel lists it (`panels::history::step_name`), so when the catalog has no plain row for a step named after an effect, such as Feather, the menu shows the translation of the effect's menu row without its ellipsis (`undo_and_redo_name_the_step_as_the_history_panel_does`).
- The History panel translates the undo step names the engine, the tools and the shell write as literals: the untranslated report (`untranslated_report`) lists any a complete catalog lacks, and a step whose catalog row exists only with an ellipsis (such as an effect's menu name) shows that row without the ellipsis.
- The History panel menu has Step Backward and Step Forward, which run Edit › Undo and Edit › Redo and are disabled when there is no step to undo or redo, and Set History Limit…, which opens Preferences at Performance, where History States is set. Not done: a new document from the selected state.
- The History panel menu has Step Backward and Step Forward, which run Edit › Undo and Edit › Redo and are disabled when there is no step to undo or redo, and Set History Limit…, which opens Preferences at Performance, where History States is set.
- The History panel menu's Clear History (`edit.clearHistory`) removes every step to undo and to redo and leaves the document as it is; it is not an undo step itself, and it is disabled when there is no step to clear.
- The New Document from State button at the bottom of the History panel (`edit.newDocumentFromState`) makes a new document, which becomes active, from the state the panel has selected ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/undo-edits-and-manage-design-history.html)). VectorCraft's own rules: the new document takes the next Untitled-N name, is marked unsaved, has nothing selected and has no steps to undo or redo, and the source document and its steps stay as they are.
- The Transform panel menu's Transform Object Only, Transform Pattern Only and Transform Both (`transformPanelPatterns`) choose whether the fields, flips and menu flips of the Transform panel and the Properties panel's Transform section move the object and leave its pattern tiles, move the tiles alone, or both (until a choice they follow General › Transform Pattern Tiles, as before); Use Registration Point for Symbol (`useRegistrationPointForSymbol`) measures and transforms one selected symbol instance from its symbol's registration point, where the instance's flips, rotations and shears put it ([Transform panel overview](https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/transform-panel-overview.html); `panels::transform::tests::the_menu_chooses_whether_the_object_its_pattern_or_both_transform`, `use_registration_point_for_symbol_measures_an_instance_from_its_registration_point`, `use_registration_point_for_symbol_follows_a_transformed_instance`, `tests_prefs::transform_panel_patterns_defaults_to_following_the_general_preference`).
- Object › Transform › Reset Bounding Box squares the box of every object inside the selected ones as well: the children of groups, compound paths, clip groups, blends, envelopes, repeats and intertwines, and the art of linked opacity masks. Ungroup and the release commands then bring back boxes square to the page (VectorCraft's own behavior; `bboxrotate::reset_bounding_box_squares_the_objects_inside_a_group`, `reset_bounding_box_squares_the_turned_members_of_a_square_group`, `reset_bounding_box_then_release_clipping_mask` and the other `reset_bounding_box_then_*` tests, `reset_bounding_box_leaves_the_art_of_an_unlinked_opacity_mask`).
- Native macOS menu bar, vector tool cursors, a ⌘K command palette, middle-button panning with any tool. Native menus explicitly end tracking on command selection, outside window presses and Escape; menu updates wait until tracking ends, and a dismissing press never starts a canvas gesture (M14.8).
- The errors of the blend spine commands (Move Spine Point, Reshape Spine Segment, Add Spine Point and Delete Spine Point) show in the status bar in the interface language: each has a row in every catalog that translates all status and error messages and in Bulgarian (`blend_spine_param_errors_are_translated`).
- The errors of the parameter checks that many commands share show in the status bar in the interface language: a value that is not a number where a number goes (`object.move` with `"dx": "60 mm"`, and `object.rotate`, `object.setBounds` and the other commands that call `check_numbers`), a missing number or index, `ids` that are not a list of object ids, an object id that is not a non-negative integer or names no object, and a date that is not Unix seconds. Each has a row in every catalog that translates all status and error messages, and in Bulgarian, Danish, Indonesian and Dutch (`shared_param_check_errors_are_translated`).
@@ -712,6 +720,9 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Delete Symbol asks first** ([Symbols panel](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html), [symbol libraries](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-or-import-symbol-libraries.html), [Alt-click Delete](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)): the Symbols panel's Delete Symbol button and menu item ask "Delete the selected symbol?" with Yes and No for a symbol no instance uses. For a symbol with placed instances they ask with Expand Instances (the instances become ordinary art), Delete Instances and Cancel; either Instances button deletes the symbol in one undo step. Alt-click on the button deletes an unused symbol without asking and still asks about one with instances. A question left open while another document became active deletes nothing. `symbol.delete` itself does not ask. Tests: `panels::symbols::tests::delete_symbol_asks_first_and_alt_click_deletes_an_unused_symbol_at_once`, `the_in_use_question_expands_or_deletes_the_instances`, `a_stale_delete_symbol_question_deletes_nothing`, `the_delete_symbol_questions_show_their_buttons`.
- **Symbols › Select All Unused** ([Symbols panel](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html), #4918): the Symbols panel menu's Select All Unused shows the symbols nothing places selected (`symbol.unused`): instances in the art (opacity masks included), graph designs, pattern tiles and brush art count, and so do instances in a used symbol's art. Delete Symbol, the button or the menu item, then asks "Delete the selected symbols?" once and deletes those still unused in one undo step (`symbol.delete {names}`); Alt-click on the button deletes them without asking, and a click on a symbol picks just it again. Tests: `tests::brushsym::symbol_unused_lists_the_symbols_nothing_places_and_delete_takes_several`, `panels::symbols::tests::select_all_unused_picks_the_unused_symbols_and_delete_deletes_them`, `dialogs::symbol_delete::tests::several_symbols_get_the_plural_question`.
- **Expand of symbol instances:** Object › Expand turns selected symbol instances (without effects, not in perspective, in groups or placed documents too) into their art as Break Link to Symbol places it, one level deep, as one undo step; the Expand dialog's Fill and Stroke reach that art (`object.expand`, `crates/engine/src/tests/expand_symbols.rs`; estimated, score unchanged).
- **Reset Transformation** ([Transform symbols](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/transform-symbols.html), [Symbols panel options](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html)): the Symbols panel menu's Reset Transformation and the Control bar's Reset, shown while symbol instances are selected, give the selected instances their symbol's own scale again, without rotation or shear, as one undo step (`symbol.instance.resetTransform`); an instance in perspective is left as it is. VectorCraft's own rules: each instance keeps the center of its bounding box in place, reflection is removed too, and the menu item and the button are enabled only while a selected instance would change. Tests: `tests::brushsym::reset_transformation_restores_the_symbols_scale_and_keeps_the_center`, `reset_transformation_keeps_the_center_of_art_away_from_the_origin`, `reset_transformation_ignores_a_stored_size_that_is_not_positive`, `reset_transformation_leaves_unchanged_instances_and_refuses_bad_ids`, `panels::symbols::tests::reset_transformation_is_enabled_for_a_transformed_instance_and_resets_it`.
- **Reset Transformation** ([Transform symbols](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/transform-symbols.html), [Symbols panel options](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html)): the Symbols panel menu's Reset Transformation and the Control bar's Reset, shown while symbol instances are selected, give the selected instances their symbol's own scale again, without rotation or shear, as one undo step (`symbol.instance.resetTransform`); an instance in perspective is left as it is. VectorCraft's own rules: each instance keeps the center of its bounding box in place, reflection is removed too, and the menu item and the button are enabled only while a selected instance would change. Tests: `tests::brushsym::reset_transformation_restores_the_symbols_scale_and_keeps_the_center`, `reset_transformation_keeps_the_center_of_art_away_from_the_origin`, `reset_transformation_leaves_unchanged_instances_and_refuses_bad_ids`, `panels::symbols::tests::reset_transformation_is_enabled_for_a_transformed_instance_and_resets_it`.
- **Reset Transformation** ([Transform symbols](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/transform-symbols.html), [Symbols panel options](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html)): the Symbols panel menu's Reset Transformation and the Control bar's Reset, shown while symbol instances are selected, give the selected instances their symbol's own scale again, without rotation or shear, as one undo step (`symbol.instance.resetTransform`); an instance in perspective is left as it is. VectorCraft's own rules: each instance keeps the center of its bounding box in place, reflection is removed too, and the menu item and the button are enabled only while a selected instance would change. Tests: `tests::brushsym::reset_transformation_restores_the_symbols_scale_and_keeps_the_center`, `reset_transformation_leaves_unchanged_instances_and_refuses_bad_ids`, `panels::symbols::tests::reset_transformation_is_enabled_for_a_transformed_instance_and_resets_it`.
- **Expand of dimensions:** Object › Expand, Expand Appearance, Create Outlines and Outline Stroke turn a dimension whose art they change into a plain group of that art, which stays as it is through later moves, scales and edits of what it measured; undo gives the live dimension back (`crates/engine/tests/it/dimensions.rs`; VectorCraft's own rule; estimated, score unchanged).
- **Brush libraries** ([public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/create-new-brush-libraries.html), [import](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/import-brushes-from-brush-libraries-or-other-files.html), [Brushes panel](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/brushes-panel-overview.html)): Window › Brush Libraries, the Brushes panel's Brush Libraries Menu and its menu's Open Brush Library open each library in a library panel of its own, as swatch, graphic style and symbol libraries do. The built-in libraries (Art Ribbons, Calligraphic Nibs, Pattern Borders, Scatter Shapes) are original brushes generated in code (`crates/engine/src/cmd/brushlib.rs`); Striped Ribbon's art is filled with the library's own pattern and Sparkles scatters the library's own symbol. A click on a brush adds it to the document's Brushes as one undo step with every resource its art uses (patterns, symbols, swatches, graphic styles, images), makes it the current brush and, with paths selected, applies it to them in the same step; a brush the document already has is not added again and a different brush of the same name comes in numbered ("Wave 2"). Shift/Cmd-clicks select brushes for Add to Brushes; a brush dragged onto a path is added and applied to it, and one dragged onto the Brushes panel is added. Save Brushes… / the Brushes panel's Save Brush Library… write the document's brushes (or the selected ones) as a `.vectorcraft` library into the `Brushes` folder next to the preferences, listed under User Defined, or to a file; Other Library… reads the brushes of any document VectorCraft opens. Thumbnails, in the library panels, the Brushes panel and Brush Options, render brush art with the patterns and symbols it uses. Copying brushed art now carries the patterns and symbols its brush's art uses too. Tests: `crates/engine/src/tests/brushlib.rs`, `crates/ui-egui/src/panels/tests_brush_libraries.rs`, `panels::brushes::tests::library_brush_previews_show_the_patterns_their_art_uses`, `canvas::tests::a_library_brush_dropped_on_a_path_is_added_and_applied_in_one_step`.
- A canvas context menu: right-click selects the object under the pointer and lists what applies to the selection (Undo/Redo, clipboard, group, isolation, join, Remove Anchor Points when anchors are selected, masks, compound paths, guides, Transform, Arrange, Select, Export Selection), or the view commands on empty canvas.
@@ -729,7 +740,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- Preferences audit (2026-10-30, #394): every Preferences dialog option is read by shipped code, by its field or its key, except User Interface › Open Documents As Tabs, Units › East Asian Type, Hyphenation › Default Language, Plug-ins & Scratch Disks › Primary and Secondary, and Clipboard Handling › Legacy vector clipboard and its mode, which are stored only; [gaps.md G12](gaps.md#g12-preferences-and-panels-that-dont-act-yet) says what each waits on.
- With Performance › Real-time Drawing and Editing off, the bounding box moves with a Selection-tool move drag and the object moves when the button is released ([help](https://web.archive.org/web/20220324053041id_/https://helpx.adobe.com/illustrator/using/real-time-editing.html); `real_time_drawing_off_moves_the_bounding_box_with_a_move_drag`).
- Performance › Real-time Drawing and Editing off defers a drag's edits to its release, but a ruler guide dragged out of a ruler or moved still shows as it is dragged, where only its position label showed (#4646; `a_guide_dragged_out_of_a_ruler_shows_while_dragged`, `real_time_drawing_off_still_shows_a_dragged_guide`).
- With Real-time Drawing and Editing off, a dragged selection whose outlines have more than 100,000 points shows its moved bounds instead, as the selection at rest does (#525, #2460; `real_time_drawing_off_draws_the_bounds_of_a_selection_too_big_to_outline`).
- With Real-time Drawing and Editing off, a dragged selection whose outline points and evaluated objects come to more than 100,000 shows its moved bounds instead, as the selection at rest does (#525, #2460; `real_time_drawing_off_draws_the_bounds_of_a_selection_too_big_to_outline`, `real_time_drawing_off_draws_the_bounds_of_a_group_too_big_to_outline`, `real_time_drawing_off_draws_the_bounds_of_a_repeat_with_too_many_objects`).
- With Real-time Drawing and Editing off, a move or handle drag of a group, blend, envelope, repeat or intertwine draws the outline of each of its objects instead of one union of them, as VectorCraft's own rule (`real_time_drawing_off_outlines_a_dragged_group_member_by_member`, `real_time_drawing_off_outlines_a_resized_group_member_by_member`, `real_time_drawing_off_outlines_a_dragged_blend_step_by_step`, `real_time_drawing_off_outlines_a_dragged_envelope_object_by_object`). A group with a Pathfinder effect draws the area the effect makes, a gradient mesh draws its mesh lines, and a symbol instance draws its bounds (`real_time_drawing_off_outlines_a_dragged_pathfinder_group_by_its_area`, `real_time_drawing_off_outlines_each_mesh_of_a_dragged_repeat_once`, `real_time_drawing_off_outlines_a_dragged_symbol_instance_by_its_bounds`).
- Dialogs move by dragging their heading (the band down from the top edge, as a title bar), stay on screen and keep where they were moved for the session; the form dialogs' choices are dropdowns (Offset Path's Joins, Average's and Shear's and Reflect's axis), and Offset Path lists Offset, Joins, Miter Limit and previews (#423).
- Dialogs open with their first field focused and its value selected: type a value and press Enter (Move, Rotate, Scale…). A dialog opened from a menu takes the keyboard focus every time it opens, not only the first (#1367), and one whose first field starts greyed out (Blend Options on Smooth Color, Layers Panel Options) gives the focus to that field once it is enabled (#1395). Number fields in panels, the Control bar and dialogs (preferences too) step with Up/Down, or with the mouse wheel over the focused field (#485: a notch a step; the panel under it then stays put, and an unfocused field leaves the wheel to scrolling) (Shift: ten, Ctrl/Cmd: a tenth of their unit), applied at once.
- While Alt (Option on macOS) is held, the Flare Tool Options' Cancel button reads Reset, which puts every option back to the Flare tool's default and leaves the dialog open (`ui.dialogReset`).
@@ -738,10 +750,10 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- The Pattern Options panel's Name field and the name fields that a double-click opens in the Layers, Artboards and Asset Export panels take Shift+Enter (Shift+Return) the same way: it applies the name and keeps the field active with the name selected.
- Scrubby labels (#400, beyond Illustrator, as in Photoshop): dragging a numeric field's label left or right (the ↔ cursor shows over it) steps the value one unit every 2 points (Shift: ten, Ctrl/Cmd: a tenth; counts stay whole), in the field's unit and within its limits, applied live as the art follows; the whole drag is one undo step and Escape puts the value back. A click on the label focuses the field; the text box itself never scrubs, and a field with no label doesn't. Text labels before a field (Transform, Properties, Control bar, Stroke, Transparency, form rows in dialogs) and the Character and Paragraph panels' icons act as labels, and so do the links that label a field (the Control bar's Stroke: and Opacity:, the Properties panel's Stroke), which a click still follows to their panel (#4088). Preferences › General › Scrub Numeric Fields by Dragging (on by default) turns it off.
- In the Transform panel, the Properties panel's Transform section and the Control bar, Alt+Enter (Option+Return) applies a value to a copy of the selection and selects the copy, in one undo step. With anchor points selected with Direct Selection it applies the value to them, as Enter does. Ctrl/Cmd+Enter in W or H applies the value and scales the selection proportionally with the link off. With the Artboard tool, it scales the active artboard proportionally. A percentage typed alone in W or H (`50%`) is that share of the size the field shows, and the link and Ctrl/Cmd+Enter keep the proportions as with a length (#2401).
- The Control bar's underlined Transform link opens the Transform panel in a popover under it (reference point, X/Y/W/H, Rotate and Shear with their icons and angle presets, flips, shape properties, Scale Corners and Scale Strokes & Effects), as its Stroke link does the Stroke panel; X/Y/W/H also show inline while the bar has room. The reference point locator is nine squares, the outer eight joined around the box.
- The Control bar's underlined Transform link opens the Transform panel in a popover under it (reference point, X/Y/W/H, Rotate and Shear with their icons and angle presets, flips, shape properties, Scale Corners and Scale Strokes & Effects), as its Stroke link does the Stroke panel; X/Y/W/H also show inline while the bar has room. The reference point locator is nine squares, the outer eight joined around the box. As VectorCraft's own keyboard rule, the locator is one tab stop in the Control bar popover, the Transform panel, the Properties panel and the dialogs, the Arrange Tools dialogs included. While it has the focus it shows an outline, and ←, →, ↑ and ↓ move the reference point one square, stopping at the edges (`the_reference_point_grid_is_one_tab_stop`, `arrow_keys_move_the_reference_point`, `the_reference_point_locator_is_one_tab_stop_and_arrows_move_it`).
- In isolation mode the Control bar has an Exit Isolation Mode button (`object.exitIsolation`), except while a pattern is edited. Outside isolation mode, with one group, compound path, path, image, mesh or placed file selected, it has an Isolate Selected Object button (`object.isolate`), which also isolates a path inside a group that Direct Selection selected.
- While an opacity mask is edited, Exit Isolation Mode (Esc, the isolation bar's back arrow, the Control bar's button, and the Layers panel menu's and canvas context menu's Exit Isolation Mode) leaves mask editing as Stop Editing Opacity Mask does, in one undo step: the mask keeps the edits, and the masked object is selected. While a pattern is edited, the Exit Isolation Mode item of the Layers panel menu and of the canvas context menu saves the pattern and leaves pattern editing, as Done does.
- Tab shows and hides the panels (in the Type tool it types a tab) without moving the keyboard focus onto a field, so the next shortcut or letter still works; in dialogs and panel fields it moves between fields, one press each — a numeric field's scrub label is not a tab stop of its own, and the Transform panel's, Properties panel's and Control bar's W/H link comes after the fields it sits between (`one_tab_moves_from_a_field_to_the_next`).
- Tab shows and hides the panels (in the Type tool it types a tab) without moving the keyboard focus onto a field, so the next shortcut or letter still works; in dialogs and panel fields it moves between fields, one press each — a numeric field's scrub label is not a tab stop of its own, and the Transform panel's, Properties panel's and Control bar's W/H link comes after the fields it sits between (`one_tab_moves_from_a_field_to_the_next`). A field's up/down spinner, the chevron that opens its presets (Stroke Weight, font size, Rotate, Export for Screens' scale) and the Transparency row's opacity slider chevron are not tab stops either. Alt+↓ with no other modifier in a field with presets opens them with the keyboard focus on the chosen one, instead of stepping the field down; Enter picks the focused preset without pressing a dialog's OK, and after a pick or Escape the field has the keyboard focus again (`tab_goes_from_field_to_field_past_the_spinner_and_the_preset_chevron`, `tab_goes_from_the_opacity_field_past_its_slider_chevron`, `alt_down_opens_a_fields_presets_for_the_keyboard`, `escape_closes_a_fields_presets_and_returns_to_the_field`).
- Shift+Tab (`window.panels`) shows and hides the dock and the floating panels and leaves the toolbar and the Control bar as they are; hiding the panels with Tab or Shift+Tab also closes a panel popped out of the icon column.
- The second Tab (`window.dock`) shows again only the bars the first Tab hid, after a restart too: a Control bar that was off stays off, a toolbar hidden with Window › Tools stays hidden, and panels shown with Shift+Tab in between stay shown.
- The function key shortcuts for Brushes, Color, Layers, Info, Gradient, Stroke, Attributes, Graphic Styles, Appearance, Align, Transform, Pathfinder, Transparency, and Symbols activate a background tab on the first press, hide its expanded dock or floating group when that tab is in front, and restore the group with all its tabs together on the next press; in an icon column they open and close the flyout while keeping the icons. A reassigned chord keeps that behavior, and Window > a hidden sibling restores the group.
@@ -793,6 +805,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Edit › Keyboard Shortcuts lists each of the Align panel's Align Objects, Distribute Objects and Distribute Spacing buttons under Align, with no default shortcut, as the reference app's [release notes](https://web.archive.org/web/20261006170350/https://helpx.adobe.com/illustrator/desktop/new-features/release-notes.html) describe for its Align and Distribute commands. A shortcut set there runs what a click on the button runs, with the Align To choice, the key object and the Distribute Spacing value (`align.horizontalLeft` and the other `align.*` UI commands).
The command palette lists `select.key` as Cancel Key Object, and a pick cancels the key object (#541).
Agent selection commands validate every object id before changing the selection and report the resulting count and ids (#784).
Commands that act on `id` or else the selection, the highlighted layer row or a new object (`css.export`, `object.isolate`, `layer.locate`, `layer.releaseToLayers`, the graphic, character and paragraph style commands, `image.info`, the `links.*` and `shaper.*` commands that take an `id`, opacity mask editing and viewing, `symbol.edit`, the blend spine commands and `path.curvature`) refuse an `id` that is not an object id (`"abc"`, `"5"`, `true`) and change nothing; `effect.apply` with the effect given as `id` still applies it to the selection.
Agents read the key object: `select.key` returns it, and `document.inspect` and the `ui.pointer` and `pointer_gesture` replies report it as `key`. `select.key` with an id that isn't a selected top-level object returns an error and leaves the key unchanged.
The Select menu's Same commands, its Object commands that select art, Next Object Above and Below, saved selections and the Magic Wand (#2655) select only objects that Select All selects and objects inside them: nothing hidden or locked, nothing in a hidden or locked group, layer or sublayer, nothing on a template layer, and in isolation mode nothing outside the isolated group or layer. In isolation mode Select All, All on Active Artboard and Inverse select nothing while the isolated group or layer, or a group or layer it is in, is hidden, locked or a template layer.
Select ▸ Object ▸ Bristle Brush Strokes selects the objects whose topmost stroke has a bristle brush, whatever the brush is named, and leaves out objects whose brush is of another type, also when its name has "Bristle" in it.
@@ -856,13 +869,14 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
While a drag rotates the view, a dial at the center of the canvas shows the angle.
- **Editing type:** double-clicking type with a selection tool (Selection or Direct Selection) switches to the Type tool with the caret where it was clicked; pressing Escape finishes editing and switches back to the selection tool used before double-clicking it (when in isolation mode, the first Escape exits text editing while preserving isolation mode, and a second Escape exits isolation).
While a Type tool edits type, a click that lands on no type ends the editing without making new type, and that tool stays active; the next click makes new type (#2871, #2854).
As VectorCraft's own rule, Cmd+Enter (Ctrl+Enter on Windows and Linux) finishes editing as Escape does and keeps the typed text; a command bound to that chord runs instead.
Inside a ligature (one glyph for several characters, such as fi), the Type tool's caret, clicks, arrow keys and selection stop between its characters, at the font's ligature carets ([OpenType GDEF ligature caret list](https://learn.microsoft.com/en-us/typography/opentype/spec/gdef)) or, when the font does not list one valid caret for each stop, at the glyph's width divided equally. Contour-point carets use the font's unhinted outline at its default variation location; pixel-size device adjustments are not applied.
Duplicate Artboards, the Artboard tool's Alt-drag and Paste give the new artboard an id that no other artboard has, also when an artboard's id is the largest possible value (#1718). Deleting the copy or the original then leaves the other's guides.
- **Touch Type:** a click with the Touch Type tool (Shift+T) selects one character and shows a box around it. Dragging the box or its lower-left handle moves the character. The upper-left handle scales it vertically, the lower-right handle scales it horizontally, the upper-right handle scales it proportionally, and the upper-middle handle rotates it. Each drag is one undo step that sets the character's kerning, baseline shift, scales or rotation, and the type stays editable (`text.touchType`; behavior reference: [Touch Type tool](https://web.archive.org/web/20261007185617/https://helpx.adobe.com/illustrator/using/tool-techniques/touch-type-tool.html), [Add text with the Type, Area Type, and Touch Type tools](https://web.archive.org/web/20260926095922/https://helpx.adobe.com/illustrator/desktop/design-with-text/add-manage-text/add-text-illustrator-text-tools.html)).
Composition's leading-space reduction stays along the line when a character rotates. An opening bracket trimmed by Line-end Punctuation Half Width Mojikumi keeps that placement under Character Rotation; its turning point follows the composition adjustment, and removing the rotation keeps the trim. Scores and estimates are unchanged.
A character rotated near or at a quarter turn follows a Touch Type drag across its line as well as along it: baseline shift moves a character up from its line at any rotation, and the character turns about its shifted pen position.
Right-to-left characters can also move along their line when a preceding logical character is on that line. Positive `text.touchType` `dx` follows reading order, toward the left in horizontal right-to-left text; the first logical character still moves only up and down. These direction rules are VectorCraft's choices; the public Touch Type page describes moving a character without specifying this case. Scores and estimates are unchanged.
- **Temporary tools:** Cmd with any other tool drags with the selection tool used last (Selection, Direct Selection or Group Selection). Held Space pans and Cmd+Space zooms (Cmd+Alt+Space out) whatever the tool.
- **Temporary tools:** Cmd with any other tool drags with the selection tool used last (Selection, Direct Selection or Group Selection). Held Space pans and Cmd+Space zooms (Cmd+Alt+Space out) whatever the tool. A single-key tool shortcut held for 0.25 s or more, or held while its tool is used, chooses that tool only until the key is let go, and the tool active before comes back (after a drag in progress ends); a quicker press chooses the tool for good. This is VectorCraft's own rule and its own threshold, and General › Hold a Tool Shortcut to Use the Tool Temporarily (`holdToolKeys`, on by default) turns it off.
Space held while the Zoom tool drags a marquee moves the marquee and keeps its size ([Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)). When Space is let go, the drag sizes the marquee again from where it moved to.
A Space+Shift drag rotates the view whatever the tool, as a Rotate View tool drag does, and Shift does not round the angle to 15°.
- **Swatches panel clicks:** Ctrl/Cmd-clicking New Swatch makes a spot color, and Ctrl/Cmd+Shift-clicking it makes a global process color.
@@ -907,6 +921,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Panel Options › Locate Selected Objects (beyond Illustrator, off by default): 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 Locate Object does, without highlighting the 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.
- **Drawing aids:** Smart Guides and snapping (moving, with the Selection, Group Selection or Direct Selection tool, and resizing by the bounding-box handles with the Selection or Free Transform tool, Shift and Alt too: the dragged corner or side lands on the other objects' anchors and centres ("anchor", "center") or lines up with their edges and centres (a line and "align"); with Smart Guides › Spacing Guides, a moved selection that lines up with nothing along an axis lands as far from its neighbour as two other objects of its row or column are apart, or halfway between its two neighbours, the equal gaps marked; with View › Snap to Grid on, a moved selection lands on the grid instead, by whichever of its edges is nearest a gridline on each axis (the center does not snap), #740, #1816; the Rotate, Scale, Shear and Reflect tools drag from the anchor they grab and snap the pointer the same way, so a corner dragged onto another object's anchor scales exactly onto it, #482; anchors dragged with Direct Selection: the one pressed on lands on other anchors, its own path's that stay put among them, on path segments or in line with them, the others following, Shift keeps 45° steps, and with Smart Guides off Snap to Point pulls it onto anchors; the Artboard tool snaps artboards, and their bleed, to other artboards, bleeds and art, the grid or whole pixels while moving, copying and resizing them, and the Transform panel shows and edits the active artboard's X/Y/W/H; drawn points, #506: hovering shows where the press goes, the point lands on the other objects' anchors, centres and paths ("anchor", "center", "path") or lines up with their anchors, edges and centres ("align"), the Pen's on the path being drawn too, near a construction guide through the anchor before it on that guide (Preferences › Smart Guides › Construction Guides and Angles), and with Shift kept to its 45° step or a square's diagonal it slides along that way into line; the targets are gathered once per document state, from the art in the window, and the anchors, centres and lines near the pointer are found by bisection, so hovering never rescans the document), guides dragged out of the rulers (#451: they snap as moved guides do, Shift to the ruler's ticks; with the Artboard tool they are artboard guides, which run across their artboard only and move, copy and delete with it, `guide.add {artboard}`) that the Selection, Direct Selection and Group Selection tools then pick and drag (#414: the selected guides highlight and move together, Alt copies them, Shift snaps the dragged one to the ticks, otherwise it snaps to pixels, the grid or the edges, centres (so the sides' midpoints) and anchors of the art and the artboards, or with Smart Guides off Snap to Point pulls it onto anchors; Shift-clicked they join the selected art, and Shift-added art keeps them, so guides and art are selected together in either order, Delete then deleting both in one step; the arrow keys nudge them, Delete deletes them, dragging one back onto its ruler deletes it, Lock Guides and Hide Guides leave them alone; `guide.list`, `guide.select`, `guide.move` and `guide.remove` reach agents), a right-click on either ruler or the origin box to pick the document's units (the current one checked; `document.setUnits`, one undo step, #528), and Draw Normal / Behind / Inside modes. Smart Guides: a point drawn with the Pen or a shape tool, and an anchor dragged with Direct Selection, snaps to where paths and ruler guides cross, labelled "intersect" (#1658, part of #1602; a moved selection's own anchors don't).
A move that Shift keeps level, upright or diagonal stays on that line when it snaps, with the Selection, Group Selection or Direct Selection tool. It slides along the line until the selection lands on the grid or on whole pixels, lines up with the other objects or is spaced evenly with them, or until the line passes an anchor or crosses a ruler guide near the point the selection was grabbed by. A level or upright move snaps along its own axis only.
Snap to Last Location: with Smart Guides on and Preferences › Smart Guides › Snap to Last Location on (off by default, VectorCraft's own choice), a selection moved by dragging with the Selection, Group Selection or Direct Selection tool, Shift too, goes back to where the move began while it is within Snapping Tolerance of it, labeled "last location"; an Alt copy is not pulled back onto its original, and with View › Snap to Grid or Snap to Pixel on the move snaps to the grid or to whole pixels instead (`prefs.set` key `snapToLastLocation`). The public [Smart Guides options](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/smart-guides-options.html) page lists the option as snapping objects back to their previous position; the distance, the label, the copy rule and the grid and pixel rule are VectorCraft's own. Tests: `select::tests::snap_to_last_location_puts_a_move_back_where_it_began`, `select::tests::snap_to_pixel_wins_over_snap_to_last_location`, `direct::tests::snap_to_last_location_puts_a_path_moved_by_its_anchor_back`, `direct::tests::snap_to_pixel_wins_over_snap_to_last_location_for_a_path_moved_by_its_anchor`, `tests_prefs::snap_to_last_location_puts_a_dragged_square_back`.
Distance Guides: with Smart Guides on (View › Smart Guides and Preferences › Smart Guides › Invoke Distance Guides, on by default), something selected and the Selection tool, holding Alt/Option over another object shows the distances between the selection's bounds and that object's: on an axis on which they lie apart, one line across the gap between their facing edges; on one on which they overlap, the offsets of their left and right (top and bottom) edges, each line running across the middle of where they overlap, with a dashed line out to the other object's edge when it passes beside it. Over an empty part of an artboard it shows the selection's distances from the artboard's left, right, top and bottom edges. Each line has ticks at its ends and its length in a pill of the smart guide colour, in the General unit; letting Alt go, pressing (a pan or zoom press too), moving off them, leaving the canvas or a floating panel, menu or dialog over the pointer takes them away, and with the pointer still they follow what lies under it as the view scrolls, zooms or turns, as the document, the tab or the selection changes, and when the Selection tool is switched to. Behaviour from the public [Use Distance Guides for accurate placement](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-distance-guides-for-accurate-placement.html) and [Smart Guides options](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/smart-guides-options.html) pages, which describe hovering only: nothing shows while moving or nudging. Tests: `select::tests::alt_over_*`, `select::tests::distance_guides_need_alt_a_selection_and_smart_guides`, `tests_prefs::invoke_distance_guides_measures_the_selection_to_the_object_under_alt`, `canvas::tests::alt_over_another_object_draws_the_distance_guide_label`, `canvas::tests::distance_guides_go_when_the_hover_ends`, `canvas::tests::distance_guides_follow_what_lies_under_a_still_pointer`.
A move pressed on a path's stroke, or within 6 px of its outline away from its anchors and center, is held by the nearest point of that path, with the Selection, Group Selection or Direct Selection tool, and a straight segment Direct Selection drags is held by its own nearest point. That point is what snaps, so the path lands exactly on a ruler guide (with "align") instead of the pressed spot landing there and the path a few pixels beside it, an offset that showed only zoomed far in (#3701). A press in the middle of a fill still snaps the pressed spot: the public [Align objects with guides](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/align-graphic-objects-with-guides.html) page has Snap to Point align the point under the pointer, which is placed exactly where the object should meet the guide.
A bounding-box handle dragged with the Selection or Free Transform tool snaps what it moves as a dragged point snaps (#2054). With View › Snap to Grid on, it snaps to the nearest gridline, and with Snap to Pixel on, it snaps to whole pixels. With Smart Guides off and Snap to Point on, it snaps to the other objects' anchors and the ruler guides within the Snap to Point distance. With Smart Guides and Snap to Point both on, it lines up with the ruler guides as well as the other objects. Shift still keeps the proportions, and Alt still scales about the center.
@@ -951,6 +966,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Geometry and effects:**
- **Open-path anchor Delete:** Delete/Backspace removes selected anchors and their adjoining segments, leaving drawable runs as separate open subpaths of the original object. Surviving curves and appearance stay intact; one Undo restores the document and selection, and a second Delete removes the selected remainder. Explicit Remove Anchor Points still reconnects/refits. Four core regressions, one full-frame Delete/Backspace regression and three existing removal/segment controls pass, covering these distinctions, multiple deletions, transformed geometry and mixed whole-object selection. Behaviour references: the legacy public [Illustrator reference guide, Add or delete an anchor point](https://helpx.adobe.com/content/dam/help/en/pdf/illustrator_reference.pdf) distinguishes keyboard deletion from dedicated removal; the current [Add or remove anchor points](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/add-or-remove-anchor-points.html) describes the dedicated tools. Other G11 gaps and scores/hours remain unchanged.
- Pathfinder (10 exact curve booleans; single-result shape operations retain source names and SVG IDs, #1293), Offset, Outline Stroke, Simplify, Clean Up, Split Into Grid, Divide Objects Below. With one group selected alone, the Pathfinder panel's buttons work on its members (among other selected objects a group still acts as one shape), and a greyed-out button says what it needs (#1450; behaviour reference: [combine objects using Pathfinder effects](https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/combine-objects-using-pathfinder-effects.html); one plain path selected alone stays disabled, which the documentation doesn't settle). Split Into Grid takes the number, height or width, gutter and total of the rows and of the columns, Add Guides and Preview, and several selected paths become one grid painted as the topmost one (#1462, `object.path.splitIntoGrid`; behaviour reference: [divide or split objects](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/divide-or-split-objects.html); a typed height or width keeps the gutter and changes the total, our choice). Object › Path › Remove Anchor Points (`path.removeAnchors`) removes the direct-selected anchors without opening their paths (the Delete key still deletes them with their segments): the neighbours keep their handle directions and their facing handles are refitted so one cubic follows the two old segments, and straight sides stay straight. The Delete Anchor Point tool uses the same refit. The Control bar, the contextual task bar, the Properties quick actions and the canvas context menu offer it when anchors are selected. With anchors selected, the Control bar and the Properties panel also convert them to corner or smooth (`path.convertAnchors`), connect end points (Join) and cut the path at them (`path.cutAtAnchors`, also on the task bar and the context menu): a closed path opens there and an open one becomes one path per piece, each cut leaving one of its two coincident anchors selected so a drag pulls the path apart. In the Pathfinder panel a group selected alone counts its nested shapes, so an expanded stroked object keeps the Shape Modes enabled (#1642, #1635).
Outline Stroke and Unite remove the overlaps of a long, low path with the sweep turned along it: when the estimated number of segments the sweep holds at once is under two thirds as large for the path turned a quarter turn (paths of 2,048 segments or more), the overlaps are removed on the turned path and the result is turned back, which moves no point by rounding. The lookups of the input curve that a piece of the result lies on, and of the input anchors that are kept, can search along y instead of x: once lookups with more than 64 candidates along x have counted 16 for each input curve or anchor, a lookup searches along y when it finds fewer candidates there (under a quarter as many for curves), and where that has rarely happened it measures along y in only one lookup of 32. On one open path of 200,000 anchors in rows of 1,000, Outline Stroke takes 17.3 s of processor time instead of 31.3 s and Unite 2.9 s instead of 6.5 s in a release build. A turned result has as many subpaths as the result without the turn, and the area where the two differ is no larger than the refit precision times the outline's length (`crates/pathops/src/sweep_axis.rs` tests).
- Divide uses open lines, bent paths and curves as cutting edges (#1307), preserving the frontmost filled face's appearance and holes. Engine tests cover implicit closure of filled open contours and unrelated objects; native automation confirms independent piece movement, one Undo/Redo, canvas/window captures and an exact saved-document read by a separate CLI process. The shared planar map keeps a segment cap (65,536 since #937, was 4,096) and its filled-area fallback; other Pathfinder operations are unchanged.
- Type that Transform effects only move and copy (no scale, rotation, reflection or Random) exports as type in each place, and PDF export writes every copy as selectable, searchable text, when its characters are in solid colors without underline or strikethrough, the type object has no fills or strokes of its own, no inline graphics and no perspective, its transform does not scale or shear stroked characters, and the copies do not overlap. When the effect is on the type itself, the type can also have transparency, an opacity mask and raster effects. When the effect is on the group or layer holding the type, the type has no transparency or effects of its own and is not in a clipping path, the group or layer has no fill, stroke or Pathfinder effect of its own, and no group between them has a fill, stroke, Pathfinder or geometry effect. Type still exports as outlines when the effect stops its copies early, or when the copies would number more than 10,000 or take more than 20,000,000 bytes of document data, and so does other type under a geometry effect.
- Extrude & Bevel and Inflate treat strokes as outlined: each visible stroke builds a solid from its outline in its own paint beside the top fill's ([three-d.md](three-d.md#fills-and-strokes)).
@@ -1012,6 +1028,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Select › Same › Fill Color, Stroke Color, Fill & Stroke, Stroke Weight, Opacity, Blending Mode, Appearance, Shape, Graphic Style, Appearance Attribute and Symbol Instance compare the other objects with the first selected object that can be selected, of any kind: an object that is hidden or locked, on a template layer, on a locked artboard while nothing is isolated (except new art while it stays selected), or outside the isolated group is passed over. This is VectorCraft's own behavior.
Select › Object › All on Same Layers takes the layers only of the selected objects that can be selected: an object that is hidden or locked, on a template layer, on a locked artboard while nothing is isolated, or outside the isolated group adds no layer. This is VectorCraft's own behavior.
Object › Flatten Transparency takes a frame of threaded text out of the thread in the same way when Convert All Text to Outlines outlines it or when the frame becomes flat regions or an image, and the other frames keep the text they hold.
Object › Flatten Transparency, the Flattener Preview, and EPS, PDF and print output that flatten transparency stop building a group's flat regions at the first region past the raster/vector balance's limit (10^(balance/20), rounded down) and rasterize the whole group without building the remaining regions. Building every region gives the same flattened art and the same files. With Clip Complex Regions on, a group builds every region and its image is clipped to them.
New Symbol and Redefine Symbol on a frame of threaded text take it out of the thread with the text it shows, which the symbol's art holds, and the other frames keep the text they hold. When the last frame goes into the symbol, the text it has no room for goes after the text of the last frame that stays. When every frame of a thread goes into the symbol, the copy of the last frame keeps all its text.
With Optical Margin Alignment on, or Roman Hanging Punctuation on in the first paragraph, the glyphs at the ends of left-to-right type on a path hang outside its brackets, measured along the path. The type hangs at the start bracket when its line is placed there, hangs at the end bracket when the line is placed there, and hangs at both when it is centered. Glyphs that hang past the end bracket do not make the type overflow, and a hang stops at an open path's end. SVG export writes such type as outlines.
The frames of threaded text share one Optical Margin Alignment setting: turning it on or off in one frame sets it in every frame, threading frames gives the thread its first frame's setting, and frames taken out of a thread keep it.
@@ -1036,6 +1053,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
When a drag on the Tabs panel's ruler or a value typed in its X field moves a tab stop past another stop, the moved stop stays selected.
The Tabs panel menu's Clear All Tabs (`text.tabs.clear`), Delete Tab and Repeat Tab (`text.tabs.set`) can be chosen while the panel's ruler shows tab stops. Delete Tab removes the stop selected on the ruler. Repeat Tab replaces the stops after the selected stop with up to 20 copies of it. The copies are spaced by the selected stop's distance from the stop before it, or from the paragraph's left indent when it is the first stop, and no copy is placed at 2,000 pt or beyond. Clear All Tabs removes every stop, so the default stops every ½ inch apply again.
- **Interface languages, vertical type:** Preferences ▸ User Interface (or VectorCraft ▸ Language on macOS) picks Automatic (the system locale) or a registered language, persisted as `interfaceLanguage`. Translations come from per-language catalogs (`i18n/*.tsv`): Traditional and Simplified Chinese, Japanese, German (every `de-*` locale), Spanish (neutral international Spanish, for every `es-*` locale), French (every `fr-*` locale), Italian (every `it-*` locale), Russian (every `ru-*` locale) and Ukrainian (every `uk-*` locale), Catalan (every `ca-*` locale) and Polish (every `pl-*` locale; AI-drafted, native review pending) cover the whole interface, Czech every menu label; Czech, German, Greek, Spanish, French, Hungarian, Italian, Japanese, Korean, Vietnamese, Polish, Brazilian Portuguese, Russian, Simplified Chinese, Traditional Chinese, Swedish, Ukrainian and Finnish also translate status-bar and error messages as they are shown (`@msg` templates, every message literal tested; the messages themselves stay English for agents); untranslated text stays English. Japanese and Chinese glyphs come from the craft-fonts build input (release builds) or the installed fonts, Czech, German, Spanish, French, Italian, Russian and Ukrainian ones from the bundled UI fonts. The web app follows the browser's languages (or `?lang=`). The Basic toolbar's Type button holds Vertical Type in its flyout. Vertical Type, Vertical Area Type and Vertical Type on a Path create vertical text (columns right to left, upright CJK glyphs and marks with `vert` alternates, Latin and longer numbers on their side, two- and three-digit numbers set as tate-chu-yoko in one em of the column (kept whole when justified), upright glyphs centred on the column (tracked or justified too), point type's anchor on the first column's centre line); kinsoku applies to horizontal and vertical Japanese (Paragraph panel › Kinsoku Set, `text.setFormat {kinsoku}`: Hard, the default, keeps closing marks, iteration marks, the prolonged sound mark and small kana off the line start and opening brackets off its end; Soft lets 々, ー and small kana start a line as JLREQ's level 3 rules do; None turns it off), and justified Japanese lines spread their room between characters (not inside Latin words); Paragraph panel › Mojikumi Set (shown with Preferences › Type › Show East Asian Options, `text.setFormat {mojikumi}`): None or Line-end Punctuation Half Width (new type's default, as in Illustrator; older documents and imported text keep None) shares one half-em space between consecutive punctuation, sets a closing mark ending a line half width and an opening bracket starting a line flush with its start (a paragraph's first line at its first-line indent, JIS X 4051's principle in JLREQ 3.1.5), and puts a quarter em between Japanese and Latin letters or digits; Paragraph panel menu › Top-to-Top / Bottom-to-Bottom Leading measures leading from em box top to top (area type's first line touches the frame) or from baseline to baseline (new type in a Japanese interface starts top to top, with em box centre character alignment, #432); Paragraph panel menu › Burasagari None / Regular / Force (`text.setFormat {burasagari: "none"|"standard"|"forced"}`) hangs an East Asian comma or full stop (、。,.、。) ending an area type line outside it, Regular (new type's default) when it doesn't fit and Force always (the rest of the line then fills the measure; closing brackets and Latin punctuation don't hang); Character panel menu › Character Alignment (with the East Asian options; `text.setFormat` or `text.setRangeStyle {charAlign}`) lines characters smaller than the largest on their line up on the Roman baseline, the em box top/right, centre or bottom/left, or the ICF (ideographic character face) top/right or bottom/left, from the font's BASE table or, without one, the average ink box of some ideographs and kana; Proportional Metrics (the same menu; `{proportionalMetrics}`) sets full-width glyphs on the font's proportional widths (OpenType `palt`) in horizontal type and upright glyphs on its proportional heights (`vpal`, shaped top to bottom; fonts without vertical metrics keep their cells) in vertical type, and Line-end Punctuation Half Width takes nothing more off the punctuation they re-spaced; Type ▸ Type Orientation switches existing text; caret, selection, hit testing and arrow keys follow the writing direction. Vertical text exports to PDF as real text and to SVG as outlines. The interface is available in Polish (`pl`): menus, panels, dialogs and messages, with one/few/many plurals; AI-drafted, native review pending (#1498).
Interface text in characters the interface's fonts lack, drawn in a bundled Japanese, Georgian or Devanagari face or in an installed font that covers them, sits on the baseline of the Latin text beside it, monospace text included (#5816; `crates/ui-egui/src/ui_fonts.rs`, `crates/ui-egui/src/theme.rs`).
Character panel menu › Standard Vertical Roman Alignment (with the East Asian options; `text.setFormat` or `text.setRangeStyle {standardVerticalRoman}`, off by default) stands the selected Latin letters, digits and other half-width characters upright one below the other in vertical type instead of rotating them ([formatting Asian characters](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html)); spaces keep their width, tate-chu-yoko blocks stay across the column, and Character Style Options gives it its own checkbox beside Character Alignment.
Character panel menu › Tate-chu-yoko Settings… (with the East Asian options; `ui.tateChuYokoSettings`, `text.setFormat` or `text.setRangeStyle {tcyUpDown, tcyLeftRight}`, in points, 0 by default) moves a marked tate-chu-yoko block in vertical type up by a positive Up/Down and right by a positive Left/Right ([format East Asian characters in vertical text](https://helpx.adobe.com/illustrator/desktop/design-with-text/edit-format-text/format-east-asian-characters-in-vertical-text.html), [formatting Asian characters](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html)); the block moves as one piece with its first character's values and keeps its one em down the column, automatic digit blocks do not move, Reset Panel sets both back to 0, and character styles and the native file keep them.
A file whose character styles hold a Tate-chu-yoko Up/Down or Left/Right offset past the coordinate limit (`MAX_COORD`) either way opens with the nearer end, as the type's own offsets do; an offset within it stays as saved.
@@ -1058,6 +1076,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Image Trace, Preview:** the Image Trace panel's Preview checkbox, before Trace, is on by default, and a setting change traces the selected Image Trace object again at once. Off, changes wait in the panel until Trace applies them as one undo step, or until Preview is turned on again. Changes stay with the object they were made for while another object is selected. When Preview is turned on with that object not selected, the object is traced with its changes when it is selected again. Preview is kept with the UI preferences, and Reset to Default leaves it. The `imageTrace.preview` command turns it on or off.
- **Image Trace small contours:** closed fits too small to form an editable subpath retain their positioned outline, including holes. Source-generated island, hole/winding and stepped-region regressions cover Corners and Noise; defaults, parity scores and estimates are unchanged.
- **Image Trace, custom presets:** Manage Presets next to the Image Trace panel's Preset dropdown saves the panel's settings as a new preset under a name (Save as New Preset…) and renames or deletes the saved preset shown; built-in presets don't change. Saved presets are kept with the preferences and follow the built-in ones in the panel, Control bar and Properties preset lists. `imageTrace.presets.save` and `imageTrace.presets.delete` save, rename and delete them, `imageTrace.presets` lists them, and `imageTrace.make {preset}` traces with one by name.
- **Image Trace, preset buttons:** six buttons above the Image Trace panel's Preset dropdown choose a preset as the dropdown does, for the options the [help page](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/image-trace-panel-options.html) lists before Preset. The presets each button chooses are VectorCraft's own: Auto Color chooses Auto Color, High Color chooses High Fidelity Photo, Low Color chooses Low Fidelity Photo, Grayscale chooses Shades of Gray, Black and White chooses Black and White Logo, and Outline chooses Outline. With Preview on, a click traces the selected Image Trace object again as one undo step; with nothing traced selected, it changes the panel's settings only. The button of the panel's preset shows as selected.
- **Create Strokes baseline (2026-10-17, measured):** the ignored centre-lines class covers 23 exact code-drawn scenes in aa and hard variants, including junctions, crossings, fills meeting strokes and varying widths. [Baseline and metric definitions](image-trace-quality.md#centre-lines). Topology is recovered from emitted geometry; width-profile metrics remain unavailable. No behaviour change or score increase.
- **Image Trace, collapsed loops keep few segments:** a loop whose fit collapses to one cubic keeps the fewest segments that hold its area and course, so a higher Paths gives no fewer segments; a code-drawn Paths sweep in `crates/trace` (see [quality notes](image-trace-quality.md#collapsed-loops-keep-few-segments)).
- **Image Trace, stacked layers only under their own areas:** with Method Overlapping, a layer reaches under the colors above it only within the connected areas that hold its own color, so a shape that sits apart from a color is no longer written again, at the same bounds, in that color, and the edge of that color's layer no longer shows through that shape's anti-aliased rim. *Measured* by `crates/trace/tests/quality` on each preset's own inputs: High Fidelity Photo 121 paths to 69.6 with ΔE 1.74 to 1.72, Low Fidelity Photo 37.1 paths to 26.1. Code-drawn disc, square and line tests in `crates/trace`.
@@ -1073,6 +1092,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type (choosing a perspective tool shows the grid; Hide Grid hides it with the tool still chosen), Gradient Mesh, Shape Builder (stroke-only open paths and lines cut the regions they cross and enclose regions of their own, and regions they bound merge without seams where the lines cross at rounded coordinates (#893); filled open paths count as closed; erase mode while Alt/Option is held: a minus pointer, red highlights of what will go, a click or drag deletes the regions touched, the pieces of open lines and the edges (outline pieces between the points where paths meet, opening their paths there) it passes over, one undo step, `shapeBuilder.merge {erase}` and `shapeBuilder.regions` edges over MCP), Live Paint (every path an edge, open lines included), Image Trace (12 presets, colour layers traced in parallel; a trace past two million anchor points is refused with advice rather than running out of memory, an image over 64 megapixels is traced scaled down to that size (aspect kept, paths at the image's own size) and one over 192 megapixels is refused, and a selection too large to outline shows its bounds (#525); Object › Image Trace › Make and Make and Expand trace at once with the Default preset, #543; with an image selected the Control bar and the Properties panel show Image Trace, whose arrow traces with any preset, Mask (`object.maskImage`: a clipping path on the image's outline, selected so its handles crop), Crop Image (a crop box on the image, starting on its part over the artboard, dragged by its handles or inside it, the outside dimmed, with X/Y/W/H, Apply and Cancel in the Control bar, Enter and Escape, #734; `object.cropImage {rect}` for agents, `{trim: true}` cuts an image to its pixels that aren't fully transparent, pixel for pixel) and Embed or Unembed in place of Fill and Stroke; with an Image Trace object selected, its Preset (choosing another traces again), View, the Image Trace panel and Expand, plus Release in Properties; the five View modes (Tracing Result, with Outlines, Outlines, Outlines with Source Image, Source Image) redraw a trace on screen without tracing again, are saved with it and `imageTrace.setView` sets them, while exports and Expand keep the result; the panel's Advanced section is a header that stays open or closed as it was left (part of #641); Color mode's Palette: Limited, Full Tone (as many colours as the tones need, Less … More), Automatic (flat art with its own colours, else Full Tone; a heuristic) and Document Library (the most used colours of the document's swatches or a swatch library, filled with the swatch colours as they are), part of #641; Advanced › Create: Fills and/or Strokes, where lines up to the Stroke width trace as stroked centre lines (distance transform, thinning, a skeleton graph with spurs pruned, curve fitting; one width per line; without Fills, wider areas become stroked outlines), part of #641), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror). A single selected mirror repeat shows a prominent axis with a center move handle and two rotation handles. Drag the line or center to change spacing, or an end to change angle; Shift snaps rotation to 45°. The original artwork stays in place, the reflected copy previews live, Escape cancels and each drag takes one undo step through `object.repeat.options`. Controls follow zoom/view rotation and hide with edges or the bounding box. Engine and headless canvas regressions passed for gestures, history and visibility; native move, rotation and Escape screenshots were reviewed. [Public mirror-repeat reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-mirror-repeat-options.html). A single selected grid repeat shows a handle below the grid for rows, one right of it for columns and one past its bottom-right corner for both, and sliders between its first two columns and rows for the horizontal and vertical spacing; the source keeps its size, each drag is one undo step and Escape cancels it ([public grid-repeat reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html), #2691). Object › Expand turns radial, grid and mirror repeats into groups of their instances (#1765, #1756).
Choosing the Blend tool again while it is active (its toolbar button, W or `tool.select`) finishes the blend: the object clicked first is dropped and the next click no longer adds to the blend just made, so the click after it starts a new blend ([Blending objects](https://web.archive.org/web/20241217212108/https://helpx.adobe.com/illustrator/using/blending-objects.html)).
Window › Blend exposes easing Type, Ramp and independent color Shift through `object.blend.options`.
Smooth Color counts the steps of each pair of keys from their color change, or from the distance between their visible bounding-box edges when their colors match or they hold gradients or patterns ([Blend Options](https://web.archive.org/web/20241217212108/https://helpx.adobe.com/illustrator/using/blending-objects.html)); the step counts, their caps and the colors compared are VectorCraft's own.
Envelope Options › Anti-Alias off draws an envelope's distorted images without smoothing, and Preserve Shape Using › Transparency gives each piece of a distorted image an opacity mask in its shape instead of a clipping mask ([Envelope panel options](http://web.archive.org/web/20260513013456/https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/envelope-panel-options.html)).
The four polynomial distributions are original approximations of the public description, not measured
numeric parity. See [Blend panel overview](https://helpx.adobe.com/illustrator/desktop/manage-colors/apply-transparency-and-blending/blend-panel-overview.html).
@@ -1099,6 +1119,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **CSS swatch libraries:** fractional linear and radial gradient stop positions are preserved, so nearby stops such as 12.5% and 12.890625% remain distinct instead of collapsing to 13%. Reference: [CSS Images 3 color stops](https://www.w3.org/TR/css-images-3/#color-stop-syntax). The original synthetic regression is `palette_io::tests::css_preserves_fractional_gradient_stop_positions` in `crates/color/src/palette_io.rs`. Scores and hours are unchanged.
- **Swatches:** names follow the colour model and are unique; Swatch Options (process/spot, Global, Gray/RGB/HSB/CMYK/Web, live preview); editing a global or spot swatch recolours every linked fill, stroke and text run in one undo step; multi-select and delete with confirmation; New Swatch and New Color Group dialogs (a group from the selected artwork's colours); swatches in colour groups convert, separate and count like any other. The Swatches panel selects ranges and whole groups, has a find field, and drags swatches to reorder, regroup or paint art; Add Used Colors, Select All Unused, Merge, Ungroup and Sort by Kind; a CMYK document keeps applied colours in CMYK. Swatch libraries: nine generated colour libraries, five gradient libraries and a pattern library (Geometric Patterns, whose swatches bring their patterns) in a library panel, saved as `.vcswatches`, `.gpl`, swatch exchange (`.ase`) or CSS, and loaded from `.vcswatches`, `.gpl` and `.ase` files (`.ase` both ways: RGB, CMYK, Lab and Gray; global, spot and process; groups) and from color books (`.acb`, read only: the RGB, CMYK or Lab colors of a book such as a Pantone book a user owns, named with its prefix and suffix, spot unless it says process, #831); `swatch.library.copyToUser` copies a loaded library into the User Defined folder without replacing a file, and a file with the same extension and bytes as a User Defined library opens as that library. Tints of global and spot colours ("Name 40%") stay linked and separate as a percentage of their plate. Spot colours can be defined in Lab (Spot Colors options). A built-in [Registration] swatch prints on every plate.
Editing, renaming, deleting or merging a global swatch also changes the colors linked to it in graph designs, the art of brushes and the tile that pattern editing's Cancel puts back, in the same undo step as the rest of the art; Default Swatches with replace unlinks them there too.
Add Used Colors (`swatch.addUsedColors`) and Select All Unused (`swatch.unused`) look colors, swatch names, links, tints and gradients up in hash sets instead of searching every swatch and color for each color or linked paint (New Color Group from artwork too), and New Swatch with several colors (`swatch.new`), New Color Group (`swatch.newGroup`) and Edit Color Group (`swatch.editGroup`) name their new swatches from one set of the names already taken. The new swatches keep their names, order and links, and Select All Unused lists the same swatches. Evidence: `add_used_colors_adds_each_new_color_once_with_free_names`, `add_used_colors_global_links_every_paint_of_a_new_color`, `unused_matches_links_tints_colors_gradients_and_patterns_exactly`, `new_swatches_from_colors_are_numbered_after_names_already_taken`, `new_color_group_numbers_its_colors_after_the_group_and_its_moved_swatches`, `new_color_group_from_artwork_lists_each_color_once_with_free_names` and `edit_color_group_numbers_extra_colors_after_the_names_it_keeps` in `crates/engine/src/tests/swatchcmds.rs`; `free_swatch_names_match_naming_each_swatch_as_it_is_added` and `free_swatch_names_match_new_swatch_names_as_swatches_are_added` in `crates/doc/src/swatches.rs`.
- **Colorized images (#1466):** a fill color applied to a selected grayscale or 1-bit image (`paint.setFill`) colorizes it with black, white, a process color or a spot color, and refuses a process color when the image has an alpha channel ([Convert color modes](https://web.archive.org/web/20251220223850/https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/convert-color-modes.html), "Colorize Grayscale or 1-bit images"); black and white are accepted there too. The canvas draws each pixel in the color as far as the pixel is dark, with white pixels transparent; PDF export writes the image in the color (a spot color as its ink, Registration in every ink) with the image as its soft mask; Separations Preview and printed separations show it on its color's plates; native files keep the color. SVG writes the fill color through a coverage mask (or a colorized PNG in Tiny 1.2); EPS writes binary coverage as an image mask in the color's ink, and a grayscale image as ink samples scaled by coverage, with white pixels left out at Level 3 and gray midtones written over white.
The prompt for deleting several swatches and groups uses the interface language's plural form for the count. Evidence: `deleting_several_swatches_uses_each_count_form` in `crates/ui-egui/src/panels/swatches/tests.rs`.
- **Colorized images (#1466):** a fill color applied to a selected grayscale or 1-bit image (`paint.setFill`) colorizes it with black, white, a process color or a spot color, and refuses a process color when the image has an alpha channel ([Convert color modes](https://web.archive.org/web/20251220223850/https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/convert-color-modes.html), "Colorize Grayscale or 1-bit images"); black and white are accepted there too. The canvas draws each pixel in the color as far as the pixel is dark, with white pixels transparent; PDF export writes the image in the color (a spot color as its ink, Registration in every ink) with the image as its soft mask; Separations Preview and printed separations show it on its color's plates; native files keep the color. EPS and SVG write the image's own pixels.
@@ -1106,6 +1127,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Swatch library and Recolor Artwork parameters:** the `swatch.library.*` commands, `swatch.resetDefaults`, the graphic style library save, load and add, the `recolor.*` commands and `color.harmony`'s `limitTo` refuse parameters of the wrong form, such as `names: "Grays"`, `limitTo: 5` or `includeImages: "false"`. Errors name the parameter, except that a `limitTo` that is not a string, such as `5`, gets an unknown-library error. A `null` counts as left out except for `recolor.apply`'s `model`, which must be a string when given.
- **Colour:** a Color Picker (colour field, H/S/B/R/G/B channel slider, HSB/RGB/Lab/CMYK fields, hex, web-only snapping, out-of-gamut correction); the Color panel shows a selected colour in its own model and, with nothing selected, the new-art defaults in the mode picked last, which it keeps between sessions; Alt-click paints the other proxy; proxies look through groups and show "?" for mixed paint; Invert, Complement, Apply Last Color (`,`) and Apply Last Gradient (`.`) keep the colour model; every paint command feeds the recent colours. The Color Guide has 21 harmony rules, Steps and Variation options and Limit to Library. Adjust Color Balance (its Global mode shifts tints) and Saturate preview live; Edit Colors and Recolor also reach meshes, images and patterns. Recolor Artwork has colour reduction, five recolour methods, presets, Limit to Library, a colour-wheel Edit tab and Edit Color Group; a local Color Themes panel saves five-colour themes. Fill/Stroke chips open swatch and mixer popovers (the Properties panel has the Control bar's Fill and Stroke chips, Stroke link and weight spinner, with nothing selected too), panels have Illustrator's Window menu keys (Align Shift+F7, Transform Shift+F8, Pathfinder Cmd+Shift+F9, Layers F7, Character Cmd+T…; every panel can be given one in Edit › Keyboard Shortcuts), and Eyedropper Options pick up and apply chosen attributes (Alt and Shift+Alt, image pixels). An ICC CMYK profile converts straight into the RGB working space (not through sRGB, so a wider space such as Wide Gamut RGB keeps colours sRGB can't hold) with the colour settings' black-point compensation, on the canvas and in RGB exports alike (the built-in Generic CMYK compensates both ways, so RGB black separated to a rich black displays as black again). Fills and strokes can overprint (Attributes panel, Overprint Black options), and CMYK documents blend in inks on screen, in PDF and when flattening. Document Color Mode converts the art, symbols, pattern tiles and swatches through the colour settings: Gray stays Gray, RGB greys separate into four-colour greys and a rich black as Illustrator does, or go on the black plate only with `grays: "black"` (also for `document.open {colorMode}`).
Plain Comma and Period, and also Shift+Comma and Shift+Period, apply the last solid color and the last gradient to the active Fill or Stroke proxy in VectorCraft ([Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html), "Select color fill mode" and "Select gradient fill mode").
Recent Colors keeps the last 30 solid colors applied, newest first and each once, and the list stays the same when another document is opened or made active; a 31st color drops the oldest ([Select colors using the Color panel](https://helpx.adobe.com/illustrator/desktop/manage-colors/select-and-adjust-colors/select-colors-using-the-color-panel.html)). The list lasts for the session. In the Color and Swatches panels the chips wrap onto further rows when the panel is too narrow for one row (`paint.recent`). Tests: `recent_colors_keep_thirty_colors_across_documents` in `crates/engine/src/tests/paintproxy.rs`, `all_thirty_recent_colors_wrap_into_a_narrow_panel` in `crates/ui-egui/src/panels/color/tests.rs`.
A Cmd/Ctrl-click on the Color panel's spectrum applies Complement and a Cmd/Ctrl+Shift-click applies Invert to the active Fill or Stroke (`paint.complement`, `paint.invert`); with Alt also held, the click recolors the inactive one, as the reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) list them.
View › Proof Setup › Color blindness – Tritanopia-type turns Proof Colors on and proofs the canvas with the tritanopia simulation of Brettel, Viénot and Mollon (1997). The Separations Preview panel's Proof Setup list offers the same target (`view.proofSetup {target: "tritanopia"}`). Grays keep their value.
The Separations Preview panel's Gamut Check button reports one color outside the CMYK gamut in the singular and more in the plural, in English and in each language's plural forms (`gamut_check_status_takes_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`).
@@ -1150,6 +1172,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
The Brushes panel menu's Select All Unused selects the brushes that are not used in the document, as the [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page (archived copy) describes. A brush counts as used when a stroke paints with it in the art (opacity masks included), in symbols, graph designs, pattern tiles or graphic styles, or in the art of a used brush (`brush.unused`).
A rename in Brush Options and Delete Brush change every stroke that paints with the brush: in the layers and opacity masks, symbols, graph designs, pattern tiles, graphic styles and the art of other brushes. A rename gives those strokes the new name, and Delete Brush makes them plain strokes, in one undo step.
In Pattern Brush Options, a menu below the Outer Corner Tile and the Inner Corner Tile sets each to the brush's own corner art (Original), None, or a tile made from the side tile: Auto-Centered stretches one side tile around the corner, Auto-Between bends a side tile on each side into the corner's diagonal, Auto-Sliced cuts a side tile on each side along the diagonal, and Auto-Overlap runs a side tile on each side past the corner. Auto-Sliced masks the tiles at the diagonal, so their outlines stay as drawn. Brushes saved without these choices draw their corners as before (`brush.options` `outer_source`, `inner_source`). Evidence: `tests::pattern_auto_corners_fill_each_corner_of_a_square`, `tests::pattern_corner_source_none_leaves_that_kind_of_corner_empty` (vectorcraft-brush), `dialogs::brush_options::tests::pattern_options_set_each_corner_tile_to_original_none_or_an_automatic_tile` (vectorcraft-ui-egui).
Pattern brush corner tiles are square to the side tiles at a right-angle corner and turn 90° from one corner to the next, as the [Using Illustrator CS5](https://help.adobe.com/archive/en_US/illustrator/cs5/illustrator_cs5_help.pdf) help (About patterns) describes. That document's steps for making corner tiles build both the outer and the inner tile as the corner joining a side tile on its right to one below it, and the brush reads corner art that way, with Flip Along and Flip Across or without (#6110). At corners that are not right angles, the tile is turned halfway between the two sides, which is VectorCraft's own rule. Evidence: `tests::pattern_corner_tiles_are_square_to_the_sides`, `tests::pattern_corner_tiles_sit_on_the_corners_diagonal` (vectorcraft-brush).
- The [About brushes](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/about-brushes.html) page describes up to five Pattern brush tiles: sides, inner corner, outer corner, beginning and end. The archived [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page says to add pattern tiles to the Swatches panel and select a swatch from the scroll list for each tile. Pattern Brush Options offers document pattern swatches in all five tile menus, including swatches named like built-in choices. The brush copies each swatch's whole art and tile frame. Side requires art; explicit None on a corner prevents fallback to the other corner's art. After a swatch is deleted, the brush keeps drawing its copied art and shows it as Original. The cited pages do not describe copying the whole art, requiring Side, suppressing corner fallback after explicit None, or showing deleted swatch art as Original. `brush.options` accepts swatch names and an object form for names that match built-in choices, and rejects unknown patterns before editing. Evidence: `tests::brushsym::pattern_tile_choices_copy_whole_swatch_art_and_reject_invalid_side` and `tests::brushsym::pattern_tile_object_selects_reserved_swatch_names` (vectorcraft-engine), `tests::pattern_corner_source_none_leaves_that_kind_of_corner_empty` (vectorcraft-brush), and `dialogs::brush_options::tests::pattern_tile_menus_use_document_patterns_and_side_none_keeps_dialog_open`, `dialogs::brush_options::tests::pattern_options_borrowed_swatch_corner_keeps_its_frame`, and `dialogs::brush_options::tests::pattern_options_selects_swatches_with_reserved_names` (vectorcraft-ui-egui).
- **Appearance:** the Appearance panel's selected row drives every paint, stroke, gradient and transparency edit (`item` on the commands); effects apply to one fill or stroke; per-item Opacity popups, whose blend-mode list and opacity slider stay open inside them (#430; the Control bar's Stroke popover too); Mixed Appearances, Layers target dots and basic-appearance rules. Effects can be reordered, moved between items and copied by dragging; Show All Hidden Attributes; clicking an effect edits it in place; the object thumbnail drags onto art; effects render on type, images, symbols, blends, envelopes, meshes and repeats, and per-item raster effects export. Groups, layers and type have their own fills, strokes and effects (Contents and Characters rows); the Layers panel's target circles target layers, groups and objects; Expand Appearance turns fills, strokes, effects and brushes into objects.
Fill and stroke paint (Fill, Stroke, None, Apply Last Color and Apply Last Gradient, also on characters selected with the Type tool), the Stroke panel's options, gradient and freeform point edits, Swap Fill and Stroke, Default Fill and Stroke, the Eyedropper, and the Appearance panel's item edits, Clear Appearance, Reduce to Basic Appearance, Remove Item, item reordering and Move Appearance record no undo step when they change nothing.
@@ -1167,6 +1191,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Graphic styles:** styles keep opacity and blend mode, capture groups and type, apply on top with Alt, and link to the objects using them; Redefine, Break Link, Graphic Style Options, Select All Unused, Sort by Name, and Select > Same Graphic Style / Appearance Attribute. The panel has list and thumbnail views with rendered previews, merge, reorder and drag and drop, plus a Control bar Style picker; six generated style libraries save and load as `.vcstyles`.
In the Graphic Styles panel, Shift-click selects a range of styles and Ctrl/Cmd-click adds or removes one.
- **Several text styles at once:** in the Character Styles and Paragraph Styles panels, Shift-click highlights a range of styles and Ctrl/Cmd-click adds or removes one, without applying them; the panel menu's Select All Unused (`charStyle.unused`, `paraStyle.unused`) highlights the styles no type uses (in the layers, opacity masks, symbols, graph designs, pattern tiles and the art of brushes), never the Normal style; Delete, the button or the menu item, deletes every highlighted style but Normal in one undo step (`charStyle.delete {names}`, `paraStyle.delete {names}`). The menu's Duplicate copies every highlighted style in one undo step and highlights the copies. A highlighted row dragged onto Create New Style or Delete carries every highlighted style: dropped on Create New Style it copies them all and highlights the copies, and dropped on Delete it deletes them all but Normal and clears the highlight; a row from outside the highlight carries only its own style. Public reference: [Character and paragraph styles](https://web.archive.org/web/20261015081534/https://helpx.adobe.com/illustrator/using/character-paragraph-styles.html).
- **Load text styles from another document:** the Character Styles and Paragraph Styles panel menus have Load Character Styles… or Load Paragraph Styles… and Load All Styles…, which copy the styles of a document picked in an open dialog, with the swatches and patterns they paint with, in one undo step (`charStyle.load`, `paraStyle.load`, `textStyle.loadAll`); a style the document already has, under its name or a numbered one, is not added again, any other whose name is taken gets a number, and the file's Normal styles are not loaded. Public reference: [Character and paragraph styles](https://web.archive.org/web/20261015081534/https://helpx.adobe.com/illustrator/using/character-paragraph-styles.html).
- **Neutral wording:** labels, MCP tool text, docs and packaging use neutral names, and `cargo xtask brands` (part of `cargo xtask ci`) fails on vendor names.
- **Colour, type and file workflows:** Recolor Artwork (dialog with harmonies), Edit Colors, Find & Replace, Change Case, Smart Punctuation, Guides, Lock/Hide Above, Transform Each, Rasterize.
The Libraries panel holds gradients and patterns: its + button adds the selection's fill or stroke gradient or pattern, a gradient or pattern swatch dragged from the Swatches panel is added, and a click paints the selection through the active Fill/Stroke proxy, a pattern bringing its definition and swatch into the document. A spot colour stays a spot colour: the library keeps its ink name and tint, and using it in any document links the paint to that document's spot swatch of that name, adding it when missing (#5812).
@@ -1193,6 +1218,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Duplicate, Copy, Cut and Move Artboard and Rearrange All Artboards take `lockedAndHidden` (default: Move Locked and Hidden Artwork with Artboard) and refuse a value other than true, false or null. When art goes along, the journal records the value, so a replay under the other preference takes the same art.
The Layers panel's New Layer (on top), Select Art and Target (Shift-click), Move Rows (the copy), Template, Paste Remembers Layers and Release to Layers (Build) refuse a value other than true, false or null, such as "true", and leave the document, the selection, the highlighted rows and the undo history as they were; New Sublayer refuses such a `top` too.
Change Case, Smart Punctuation, Find and Replace, Convert To Area Type, Convert To Point Type, Type on a Path Options, the Type on a Path effects and Fill with Placeholder Text record no undo step when they change nothing.
Find and Replace, Find Next, Change Case, Smart Punctuation, Insert Character and Type on a Path Options refuse a text option other than text or null, such as a replacement of 5, and a switch other than true, false or null, such as "true". Smart Punctuation also refuses a scope other than selection or document, Type on a Path Options a spacing given as text, and Find and Replace `ids` that are not a list of existing object ids. Each leaves the text, the selection and the undo history as they were.
The commands that take `keepModel` (fills and strokes, Appearance rows, Live Paint, Shape Builder fills, gradient stops and freeform points, gradient mesh points, type colors, the Symbol Stainer and the swatch commands) refuse a `keepModel` other than true, false or null, such as "true". Toggle Fill/Stroke Focus (`fill`), Invert, Complement, Apply Last Color and Apply Last Gradient (`stroke`), `paint.setFill` and `paint.setStroke` (`focus`, `none`), and `appearance.setItem`, `livePaint.fill`, `livePaint.strokeEdge`, a `shapeBuilder.merge` `fill`, `swatch.new` and `swatch.edit`'s `paint` (`none`) refuse such a value too. Each leaves the document, its undo history and the defaults for new art as they were.
The shape tools' commands (Rectangle, Polygon, Star, Flare, Spiral, Arc, Rectangular Grid and Polar Grid), `path.create` (with `anchors` or `d`), the type commands (new type, type in a path, character formats, the byte range a text command takes, Touch Type, font size steps, `text.getRange`, Optical Margin Alignment, System Layout, Fractional Widths, Ruby and Area Type Options), the symbol commands (Place, Delete, the Symbol Sprayer and the symbolism tools), the swatch commands, the Appearance panel's commands, the gradient and freeform gradient commands, Shape Builder, Live Paint and Image Trace refuse an option other than true, false or null and a number given as text, such as "true" or "40", and leave the document and its undo history as they were.
- **Fonts:** a font is found by any of its names (family and style in every language of its name table, legacy family/style pairs, PostScript name), so documents from other apps open with the font they name; Find Font and `text.fonts` say for each font whether it was found exactly, by another name, substituted or missing, and how many glyphs are missing. PDF and `.ai` import brings Japanese text back as live type: CID fonts without a ToUnicode map read through the standard CMaps, vertical columns (WMode 1) as vertical type, letter spacing as tracking; a glyph is live type only when the installed font (in the closest style of its family) draws the same glyph, and text in a font that isn't installed comes in as live type shown in the fallback font until the font is installed (exports that draw it — PDF, EPS, EMF/WMF, raster, SVG outlines or embedded fonts — warn that they wrote the fallback font, and Print and Print Preview warn about the type that prints in it; `textAs: outlines` keeps the PDF's embedded glyphs). Two installed versions of one family and style (a font library keeping an old file beside a new one) both load, the first staying the one the family and style resolve to; PDF and `.ai` import tries each version when the glyphs differ, and type set in a version other than that one names it (`CharStyle::font_version`), which layout, the substituted-glyph highlight, Find Font, Package and Document Info follow. Fonts outside the font folders are listed too: those font services such as Adobe Fonts load on Windows, through DirectWrite and, for those only GDI lists, through GDI (#1258), and those apps and font managers register with CoreText on macOS (#579). When a document opens with fonts that aren't available, fonts used only in symbols included, a Missing Fonts dialog lists them after the linked-file questions, with Find Fonts (Type › Find Font on them) and Close. Its Find in Folder… (in Find Font too) searches a folder on several threads, reads only what the font scan reads of font files and skips app packages, the Program Files and ProgramData folders, the system's folders and the app data folders in a home folder; Add Fonts copies the font files the user chooses, suitcase fonts with their resource fork, into VectorCraft's own Fonts folder, which the desktop app and `vectorcraft-cli` read (`text.missingFonts`, `text.findFontFiles`, `text.addFontFiles`). The font scan on Linux also reads the folders fontconfig's configuration lists, such as TeX fonts under `/usr/share/texmf/fonts` (#1548, fixes #1529; not verified on Linux).
@@ -1231,6 +1257,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
At a precision of 9 decimals or fewer, SVG export writes each number from an integer when the integer rounds it as the decimal formatter does; the formatter still writes values halfway between two written numbers and values too large for the integer. Exporting one path of 200,000 anchors takes a median of 43.4 ms instead of 167.0 ms on Windows 11, and 20,000 rectangles 81.4 ms instead of 141.4 ms, with the same SVG (`crates/svg/src/lib.rs`).
SVG import reads each `<circle>` and `<ellipse>` as the ellipse VectorCraft draws: four smooth anchors on the axes, starting at the right, with handles of the standard length, so an ellipse exported as `<ellipse>` comes back with the anchors it was written from and draws the same (`circles_and_ellipses_import_as_the_ellipse_drawn` in `crates/svg/tests/it/basic_shapes.rs`, `filled_ellipse_on_the_artboard_edge_survives_visually` in `prop_svg.rs`).
SVG import keeps attributes in other applications' XML namespaces (`plotter:pen="2"`) on the objects and layers they belong to, as it keeps their `data-*` attributes: copies, duplicates and `.vectorcraft` files keep them, and SVG export writes them back with each namespace declared once on the root (the rest of #522; `crates/svg/tests/it/foreign_attributes.rs`, `tests/attributes.rs`).
A PDF-compatible `.ai` saved with Include Editing Data for Other Apps defines every symbol in its editing copy with its art, registration point and place in the Symbols panel's order, and writes its instances as symbol instances, which VectorCraft's `.ai` reader opens as symbols and instances again (`symbols_and_their_instances_are_read_back` in `crates/eps/src/ai_editing/tests.rs`; [file-format-parity.md](file-format-parity.md) lists the instances written as their art).
A PDF-compatible `.ai` saved with Include Editing Data for Other Apps writes eligible paths, compound paths and groups with live effects with an art style when the writer supports their appearance and effects (not a filled path that uses the even-odd rule, and none on a large canvas), which VectorCraft's `.ai` reader opens as the same appearance with its effects live (`objects_with_live_effects_are_read_back` and `every_effect_setting_written_on_an_object_is_read_back` in `crates/eps/src/ai_editing/tests.rs`; [file-format-parity.md](file-format-parity.md) lists the objects written without one and the effects they lose).
A PDF-compatible `.ai` saved with Include Editing Data for Other Apps writes point type, area type and type on a path as type in its editing copy, and objects next to it keep their own transparency (`crates/eps/src/ai_editing/tests.rs`, `cmd::fileio::tests_pdfedit::type_comes_back_from_the_editing_copy`; [file-format-parity.md](file-format-parity.md) lists what is written as outlines).
A PDF-compatible `.ai` saved with Include Editing Data for Other Apps keeps the document's variables, data sets and the bindings of objects to variables in its editing copy, in the form the variable library reader above reads, so they come back when the file is opened. Bindings to layers, to type written as outlines, to graphs and to linked images written as other objects, data set values of graphs, and variables, data sets, bindings and values past the writer's limits are left out with a warning (#6024; `crates/eps/src/ai_editing/tests.rs` `variables_data_sets_and_bindings_are_read_back`; `crates/eps/src/ai_editing/writer/variables.rs` lists what is left out).
In a `.ai` or EPS opened from its editing data, type has its text object's opacity, blend mode and knockout shape flag, whether it is made from the file's text document or taken from the page, and type made from the text document takes its text object's lock. Where such type has transparency and comes in with other art (the rest of its look, or page type and strokes that go with it), a group of them has it. Where the page draws its type, or the strokes around it, with transparency that is neither its text object's nor its text object's combined with that of the objects around it, the page's type and strokes keep their own instead (`crates/eps/src/import/tests_native.rs`). Where the page draws type of no text object over text objects made from the text document, with their words in any case, that type is theirs as the page draws it and is left out; the page's other type of no text object goes only to text objects whose type the text document can't make, or else is left out with a warning.
@@ -1253,8 +1281,11 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Blends and envelopes in the definitions of a `.ai`'s symbols, patterns and brushes' art count against the same per-file limits on blend layout and envelope checks as the layers' art, so a file with many definitions does not get those limits again for each one (`crates/eps/src/import/ai/read/directives.rs`).
A `.ai` or EPS whose editing data has brushes opens with the file's Calligraphic, Scatter, Art and Bristle brushes in the Brushes panel in place of the default ones, in the file's order: the Calligraphic size, roundness and angle with their variation, the Scatter size, spacing, scatter and rotation, the Bristle settings, and the Scatter and Art brushes' art. A brush library `.ai` (brushes and no layers) opens from its PDF part with its brushes, so Other Library lists them. An Art brush in Tints keeps that colorization. Pattern brushes and brushes that can't be read are left out with a note. Paths with a Calligraphic brush, and paths with an Art brush at 100% in Tints that stretches art of solid-filled paths unflipped, open as the path with the brush on its stroke, and other brushed paths still come in as their drawn look (part of #372 and #637; `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`).
In a `.ai` opened from its editing data, a path or type object with Offset Path, Outline Object or Outline Stroke on the whole object opens as itself with those effects live, in their order and shown or hidden; on a fill or stroke of type they stay on that fill or stroke. Offset Path keeps its Offset, Joins and Miter Limit, and Outline Stroke takes its width from the stroke. An Offset Path with a setting missing, out of range or not read keeps the drawn art, as does a path with a Path effect on one fill or stroke (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`).
In a `.ai` or EPS opened from its editing data, a Scatter brush whose last 15 numbers start with 1 comes into the Brushes panel in Tints, and each brush art section is read without its frame, an unnamed, unpainted rectangle with no settings of its own at the art's bounds (within 1/1000 pt). A wider frame stays in the art, and an art group with a state or look of its own (hidden, locked, isolated, knockout, an opacity, a blend mode, a name, a mask, a note or an appearance) stays a group (`crates/eps/src/import/ai/tests.rs`).
In a `.ai` opened from its editing data, type with Warp on the object or on one of its fills or strokes opens as type with the effect live when the live warp draws as the art saved with it. When it draws differently, or when the file's limit of checks has no check left for it, it opens as that art, and one note says how many did. Type stays live without a comparison when the saved art paints nothing or draws type of its own, in frames of threaded type, when a text variable binds it, and when its text object doesn't come in as one text object (`crates/eps/src/import/ai/envelope.rs`, `crates/eps/src/import/tests_native.rs`).
In a `.ai` opened from its editing data, an object with a Tweak that moves nothing or Pucker & Bloat at 0 opens as itself with the effect live. A path, compound path or group whose only effect is Transform without copies, or a Free Distort whose corners make a parallelogram, opens as itself with it live, and so does an unstroked path or compound path with straight segments whose only effect is Twist. A gradient or pattern, an image, type, a clipping group, an opacity mask or effects inside the object, or a stroke with a Transform that scales, rotates or reflects, keep the drawn art (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/ai/read/ops.rs`).
A PDF page whose `UserUnit` (PDF 1.6) is above 1 opens at its size in points, its numbers times the `UserUnit` (one above 75,000 counts as 75,000), and `document.pdfInfo` gives its size and boxes in points too. A `.ai` whose editing data has its artboards in the units of such a page opens from that data at the page's size, with its layers, and when the layers can't be read its pages still take the artboard layout of the data, scaled the same way (part of #1482).
`document.open`, `file.place` and `document.pdfInfo` reject a `cropTo`, `crop`, `password`, `colorMode` or `textAs` that is not a string, such as `"textAs": true`, and `document.pdfInfo` rejects a `thumbnailSize` that is not a number; the error names the param, and nothing opens or is placed. A param left out or given as null keeps its default (`crates/engine/src/cmd/fileio/tests_pdfimport.rs`).
When the PDF content of a `.ai` has a page for each artboard, the layers are compared with every page on its artboard (until the pages compared take 8,388,608 pixels at the comparison's scale, about 67 letter-size pages, or number 128): art that only the layers or only a later page draws is handled as art that differs on the first page is, and a text object that a later page doesn't draw is left out unless its layer doesn't print (`crates/eps/src/import/tests_native.rs`).
When the editing data of a `.ai` or EPS is for a page of another size than the file's, the warning that its layers weren't read gives both sizes in points, and the scale of the editing data's page to the file's page (1:10 for a tenth of its size) when their sides are in proportion (#2034; `crates/eps/src/import/tests_native.rs`).
A `.ai`, or a PDF saved with Illustrator's editing data, whose editing data can't be decompressed opens from its PDF part with a warning that its layers weren't read and why, and an export or Save As to its own path asks first (`acknowledgeLoss`), as for a file whose layers the reader can't read (`crates/eps/src/import/tests_native.rs`, `crates/engine/src/cmd/fileio/tests_aiimport.rs`).
@@ -1318,6 +1349,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- On the desktop with Enable Background Export on (Preferences › File Handling), File › Package… collects the linked files and fonts and writes the package from the document as it was at OK on the thread that writes background saves and exports, with "Packaging" in the status bar and the busy spinner until it is written; a save of unsaved changes that Package starts is written first. `file.package` over MCP and scripts replies as before.
- On the desktop with Enable Background Export on, Export for Screens and the Asset Export panel's Export encode and write their files (into the folder, or one ZIP in it) from the document as it was at OK on the thread that writes background saves and exports, with "Exporting" in the status bar and the export progress dialog, which counts the files and whose Cancel stops before the next file. `document.exportForScreens` and `assets.export` over MCP and scripts reply as before.
- Missing linked files can be looked for in a folder and its subfolders: Find in Folder… in the Linked File Not Found dialog and in the Links panel menu lists the files with each one's name, those with the size and contents saved with the link first, and relinks the files chosen, copying an image's file into the document's folder when asked, without replacing a file there (`links.findFiles`, `links.relink {copy}`). In the Links panel it looks for the selected linked files when any are selected.
- The Links panel's Relink… opens a Relink dialog whose Replace menu picks which links take the new file: Selected Instances (the selected images, as before), All Instances (every image linked to the same file), Missing Links from This Folder (then every other missing link whose file name is in the new file's folder) and Each Link Separately (a file for each selected link in turn). Each relinks as one undo step (`links.relink {scope}`, `ui.relinkDialog`). Relink to Folder… stays as it is.
- Linked files inside symbols, graph designs, patterns and opacity masks are saved with their paths relative to the document, as linked files in the layers are, so they are found after the document and its files move to another folder together; on open each of those links takes the path where its file was found. Update Links, and opening with Update Links: Automatically, read a modified file again for that art too, and saving a document refreshes its placed copies inside symbols and patterns of the other open documents. Evidence: `links_outside_the_layers_find_their_files_after_the_folder_moves`, `update_links_reads_files_again_inside_symbols_patterns_and_masks`, `saving_a_document_refreshes_its_placed_copies_inside_symbols_and_patterns`.
- On Windows, a document opened from a mapped network drive finds its links stored with the share's own path (`\\server\share\…`), as the same document opened from the share does; links on other computers stay missing.
- On Windows, a link on a drive mapped to a network share other than the document's (`Y:\…`) is missing, with the reason that the path is on another computer, as a link stored with that share's own path is; links on this computer's drives and on the document's share are followed.
- In the Links panel, Shift-click selects a range of links and Ctrl/Cmd-click adds or removes one.
@@ -1356,6 +1389,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
When a path object moves without changing shape, the renderer draws its stroke from the outline it made before the move, moved by the same offset. It outlines the stroke again when the path is scaled, rotated or edited, when the stroke changes, and when the move takes the view past the part of a long path it outlined. The pixels are the same. At 1600 × 1200, the frame after a 1 pt nudge takes 123 ms instead of 403 ms with an open path of 200,000 anchors in view (103 ms with nothing changed), and 13 ms instead of 26 ms with a closed path of 50,000 anchors whose stroke is dashed and has a width profile.
When the stroke of a path moved this way has a gradient along or across it, the renderer also draws the gradient from the slices it made before the move, moved by the same offset. It makes the slices again when the path is scaled, rotated or edited, and when the stroke or its gradient changes. The pixels are the same. On Windows at 1600 × 1000, the frame after a 1 pt nudge of an open path of 200,000 anchors takes 75 ms instead of 1,237 ms with the gradient along the stroke and 69 ms instead of 1,420 ms with it across, and with an open path of 50,000 anchors 22 ms instead of 313 ms and 21 ms instead of 352 ms (medians of five interleaved runs).
The renderer keeps the path of a path or compound path of 1,024 anchors or more by object, with the bounds of its segments and of its miter spikes in runs of 128 segments. When anchors move and the subpaths and anchor counts stay the same, the next frame changes only the segments that start or end at a moved anchor and measures again only the runs that hold them or the segments with length before them, instead of making the whole path again and measuring it whole for culling. The pixels and the cull bounds are the same. On Windows at 1600 × 1200, the frame after one anchor moves takes 8.2 ms instead of 30.1 ms with an open path of 200,000 anchors, 4.3 ms instead of 10.4 ms with a closed path of 50,000 anchors, and 6.6 ms instead of 11.9 ms with the filled closed path of 50,000 anchors and a dashed stroke (medians of five runs of `anchor_bench`).
When only anchors of a path or compound path move, the canvas draws again only around the segments that start or end at a moved anchor, grown by the farthest its strokes reach, instead of the whole object before and after. This holds while every fill and stroke is a solid color, and the strokes have no dashes, width profile, arrowheads or brush, and the object has no effects or opacity mask; any other object still counts whole. On a loaded Windows machine at 1600 × 1200, single-threaded in a development build, the frame after one anchor of a filled closed path moves takes 16.6 million CPU cycles instead of 56.4 million with 50,000 anchors and 65.6 million instead of 180.0 million with 200,000 anchors (medians of 15 edits, the two builds run in turn three times).
Opening a document keeps the layouts it makes for its type objects, up to 128 MiB, and the first frame uses them instead of laying those objects out again. It does not keep a layout with bitmap or color glyphs or a text object with a gradient. The frame uses a layout only for a text object equal to the one laid out, and only while the fonts and the hyphenation exceptions have not changed. It uses each layout once. The renderer drops the rest when a frame that used one ends, and all of them after eight frames that use none. The frames of a threaded story are laid out while the file is read, and the first frame lays them out again. The pixels are the same. On Windows at 1600 × 1200, in medians of seven interleaved rounds of a local example, counted in the main thread's CPU cycles because other builds were running: with 5,000 point type objects in view the first frame takes 409 million cycles instead of 894 million and the open 816 million instead of 665 million, so the two together take 1,225 million instead of 1,559 million; with one area type object of 500 paragraphs the first frame takes 697 million instead of 1,193 million and the open 535 million instead of 568 million.
Type › Show Hidden Characters keeps the marks it places on each text object from one frame to the next. The canvas lays a text object out again when an edit changes it or its threaded story, or when the last frame that showed the marks did not draw it, and it lays out all type in view again when the fonts or the hyphenation exceptions change. With 1,000 two-line labels in view, drawing the marks takes 0.8 ms per frame in a release build instead of 19.7 ms.
A selection with more than 100,000 points to outline shows its bounds instead of each object's outline and anchors. Paths count their anchors. Area type and type on a path count the anchors of their frame or path, and point type counts six: its box's corners and the ends of its baseline marker. Images, symbol instances, placed documents, and area type or type on a path in perspective count the four corners of their box, and gradient meshes and envelopes count their points and the ends of their mesh lines. The hover highlight leaves out an object past that count, and such an object shows its bounds as the key object. With 32,768 type objects, images, symbol instances, gradient meshes or envelopes selected, the selection's outlines and anchors add fewer than 100 vertices to the canvas's mesh (#2460).
@@ -1372,6 +1406,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
With the Perspective Selection tool, holding Shift while moving or copying constrains each object's displacement to the nearest axis or 45° diagonal of its own plane; releasing Shift resumes the raw pointer position, and 5 still moves perpendicular to the plane. Snap to Grid follows the constrained line on the first object's plane and depth.
When the desktop app opens a document on a thread of its own (#1245), that thread also reads the document's linked files and lays out its type, and the UI thread only adds the finished document: in release builds, the step left on the UI thread takes 0.02 ms instead of 1.04 s for a document with 12 linked 3000 × 2000 TIFFs, and it takes 0.1 ms instead of 0.85 s for one with 300 Auto Size area type objects, every second one in three columns.
Laying out type keeps each font face's shaping plans, at most 32 per face, so type objects of the same font, script, direction, language and features share one plan: the layouts of a document of 5,000 short point type objects take 59 ms instead of 73 ms in a release build, and the glyphs, bounds and baselines do not change.
Laying out type looks up the font face of each style run once and uses it for the run's glyphs and for the line metrics of a paragraph that starts in that run: the layouts of a document of 5,000 short point type objects take a median of 215 million CPU cycles instead of 236 million in a release build (11 alternating runs of each build on Windows 11, measured while other builds were running), and the glyphs, bounds and baselines do not change.
The Layers panel's Delete Selection, Duplicate Selection, Collect in New Layer, Merge Selected, Flatten Artwork, Release to Layers (Sequence) and row drags also take time in proportion to the size of the document, and so do Cut Artboard and Duplicate Artboard with their art and each move of an Alt-drag of one artboard with the Artboard tool while Move/Copy Artwork with Artboard is on (#1429).
In Outline view the renderer draws paths and compound paths from the geometry it keeps for them between frames, and it culls the members of clip groups by the bounds it keeps for them. Layers with Preview off and the outlines of Image Trace objects are drawn the same way.
- **Graphics processor:** the canvas is rasterized on the CPU and the GPU only composites it, so any GPU that can show the window will do. Preferences › Performance › Graphics Processor (`gpuPreference`) is Automatic by default: the power-saving GPU on Windows and macOS (the integrated one on hybrid-graphics laptops, where presenting from the discrete GPU made the window flicker, #306), and the GPU the desktop runs on under Linux, where KWin on an NVIDIA GPU refused frames from a Ryzen's integrated GPU and 0.5.0 crashed at startup (#502). Power Saving and High Performance pick a kind of GPU; `WGPU_POWER_PREF`, `WGPU_ADAPTER_NAME` and `WGPU_BACKEND` override them. The app picks among the adapters that can present to the window (native backends before OpenGL, hardware before software, and on Windows a GPU's DX12 adapter before its Vulkan one: Intel's Vulkan driver made the whole window flicker black, #545), and if the window fails on one while starting up it starts again without it instead of crashing; after a start that a graphics driver crashed before its first frame, the next starts leave that adapter out, or list OpenGL's adapters alone when the crash came while the adapters were being listed (#2243; verified by unit tests, not on the reporter's Intel UHD Graphics 605); Help › About, `ui.inspect` and the log name the adapter in use. When the browser drops the page's WebGPU device or WebGL context, the web build writes recovery copies, starts again on new graphics with the same app and uploads its textures again; if it can't, it removes the stale canvas and says to reload, which offers the copies back. The desktop app writes the copies and says to restart (#369). On Windows and Linux the tools' cursors are OS cursors (bitmaps rasterized once per display scale), which the system moves itself: painted into the window, they trailed the pointer by the frames waiting to be shown, and the window now queues one frame instead of two (#444). macOS and the web still paint them (macOS sizes cursor bitmaps in points, eframe has no bitmap cursors on the web). Tool states have cursors of their own as the public manual describes them: Direct Selection shows a hollow or filled square on its white arrow over an anchor (path unselected or selected) and the Reshape Segment cursor over a curved segment, as do the Anchor Point tool and the Pen with Alt (whose caret it lends); Group Selection, and Direct Selection with Alt, show a plus; a drag shows the filled arrow, hollow while it snaps to a point or a guide, and an Alt-drag copy a double arrowhead; Curvature has its own pen and Free Transform's Cmd/Ctrl side-shear two arrows passing along the side. Lasso, Magic Wand, the freehand tools, Shaper, Live Paint Bucket, Measure, Print Tiling, the symbolism tools, vertical type, Scissors, Mesh and Puppet Warp return cursors of their own, which borrow the crosshair, the I-beam, the Pen's add cursor or not-allowed until they're drawn; there's no busy cursor yet. `crates/tools/src/cursors.toml` lists every cursor, when it shows and its hotspot, for whoever draws them. Pens and touch drive the tools as the mouse does, and a tap pressed and lifted within one frame clicks where it tapped (#491); pen pressure reaches the Paintbrush's pressure-sensitive brushes and the Liquify tools' Use Pressure Pen on Windows, on the web and on Linux under X11 and XWayland, while winit 0.30 reports none on macOS or native Wayland. On Linux, see `docs/development.md` for pens under Wayland.
@@ -1406,6 +1441,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Type drawn as glyph outlines:** under a geometry effect, in EMF and WMF export, in DXF export when it is written as outlines, flattened with Convert All Text to Outlines, and inside an envelope, type keeps its underline and strikethrough, the weight and dashes of its character strokes when it is scaled, and the placement of its gradients and patterns, as the canvas draws it. A group's or layer's own fills and strokes also paint the underline and strikethrough of the type in it.
- **Envelopes of type map from the glyphs:** an envelope maps point type, area type, type in groups and layers and type used as a clipping path from the bounds of the glyph outlines, underlines and strikethroughs instead of the type's laid-out box (ascent to descent, or the area's frame), with inline graphics counted by their boxes; rotated type and type in perspective map from their outlines as drawn. Make with Mesh, Make with Top Object and Reset with Mesh without Maintain Shape use those bounds. Type that draws nothing keeps its laid-out box, and so does type inside any other object, such as a blend, a repeat or a symbol instance. This is VectorCraft's own rule; envelopes of type made in VectorCraft and in saved files redraw by it. Tests: `crates/engine/src/tests/envelope.rs`, and `tests/envelope_ink.rs` in `vectorcraft-eps`, `vectorcraft-render` and `vectorcraft-effects`. Scores and hours unchanged.
- **Texture redraws (#1257):** Craquelure, Mosaic Tiles and Stained Glass compare squared distances when finding their nearest cells; rounding/tie boundaries use the original distance ordering. Brute-force candidate tests and three 1800×1500 viewport comparisons preserve the old pixels. A CMYK rectangle move at 190.07%/DPI 1.5 is reproducible with `cargo test -p vectorcraft-render texture_drag_perf -- --ignored --nocapture`; idle-machine Windows interaction is still unverified.
- **Smart Blur modes:** Effect › Blur › Smart Blur… has a Mode menu with Normal, Edge Only and Overlay Edge (`blurMode`: `normal`, `edgeOnly` or `overlayEdge`; an effect saved without it draws as Normal). Edge Only draws the edges in white on black, and Overlay Edge draws them in white over the Normal blur. A pixel is on an edge when one of the pixels the search reads within Radius of it differs from it by more than Threshold in a channel and its channels, alpha included, add up to at least that pixel's. The search reads neighbors as the Normal blur does, kept within Radius, and always the four nearest pixels, so a color step gives a line about as wide as Radius, and at least one pixel wide, on the side with the larger sum (lines on both sides when the sums are equal). The white and the black keep each pixel's coverage. Behavior reference: [Summary of effects](https://helpx.adobe.com/illustrator/using/summary-effects.html).
@@ -1438,15 +1475,20 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Blend and envelope options given as text:** `object.blend.make` and `object.blend.options` (`smooth`), `object.blend.spine.moveAnchor` (`independent`) and `reshapeSegment` (`perpendicular`), `object.envelope.makeWithWarp`, `resetWithWarp` and `options` (`horizontal`), `object.envelope.resetWithMesh` (`maintainShape`), `object.envelope.options` (`antiAlias`, `distortAppearance`, `distortLinearGradients`, `distortPatternFills`) and `object.envelope.editContents` (`editing`) refuse a value other than true, false or null, such as `"false"`, and leave the document and its undo history as they were.
- **Envelope names given as another type:** `object.envelope.makeWithWarp`, `resetWithWarp` and `options` refuse a `style` or `orientation` other than text or null, such as `5`, and an `orientation` other than `horizontal` or `vertical`; `object.envelope.options` refuses such a `preserveShape`. `object.envelope.options` checks the warp keys with no envelope or only mesh envelopes selected, too. Each leaves the document, the options new envelopes get and the undo history as they were.
- **Exporter plug-ins (G17):** a WebAssembly plug-in of kind `exporter` adds a write-only file format. File › Export › Export As lists it after the built-in formats, with Use Artboards greyed out (the plug-in writes the whole document) and its parameters in a generated dialog once the file is picked; `document.export` writes it by its format id, `plugin.<id>`, or by one of its extensions, and built-in formats keep theirs. The plug-in is given the native file's JSON (large images without their data) and `_context` (`mode`, `extension`, `name`), with 10 G instructions plus 4,000 per byte of input and 300 s, and Background Export's Cancel stops it between two slices of that budget. A decline or invalid output writes nothing; its warnings come back with the export's. Regressions: `crates/engine/tests/plugin_export.rs`, `crates/plugins/tests/abi.rs`, `crates/ui-egui/src/dialogs/tests_plugin_export.rs`. Scores and hours unchanged.
- **Importer plug-ins (G17):** a WebAssembly plug-in of kind `importer` adds a read-only file format, as [the help page](https://ai-sdk-2020.84.nz/html/struct_a_i_file_format_suite) describes for file format plug-ins that read: File › Open lists its files under "All readable files" and its own filter, and File › Open With lists each importer by name. `document.open` uses it for a file whose extension no built-in format reads, by content or extension (the importer whose id sorts first, when several list the extension), or the one `importer` names, also for an extension a built-in format reads; a damaged file of a built-in format reports that format's error. The plug-in is given at most 128 MiB of the file and `_context` (`mode: "import"`, `extension`, `name`), and returns paths and compound paths in the filters' object JSON, checked as a filter's output is; they become a new document whose artboard is their bounds, which Save asks a name for. A decline, invalid output, a trap or Cancel opens nothing. The routing rule, the limit and Open With are VectorCraft's own. Regressions: `crates/engine/tests/plugin_import.rs`, `crates/plugins/src/import.rs`, `crates/ui-egui/src/dialogs/tests_plugin_import.rs`. Scores and hours unchanged.
- **Plug-in instruction budget at a bulk memory instruction:** a run that pauses where one slice of its instruction budget ends keeps the instructions the slice still held. A `memory.fill` or `memory.copy` that the slice can't pay for in full runs on the next slice, so a run whose budget covers its work finishes; a run ends with `Limit("instruction budget")` only when the instructions it has left can't pay for the next instruction. Regressions: `crates/plugins/tests/abi.rs`. Scores and hours unchanged.
- **Plug-in parameters as their manifest presents them (G17):** each parameter may have a `label`, a `unit` shown after its value, `help` on its label and a `helpUrl` page opened from a button, a `group` (sections that fold, after the parameters outside any group) and a `visibleWhen` that shows it only while the checkboxes and choices it names have one of the listed values (a hidden parameter still goes to the plug-in). `string` and `numberList` parameters join number, int, bool and choice, and a plug-in declares up to 256 parameters, at most 64 outside groups. The filter, live-effect and exporter dialogs use these keys, and the Appearance panel's effect editor the labels, units, help and conditions; hosts from before them ignore them. Regressions: `crates/plugins/src/dialog.rs`, `crates/plugins/src/manifest.rs`, `crates/ui-egui/src/dialogs/tests_plugin_fields.rs`, `crates/ui-egui/src/panels/appearance.rs`. Scores and hours unchanged.
- **Live-effect plug-ins read their object's paints (G17):** a live effect is given the object's `fills` and `strokes` as a filter gets them, read only, and `_context.item` (`"object"`, or the fill or stroke whose own effects hold it); the result is cached by paint too, so a change of paint runs it again. Regressions: `crates/engine/src/tests/plugins.rs`, `crates/plugins/src/effect.rs`. Scores and hours unchanged.
- **Filter plug-ins set effects and stroke styles (G17):** a filter is given each object's effect records and its strokes' `join`, `cap` and `miterLimit`, and may return them; the records are admitted as `effect.apply` admits them (a known effect or installed live-effect plug-in, parameters it takes, one 3D effect that fits, no texture read from a file), and one refused refuses the output. Regressions: `crates/engine/src/tests/plugins.rs`, `crates/plugins/src/objects/tests.rs`. Scores and hours unchanged.
- **Plug-in data kept with the document (G17):** a filter writes JSON of at most 1 MiB with `documentData` (one undo step with its objects; `null` removes it) and reads it back in `_context.documentData`, which no other filter is given. It is the document's foreign data (`unknown.pluginData`, by plug-in id), as the perspective grid is, so the native file keeps it with no section of its own; an exporter is given the whole document, so it can read every plug-in's. Regressions: `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_export.rs` (the exporter), `crates/doc/src/plugin_data.rs`, `crates/plugins/src/objects/tests.rs`. Scores and hours unchanged.
- **Heavy filter plug-ins (G17):** a filter's manifest may ask for `"budget": "heavy"`: 1 G instructions (20 times a light run's) and 300 s a run, for routing, packing or simulation. Install Plug-in… and File › Open ask the user first, `plugin.install` needs `allowHeavy: true` (`plugin.inspect` tells beforehand), the Additional Plug-ins Folder skips such filters, and the plug-in store keeps the answer. Its dialog has no Preview; the app runs it on a thread of its own with a progress window (Cancel, Continue in Background) and applies the result as `plugin.run` with the same parameters, one undo step, unless the document or the plug-in changed meanwhile. Regressions: `crates/plugins/tests/abi.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/ui-egui/src/tests/plugin_runs.rs`. Scores and hours unchanged.
- **Plug-in instruction budget at a bulk memory instruction:** a run that pauses where one slice of its instruction budget ends keeps the instructions the slice still held. A `memory.fill` or `memory.copy` that the slice can't pay for in full runs on the next slice, so a run whose budget covers its work finishes; a run ends with `Limit("instruction budget")` only when the instructions it has left can't pay for the next instruction. Regressions: `crates/plugins/tests/abi.rs`. Scores and hours unchanged.
- **Plug-in permissions (G17):** a manifest may list `permissions` (`doc.read`, `doc.write`, `state`, `effect.render`, `export.write`; the first three follow storytold/photocraft#662) and an `api` level; without the list a plug-in gets what its kind implies and installs as before. Install Plug-in… and File › Open show the name, id, version, file SHA-256 and each permission in plain words, with control and bidirectional characters escaped, and install nothing on Cancel; `plugin.install` needs `grant`, and folders install only granted plug-ins. The grant is kept with the module's hash, a module asking for more asks again, and a filter run refuses output its permissions don't allow. Regressions: `crates/plugins/src/permissions.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`, `crates/ui-egui/src/tests/plugins.rs`. Scores and hours unchanged.
- **Plug-in progress, Cancel and log lines (G17):** at plug-in API level 2 a plug-in with the `progress` permission imports `env.vc_progress`, `env.vc_cancelled` and `env.vc_log`. A heavy filter's and an export's progress windows show its progress as a percentage bar, their Cancel reaches it through `vc_cancelled`, and its log lines go to the app's log with its id, capped per line and per run. Installing checks every import and names the one it refuses. Regressions: `crates/plugins/src/host.rs`, `crates/plugins/tests/abi.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`, `crates/ui-egui/src/tests/plugin_runs.rs`. Scores and hours unchanged.
- **Plug-in effects after a plug-in changes:** when a plug-in is installed for the first time, installed again or removed (`plugin.install`, `plugin.reload`, `plugin.remove`, File › Open of a `.wasm` file, the Additional Plug-ins Folder preference), the next frame the canvas draws uses the plug-ins then installed for objects with a plug-in effect and their raster effects, for the blends, envelopes and repeats that show such objects, and for pattern tiles and symbol instances whose art holds one.
+30 -1
View File
@@ -14,7 +14,36 @@ Select a path or compound path, then choose **Effect › 3D and Materials › Ex
artboard.
Preview changes are temporary until OK; Cancel restores the document; OK is one undo step. An object takes one
3D effect: edit its options in the Appearance panel, or remove it before applying another.
3D effect: edit its options in the Appearance panel or the 3D and Materials panel, or remove it before applying
another.
## 3D and Materials panel
**Window › 3D and Materials** opens a panel that edits the 3D effect of the selection without a dialog, as the
public [3D and Materials panel options](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/3d-materials-panel-options.html)
and [Create 3D vector artwork](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/create-3d-vector-artwork.html)
pages describe it.
- The object types **Plane**, **Extrude**, **Revolve** and **Inflate** sit at the top. A type applies its effect
to each selected object that has no 3D effect (Plane is Rotate, Extrude is Extrude & Bevel), and changes the
3D effect of each other selected object in place (`effect.setParams` with `threeDType`): the settings both
effects take keep their values, and the rest start at the new effect's defaults. One click is one undo step,
and an object whose effect already has the type does not change.
- Below them are the Object, Rotation and Lighting tabs of the effect's options dialog, with the same controls
and ranges, showing the 3D effect of the first selected object (its own effects first, then those of its fills
and strokes). A changed setting goes to the 3D effect of each selected object whose effect takes that setting;
other effects and objects without a 3D effect do not change. A setting the selected 3D effects hold with
different values shows its default with its reset dot filled; a checkbox shows a dash instead, and a mixed
Shading leaves the light settings available. Its reset dot, a choice from its menu or a click on its checkbox
(which turns a mixed one on) sets it on all of them; a typed or dragged number sets it when it differs from the
default shown. Other changes leave it as each effect has it.
The list of lights shows the first object's lights, and an edit to it (Add Light, Delete Light, or a setting
of one light in a list) reaches only the objects whose list of lights matches the first object's. Add Light is
unavailable while a setting of a single light differs between the selected effects.
- Each change is one undo step. Dragging a value or a slider previews it on the artboard and records one step
when the pointer is released; a typed value is recorded when it is entered. Drawing the panel records nothing.
- The Materials tab, shadows and ray-traced rendering are not built (see below). The rotation and light rings on
the artboard are part of the options dialog only.
## Extrude & Bevel
+1 -1
View File
@@ -21,7 +21,7 @@ against Illustrator's public documentation; **estimated** unless noted.
| Mouse wheel over the closed font-family field | done | #5253: Character, Properties and Control step selected text through the family list using saved font filters (excluding search text), one undo step per notch; the style is matched and the list stops at either end. Escape and leaving the field keep the change. Order, bounds and Undo grouping are VectorCraft's choices; scores unchanged. |
| Soft return (forced line break, Shift+Enter) | done | Shift+Enter inserts a soft return (U+2028), breaking the line without starting a new paragraph; paragraph spacing (space before/after) and first-line indents apply only at paragraph ends; continuation lines in lists do not repeat the marker; soft return lines in justified text do not spread (#2754) |
| Native font-family labels | partial | With Show Font Names in English off, localized legacy names are used when typographic names contain no native label (#2208); loaded-font and catalog-only regressions. Choosing among multiple native languages is unchanged. |
| Point, area and type-on-a-path type; vertical variants | done / partial | vertical point, area and path type have initial support; fullwidth `!` / `?` keep the nominal glyph upright when no vertical alternate exists (Unicode Tu); in a non-rectangular area a word too wide for the top lines moves down to the first line that holds it (#1615); Escape while typing ends the edit, keeps the type selected and switches to the Selection tool (#1802); `text.create` and `text.createInPath` take `justify` (#1752); MCP `add_text` forwards `vertical` to both commands; the Type and Vertical Type tools make area type when a closed path's outline is clicked and type on a path when an open one is, with pointers that say which, as the public help describes ([Create text](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html), [Create type on a path](https://web.archive.org/web/20220127090635/https://helpx.adobe.com/illustrator/using/creating-type-path.html)) |
| Point, area and type-on-a-path type; vertical variants | done / partial | vertical point, area and path type have initial support; fullwidth `!` / `?` keep the nominal glyph upright when no vertical alternate exists (Unicode Tu); in a non-rectangular area a word too wide for the top lines moves down to the first line that holds it (#1615); Escape while typing ends the edit, keeps the type selected and switches to the Selection tool (#1802), and Cmd+Enter (Ctrl+Enter on Windows and Linux) does the same unless a command is bound to it; `text.create` and `text.createInPath` take `justify` (#1752); MCP `add_text` forwards `vertical` to both commands; the Type and Vertical Type tools make area type when a closed path's outline is clicked and type on a path when an open one is, with pointers that say which, as the public help describes ([Create text](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html), [Create type on a path](https://web.archive.org/web/20220127090635/https://helpx.adobe.com/illustrator/using/creating-type-path.html)) |
| Area Type Options (rows, columns, inset, first baseline, Text Flow by rows or by columns, Auto Size, Shrink Text to Fit) | done | Text Flow fills the cells down each column first (By Columns, the default, as files from before it open) or across each row first (By Rows), in the dialog and `text.areaOptions {flow}` (#4978); the commands that make or restyle type say when it does not fit its frame; MCP `add_text` takes Auto Size and Shrink Text to Fit (#1471) |
| Threaded text, Fit Headline, Text Wrap | done | Fit Headline sees wrapping in a frame one line tall (#1444); a thread holds area type and type on a path in any mix, as Illustrator links type in an area or along a path ([help](https://helpx.adobe.com/illustrator/using/manage-text-area.html)) |
| Threading from the ports: selected area type and type on a path show an in port and an out port, empty, with an arrow on a side where the frame is threaded, or with a red "+" in the out port when the text doesn't fit; a click on either port loads the text; a click on a closed path, area type or type on a path threads into it, a click on empty canvas makes a frame of the same kind, shape and size there, a drag draws a rectangular frame; the frame placed comes after the clicked one from its out port and before it from its in port (the story then starts there); Escape or another tool cancels; a double-click on a threaded port breaks the thread there | partial | #2572: the Selection tool, `text.thread.add {from, to?, rect?, at?, before?}`, as the reference app's help describes ports and threading ([archived help, Threading text between objects](https://web.archive.org/web/20210119051048/https://helpx.adobe.com/illustrator/using/creating-text.html), [create text threads](https://helpx.adobe.com/illustrator/desktop/design-with-text/add-manage-text/create-text-threads-between-objects.html)); area type's in port sits on the left side below the top-left corner, type on a path's just before its start bracket; Show Text Threads draws its lines between the same ports; with more than 1,000 such objects selected no ports are drawn. a double-click on a port where the frame is threaded breaks the thread there, the story flowing into the frames before the break and the frames after it left empty, still threaded with each other when two or more (`text.thread.break`, [archived help](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html)). Remaining: the ports only take clicks with the Selection tool, and the loaded cursor borrows the I-beam until it's drawn |