Merge main into feat/color-rgb-percent

This commit is contained in:
dotCooCoo
2026-11-12 15:11:03 -08:00
467 changed files with 36566 additions and 2846 deletions
+21 -3
View File
@@ -1,6 +1,6 @@
# Control protocol
> **Last updated:** 2026-10-31 · **Change:** Confining file access refuses a `:` in a name after the drive on Windows, an NTFS alternate data stream such as `x.svg:evil` (#3604); previously: Object → Expand… dimmed while the selection has an appearance Expand Appearance expands (`object.expand.info`'s `appearance`, #3089); previously: brush library panels (`window.brushLibrary`, `window.brushLibrary.other`, `ui.saveBrushLibrary`); previously: Transform Each opens on the values it was last confirmed with; its Copy button confirms with `copy: true` (#1732); previously: panel docking on craft-ui's layout: `ui.docking`, the parameters of `window.panel.float` and `window.panel.dock`; previously: `ui.screenshot`'s 64 MiB inline-PNG cap and its path-plus-data downgrade; symbol library panels (`window.symbolLibrary`, `window.symbolLibrary.other`, `ui.saveSymbolLibrary`); synthetic input budgets (#1709); bounded transport waits (#1726).
> **Last updated:** 2026-11-07 · **Change:** text style commands take a feature with a value (`"features": ["salt=2"]`), the font's alternate number, and shape runs that differ only in features as one piece of text; previously: Confining file access refuses a `:` in a name after the drive on Windows, an NTFS alternate data stream such as `x.svg:evil` (#3604); previously: Object → Expand… dimmed while the selection has an appearance Expand Appearance expands (`object.expand.info`'s `appearance`, #3089); previously: brush library panels (`window.brushLibrary`, `window.brushLibrary.other`, `ui.saveBrushLibrary`); previously: Transform Each opens on the values it was last confirmed with; its Copy button confirms with `copy: true` (#1732); previously: panel docking on craft-ui's layout: `ui.docking`, the parameters of `window.panel.float` and `window.panel.dock`; previously: `ui.screenshot`'s 64 MiB inline-PNG cap and its path-plus-data downgrade; symbol library panels (`window.symbolLibrary`, `window.symbolLibrary.other`, `ui.saveSymbolLibrary`); synthetic input budgets (#1709); bounded transport waits (#1726).
`vectorcraft --control <port>` listens on `127.0.0.1:<port>` (loopback only). The port must be a number
from 1 to 65535 (`--control=<port>` works too), given once; otherwise the app prints an error and exits
@@ -102,7 +102,7 @@ the two can still win that race.
| `ui.click` / `ui.drag` | `{x, y, button?, count?, shift?, alt?, ctrl?, cmd?}` / `{x, y, toX, toY, steps?, count?, button?, shift?, alt?, ctrl?, cmd?}` | egui pointer input; see synthetic input budgets above |
| `ui.wheel` | `{x, y, dy?, dx?, unit?: "line"\|"point", shift?, alt?, cmd?}` | a mouse wheel turn over screen point (x, y): `dy` notches up (+) or down, `dx` sideways. Over the canvas the wheel scrolls, Cmd-wheel (Ctrl on Windows and Linux) scrolls sideways and Alt-wheel (Option on the Mac) zooms about the pointer (with the `ctrlWheelScrollsSideways` preference off, Cmd-wheel zooms too); with the `zoomWithMouseWheel` preference the wheel and Alt-wheel zoom about the pointer, Shift-wheel scrolls up and down and Cmd-wheel (Ctrl on Windows and Linux) sideways. Over a focused numeric field (click it first) each notch steps its value as Up/Down do (Shift: ten, Cmd/Ctrl: a tenth) and the panel stays put; over anything else in a panel the wheel scrolls it. Over the canvas, the wheel preferences in `docs/mcp.md` change what these keys do |
| `ui.set` | `{brightness?, panel?, rulers?, outline?, grid?, smartGuides?, boundingBox?, controlBar?, imageTracePreview?}` | `outline`: View › Outline for the active document only (each document has its own, a document opens in Preview, and `ui.inspect` shows it as `view.outline`; an error without a document). `imageTracePreview`: the Image Trace panel's Preview (`ui.inspect` shows it as `ui.image_trace_preview`) |
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value, action?}` or `{fields: [{field, value}…], action?}` | fill and submit the open dialog. `ui.dialog.set` takes the fields the dialog has (`ui.inspect`), `__` fields, `discard` on a dialog with a discard button, `preview` on one with a live preview, the fields that the dialog reads but adds only when a control changes (such as `searchFolder`, or `copy` in the Transform dialogs), and the fields it passes to the command it runs: every parameter of the `command` dialog's command (`ui.paramDialog`), and the `textureImage` that Load Texture… sets in the Glass and Texturizer effects; any other field is an error that lists the dialog's fields, and the dialog stays as it was `fields` sets the list (at most 256 items) in order (a missing `value` is `null`) and checks every item first: when one is refused, the call is an error and no field changes. `action: "confirm"` or `"cancel"` on `ui.dialog.set` then confirms or cancels the same dialog in the same request, as `ui.dialog.confirm` and `ui.dialog.cancel` do, and the reply is `{dialog, result}`: the dialog after the fields were set and the reply to confirm (`null` for cancel); any other `action` is an error and no field changes |
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value, action?}` or `{fields: [{field, value}…], action?}` | fill and submit the open dialog. `ui.dialog.set` takes the fields the dialog has (`ui.inspect`) other than its `__` fields, the `__` fields that are controls of the dialog (such as `__section` and `__allArtboards` in Save PDF, `__constrain` in Transform Each and the shape dialogs, and `__search` and `__category` in Preferences; Save PDF's and Print's `__savePresetAs`, the preset name, only while Save Preset… asks for it, since OK then saves a preset), `discard` on a dialog with a discard button, `preview` on one with a live preview, the fields that the dialog reads but adds only when a control changes (such as `searchFolder`, or `copy` in the Transform dialogs), and the fields it passes to the command it runs: every parameter of the `command` dialog's command (`ui.paramDialog`), and the `textureImage` that Load Texture… sets in the Glass and Texturizer effects; any other field is an error that lists the dialog's fields, and the dialog stays as it was. Any other `__` field, held or not, holds what the dialog's OK acts on (such as the `__command` and `__params` of a `confirm` or `command` dialog) or the dialog's own state: setting it is the error "the `<kind>` dialog cannot set protected field `<field>`", and the dialog stays as it was. `fields` sets the list (at most 256 items) in order (a missing `value` is `null`) and checks every item first: when one is refused, the call is an error and no field changes. `action: "confirm"` or `"cancel"` on `ui.dialog.set` then confirms or cancels the same dialog in the same request, as `ui.dialog.confirm` and `ui.dialog.cancel` do, and the reply is `{dialog, result}`: the dialog after the fields were set and the reply to confirm (`null` for cancel); any other `action` is an error and no field changes |
| `ui.screenshot` | `{path?, data?}` | capture the window (PNG), written to `path`; `data: true` sends it back as `pngBase64` too. The inline PNG is capped at 64 MiB: with a `path` the file is still written and `pngBase64` is omitted when the image is larger, while a path-less `data: true` request fails if either the raw RGBA image or the encoded PNG exceeds the cap. Needs a presented frame: with the screen locked or the window minimized/covered it fails after ~8 s with an explanatory error |
| `ui.render` | `{path?, scale?, artboard?, data?, traceViews?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first; `traceViews: true`: Image Trace objects draw in their View, as on the canvas, instead of their tracing result): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
| `ui.render` | `{path?, scale?, artboard?, data?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
@@ -148,7 +148,7 @@ fill or stroke (fields as in `swatchOptions` plus `group`; a gradient or pattern
The Color Picker is a dialog too: `engine.execute {command: "ui.colorPicker", params: {stroke?, color?}}` opens it for the fill (or stroke) proxy; `ui.dialog.set {field: "hex", value: "00FF00"}` (or `color`, `channel`, `webOnly`, `swatches`) then `ui.dialog.confirm` applies the colour through `paint.setFill` / `paint.setStroke`.
Sample Color from Screen: `ui.sampleScreenColor {stroke?, picker?}` shows the system's color sampler (macOS, and browsers with an EyeDropper) and returns `null` at once. When the person at the computer clicks a color anywhere on the screen, it runs `paint.setFill` (or `paint.setStroke`) with that color as one undo step, or with `picker: true` sets the open Color Picker's `color`; Escape cancels it and the colors stay as they were. The sampled display sRGB color is converted into the working RGB space. `ui.inspect` reports `screenSampler: true` until the sampler answers. Without a sampler (Windows, Linux, other browsers) the call returns an error. To read a color from VectorCraft's own window without a person, call `ui.screenshot`.
Sample Color from Screen: `ui.sampleScreenColor {stroke?, picker?}` shows the system's color sampler (macOS; Linux through the XDG desktop portal's [PickColor](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html); browsers with an EyeDropper) and returns `null` at once. When the person at the computer clicks a color anywhere on the screen, it runs `paint.setFill` (or `paint.setStroke`) with that color as one undo step, or with `picker: true` sets the open Color Picker's `color`; a cancel (Escape on macOS and in browsers) leaves the colors as they were. The sampled display sRGB color is converted into the working RGB space. `ui.inspect` reports `screenSampler: true` until the sampler answers. On Linux, a session without a portal that has PickColor, a portal that does not return from the call within 25 seconds (VectorCraft's own limit) or leaves the bus before it answers, and a portal that ends the request in another way each put a message in the status bar and end the wait, so the sampler can be shown again. A portal that returns from the call and then neither answers nor leaves the bus keeps the sampler open. Without a sampler (Windows, FreeBSD, DragonFly BSD, NetBSD, OpenBSD, other browsers) the call returns an error. To read a color from VectorCraft's own window without a person, call `ui.screenshot`.
Graphic styles: `ui.graphicStyleOptions {name}` opens the `graphicStyleOptions` dialog (field `name`) for a style and
`ui.dialog.confirm` renames it with `graphicStyle.rename` (a name another style has returns an error and keeps the
@@ -992,6 +992,23 @@ handle); a Mesh tool click inside a mesh envelope adds a row and a column. While
selected the Control bar shows its controls (Edit Envelope / Edit Contents, warp style, orientation, bend and
distortions or mesh rows and columns, Reset, Envelope Options); each runs an `object.envelope.*` command.
## OpenType feature values in text commands
`text.setRangeStyle`, `text.setStyle` and the character style commands take OpenType features as a list of tags in
`features` (the whole list) or `featureChanges` (changes on top of each character's own): `"dlig"` on, `"-liga"` off.
A feature with alternates (`swsh`, `ss01`–`ss20`, `salt`, `titl`, `pnum`, `lnum`, `sups`, `subs`, `numr`, `dnom`,
`aalt`) also takes `tag=N`, `N` from 0 to 65535, the font's alternate number: `"salt=2"` draws the second alternate of
each glyph in the range that has one. It is the value the shaper gets (an OpenType alternate substitution picks the
glyph at that position of its set), so `"salt"` is `"salt=1"`, and `"-salt"`, `"salt=0"` and a missing tag are off. A
later value replaces an earlier one; `salt=x`, `salt=65536`, `-salt=2` and `liga=2` are refused. The style's
`features` list and the saved file keep the string as typed (`["salt=2"]`), and SVG export writes CSS
`font-feature-settings: "salt" 2`. Runs that differ only in `features` are shaped as one piece of text with each run's
features over its own characters, so one letter of an Arabic word with `salt=2` keeps its neighbours' joining forms.
```json
{"id":1,"method":"engine.execute","params":{"command":"text.setRangeStyle","params":{"id":7,"start":4,"end":6,"features":["salt=2"]}}}
```
## Shared panel docking
The canvas and the panels form one craft-ui docking layout: the protected `canvas` panel beside a dock at the
@@ -1087,6 +1104,7 @@ hidden tab (whose frames the browser pauses) answers when it is shown again.
| Date | Change | Summary |
|---|---|---|
| 2026-11-07 | minor | Documented OpenType feature values in text commands (`"salt=2"`, the font's alternate number) and that runs differing only in features shape as one piece of text. |
| 2026-10-31 | minor | Confining file access: on Windows a `:` in a name after the drive (an NTFS alternate data stream) is refused with the device names (#3604). |
| 2026-10-25 | minor | Panel docking on craft-ui's layout: `ui.docking`, the floating and dock parameters, native floating windows. |
| 2026-10-25 | minor | Documented that Object → Expand… is dimmed, and `ui.expandDialog` refuses, while the selection has an appearance Expand Appearance expands (`object.expand.info`'s `appearance`, #3089). |
+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), contours that cross, touch or overlap merged first ([three-d.md](three-d.md)), Position presets, rotation and light gizmos, SVG/PDF output, Expand Appearance ([three-d.md](three-d.md)) |
| 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; hard-edged shadows behind or below the object from the first light ([three-d.md](three-d.md#shadows)); no materials, soft 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, and Quick Actions › Expand as Wireframes ([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; hard-edged shadows behind or below the object from the first light ([three-d.md](three-d.md#shadows)); no materials, soft shadows or ray tracing |
| 3D (Classic): Extrude & Bevel, Revolve, Rotate | Extrude & Bevel (Classic) initial: live, Extrude Depth, Cap, five original bevel shapes with Height and Bevel Extent In or Out, Position presets and rotation gizmo, Perspective, Wireframe, No Shading, Diffuse and Plastic surfaces with one to ten lights, Light Intensity, Ambient Light, Highlight Intensity and Size, Blend Steps, Black, Custom or no Shading Color, Draw Hidden Faces ([create 3D objects](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/create-3d-objects.html)); Revolve (Classic) initial: live, Angle, Cap, Offset from the left or right edge, Position presets and rotation gizmo, Perspective, the same four surfaces, lights, highlight, Blend Steps and Shading Color, groups and layers revolved about one shared axis (same page); Rotate (Classic) initial: live, every fill and stroke turned as one plane, Position presets and rotation gizmo, Perspective, No Shading and Diffuse surfaces with lights, Ambient Light, Blend Steps and Shading Color ([rotate objects in 3D](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/rotate-objects-in-3d.html)); Map Art: none |
| Turntable (30.0 beta) | none |
| Export 3D objects (OBJ, USDA, glTF) | initial: Asset Export's Export 3D Object collects an object with Extrude & Bevel, Revolve, Inflate or Rotate as a 3D asset, and Export writes its solids as a USDA (the default), USDZ, glTF or OBJ mesh with a material in each solid's paint color (`assets.add3D`, `assets.set3DFormat`, `assets.export3D`); no textures, map art or shading ([3D export help](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/export-3d-vector-artwork.html)) |
File diff suppressed because one or more lines are too long
+24 -13
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -22,7 +22,7 @@ It reads the pen's tilt and barrel rotation on macOS and on Linux under X11 and
| Hybrid-graphics GPU choice | automatic | power-saving GPU by default, Preferences › Graphics Processor | same; DX12 then OpenGL; no Vulkan unless asked (#545, #806) | the desktop's GPU | browser | done |
| Restart on another GPU when the first fails to present | — | yes (#502, #964) | yes | yes | restart on lost device (#369) | done |
| Pen pressure | yes (Wacom, Windows Ink, macOS tablet events) | yes, read from AppKit's tablet events before the window gets them (winit reports none), each move of a stroke with its own pressure, mouse event coalescing off (#852; not yet tried on a Mac) | yes (pointer force via `WM_POINTER`) | under X11 and XWayland (Preferences › Performance › Linux Display Server, #1151, #2028; Automatic uses XWayland when a pen is attached on a Wayland session, #4967), read from XInput2 raw events (#764); **no** on native Wayland, where winit reports none and the pen may not reach the window (#491) | browser pointer events, with coalesced events; Apple Pencil on iPad (hover, palm rejection: fingers navigate after a pen contact until reload) | partial: 2–4 h for Wayland (winit 0.31), plus a check on a Mac |
| Pen tilt, bearing, barrel rotation (6D Art Pen) | yes, drives brushes | read from AppKit's tablet events (tilt, and the barrel rotation of a pen that reports one); Calligraphic and Scatter brushes vary with them (#764; not yet tried on a Mac) | with Windows Ink, read from the pen's `POINTER_PEN_INFO` (tilt and barrel rotation where its pen mask says the pen reports them); Calligraphic and Scatter brushes vary with them (#372; not yet tried with a pen) | under X11 and XWayland, read from XInput2 raw events; Calligraphic and Scatter brushes vary with them (#764); **no** on native Wayland | no | partial: native Wayland and the web read none, and Art, Pattern and Bristle brushes do not use them; 3–6 h (#372) |
| Pen tilt, bearing, barrel rotation (6D Art Pen) | yes, drives brushes | read from AppKit's tablet events (tilt, and the barrel rotation of a pen that reports one); Calligraphic and Scatter brushes vary with them (#764; not yet tried on a Mac) | with Windows Ink, read from the pen's `POINTER_PEN_INFO` (tilt and barrel rotation where its pen mask says the pen reports them); Calligraphic and Scatter brushes vary with them (#372; not yet tried with a pen) | under X11 and XWayland, read from XInput2 raw events; Calligraphic and Scatter brushes vary with them (#764); **no** on native Wayland | no | partial: native Wayland and the web read none, and Pattern and Bristle brushes do not use them (an Art brush's Width follows them); 3–6 h (#372) |
| Pen eraser end | yes | the eraser end (AppKit's tablet proximity events) selects the Eraser tool and the tip brings back the tool used before, as on Linux (#852; not yet tried on a Mac) | with Windows Ink, an inverted pen or its eraser button (`PEN_FLAG_INVERTED`, `PEN_FLAG_ERASER` in the pen's pointer messages) selects the Eraser tool and the tip brings back the tool used before, as on Linux (not yet tried with a pen) | under X11 and XWayland, the eraser end selects the Eraser tool and the tip brings back the tool used before (#764); **no** on native Wayland | a press of the eraser end (Pointer Events eraser button, `button` 5) selects the Eraser tool, and that press erases; a press of the tip brings back the tool used before; Pointer Events report no end while the pen hovers | partial |
| Touch gestures (pinch zoom, two-finger pan, rotate view, Touch workspace) | yes (Windows touch, Surface) | trackpad pinch, scroll and rotate | pinch, pan (a second finger cancels the first finger's tool press, #4198); two-finger tap undo, three-finger redo (#585) | same where winit reports touch | eframe until the first pen contact, then Pointer Events (pen and fingers told apart); long press opens the context menu | partial: touch workspace and touch-screen rotation missing, 3–5 h |
| Trackpad (smooth scroll, pinch) | yes | yes | precision touchpads | yes | yes | done |
+10
View File
@@ -87,6 +87,16 @@ pixels and 16,384 pixels on a side. A raster export shows the line as the canvas
Type Optimized anti-aliasing, or when it uses Art Optimized and is larger than either limit.
Overlapping stacks the colors instead: a lower color continues under the edge of the color above it,
and no background shows along that edge.
**Flat Logo with named colors.** With `logoColors` named, Flat Logo decides whether the image is flat
art by the image's own colors. It finds them the same way as when no colors are named, and looks for as
many colors as are named, with white, or as the Colors setting gives, whichever is more. How far the named colors
are from the image's colors does not change whether the image is refused. While no more colors are
named than the Colors setting, an image is refused exactly when it is refused without named colors.
The trace then uses only the named colors, and white while white is a color of its own. An area of a
color that was not named is traced too: each of its pixels goes to the named color, white or
transparent that has the largest share in the blend of them that comes closest to the pixel.
**Large images.** An image of more than 1500 pixels on a side is traced as any other, only more
slowly, and the trace says so: `imageTrace.make` returns the note in `warnings` and the status bar
shows it (the [results optimization](https://helpx.adobe.com/illustrator/using/image-trace-results-optimization.html)
+30
View File
@@ -126,6 +126,36 @@ flipped crack's, and the image does have cracks the one-sided reading gets wrong
48 px noisy ripple in Black and White at thresholds 140, 157, 170 and Paths 0, 50, 100, sampled on
the 4 × 4 grid per pixel the shared-boundary check uses). With the one-sided reading 482–1,228
sample points per trace were uncovered; now none are. The crate's other tests pass unchanged.
## Band borders in smooth areas
In a gradient or a shaded area, the palette colors on either side of a band border differ by more than
the image changes across it. Pixel noise placed the border, and the fitter smoothed it over 0.5 px as it
smooths an edge, so the border wobbled, kept stair steps and took many anchors. For each crack between
two colors, `Layers::step` now reads the share of the step between the two colors that the image makes
next to the crack and a few pixels further out, how far the image changes next to the crack along the
line between the two colors and in any direction, and how much it changes within each side, as RGB
distances (0 to 255 a channel). `subpixel::low_contrast` makes a piece of outline the contour of a smooth
area when, over its cracks, the near share is under one half; the far share is at least 1.25 times the
near one, the change along the line is under 12 (about 7 levels in each channel of a gray), or no crack
has a far share; the sides change within themselves, by at least 1/32 of the change along the line; and the
whole change is under twice the change along the line, or under 24. Black and White traces do not use
the rule. A contour piece is smoothed over 4 px (a closed one over at most an eighth of its smaller width
or height), keeps no corners, is fitted to at least 0.5 px and is checked against its smoothed points.
Grayscale now reads each pixel as its gray for this rule and for every edge's sub-pixel shift
(`Layers::shift`), which read the pixels' colors against gray palette entries before.
Measured with the harness below, before and after the change: Color traces of the "flat colour art"
inputs, and corners % in every row, do not change. Shades of Gray on those inputs: off mean 0.263 to 0.165 px,
off max 1.47 to 1.04 px, anchors 196 to 122. Gradient and photo inputs: anchors 2,138 to 798 (16 Colors,
gradient and photo), 1,372 to 200 (16 Colors, linear gradients), 10,063 to 6,766 (High Fidelity Photo,
gradient and photo); ΔE and edge ΔE change by −0.25 to +0.02. Specks rise by up to 0.8 per image (16
Colors on gradient and photo 0.0 to 0.8, Auto Color 0.0 to 0.5). Auto Color, enhanced (Shapes on) on
gradient and photo leaves a gap of 0.3 px (was 0.0) and an overlap of 0.4 px (was 0.1). The tables below
are the previous measurement, not a rerun.
Tests on code-drawn images: `tests::the_borders_of_a_gradients_bands_follow_its_contours` (a 256 × 128
gradient whose contours wave, with noise, and a dark square with crisp sides: the band borders stray
under 0.9 px RMS from the gradient's contours, 1.13 px when smoothed as edges, and the square keeps its
four corners); `layers::tests` (a ramp, flat colors in palette entries far apart, close flat colors, a
thin flat stroke, colors off the line between their entries, and Grayscale's grays, each read from both
sides of the crack) and `subpixel::tests` (the rule, and one decision for a whole piece from either side).
## A simpler contour walk
`contour::trace_mask` follows each region's outline along the pixel cracks with the inside on the
right. It used to build a table of outgoing directions for every pixel corner in a raster pass and
+489 -56
View File
@@ -131,7 +131,9 @@ the suite) keep the agent to the folders you give it:
written beside it by their file names. A save without a path known (`file.saveAs` and `file.saveCopy` without
one, a document never saved) leaves out the suggested `folder`. `document.exportForScreens` into the `folder`
of its `exportPreset` leaves out the ZIP's `path` and lists `files` relative to that folder, and an error
for that export names the folder by its name alone.
for that export names the folder by its name alone. With Background Export on, an export into the `folder` of
its `exportPreset` runs in the background as one into a `folder` the agent gives, and its replies, errors and
status bar text name that folder the same way.
`document.exportSettings`, `document.exportPresets.list` and `document.exportPresets.export` (as data and as
a file) leave out a kept `folder` outside the read roots. `file.package`
names the document's file and the linked files outside the read roots by their file names in `warnings`, in
@@ -143,24 +145,64 @@ the suite) keep the agent to the folders you give it:
`file.openRecentN` for a file outside the roots ends with `no such recent file`, as an empty slot does.
- `ui_inspect` (the control channel's `ui.inspect`) leaves out the paths outside the read roots: in `opening`, the
documents still being read; in `ui.recent_files`, the recent files; a library panel's `path` in `ui.library_panels`;
and the folder preferences in `ui.engine_prefs` and in an open Preferences dialog's `ui.dialog.fields`
in a recorded action's steps in `ui.action_sets`, also in a batch step's commands, the `path`, `paths`, `file`,
`files` and `folder` params, a param whose name ends in `Path`, `Paths`, `File`, `Files` or `Folder` (`diffPath`,
`destinationFolder`), the entries of a list under such a param, and under any other param, as a key or in any
list, an absolute path or a `file:` URL outside the roots (any case of `file:`, `%XX` escapes decoded; a URL that
names another host is always left out), and `~`, a path under `~` or a path that starts with `./` or `../`
(either separator) that resolves outside the roots from the app's working folder; and the folder
preferences in `ui.engine_prefs` and in an open Preferences dialog's `ui.dialog.fields`
(`fontsFolder`, `pluginsFolder`, `scratchPrimary`, `scratchSecondary`, `recoveryFolder`, `templatesFolder`; an
empty folder and the scratch disk choices `Startup` and `None` stay). A library loaded from a file outside the
roots shows as `loaded/<file name>` wherever `ui_inspect` and `ui.docking` name it: its id, its panel's id, the
Color Guide's `ui.color_guide_limit` and a closed panel's key in `ui.docking_hidden`, also when its panel is
closed. When another hidden library has the same file name, the libraries take `loaded/<file name>`,
`loaded/<file name> (2)`, `(3)` and so on in the order of their full ids (the libraries loaded in this session
first, then those only the UI state names), and each one keeps its id in both replies and in the library
commands below. The other entries keep their order, and the app's own state is not changed.
Color Guide's `ui.color_guide_limit`, a closed panel's key in `ui.docking_hidden` and the `library` and `panel`
params of a recorded action's steps in `ui.action_sets`, and the keys of shortcut overrides (`panel:<panel id>`
in `ui.shortcut_overrides` and an open Keyboard Shortcuts dialog's `ui.dialog.fields.overrides`), also when its
panel is closed. A path written with
either separator counts as a path, also one saved on another platform, and outside Windows a path that starts
with a drive (`C:`) or a `\` counts as outside the roots. Outside Windows, any other path keeps a `\` in its
file name (`/libs/a\b.ase` shows as `loaded/a\b.ase`), and a reply matches it only as written, not with `/`
in place of the `\`. When another hidden library has the same
file name, the libraries take `loaded/<file name>`, `loaded/<file name> (2)`, `(3)` and so on in the order they
are first shown. Libraries first shown in the same reply are numbered in the order of their full ids, the
libraries loaded in this session first, then those only the UI state names. Each one keeps its id for the rest
of the session in both replies and in the library commands below, also after another library with the same
file name is loaded. A hidden library skips an id that a library loaded without a path already has, and only a
library loaded later from data under that file name, which takes the id itself, moves a hidden one to the next
free number. The other entries keep their order, and the app's own state is not changed. A request may name such a library or its panel by the id
shown in a `panel`, `library` or `panels` param, in a docking destination (`anchor`, `target`, `before`, `onto`,
`above`, `below`) and in `window.panel.layout`'s `action` (`ui.set {panel: "library:swatches:loaded/<file name>"}`
reopens its panel), and the reply to such a request shows that id and file name wherever the library's id or path
appears, an error included. Every reply under the read roots shows a hidden library's id as the id shown and its
path as the file name in every string and key, the status text (`ui.status`), dialogs, the command palette query
(`ui.palette_query`), a script's report (`scriptReport`), a screenshot's reply and the error from an internal
fault included, for the libraries hidden before the request ran and after it. The path is also matched with `%XX` escapes as a `file:`
URL writes it, and on Windows in any case of its letters; a `file:` URL of an absolute path is shown as the file
name alone, without `file:` or its host. A hidden id or path stays only where it begins
another loaded library's whole id with no `.` or `..` part, so a readable library's id that begins with a hidden one stays. A reply also shows a path outside the
read roots that the request sent by its file name, whether or not a library was loaded from it and whether the
request succeeded or failed, so that the reply reads the same either way. The one exception is the `path`
and `files` entries of a reply's result, a `command.batch` step's result included, that name the file the
request wrote inside the write roots at the path the request gave: they stay as
given, as for the save and export replies above. A path param (`path`, `file`,
`destinationFolder` and the like) counts with a relative path too. The hidden libraries' ids and paths are
matched before the paths the request sent, so a sent path written over part of a hidden library's path does not
keep that path in the reply. A relative path is not replaced where it ends a longer path (`/work/data/icon.svg`
keeps `data/icon.svg`), and the absolute path it reads as from the app's working folder is replaced too. Encoded file data, a base64 string under `dataBase64`,
`pngBase64`, `thumbnailBase64` or `thumbnail` (`library.get`, `document.pdfInfo`), is sent as it is.
- `swatch.library.list`, `brush.libraries`, `symbol.libraries` and `graphicStyle.libraries` list every library
but leave out the paths outside the read roots: a library's `path` and the user library folder in `userFolder`
are `null` there. A library loaded from a file outside the roots is listed as `loaded/<file name>`, numbered
`(2)`, `(3)` and so on as in `ui_inspect` when two have the same file name. That id finds the library in the
other library commands (`swatch.library.get`, `swatch.library.add`, `brush.library`, `brush.addFromLibrary`,
`symbol.library`, `symbol.addFromLibrary`, `graphicStyle.library`, `graphicStyle.addFromLibrary` and the
others that take `library`), whose replies show it too. `swatch.library.copyToUser` refuses a library whose file
is outside the read roots without naming the file, and it and the library saves with `user: true` leave out
`path` when the user library folder is outside the read roots.
others that take `library`), whose replies show it too. Over the control channel, `window.swatchLibrary` and
the other library panel commands answer with that id and its panel's id (`library:swatches:loaded/<file name>`),
and `ui.libraryPanel` and the `window.panel.*` commands take that panel id in `panel`, `anchor`, `before`,
`onto`, `above` and `below`. `swatch.library.copyToUser` refuses to copy a library file outside the read roots
without naming the file (the swatches of a document loaded from there are written from the loaded library, as
for a library loaded from data, and the document is not read again), and it and the library saves with
`user: true` leave out `path` when the user library folder is outside the read roots.
- `links.list`, `links.check`, `links.info` and the `missingLinks`, `modifiedLinks` and `updatedLinks` rows of
`document.open` leave out a linked file's path when the file is outside the read roots (a relative path is taken
from the document's folder): `path`, `found`, and in `links.info` also `link` and `location`, with the `fileSize`
@@ -180,6 +222,14 @@ the suite) keep the agent to the folders you give it:
`null`; an empty folder and the scratch disk choices `Startup` and `None` stay. In `prefs.get {}` and
`prefs.reset`, an export preset in `screenExportPresets` leaves out its `folder` when the folder is outside the
read roots, and the preset stays. The preferences themselves are not changed.
- `document.inspect` (`inspect_document`) gives `path` as null when the document's file is outside the read roots.
`document.json` names a linked image or placed file outside them by its file name alone in the link's `path`, and
leaves out the link's `relative`, `size` and `modified`. This covers the art of the brushes in `unknown.brushes`
too. A link whose file is inside them keeps its `path` but leaves out a `relative` that leads outside them. A
stored brush entry that does not read as a brush is left out of `unknown.brushes`. `document.json` also names a data set's image
value and a linked JavaScript file in `svg_scripts` outside them by its file name alone (a JavaScript file given
as a URL is kept), and leaves out the `folder` of `export_settings` (the settings Export
for Screens last used) when that folder is outside them. The document itself is not changed.
- `plugin.list`, `plugin.safeMode`, `file.recovery.*` and `ui_inspect`'s `scriptReport` leave out the paths
outside the read roots, also those the person or an earlier request put there. In `plugin.list`, the plug-in
folder last loaded is `null` in `folder.folder`, and a failed file in `folder.failed` and a plug-in's `source`
@@ -215,6 +265,12 @@ the suite) keep the agent to the folders you give it:
`outside the write roots /work/project/out, /tmp/scratch: …`) or
`automation filesystem access is not granted: write authority is absent: /work/project/out/x.png`.
Links in an opened document that point outside the read roots show as missing.
- An error names a path the client gave as given. A path the client did not give (the document's own file
in a save without `path` and in `file.revert`, also inside `action.batch`, an export preset's folder, the
Scripts folder, the document's folder in `links.relink {copy}`) is named
whole only inside the read roots; outside them the error names it by its file name, or as
`a file or folder outside the read roots` when it has none, and keeps the roots and the reason, for example
`automation path rejected: outside the write root /work/project/out: poster.vectorcraft`.
- The preferences that move folders the app reads or writes on its own (`fontsFolder`,
`pluginsFolder`, `recoveryFolder`, `templatesFolder`) can only be set inside the roots. The app's own folders
(preferences, Data Recovery, the User Defined library folders, VectorCraft's Fonts folder) are not
@@ -536,6 +592,19 @@ the panel menu's Show Recent Colors, which hides the Color panel's Recent Colors
applied colors still join the list while it is hidden. `ui.colorPanel {"rgbPercent": true}` shows the Color panel's
RGB fields in percent, as a double-click right of an RGB field does, and `false` shows them from 0 to 255 again.
In remote desktop mode, `invoke_menu` with `{"command":"ui.sampleScreenColor","params":{}}` shows the system's
screen color sampler and returns `null` before a color is picked: the system's sampler on macOS, the XDG desktop
portal's [PickColor](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html)
on Linux, and the EyeDropper API in browsers that have it. `inspect_ui` reports `screenSampler: true` while it
waits. The picked sRGB color is converted into the working RGB space and becomes the fill or the stroke of the
active proxy in one undo step; `stroke: true` picks the stroke, and `picker: true` sends the color to the open
Color Picker. A cancel leaves the colors as they were. On Linux, a session without a portal that has PickColor
shows "this system has no screen color sampler" in the status bar, a portal that does not return from the call
within 25 seconds (VectorCraft's own limit) or leaves the bus before it answers shows "the screen color sampler
did not answer", and a portal that ends the request in another way shows "the screen color sampler failed"; each
ends the wait, so the sampler can be shown again. Windows, the BSDs and headless sessions have no screen color
sampler.
| Tool | Arguments | Notes |
|---|---|---|
| `list_commands` | `{filter?, query?, menu?, enabledOnly?, brief?}` | The command catalogue: id, label, menu, shortcut, params doc, enablement, and `schema` and `inputSchema` for a command with a parameter schema (the params doc and both schemas are left out with `brief`). |
@@ -550,7 +619,7 @@ RGB fields in percent, as a double-click right of an RGB field does, and `false`
| `set_paint` | `{fill?, stroke?, strokeWidth?, ids?}` | Applies to the selection (or `ids`) and becomes the default for new art. Returns `{applied, objects}`: `objects` is the number of objects painted, as the `objects` of the `paint.setFill`, `paint.setStroke` and `stroke.set` replies (the largest of them): a selected group counts the objects inside it. When it is 0, a `note` says that only the paint for new art changed. |
| `press_key` | `{key, mods?}` | Remote: a real key event. Headless: runs the command or tool bound to that shortcut, or sends the key to the busy tool (digits too: `5` while dragging with the Perspective Selection tool; `e` while dragging a Selection or Group Selection marquee; `c` and `f` while dragging out an arc; `f`, `v`, `x` and `c` while dragging out a rectangular or polar grid). |
| `type_text` | `{text}` | Remote only. |
| `answer_dialog` | `{set?: [{field, value?}], action?}` | Remote only. Sets fields of the app's open dialog in the order given with one `ui.dialog.set {fields, action?}` (a missing `value` is `null`). The app stores all of them or none, then answers that same dialog with `action` in the same request: `confirm` (OK) or `cancel`. Another connection cannot open a dialog between the two steps. When the app refuses the `ui.dialog.set`, for example because no dialog is open, the call is an error and the dialog is not confirmed or canceled. `action` alone is sent as `ui.dialog.confirm` or `ui.dialog.cancel`. Give `set`, `action` or both; `set` holds at most 256 fields. `inspect_ui` shows the open dialog and its fields as `ui.dialog`. Returns `{dialog, result}`: the dialog after the fields were set (or `null`) and the reply to `confirm` or `cancel` (or `null`). After `invoke_menu` `app.quit`, `file.close` or `file.closeAll` replies `{"pending": "saveChanges"}`, `{action: "confirm"}` saves, `{set: [{field: "discard", value: true}], action: "confirm"}` closes without saving and `{action: "cancel"}` cancels the close or quit. |
| `answer_dialog` | `{set?: [{field, value?}], action?}` | Remote only. Sets fields of the app's open dialog in the order given with one `ui.dialog.set {fields, action?}` (a missing `value` is `null`). The app stores all of them or none, then answers that same dialog with `action` in the same request: `confirm` (OK) or `cancel`. Another connection cannot open a dialog between the two steps. When the app refuses the `ui.dialog.set`, for example because no dialog is open, the call is an error and the dialog is not confirmed or canceled. `set` cannot change a `__` field other than the dialog's controls held under a `__` name (such as Save PDF's `__section` or the Preferences `__search`): the `__command` and `__params` of a `confirm` or `command` dialog, and every other `__` field that holds what OK acts on, are refused with "the `<kind>` dialog cannot set protected field `<field>`", so OK runs what the dialog was opened for. `action` alone is sent as `ui.dialog.confirm` or `ui.dialog.cancel`. Give `set`, `action` or both; `set` holds at most 256 fields. `inspect_ui` shows the open dialog and its fields as `ui.dialog`. Returns `{dialog, result}`: the dialog after the fields were set (or `null`) and the reply to `confirm` or `cancel` (or `null`). After `invoke_menu` `app.quit`, `file.close` or `file.closeAll` replies `{"pending": "saveChanges"}`, `{action: "confirm"}` saves, `{set: [{field: "discard", value: true}], action: "confirm"}` closes without saving and `{action: "cancel"}` cancels the close or quit. |
| `invoke_menu` | `{command, params?}` | Invokes a menu item by command id. UI commands such as `view.*` and `window.*` are in `command_list` with `ui: true`; running them needs the desktop app (`--connect`). |
| `open_panel` | `{panel}` | Remote only. `panel` is a panel id (`layers`, `swatches`, `colorGuide`, …, as `window.panel` takes) or its display label (`"Color Guide"`), in any case. |
| `screenshot` | `{path?, scale?, artboard?, window?, max_side?, traceViews?}` | Returns MCP image content (`image/png`, base64) plus a text block. Renders the artboard with the pixels of a PNG `export` with `background: "white"` at the same scale; `traceViews:true` draws Image Trace objects in the view set with `imageTrace.setView`, as in the app's window; `window:true` captures the app window (remote only), as `ui_screenshot` does, and `max_side` (1–4096, window captures only) scales the returned image down as described for `ui_screenshot`. With `path` the backend (the headless session or the app) writes the PNG there too; a window capture written there keeps its full size with `max_side`. |
@@ -763,7 +832,20 @@ lists every option with its default. `encoding` is `utf8`, `utf16` (big-endian a
a warning). `embedFonts: true` embeds the fonts type uses as `@font-face` rules subset to the characters used; a
font whose licence (OS/2 `fsType`) forbids subsetting is embedded whole, one that forbids embedding is left out
with a warning. Type names its faces by numeric `font-weight` (600 for Semibold and for a Japanese face's W6, 300 for Light, `bold` for 700),
embedded or not. `svgz` takes the same options and writes the SVG gzipped (`.svgz` files also open,
embedded or not. `variableData: true` (SVG Options › Include Variable Data) writes the document's text variables and
data sets as a `variableSets` element in the layout `variable.loadLibrary` reads, so the SVG works as a data merge
template ([help](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/merge-data.html)).
Each text variable names its bound type object by the element's id, `docRef="id('…')"` (VectorCraft's own form); an
unnamed object, or any object with `objectIds: "minimal"`, is given an id for it. A variable bound to several type
objects names the first by object id. Variables of other kinds, text variables whose names aren't XML names, and data
sets whose names are blank, have white space at either end, hold characters XML can't hold or repeat a name are left
out, each with a warning. Only the first 256 text variables and the first 1,000 data sets are written, and variable
data larger than 8 MiB or holding more than 2,000,000 XML nodes is left out (the limits `variable.loadLibrary`
reads), each with a warning. With `outlineText`, the bound type is written as outlines, so its text variables open
unbound (with a warning). `document.open` reads the variable data of an SVG back (unless the SVG reopens from
Preserve Editing): its text variables and data sets, no active data set, and each text variable bound to the type
object its `docRef` names; the art stays as the file draws it. Variable data that `variable.loadLibrary` would refuse
is left out with the warning "the file's variable data wasn't read", and the art opens. `svgz` takes the same options and writes the SVG gzipped (`.svgz` files also open,
place and paste). `run_command document.save {path: "x.svg", svg: {…}}` saves as SVG (or `.svgz`).
Colorized grayscale and 1-bit images export in their fill color; linked mode writes a coverage PNG, and Tiny 1.2 writes a colorized PNG.
@@ -787,7 +869,11 @@ a path whose extension names another format has
it replaced by the template's extension, and any other path that does not end in that extension has it added, so
`card.png` is saved as `card.vctemplate` and `card` as `card.vctemplate`; the suggested name and the Templates folder
come back without a path), `file.newFromTemplate {path}`, `file.revert` and `file.formatOptions {format?}` (a format's options
with the values a save would use). Without a path the save commands return `{dataBase64, name, folder?, warnings}`. A document saved as SVG or PDF
with the values a save would use). Without a path the save commands return `{dataBase64, name, folder?, warnings}`.
In the replies of the save and export commands, and of `clipboard.exportPdf` and `clipboard.exportPng`, `warnings`
lists what the file leaves out of the document or writes differently first. The notes on the options follow: an
option not applied or applied another way while the art reaches the file as it looks, or something an option turns
off, such as `includeImages: false` in DXF. Each kind keeps the order the writers give it. A document saved as SVG or PDF
remembers its options, so the next save reuses them; a document not yet saved as a native file is offered the
VectorCraft Options the last native Save or Save As used (not its artboards, nor Use Compression). A PDF opened with `pages`, `cropTo` or `password` is only part
of the file, so Save asks for a new name instead of writing it back.
@@ -816,7 +902,7 @@ symbol (named after its `data-name`, else its id) with an instance per `<use>`;
symbol with strokes) stays art. `feGaussianBlur`, `feDropShadow` and the usual shadow chains (offset, blur, flood or
colour matrix, composite, merge) become Gaussian Blur, Drop Shadow, Outer Glow, Inner Glow and Feather effects; other
filters are listed in `warnings`. Undisplayed objects (`display:none`) come back hidden, nested clip paths intersect,
and reflected or repeated gradients (`spreadMethod`) are expanded into stops. `<image>` files (relative links are
and reflected or repeated gradients (`spreadMethod`) import as gradients that repeat or reflect (see Spread under Examples). `<image>` files (relative links are
found in the SVG's folder; only files on this computer or the SVG's network share are read, and pasted SVG reads only
absolute paths) stay linked images (see Linked images); SVG files and `data:` SVGs become art; a file that
can't be read becomes a placeholder in its box, named in `warnings` and `missingLinks`.
@@ -899,7 +985,7 @@ Long-tail commands:
PDF files take the Save PDF dialog's options: `preset`, `standard`, `compatibility`, the General toggles and the
`compression`, `marks`, `bleed`, `output`, `advanced` and `security` sections (`list_commands` with filter `exportPdf`
documents every field). `document.exportPdf` and `export` (format `pdf`, the options in `options`) return `warnings`:
options accepted but not applied yet, and features approximated or left out. An image whose pixel data can't be
features approximated or left out, then options accepted but not applied yet. An image whose pixel data can't be
decoded (a PNG cut short) is left out of the pages, and `warnings` holds the note "an image couldn't be decoded and
is missing from the picture" once, as raster exports give it; `.ai` saves and `file.print` do the same. A standard with a
PDF version it doesn't allow is refused (PDF/A-2b at 1.3 or 2.0, PDF/X-4 above 1.6, PDF/X-1a and PDF/X-3 above 1.4);
@@ -1165,6 +1251,49 @@ another form gives a warning:
{"name":"run_command","arguments":{"command":"paint.setFill","params":{"gradient":{"kind":"diamond","start":[150,150],"end":[200,150],"aspect":60}}}}
```
Spread (the Gradient panel's menu, beside a Repeats field) says what a linear, radial, conic or diamond gradient
paints past the ends of its stops. `pad` (the default) carries the first and last colors on. `repeat` and `reflect`
run the stops `repeats` times along the vector (1 to 100, VectorCraft's own limit; default 1) and keep going past
both ends of it, `repeat` starting the stops again each time and `reflect` running every other run backwards. A
radial or diamond gradient repeats in rings or squares out from its center (an off-center radial's rings grow about
its focal point), and a conic one repeats around the turn. A freeform gradient has no ends, so a `spread` other
than `pad` is an error for one, and so is a `repeats` that is not a whole number from 1 to 100. Both edit as one
undo step, and an edit that changes nothing records none. A gradient set back to `pad` keeps its count for later,
and it matches a preset or swatch of the same gradient whatever count it keeps. The Gradient tool marks each place
along the bar where one run meets the next (on a conic, across its extent ellipse). A gradient along or across a
stroke runs its stops `repeats` times along or across it, a blend between two gradients that repeat (or reflect)
steps the count from one to the other, and a gradient swatch saved as CSS gets its stops repeated across the box. The
Gradient tool's stop and midpoint markers lie along the whole bar, as on the Gradient panel's slider, and edit the
stops of one run.
SVG export writes `spreadMethod` with the gradient's vector shortened to one run
(https://www.w3.org/TR/SVG2/pservers.html), and SVG import reads
`spreadMethod` as the spread, with a count of 1. CSS writes `repeating-linear-gradient` and `repeating-radial-gradient` over one run, or
one run and its mirror for `reflect` (https://www.w3.org/TR/css-images-3/#repeating-gradients), and a conic gradient
with its stops repeated around the turn. PDF writes a conic gradient's sweep shading over one run, repeated or
reflected past it. PDF, EPS and legacy `.ai` gradients pad past their ends, so a linear, radial or diamond gradient
is written with its stops repeated as many times as the shape needs: at most 256 runs and 16,384 stops (256 stops in
legacy `.ai`), and for a radial or diamond gradient no farther from its center than 65,536 runs, or than a million
times the smallest gap between its stops (measured in runs) when that is less. Past those it pads, with a warning.
The `.ai` editing data has no spread either: it holds the stops repeated across the shape, at most 256 of them, with
a warning. When the runs need more stops than these limits allow, every run is still written, without the stops that
change its colors least. Each run keeps its ends; sharp steps and swatch-linked stops are the last to be left out,
but a run with more of them than its share of the limit loses some. The same applies to a gradient along or across a
stroke, and to CSS's conic gradient, past 16,384 stops, and to Outline Stroke's mesh past 1,024. For CSS the 16,384
counts the gradient's stops before they are written: CSS then adds a stop for each midpoint, and for a Perceptual
gradient stops along each span's curve, so the written gradient can hold more. PDF
import finds a sharp step between two colors in an axial or radial shading by halving the spans between its samples,
so the step reads back where it is in the file (to 1/64 × 2^-14 of the axis) instead of blurred across the samples
around it. A shading takes at most 16,384 samples, so the steps past the point where that budget runs out stay
blurred, and a color band narrower than 1/64 of the axis can be missed when no sample lands in it. Object › Expand
expands the repeated stops, into the same whole number of `steps` objects for each run (at least one, so about
`steps` in all), each run starting on an object's edge; a gradient that needs more than 256 runs across the box stays
a gradient:
```json
{"name":"run_command","arguments":{"command":"paint.editGradient","params":{"spread":"reflect","repeats":3}}}
```
Dither (the Gradient panel's box) draws a linear, radial, conic or diamond gradient fill with fine noise that hides
banding; SVG and PDF exports write such a fill as an image at the Document Raster Effects resolution. It applies to
fills (`stroke: true` is refused):
@@ -1403,6 +1532,11 @@ key, the Batch dialog or `ui.action.play`), a key a command no longer reads, rec
and the action still plays. `Session::replay` plays a journal the same way. The command still refuses an effect
`color` or `channel` it can't read.
`window.panel` refuses every request in Presentation Mode, which hides every panel, and changes nothing. With
`toggle` it hides only a panel that can be seen, which is the panel the Window menu checks. While Tab or Shift+Tab
hides the dock, no panel is checked, and the panel shows again with the dock in its earlier place; the toolbar and the
Control bar stay as they are.
The export commands limit those keys. `document.export`, `document.exportSelection`, `document.serialize`, `app.export`
and `file.export` refuse a key that no format takes, and ignore a key that only another format takes
(`{"format": "pdf", "quality": 80}` writes the PDF). `document.exportPdf`, `document.pdfSettings` and `pdf.preset.save`
@@ -1582,7 +1716,9 @@ wrong. A value that is not a number gets `` `dx` must be a number, not "10" ``:
`` `size` must be a number, not true ``, and a `tracking` that is neither a number nor a number written as a string
(`"20"`) with `` `tracking` must be a number, not false ``. It and `text.setRangeStyle` refuse a `font` or `style` that is not a string
with `` `font` must be a string ``, and a `features` list with an entry that is not a string with
`features must be a list of tags`; `text.setStyle` refuses such a `justify` too. `type.ruby` refuses a `text`,
`features must be a list of tags`; `text.setStyle` refuses such a `justify` too. `text.create`, `text.createInPath`,
`text.setStyle` and `text.setRangeStyle` refuse a `size` outside 0.1 to 1,296 pt with
`` `size` must be from 0.1 to 1296 `` and change nothing. `type.ruby` refuses a `text`,
`position` or `align` that is not a string, and `text.setPathHandle` refuses a `which` other than `in` or `out` with
`` `which` must be one of in, out ``. Each leaves the document, the selection and the undo history as they were, also
when the call gives another param it could apply. A null is read as a key left out.
@@ -1907,8 +2043,9 @@ no engines, and the engines it left are dropped when their own thread next uses
in the earlier run's script, a file it included or a file its `$.evalFile` ran, and the report's `file` names that
file. For an earlier script given as `source`, `file` is that run's name. The report's `name` and the console line's
name stay the calling run's, as in `caller.js: /path/lib.jsx:12:5: Error: kept`. Each engine keeps these places
for 8 MiB of source maps, the oldest going first; an error in code whose place is gone, or in code that `eval` or
`Function` compiled, is reported without a line and column, and the code still runs.
for 8 MiB of source maps, the oldest going first, and a run whose script does not compile keeps none. An error in
code whose place is gone, or in code that `eval` or `Function` compiled, is reported without a line and column, and
the code still runs.
- `vc`, `$`, `alert`, `confirm` and `prompt` act for the run that calls them, including a copy an earlier run kept in a
global. `vc.args` is the calling run's `args`, through a `vc` object an earlier run kept too. An engine
holds no document: `vc.document` is a copy, read when it is called. What a run sets on `$` (`$.locale`,
@@ -2416,7 +2553,9 @@ each of the others, whose hidden or locked objects are left as they are; when th
other group takes its objects at that level, keeping its own ids in order. Whenever the document is put back (Undo,
Redo, a cancelled drag or scrub), the matches it no longer has leave the Global Edit, which ends when fewer than two
are left. An edit that would take any match past the canvas fails with the coordinates-out-of-range error and changes
nothing. Nothing of it is saved.
nothing. Nothing of it is saved. Before Global Edit starts, while one object that `select.globalEdit` accepts is
selected with the Selection tool, the canvas highlights the other objects a start would take with the current Global
Edit Options (none while a drag is in progress, or when there are none).
`select.globalEditExclude {id}` leaves the match `id` out of the Global Edit, as a Shift-click on a highlighted match
(or on an object inside it) with the Selection tool does: it is no longer highlighted, and later edits leave it as it
@@ -2507,6 +2646,24 @@ and changes nothing. Behavior reference:
{"name":"invoke_menu","arguments":{"command":"learn.show","params":{"tutorial":"pen-paths"}}}
```
A step that names a tool shows the tool's name as a blue link. A click on it, or Enter or Space while it has the
keyboard focus, runs `learn.locateTool {tool}`, which marks where the tool is in the current layout without choosing
it: its toolbar button, or the tool's row in its group's flyout, which opens when the button shows another tool of the
group. A group torn off into a strip comes to the front with the tool marked in it. A tool that no shown toolbar holds
is marked in the All Tools drawer, which opens; the tool is not added to a toolbar. A Tools panel hidden with
Window › Tools shows again. A target below the window's bottom scrolls into view. The mark lasts 3 seconds after it
first shows; Escape or the next locate removes it. The reply is `{tool, location, toolbar}`: `location` is `toolbar`,
`flyout`, `strip`, `allTools` or `unavailable`, and `toolbar` names the toolbar made with New Toolbar that holds the
tool (null for the Tools panel's). While Presentation Mode or Tab hides the toolbars, the reply is `unavailable` with
a `reason`, and nothing changes. A missing, non-string or unknown `tool` is an error and changes nothing. The mark,
its 3 seconds, the flyout, strip and All Tools behavior and the hidden-toolbar replies are VectorCraft's own rules;
the help page (https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html)
describes selecting a tool's name in a step to find the tool.
```json
{"name":"invoke_menu","arguments":{"command":"learn.locateTool","params":{"tool":"smooth"}}}
```
## Ruler guides
Each ruler guide is an object of a layer or sublayer, directly in it and above its art, and takes an id from the
@@ -2819,16 +2976,23 @@ already walks Image Trace. Short agent entry points:
use version 2, so older builds refuse them
instead of playing the excluded steps or failing at a stop; other files use version 1. A file with another `format` or a `version` other than
1, 2 or 3, a stop whose message is not text or whose `allowContinue` is not true or false, a file larger than 8 MiB, one with more than 1,000 actions and one with an action of more than 10,000 steps are
refused, and no set is added. The Actions panel menu's Save Actions… and Load Actions… run these; Load Actions… is
refused, and no set is added. So is a file whose set `ui.actionSet.save` would write larger than 8 MiB (a file with
less white space, a set name that gets a number, or an `.aia` set), with the error `the action set would be larger
than 8 MiB`. The Actions panel menu's Save Actions… and Load Actions… run these; Load Actions… is
disabled where there is no open dialog (the web). `ui.actionSet.load` also reads the action sets other apps save as
text (`.aia`); see "Action sets from other apps".
- Actions: `ui.action.rename {set, action, name}` renames the Actions panel's action at `action` in set `set` (0 is
the top set) to `name` with spaces trimmed from both ends, keeps its steps, function key and color, and returns
`{name}`. An index past the last set or action, or an empty name, is an error, and then nothing is renamed.
`{name}`. An index past the last set or action, or an empty name, is an error, and then nothing is renamed. So is a
name that would give the set's action set file more than 8 MiB, counting the steps the recording in progress adds
to the set (`the action set would be larger than 8 MiB`); a name that leaves the file as large or makes it smaller
is never refused. The same holds for `ui.action.options` and for the names Set Options… and New Set… give.
- Actions: `ui.action.stepsIncluded {set, action?, step?, included, others?}` includes or excludes all steps of a set,
one action, or one step. With `step` and `others: true`, it leaves that step as it was and applies `included` to the
other steps of its action (the panel's Alt/Option-click). An invalid set, action or step changes nothing. The panel's
checkmarks run this command; normal playback and Batch… skip excluded steps.
other steps of its action (the panel's Alt/Option-click). An invalid set, action or step changes nothing. So does an
exclusion that would give the set's action set file more than 8 MiB, counting the steps the recording in progress
adds to the set; it is an error. A change that leaves the file as large or makes it smaller is never refused. The
panel's checkmarks run this command; normal playback and Batch… skip excluded steps.
- Actions: `ui.action.new {set, name, key?, cmd?, shift?, color?}` adds an action named `name` (spaces
trimmed) after the last action of set `set` (0 is the top set), starts recording into it and returns
`{set, action}`; Stop Playing/Recording (`ui.action.stop`) ends the recording. `key` 1 to 15 is the function key F1 to F15 that plays the
@@ -2836,12 +3000,23 @@ already walks Image Trace. Short agent entry points:
(`red`, `yellow`, `green`, `blue` or `violet`; null or `""` for none), which the panel does not show. Action function
keys take priority over command shortcuts and preserve the command's binding. On Windows F1, Ctrl+F4 and Ctrl+F6
cannot be used. While an action is recording, and for an index past the last set,
an empty name, or a key or color that is not one of these, it is an error and nothing is added. The panel's Create
an empty name, or a key or color that is not one of these, it is an error and nothing is added. So is an action that
would give the set more than 1,000 actions (`the action set file holds more than 1,000 actions`) or a file larger
than 8 MiB (`the action set would be larger than 8 MiB`), and then no recording starts. The panel's Create
New Action button and menu's New Action… open the `actionNew` dialog, whose Record runs this.
- Actions: `ui.action.record {set?}` starts recording a new action in the chosen set, or the current set when `set`
is left out, and returns `{set, name}`. With no sets, it creates Set 1. The action gets the next available
`Action N` name in that set. The Actions panel's Begin Recording button runs this command. An invalid set or an
existing recording is an error and leaves the sets and recording unchanged.
existing recording is an error and leaves the sets and recording unchanged, and so is a set where the new action
would give it more than 1,000 actions or an action set file larger than 8 MiB. While a recording (this one or
`ui.action.new`'s) is in progress, a recorded command whose step would give the action more than 10,000 steps, or
its set's action set file more than 8 MiB, is an error with the reason (`the action already has 10,000 steps`, `the
action set would be larger than 8 MiB`): it does not run, and the recording goes on. A batch (`command.batch`) is
such a command too. A command whose step grows past a limit only as it runs, from values it adds to its step,
keeps its edit and result; the status bar names the limit, later recorded commands do not run, and
`ui.action.stop` leaves its step out. A stop that playback reaches
then is not recorded, and the status bar shows the reason. `ui.action.stop` leaves out any recorded steps still past
either limit and shows which one in the status bar.
- Actions: `ui.action.options {set, action, name?, key?, cmd?, shift?, color?}` changes the action at
`action` in set `set` and keeps its steps: `name` renames it, `key` with `cmd` and `shift` sets its function key,
and `color` its Button Mode color, as `ui.action.new` takes them; a param left out keeps what the action has, and
@@ -2927,7 +3102,8 @@ already walks Image Trace. Short agent entry points:
`ui.actionSet.duplicate {set?: index, action?: index}` copies the action at `action` in set `set` to right after it
in the same set, and returns `{set, action}`; without `action` it copies the set with all its actions to right after
it, and returns `{set}`. In both cases `set` defaults to the current set. The copy is named after the original with
" copy" added, a copied set gets an id of its own, and the copy is selected. An index past the last set or action is an error. The
" copy" added, a copied set gets an id of its own, and the copy is selected. An index past the last set or action is an error,
and so is a copy that would give its set more than 1,000 actions or a file larger than 8 MiB; then nothing is copied. The
panel menu's Duplicate runs this for the selected action, else the current set.
- Image Trace: `imageTrace.presets`, `imageTrace.make {id?, preset?, params?, view?}` (or `imageTrace.makeAndExpand`),
`imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`. A key inside `params` that is not one of the trace
@@ -3250,6 +3426,28 @@ checked as that effect's. It is one undo step. Any other `threeDType` value, or
{"name":"run_command","arguments":{"command":"effect.setParams","params":{"index":0,"item":null,"threeDType":"inflate","params":{"volume":70}}}}
```
## Expand as Wireframes
`effect.expandAsWireframes {ids?}` (**Window › 3D and Materials** › Quick Actions › Expand as Wireframes) replaces
each selected object, or each object in `ids`, that has a visible 3D effect (Extrude & Bevel, Revolve, Inflate,
Rotate or one of their 3D (Classic) forms, on the object or on its fills and strokes) with a group of paths: the edges of
the faces the effect draws for the object as it is turned and projected now
([three-d.md](three-d.md#3d-and-materials-panel)). Each edge appears once; edges that meet end to end form one
path, closed where it comes back to its start, and curved edges stay curves. Geometry effects after a 3D effect,
on the object or on a fill or stroke, reshape its edges as they reshape the drawn surface. The paths have a 0.5 pt
black stroke with round joins and no fill (VectorCraft's own choice). The group keeps the object's place, id, name,
visibility, lock, opacity, blending mode, knockout, opacity mask, attributes, slice and text wrap, and the groups
are selected.
A layer in `ids` stays a layer: its members are replaced by the edges, which are selected, and its ruler guides and paths made into guides stay. A path inside a compound path in `ids` stands for the compound path. Objects without a visible 3D effect do not change. It is one undo step and
returns `{ids}`, the groups. It is an error, and nothing changes, when nothing is selected or given, when no object
has a visible 3D effect, when an object's solid can't be built or the canvas draws its source art in its place, or when one object's wireframe has more than
1,000,000 edges.
```json
{"name":"run_command","arguments":{"command":"effect.expandAsWireframes","params":{}}}
{"name":"run_command","arguments":{"command":"effect.expandAsWireframes","params":{"ids":[12]}}}
```
## Extrude & Bevel, Inflate and Rotate
`effect.apply {effect: "threeD.extrude" | "threeD.inflate" | "threeD.rotate", params: {...}, ids?, item?}` applies
@@ -3611,7 +3809,7 @@ error.
## Brush libraries
Brush libraries are read-only sets of brushes: eight generated in code (Art Ribbons, Calligraphic Nibs, Bristle
Brush libraries are read-only sets of brushes: nine generated in code (Art Ribbons, Ink and Wash, Calligraphic Nibs, Bristle
Brushes with one bristle brush for each tip shape, Pattern Borders, Scatter Shapes, Game Trails, Energy Beams, and Dry Media with the Chalk Grain, Graphite Pencil, Wax Crayon and
Charcoal Dust art brushes, id `dry-media`), the `.vectorcraft` files in the user library folder of the desktop app (User
Defined) and the brushes of any document loaded with `brush.loadLibrary` (a document without brushes of its own has
@@ -4135,6 +4333,34 @@ the `…Open` rings…) and marks (`Bar`, `DoubleBar`, `DotOnBar`, `Slash`, `Dou
`arrowScale`, at least 0.25 pt. `LineArrow` has two arms of the head's weight (the line's weight at 100 % for lines of
0.25 pt or more) at right angles and spreads twice as wide.
Custom arrowheads come from the arrowheads file, `Arrowheads.vectorcraft` next to the preferences (none on the web or
in runs with `VECTORCRAFT_NO_PREFS`). `stroke.arrowheads.edit` (Stroke panel menu › Edit Arrowheads…) opens it, and
first creates it with one example head when it is missing. Each symbol in the file is one head, named after the
symbol and listed after the built-in heads in both arrowhead menus. These rules are VectorCraft's own: a head points
right, the symbol's registration point is its tip, and the art's height is drawn as wide as the built-in heads (four
head weights). The visible fills and the outlined visible strokes of its paths, compound paths and groups, and its
gradient meshes, make the head, with geometry effects applied and clip groups clipped, and the line stops halfway along its length. A head is
one solid shape in the stroke's paint: art at 0 % opacity is left out, and other opacities, blend modes and opacity
masks do not change the shape. The file is read when the app starts and again from disk each time VectorCraft saves it;
`stroke.arrowheads.reload` reads it again. A file larger than 64 MB, or whose document or images unpack to more than 64 MB, is
not read, at most 256 symbols are read, and a symbol that paints nothing, reaches more than 64 head weights from its
tip, has more than 65,536 anchors in its art or in the shapes it paints, has shapes that cannot be combined into one,
or has more than 4,096 anchors once united is listed in `skipped`. `stroke.arrowheads.list` returns each head's `name`, `reach` and `halfWidth`, the `file`
path, the `skipped` symbols and any read `error`.
`stroke.set {endArrow: {"custom": name}}` puts a custom head on a stroke. The stroke keeps a copy of the head's art,
so the document draws it, saves it and exports it to SVG and PDF the same way without the arrowheads file.
`document.inspect` reports such a head as `{"Custom": {name, path}}`, which `stroke.set` takes back unchanged. It
refuses a `path` that fills no area (non-zero rule; parts thinner than a millionth of a head unit count as none) or
has more than 4,096 anchors, more than 4,096 subpaths or a point more than 64 head units from the tip. The help page
[Apply stroke on an object](https://helpx.adobe.com/illustrator/using/stroke-object.html) describes defining custom
arrowheads in a separate arrowheads file.
```json
{"name":"run_command","arguments":{"command":"stroke.arrowheads.list","params":{}}}
{"name":"run_command","arguments":{"command":"stroke.set","params":{"ids":[9],"endArrow":{"custom":"Example Arrowhead"}}}}
```
## Stroke bounds and clicks
Visual bounds (Fit to Selected Art, Rasterize, exporting all art) and clicks on the canvas take
@@ -4821,12 +5047,59 @@ They list such a command while the user has changed its shortcut (`overridden: t
shortcut for that command runs what a click on the item runs: Object › Path › Clean Up…, Print Ink Fixes… and Edit ›
Find and Replace… open their dialogs.
A command without a menu path of its own is listed when an item of the menu bar runs it with no params, or when every
item that runs it gives it the same params. It is listed in the group named by the first part of its id. These
commands are listed this way:
- `addon.pathtools.cornerSet` (Object › Plug-ins › Path Precision Tools › Set Corners…, which opens its dialog),
under Addon;
- `perspective.grid.lock`, `perspective.grid.lockStation`, `perspective.grid.rulers` and `perspective.grid.snap`
(View › Perspective Grid), and `perspective.attach`, `perspective.editText` and `perspective.plane.matchObject`
(Object › Perspective), under Perspective;
- `text.spelling.dictionary` (Edit › Edit Custom Dictionary…), under Text;
- the UI commands `ui.recolorDialog` (Edit › Edit Colors › Recolor Artwork…), `ui.flattenerPresetsDialog`,
`ui.printPresetsDialog`, `ui.pdfPresetsDialog`, `ui.perspectivePresetsDialog`, `ui.svgFilterDialog`,
`ui.saveSwatchLibrary`, `ui.saveBrushLibrary`, `ui.saveGraphicStyleLibrary` and `ui.saveSymbolLibrary`, under Ui;
`file.exportAs` and `file.exportSelection`, under File; `links.editOriginal`, under Links; `type.applyBullets` and
`type.applyNumbers`, under Type; and `window.toolbar.new`, `window.toolbarColumns`, `window.toolsBesideCanvas`,
`window.workspace.new`, `window.arrange.floatInWindow` and `window.arrange.consolidateAllWindows`, under Window.
When such a command has the same group and label as an engine command that no item of the menu bar runs, the
command the menu item runs keeps the name, and the engine command is listed in the group named by the first part of
its id: File › Export Selection… runs `file.exportSelection`, as the menu item does, and `document.exportSelection`
is listed as Document › Export Selection…. A shortcut already given to `document.exportSelection` keeps running it.
When the other entry is anything else, the command is not listed: `window.learn` is left out, and Window › Learn is
the Learn panel's entry.
No two rows of Edit › Keyboard Shortcuts have the same English name. Rows that would have the same group and label
are named this way:
- A command that an item of the menu bar runs, with no params or with the params all its items give, is listed under
that item's menu path: `window.brushLibrary.other`, `window.graphicStyleLibrary.other`,
`window.swatchLibrary.other` and `window.symbolLibrary.other` are listed as Window › Brush Libraries › Other
Library…, Window › Graphic Style Libraries › Other Library…, Window › Swatch Libraries › Other Library… and
Window › Symbol Libraries › Other Library….
- A command whose id is another command's id with one more part is listed under that command's label:
`ui.charStyleOptions.resetPanel` and `ui.paraStyleOptions.resetPanel` are listed as Character Style Options ›
Reset Panel and Paragraph Style Options › Reset Panel.
- The other rows that still have the same name show their id in parentheses after it. The one row of them that an
item of the menu bar runs keeps the name without its id: File › New… is `file.newDialog` and `file.new` is File ›
New… (file.new), and Type › Fit Headline is `text.fitHeadline` and `type.fitHeadline` is Type › Fit Headline
(type.fitHeadline). `file.exportInBackground` and `file.openInBackground` are File › Continue in Background
(file.exportInBackground) and File › Continue in Background (file.openInBackground).
`shortcuts.list` gives such a row's group in `group` and the command's own label in `label`, so the rows that show
their id have the same `group` and `label` there, and `id` tells them apart. `shortcuts.exportText` writes these
names. When a shortcut is already in use, the message names each row that has it by its whole name, group included,
for every row: a `shortcuts.set` error ends with "is already used by Window › Swatch Libraries › Other Library…".
Each installed filter plug-in's Object › Plug-ins item is its own row, with id `plugin:<id>` (the plug-in's id), its
menu label and no default shortcut, so two filters with the same name have two rows. `shortcuts.set {id:
"plugin:<id>", shortcut}`, `shortcuts.list` and Import Shortcut Set use that id, `shortcuts.exportText` writes one line
for the row under its menu path and label, and the key runs what a click on the item runs, `plugin.dialog {id}`. A
removed plug-in's row goes away and its key runs nothing; its shortcut stays saved and applies again when a plug-in
with the same id is installed.
menu label and no default shortcut, so two filters with the same name have two rows. When the two rows have the same
menu path and label, each shows its id after the name. `shortcuts.set {id: "plugin:<id>", shortcut}`,
`shortcuts.list` and Import Shortcut Set use that id, `shortcuts.exportText` writes one line for the row under its menu
path and label, with its id when the row shows it, and the key runs what a click on the item runs, `plugin.dialog
{id}`. A removed plug-in's row goes away and its key runs nothing; its shortcut stays saved and applies again when a
plug-in with the same id is installed.
`shortcuts.exportText {path?}` saves the current set's shortcut list as a UTF-8 text file, as Edit › Keyboard
Shortcuts › Export Text… does with the dialog's set as edited so far. The file starts with a "Keyboard Shortcuts" line
@@ -5052,11 +5325,16 @@ 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). The tool shortcut
96 screen points, an inch at the system's reference density whatever the display scaling). `uiScaling` scales the
interface and not the art: a zoom shows the art at the same size at every UI 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
repeat chooses the tool. The canvas preference `boundingBoxWhileMovingAndDrawing` (off by default) also needs the
desktop app: on, with View › Show Bounding Box, the bounding box and its handles stay visible while a drag moves the
selection, and the Rectangle, Rounded Rectangle, Ellipse, Polygon and Star tools draw the box around the shape being
drawn; off, the box hides during both, except that a Selection tool move with `realTimeDrawing` off still moves the
box. `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`
@@ -5421,7 +5699,11 @@ value outside that range is read as the nearer end), `previewMode` and `alignNew
`file.newPresets {category?}` lists the categories and presets (Recent: the last sizes used; Saved: the user's presets,
kept in the preferences by `file.newPresets.save` and removed by `file.newPresets.delete`). A listed preset can be
passed straight back to `file.new`. Print presets and sizes without `units` start in `unitsGeneral`; screen presets
(mobile, web, video, social) in Pixels. A saved or Recent preset whose size (up to 163,830 pt), `artboards` (1 to
(mobile, web, video, social) in Pixels. `file.new` and `artboard.new` take the same artboard sizes: each side more than 0
and at most 3,999,856 pt (the 4,000,000 pt the canvas reaches from the origin, less a 72 pt bleed on both sides). A side outside that range is
refused with `width and height must be more than 0 and at most 3999856 pt`, followed by the limit in the document's
units in parentheses when they are not points, such as `(1411.0603 m)`, and no document or artboard is made.
A saved or Recent preset whose size (up to 3,999,856 pt), `artboards` (1 to
1,000), spacing (0 to 100,000 pt) or bleed (0 to 72 pt a side) lies outside those ranges is read with the nearer end of
each range, and a size of 0 or less is read as 1 pt.
The desktop app's `file.newDialog` (Cmd/Ctrl+N) opens the New Document dialog on the first Recent entry, or on the
@@ -6222,12 +6504,16 @@ result carries the same files as a zip (`{name: "<name>.zip", dataBase64}`, entr
`document.info {selectionOnly?, category?, format?: "text"}` adds `sections` (`[{id, title, rows: [[label, value]]}]`:
document, objects, graphicStyles, brushes, spotColors, patterns, gradients, symbols, fonts, fontDetails, linkedImages,
embeddedImages, repeats), `brushNames` (the Brushes panel's brushes; with `selectionOnly`, the brushes the selected
objects' strokes use), `repeatObjects` (`[{id, name, type, instances, rows?, cols?}]`: every Repeat object, those in
embeddedImages, repeats), `brushNames` (the brushes the art's strokes use, each once, hidden strokes, strokes at an opacity of 0, strokes with no
paint or width, guides, paths with no segment, type that draws no characters, a group's or layer's own strokes, a compound path member's own strokes and an image's strokes without a
shape left out; with `selectionOnly`, the brushes the selected
objects' strokes use), `brushes` (`[{name, type}]`: those brushes with their types, `calligraphic`, `scatter`, `art`,
`pattern` or `bristle`, and `null` for a brush the Brushes panel does not hold; the brushes category's rows give each
name with its type, such as `Art brush`, or `missing from the Brushes panel`), `panelBrushNames` (the Brushes panel's brushes, whether art uses them or not), `repeatObjects` (`[{id, name, type, instances, rows?, cols?}]`: every Repeat object, those in
groups and in other repeats included, with its type `radial`, `grid` or `mirror` and the number of instances it makes,
and a grid's `rows` and `cols`; with `selectionOnly`, the Repeat objects in the selection), `patternObjects`
(`[{id, name, patterns}]`: every object filled or stroked with a pattern, once each, those in groups and type objects
whose drawn characters use a pattern included, hidden fills and strokes, strokes with no width, guides, a compound path
whose drawn characters use a pattern included, hidden fills and strokes, fills and strokes at an opacity of 0, strokes with no width, guides, a compound path
member's own fills and strokes, an image's fills and strokes that no Convert to Shape or Outline Object effect gives a
shape, and characters that draw nothing (spaces and line breaks without an underline or strikethrough) left out, with
the patterns it uses, which the patterns category lists as Pattern Objects and `objects.patterns` counts; with `selectionOnly`, those in the selection),
@@ -6235,7 +6521,13 @@ the patterns it uses, which the patterns category lists as Pattern Objects and `
stroked with a gradient, by the same rules as `patternObjects`, with the gradients it uses: the name of the gradient
swatch a gradient is linked to, else `Linear gradient`, `Radial gradient` or `Freeform gradient`; the gradients category
lists them as Gradient Objects and `objects.gradients` counts them; gradient swatches no art uses are not listed; with
`selectionOnly`, those in the selection), `gradientNames` (the gradients those objects use) and `pathLength` (pt: the total length of the paths, compound paths' pieces included and guides left
`selectionOnly`, those in the selection), `gradientNames` (the gradients those objects use), `spotObjects`
(`[{id, name, spotColors}]`: every object filled or stroked with a spot color swatch, as a solid color or in the
stops of a gradient with a stop above opacity 0, at a tint above 0, and every grayscale image colorized with one, by
the same rules as `patternObjects`, with the spot colors it uses; a process
color, even one of the same color, and Registration are not spot colors; the spotColors category lists them as Spot
Color Objects and `objects.spots` counts them; spot swatches no art uses are not listed; with `selectionOnly`, those
in the selection), `spotColorNames` and `spotColors` (the spot colors those objects use) and `pathLength` (pt: the total length of the paths, compound paths' pieces included and guides left
out, which objects lists as Path Length in the document's units, say for a cut line or an LED strip along the letters);
`format: "text"` returns the plain-text report Document Info › Save… (`docInfo.save {path?, selectionOnly?, category?}`:
only `category` when given, else every category) and the package report write.
@@ -6309,7 +6601,8 @@ the later file is written as `-2`, `-3`… (`Artboard-1-2.png`) and listed under
rows write one file, and row folders that differ only in case (`Print`, `print`) are one folder, spelled as the first
row spells it. The document remembers the last params: `document.exportSettings`. As in `document.export`,
the reply's `warnings` lists the notes of every file exported (an image that couldn't be decoded, type written in the
fallback font…), each note once; the export still writes or returns every file.
fallback font…), each note once, what any of the files loses before the notes on the options; the export still
writes or returns every file.
Batch exports (#1935):
- **What:** `layers: [id…]` (each layer's art alone) or `selection: "one" | "each"` instead of artboards
@@ -6741,9 +7034,11 @@ says when CSS can't describe the art exactly (`unsupported`: other shapes, image
written as its box, or with `rasterize: true` as `background-image: url(<class>.png)`. `units` is `px` (1 px per
point, as in SVG export), `pt`, `mm`, `cm` or `in`; `unnamed: false` leaves out unnamed objects (counted in
`skipped`). `css.generate` does the same for the whole document, and `css.export {path?, scope?: selection|all}`
writes the `.css` file with the PNGs of rasterized art next to it (no path: `data` and `images` as base64). Its
`warnings` add the PNGs' notes to the CSS's, each once (an image that couldn't be decoded, type written in the
fallback font…). In the app, `css.copy` copies the CSS and `css.exportFile` writes it through a save panel (the web
writes the `.css` file with the PNGs of rasterized art next to it (no path: `data` and `images` as base64). A left-out
`scope` exports the selection; a `path` or `scope` given as anything but a string, `null` included, is an error, and
nothing is written. `css.generate` and `css.export {scope: "all"}` do not use `ids` or `id`, but a malformed one is still
an error. Its `warnings` add the PNGs' notes to the CSS's, each once (an image that couldn't be decoded,
type written in the fallback font…). In the app, `css.copy` copies the CSS and `css.exportFile` writes it through a save panel (the web
downloads it) and shows the first note in the status bar, with how many there are; resting the pointer on the
message lists up to 30 of them, fewer when the list would not fit in the window, with how many were left out.
@@ -6891,7 +7186,7 @@ dictionaries (the standard ones are values in `systemdict`; `dictstack`, `intern
intervals share storage, resources (categories made from `Generic`, `resourceforall`), executable filters (`cvx exec`
runs their data; `flushfile` skips it), axial and radial shadings and shading patterns (gradients), images and image
masks (data in the file through ASCII85, hex, run-length, Flate, LZW or DCT filters, or from procedures), and type as
point type in the font the file names (embedded Type 1 and Type 42 fonts' characters and widths come from the font program they carry, and the show operators' spacing along the baseline is kept as tracking, while spacing that moves characters off it (`yshow`, `xyshow`) makes a text object per character; CID-keyed fonts draw the glyphs of the Type 0 and Type 2 CIDFonts the file carries, CIDFonts in a FontSet's Compact Font Format data included). In a file in Illustrator's own format
point type in the font the file names (embedded Type 1 and Type 42 fonts' characters and widths come from the font program they carry, and the show operators' spacing along the baseline is kept as tracking, while spacing that moves characters off it (`yshow`, `xyshow`) makes a text object per character; CID-keyed fonts draw the glyphs of the Type 0 and Type 2 CIDFonts the file carries, CIDFonts in a FontSet's Compact Font Format data included; a CID-keyed font whose CIDFont the file only names, over a predefined CMap such as `UniJIS-UCS2-H`, `90ms-RKSJ-H` or `GBK-EUC-H`, sets the characters its codes stand for as type in the font of the CIDFont's name, as vertical type over a `-V` CMap, and type in a predefined CMap that can't be read is left out with a warning). In a file in Illustrator's own format
(Illustrator 3–8 `.ai` and their EPS, written with the prolog that defines its operators) the groups it writes (`u` …
`U`) come in as groups, nested as they were; clips and groups nest at most 128 deep. The artboard is the `%%HiResBoundingBox`
(else `%%BoundingBox`; a letter page without one). A program the interpreter can't run (an operator it doesn't know,
@@ -7426,6 +7721,27 @@ Panel sets it back to Push Out Only. One undo step.
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"kinsokuType":"pushInFirst"}}}
```
## Bunri-Kinshi
`text.setFormat {bunriKinshi: bool}` (Paragraph panel menu › Bunri-Kinshi, shown with the East Asian options) turns it
on or off for the paragraphs of the selected type (or `ids`, or the paragraphs a `start` / `end` range touches). On, a
line never breaks inside an inseparable pair: two em dashes (U+2014), two horizontal ellipses (U+2026), two two-dot
leaders (U+2025), or the upper half of a vertical kana repeat mark (U+3033 or U+3034) followed by its lower half
(U+3035), the pairs W3C JLREQ lists for its class cl-08 (https://www.w3.org/TR/jlreq/#cl-08), which are VectorCraft's
set. A chain of em dashes stays together as well. A pair is two neighboring characters, whatever their fonts and
styles; a forced line break between them ends the line as before. Both composers, vertical type, Kinsoku Shori Type,
hyphenation and No Break follow it, and a pair wider than the line stays whole on it. Justification adds no space inside
a pair. The setting works only with a Kinsoku Set: with Kinsoku Set None the paragraph keeps the value without effect,
and the menu item is unavailable. Off by default, for new type and for paragraphs saved before the option. It is saved
with the paragraph (`bunri_kinshi` in `para`) only when on, paragraph styles carry it (Paragraph Style Options › East
Asian Composition Settings), and Reset Panel turns it off. A value that is not true or false is refused. One undo
step. `.ai` editing data does not hold it, and a save reports a paragraph with it on and a pair in it as left out. The help page:
https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html
```json
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"bunriKinshi":true}}}
```
## Korean Line Break Anywhere
`text.setFormat {koreanLineBreakAnywhere: bool}` (Paragraph panel menu › Korean Line Break Anywhere, shown with the
@@ -7946,20 +8262,23 @@ it, as with any style change to one character (`text.setRangeStyle`). The type s
## Alternate glyphs
The Glyphs panel's Show › Alternates for Current Selection lists the alternate glyphs of the character selected with
the Type tool (a range's first character, else the one before the caret), with the default glyph first when the font
does not list it, and a double-click on one draws that character with it. `text.glyphAlternates {id, start}` lists them for the character at byte `start`: `{char, font, style, default,
the Type tool (a range's first character, else the one before the caret) as it stands in its word, with the glyph the
shaper makes for it first, and a double-click on one applies the OpenType feature that draws it to that character alone
(see [Alternates as OpenType feature values](#alternates-as-opentype-feature-values)); a glyph no feature draws there is
drawn by `text.setGlyphAlternate`. `text.glyphAlternates {id, start}` lists them for the character at byte `start`: `{char, font, style, default,
chosen, alternates: [{gid, feature}]}`, where `default` is the glyph the font's character map gives, `chosen` the glyph
drawn now, and `alternates` the glyphs the font's `GSUB` single and alternate substitutions put in place of the default
glyph: `aalt` first, then `salt`, `ss01` to `ss20`, `swsh`, `cv01` to `cv99`, `hist`, `jalt`, `nalt`, `ornm` and
`titl`, in lookup order within each, each glyph once. The default glyph is listed only when `aalt` lists it. Ligatures
and contextual substitutions are not listed. `text.setGlyphAlternate {id, start, gid}` draws the character with glyph
`gid`, one of those alternates or the default glyph, as one undo step; `gid: null` takes the choice away, and the
`gid`, one of those alternates, a glyph that no character maps and no feature reaches whose name is not another letter's
(`a.salt` for `a`, in a font with no `salt`), or the default glyph, as one undo step; `gid: null` takes the choice away, and the
character is drawn as its style draws it (a feature such as `salt` can draw another glyph than the default). The text
keeps the character and its other attributes, and characters typed next to it take their default glyphs. The choice is
saved with the character's style as `glyphAlternate: {ch, gid, font, version}`, the face's PostScript name and version
string: set in another font or version, or with the font missing, the character takes its default glyph. Both commands
refuse a character whose font is not available, and `text.setGlyphAlternate` refuses a glyph that is not one of the
character's alternates and a glyph that the character's style does not draw (All Caps, Small Caps or another numeral
refuse a character whose font is not available, and `text.setGlyphAlternate` refuses a glyph that is not one of those
and a glyph that the character's style does not draw (All Caps, Small Caps or another numeral
system). Choosing the glyph a character already draws is not an edit. Character styles do not take `glyphAlternate`:
`charStyle.new`, `charStyle.edit` and `charStyle.setAttrs` refuse it.
@@ -7968,6 +8287,36 @@ system). Choosing the glyph a character already draws is not an edit. Character
{"name":"run_command","arguments":{"command":"text.setGlyphAlternate","params":{"id":7,"start":2,"gid":1043}}}
```
### Alternates as OpenType feature values
`text.setGlyphAlternate`'s choice swaps a glyph id into one character and shapes that character apart from its neighbours.
A feature with a value keeps the text real and joined: `features`, `featureChanges` (`text.setRangeStyle`,
`text.setStyle`, `charStyle.*`) take `tag=N` for `swsh`, `ss01` to `ss20`, `salt`, `titl`, `pnum`, `lnum`, `sups`,
`subs`, `numr`, `dnom` and `aalt`, `N` from 0 to 65535, and the style's `features` list keeps the string:
`"salt=2"` draws the font's second alternate of every glyph in the run that has one (the OpenType alternate
substitution picks the glyph at that position of its set; a glyph without one, or with fewer alternates, keeps the
glyph it has). A plain `"salt"` is `"salt=1"` and is written so; `"salt=0"`, `"-salt"` and leaving it out are off. The
next value for a feature replaces the last one, in `features` and in `featureChanges`, and a tag the build does not know
(`salt=x`, `salt=65536`, `-salt=2`, `liga=2`) is refused with the unknown-feature error. SVG export writes the value as
CSS `font-feature-settings: "salt" 2`.
Runs that differ only in their `features` are shaped together as one piece of text, each run's features over its own
characters: styling the middle letter of an Arabic word with `salt=2` leaves every other letter its initial, medial or
final form and the letters still join, as the same word typed with no style change. Runs that differ in font, size,
scale, case, language, script or direction are still shaped apart.
`text.glyphAlternates` lists a character's alternates by the glyph the font's character map gives it; the Glyphs panel
instead lists those of the glyph the shaper makes for the character in its word (a medial letter's medial form), finds
for each the feature value that draws it there, and on a double-click runs one `text.setRangeStyle` on exactly that
character: `featureChanges` turns its other alternate features (`salt`, `aalt`, `swsh`, `ss01`–`ss20`) off and sets the
value (`["-ss01", "salt=2"]`), and `glyphAlternate: null` takes away a glyph choice that would otherwise keep drawing
its own glyph (`text.setRangeStyle` takes it; any other value is refused). It is one undo step.
```json
{"name":"run_command","arguments":{"command":"text.setRangeStyle","params":{"id":7,"start":4,"end":6,"features":["salt=2"]}}}
{"name":"run_command","arguments":{"command":"text.setRangeStyle","params":{"id":7,"start":4,"end":6,"featureChanges":["-salt"]}}}
```
## New type in a Japanese interface
While the interface is in Japanese (Preferences › User Interface › Language, or `auto` on a Japanese system), new type starts with em
@@ -8093,6 +8442,28 @@ stops, the command changes nothing and records no undo step. `text.tabs.set` rej
holds at least that many, so stops can be removed from a paragraph that holds more. It sorts the stops by position and
keeps the first of stops at one position.
`text.setFormat {paragraphIndents: [{id, start, leftIndent?, firstLineIndent?}…]}` gives each paragraph indents of its
own in one call: each entry names one paragraph of a type object by the byte `start` where the paragraph begins, and
sets its `leftIndent`, its `firstLineIndent` or both, in points from -1296 to 1296. The call takes no other key, and
at most 100000 entries. It checks every entry before it changes anything, and fails on an empty list, an unknown key, an
entry with neither indent, a value that is not a number in range, an `id` that is not a type object, a `start` where no
paragraph of that object begins, and two entries for one paragraph with other values (the same entry twice counts once).
The whole change is one edit: each type object's paragraph styles are rebuilt once, and a threaded story flows through
its frames again once, however many of its paragraphs change, where one `text.setFormat {ids, start, end}` for each
paragraph flows it again after every paragraph. When no paragraph's indents change, the command records no edit and no
undo step. It returns `{ids}`, the type objects the entries name. The Tabs panel's indent markers use it when the
paragraphs they move have indents of their own, inside a `command.batch` that names the undo step after the marker.
```json
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"paragraphIndents":[{"id":12,"start":0,"firstLineIndent":36},{"id":12,"start":40,"leftIndent":24,"firstLineIndent":12}]}}}
```
The single-value form of `text.setFormat` fails, naming the key, when a number such as `leftIndent` or `baselineShift` is
given as a string, a switch or a list, or a switch such as `underline` as anything but true or false; it changes nothing
then. A null value of any attribute leaves that attribute as it is, as an omitted one does, so a call whose attributes
are all null fails with "nothing to change" and records no edit and no undo step. A call whose values are the ones the
text already has succeeds and returns its `ids`, but records no edit and no undo step.
`text.tabs.get {start?}` and `paraStyle.new` / `paraStyle.redefine {id, start?}` read the
paragraph at `start`; `paraStyle.list` counts paragraphs as uses, and `charStyle.list` counts runs; both count the type in
the layers, opacity masks, symbols, graph designs, pattern tiles and the art of brushes. `charStyle.unused` and
@@ -8938,9 +9309,30 @@ so its point at `t` moves by (dx, dy), turning a straight spine into a curve (`p
{"name":"run_command","arguments":{"command":"object.blend.spine.moveAnchor","params":{"id":40,"anchor":1,"x":150,"y":20}}}
```
`object.blend.setEasing {id?, easingType?, ramp?, colorShift?}` sets one blend's easing and color shift (the
first selected blend without `id`) and returns `{id, easingType, ramp, colorShift, changed}`; omitted values are kept.
`easingType` is `easeIn`, `easeOut`, `easeInOut` or `easeOutIn`, `ramp` is 0 to 100 and `colorShift` -100 to 100.
`easingType` and `ramp` need a blend whose steps follow a spine (a replaced or edited spine, or
`orientation: "path"`), as the Blend panel's Type and Ramp do. A blend made without a spine
(`createSpine: "never"`) takes them once Replace Spine has given it one. A value out of range, a hidden or locked blend and a target that is not a blend are errors,
and a call that changes nothing records no undo step (`changed: false`). With one blend selected that has a spine,
the Selection and Blend tools show the same values as widgets
16 px above and below the spine's halfway point (where the boxes of three or more other visible objects hold that
spot, a widget moves out in 16 px steps, up to 144 px, joined to the halfway point by a line): the upper one sets
`colorShift` and the lower one, shown only when the steps follow the spine as `ramp` needs, sets `ramp`, by one per screen pixel dragged along the spine and by one per arrow key
during the drag (ten with Shift). Each drag is one undo step, and `press_key` Escape or a switch to another tool
during it puts the value back:
```json
{"name":"run_command","arguments":{"command":"object.blend.setEasing","params":{"id":40,"ramp":35}}}
```
How blends interpolate: closed shapes start where they twist least unless `starts` picks the anchors; strokes
interpolate weight, dashes (a solid stroke counts as the dashed one with its gaps closed), miter limit, arrowhead scale
and width profiles, while caps, joins, alignment, arrowheads and brushes switch halfway; gradients of one kind with
interpolate weight, dashes (a solid stroke counts as the dashed one with its gaps closed), miter limit, arrowhead scale,
width profiles and the pen pressure, tilt, bearing and rotation recorded with the Paintbrush (a stroke without a
recording counts as half pressure, half tilt and 0°, as brushes read it; angles turn the shorter way round; a key's
profiles, recordings and arrowheads turn with its path when the blend starts it at another anchor), while
caps, joins, alignment, arrowheads and brushes switch halfway; gradients of one kind with
different stop counts resample to a common set of stops. Groups pair their members in stacking order (members only
one group has grow out of the other's centre), compound paths stay compound paths, and type, symbol instances and
images interpolate their transforms (type also its colours, stroke weight and size run by run; the words and the
@@ -9576,8 +9968,9 @@ it ends up in front), baked with `effect.expandAppearance`, then `object.pathfin
## Cutting Tools add-on
Cutting Tools is a built-in add-on that prepares art for cutting. Its command is `addon.cutting.bridges`; in the desktop
app it is Object › Plug-ins › Cutting Tools › Stencil Bridges….
Cutting Tools is a built-in add-on that prepares art for cutting. Its commands are `addon.cutting.bridges` and
`addon.cutting.weed`; in the desktop app they are Object › Plug-ins › Cutting Tools › Stencil Bridges… and Weed Box and
Lines….
### Stencil Bridges
@@ -9628,6 +10021,44 @@ Crossing) and Keep Original.
{"name":"run_command","arguments":{"command":"addon.cutting.bridges","params":{"width":3,"count":2,"direction":"shortest","keepOriginal":false}}}
```
### Weed Box and Lines
`addon.cutting.weed {margin?, box?, direction?, count?, spacing?, gap?, weight?, swatch?}` adds a weed box around the
selected art and weeding lines across the box, so the waste vinyl around the art lifts off in pieces
([what a weeding border is](https://help.arlon.com/what-is-a-weeding-border)). The ranges, defaults and rules below are
VectorCraft's own. The weed box is a rectangle `margin` points (0 to 100000, default 9) outside the art's bounds;
`box: false` leaves it out. The weeding lines run `"horizontal"`, `"vertical"` or `"both"` ways (default `"both"`) from
one side of the box to the other and divide it evenly: `count` lines each way (a whole number from 0 to 1000, default 1),
or with `spacing` instead, as few lines as keep them at most that far apart. A line is left out over the art's area and
within `gap` points of its outlines (0 to 100000, default 0: the lines stop at the outlines), so it ends in several
pieces, each a straight path. The lines and the box are stroked `weight` points wide (0.01 to 1000, default 0.3) in the
color swatch `swatch` (default `"[Registration]"`), with no fill.
The art is measured by the outlines a cutter cuts. Closed paths enclose their area by their fill rule, so the hole of a
compound path filled even-odd gets lines too, and open paths are lines with no area. Type is measured by its glyph
outlines, a clip group by what shows inside its clipping path (an open clipping path clips as if closed), an image by
its frame, a placed file by the art it shows (by its box when the file can't be read), a symbol instance by its art (an
instance of a symbol the document doesn't have adds nothing) and a live object such as a blend by the art it draws. An
object selected inside a clip group is measured by what shows of it, and a clipping path selected on its own as its clip
group. Strokes, effects, hidden objects and objects in a hidden group or layer are left out.
The result is one group named "Weeding" (the box is its member "Weed Box"), added on top of the current layer and
selected, as one undo step. While a clip group, or a group inside one, is edited in isolation, the group goes on top of
the container that holds the outermost clip group around it. With nothing selected, or with a selection that has no
outlines, the command fails. It also fails when the art's outlines, followed in straight segments, have more than
200000 points; when groups, symbol instances and live objects are nested more than 128 levels deep; when measuring the
clip groups' outlines against each other takes more than 40000000 tests of one segment or outline point against another,
counted over every clip group in the selection together and including the outlines a nested clip group hides; when, for the
lines one way, more than 10000000 pairs of a line and an outline segment within `gap` of it are found, or closed
outline segments are checked for crossings with the lines more than 10000000 times; when the lines would be more than
100000 pieces; and when no line fits outside the art while `box` is false. Result: `{id, box, lines}`: the group's id,
the weed box's id (null without one) and the number of pieces of weeding line. Weed Box and Lines… opens the command
dialog with Preview; a Spacing above 0 is used instead of the Count.
```json
{"name":"run_command","arguments":{"command":"addon.cutting.weed","params":{"margin":"0.25 in","direction":"both","spacing":"2 in","gap":1}}}
```
## Color Effects add-on
Color Effects is a built-in add-on: its effects are colour adjustments in Effect › Color Effects, its command is
@@ -9944,9 +10375,11 @@ change in one undo step, and an image that `ids` names twice changes once. An im
as it is, and when none changes the command adds no undo step and returns `changed: 0`. `cleared` counts the pixels
made fully clear in the images that changed. The command refuses, and changes nothing, when an image is CMYK, a linked
image shows its preview (embed or relink it first), an image would need more than the decode allowance while it is
worked on (2 GiB, or 512 MiB in the web app, counted from the file's header before it is decoded), or every pixel of an
image would be cleared. Remove Background… opens the command's dialog with Preview off; with Preview on, the canvas
shows the result for the values in the dialog, and Cancel puts the image back. While a dialog previews the command,
worked on (2 GiB, or 512 MiB in the web app, counted from the file's header before it is decoded), an image is
grayscale and colorized with a process color (the result has an alpha channel, and only a spot color, black or white
colorizes a grayscale image with transparency, as `paint.setFill` requires), or every pixel of an image would be
cleared. Remove Background… opens the command's dialog with Preview off; with Preview on, the canvas shows the result
for the values in the dialog, and Cancel puts the image back. While a dialog previews the command,
the session keeps the decoded pixels of the images it changes (at most 128 MiB, or 32 MiB in the web app), so the next
preview step does not decode them again; it releases them when the dialog closes with OK or Cancel. A run that is not
a preview step does not keep them.
+12 -5
View File
@@ -1,6 +1,8 @@
# Roadmap: milestones and what's next
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: M10/G5: Energy Beams brush library; MCP docs list Energy Beams; previously minor: M10/G5: Skill Icons symbol library; MCP docs list Skill Icons; previously minor: M10/G5: Status Effects graphic style library; MCP docs catch-up for symbol libraries; previously minor: M10/G5: Inventory Icons built-in symbol library; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: M10/G5: Game HUD graphic style library; previously minor: resizable icon-strip flyouts and bottom-aligned panel footers, #5713; previously minor: M10/G5: Map Markers built-in symbol library; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: G2: Effect Gallery thumbnails and in-dialog preview are in; Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: EPS import: embedded fonts (composite, Type 42, CID-keyed, Type 1, CFF FontSet) draw their glyphs; spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; M4/M7: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; M4/M7: `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; numeric fields step on wheel hover without taking keyboard focus, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Selection QoL: marquee modes and visible feedback, #5313; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Indian-language hyphenation review and license allowlist, #4947; Hindi catalog and contributor loanword preferences; full wording review in progress; Effect dialogs: a colour swatch beside a hex colour opens the Color Picker, #5365; shape tools move the selection from its centre point and turn it from the zone outside a box corner, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (icon-strip flyouts also resize from the top edge and the canvas-side top corner, keeping the moved top in workspaces (storytold/craft-libs#1094); scores and estimates unchanged; previously: #6865: explicitly requested oversized editing artboard; Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: M10/G5: Energy Beams brush library; MCP docs list Energy Beams; previously minor: M10/G5: Skill Icons symbol library; MCP docs list Skill Icons; previously minor: M10/G5: Status Effects graphic style library; MCP docs catch-up for symbol libraries; previously minor: M10/G5: Inventory Icons built-in symbol library; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: M10/G5: Game HUD graphic style library; previously minor: resizable icon-strip flyouts and bottom-aligned panel footers, #5713; previously minor: M10/G5: Map Markers built-in symbol library; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: G2: Effect Gallery thumbnails and in-dialog preview are in; Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: EPS import: embedded fonts (composite, Type 42, CID-keyed, Type 1, CFF FontSet) draw their glyphs; spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; M4/M7: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; M4/M7: `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; numeric fields step on wheel hover without taking keyboard focus, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Selection QoL: marquee modes and visible feedback, #5313; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Indian-language hyphenation review and license allowlist, #4947; Hindi catalog and contributor loanword preferences; full wording review in progress; Effect dialogs: a colour swatch beside a hex colour opens the Color Picker, #5365; shape tools move the selection from its centre point and turn it from the zone outside a box corner, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
>
> Oversized artboard import update (2026-11-12): Explicit editing-data import retains a single artboard beyond a capped 14,400 pt PDF page (#6865).
>
> Vector Halftone preview update (2026-11-10): temporary dot selection marks are hidden, and the bounding box stays available (#6369).
>
@@ -10,11 +12,13 @@
## Feature submission evidence
M4: Explicitly requested editing data keeps a single artboard larger than its 14,400 pt capped PDF page (#6865). Scores and estimates unchanged.
Numeric fields step on wheel hover without taking keyboard focus (#5949). Arrow keys still require focus; modifiers, units, limits, mixed values and existing rounding stay in the shared stepping path. Headless regressions in `tests/numfields.rs` cover hovered unfocused fields, focused fields, trackpad accumulation and panel scroll exclusion. Scores and estimates unchanged.
Raster rendering fits and preflattens curved stroke outlines more tightly only below four times the raster tolerance, retaining thin-band coverage without changing stroke weight or vector export geometry. Current cached character/object outlines, cropped long strokes, gradient clips and effect rasters share the helper; cropped-region reuse and guarded flattening remain. The unchanged `fractional_curved_strokes_retain_coverage_between_notches` regression covers fractional weights, rotation, phase, opacity and f32-rounded endpoints. This is a quality/cost tradeoff, not a speed claim. Scores and estimates unchanged.
Icon-strip flyouts resize from the canvas-side edge and bottom, keeping the icon-column edge anchored (#5713). Independent group heights and flyout widths persist in workspaces; saved heights override content fitting while untouched heights still fit content. Shared panel button bars and the Glyphs font row stay at the bottom after enlargement; the Artboards list grows into the extra space. Regressions: `panel_docking::tests::glyphs_and_artboards_flyouts_keep_user_sizes` and `expanded_panel_footers_stay_at_the_bottom_with_short_and_long_lists`. Scores and estimates unchanged.
Icon-strip flyouts resize from the canvas-side edge, the top, the bottom and the corners on the canvas side, keeping the icon-column edge anchored (#5713, storytold/craft-libs#1094); a top-edge resize keeps the bottom and saves the moved top per group in workspaces. Independent group heights and flyout widths persist in workspaces; saved heights override content fitting while untouched heights still fit content. Shared panel button bars and the Glyphs font row stay at the bottom after enlargement; the Artboards list grows into the extra space. Regressions: `panel_docking::tests::glyphs_and_artboards_flyouts_keep_user_sizes` and `expanded_panel_footers_stay_at_the_bottom_with_short_and_long_lists`. Scores and estimates unchanged.
**PR #3273 revision — 2026-11-04:** Thin curved stroke coverage ported to the current raster-only cache, cropped-long-stroke and effect paths with guarded flattening; vector export geometry unchanged. Scores and estimates unchanged.
@@ -55,6 +59,7 @@ M7 type: Touch Type moves a right-to-left character along its line and up from i
| 2026-11-07 | minor | M4/M7: `.ai`/EPS type from the text document keeps auto leading at the paragraph's share, the paragraph's hyphenation options and may show more of the last lines the file sets in an area frame of its own, when growing the frame by its lines' deepest descent plus 0.05 pt shows them, and a line a soft return ends is justified as a wrapped line is (#6137); a user's `.ai` that opened from its PDF part now opens from its layers. Limits: frames grow only for area type of one frame, horizontal, by its lines' deepest descent plus 0.05 pt; type in a font that isn't installed, and hyphenation in a language that isn't imported, still lay out differently. Scores and estimates unchanged. |
| 2026-11-07 | minor | #3380 (deletion only): independent surviving path objects; transformed appearance, stacking, selection, cubic handles and Undo/Redo covered by `delete_path_pieces`; compound semantics retained. Endpoint highlighting remains open; scores and estimates unchanged. |
| 2026-11-07 | minor | Indian-language hyphenation (#4947): eleven MIT-licensed pattern sets, with grapheme and shaping-cluster protection in both composers and existing paragraph limits. Assamese and Sanskrit are selectable; Sanskrit automatic patterns remain pending an allowlisted source. Scores and estimates unchanged. |
| 2026-11-12 | minor | Icon-strip flyouts also resize from the top edge and the canvas-side top corner; the moved top is kept per group in workspaces (storytold/craft-libs#1095). Scores and estimates unchanged. |
| 2026-11-06 | minor | Icon-strip flyouts resize on their free edges and retain sizes with workspaces (#5713); saved heights beat auto-fit; panel footers stay at the bottom after enlargement. Scores and estimates unchanged. |
| 2026-11-05 | minor | Effect dialogs: a colour swatch beside a hex colour field follows typed values, and a double-click opens the Color Picker over the dialog, whose OK fills the field (#5365); scores and estimates unchanged. |
| 2026-11-05 | minor | Selection Tools add-on: Select Linked, Embedded, Missing and Modified select the images and placed documents with that link status (Object › Plug-ins › Selection Tools and the Links panel menu; Shift adds), and Select by Link Status… lists them by name, status and format to pick from, optionally zooming to them; what is hidden, locked, on a template layer or outside the isolated group is left out and the status bar says how many (#5526, a VectorCraft addition); scores and estimates unchanged (estimated). |
@@ -143,13 +148,13 @@ native wording review, layout checks and a web Devanagari font remain. [Notes](l
| M1 | Selection, transform, layers, MCP (common command tools, strict keys, session resources and version-aware result hints) | ✅ mostly done (document-tab artwork drag with lossless resource transfer and one target Undo; malformed MCP app replies discard the connection without replay, #1761; bounded control socket waits and connection-slot recovery, #1726; bounded synthetic control input, #1709; no-op anchor Align keeps Undo, Redo and saved state (#1180 follow-up); transform reference point snaps to anchors/centres; rotated objects keep a rotated bounding box and their angle; Free Transform, Scale and Reflect along a rotated box pending; the Transform fields take one Tab each, the W/H link after them) | 4–6 |
| M2 | Drawing tools + smart guides | ✅ mostly done (#3380 deletion creates independent selected path pieces and keeps bounds-fitted gradients in place; endpoint highlighting remains open; Polygon/Star exact-size count persistence, with repeated-drag, tool-switch and Cancel regression coverage; Curvature point markers without direction handles (#1236, in part); Direct Selection segment click/Delete preserves neighboring edges, #1278; Flare, Reshape, Live Corners widget dragging and per-corner radii and kinds landed, on any path's corners; Shaper rough-shape recognition and orientation steps, the remaining Pen modifier nuances; Shaper Groups with editable sources and separate fill/stroke edits landed; custom ruler origin with drag preview, active-artboard reset, relative X/Y fields and native round-trip (#1212); construction widget/refinements pending); a shape just drawn moves by its centre point and turns from the zone outside a corner with its tool still active (#5477); Tangent and straight-segment perpendicular snapping use saved Smart Guides Geometric Guides preferences and Control bar quick access (#2537), including the existing Snap to Glyph and Alignment Guides toggles (#6164); no View submenu; Copy and Cut with the Pen or Curvature tool take the anchors a Cmd/Ctrl-lent Direct Selection picked (#6123) | 12–18 |
| M3 | Paint & appearance (swatches, color, gradient, stroke, appearance, transparency, styles) | ✅ done (M3.7–M3.98): swatches, groups, libraries, tints, Lab spots and Registration; Eyedropper copies transformed character dimensions (#6404) and styles highlighted text only (atomic transparency rejection); Eyedropper reads visible locked and outside-isolation sources (#1251); Color Picker, Color panel, Color Guide, Color Themes, Edit Colors and Recolor Artwork (screen-bounded Edit chip list, #3416); gradients that follow every transform, the annotator, linked stops, freeform gradients, focal points, gradients on strokes and Expand; visible strokes when paint is added to inherited None/zero-width art (#1253); stroke geometry, arrowheads, dashes fitted to corners, stroke on type, width profiles and the Width tool (Shift-double-click enables adjoining adjustment in Width Point Edit; finite-arithmetic follow-up regressions and full CI passed ([Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area))), Scale Strokes and preview bounds; Appearance targeting, container appearance, target circles, Expand Appearance; knockout, isolation, blend accuracy, CMYK blending, overprint, Attributes, masks and clipping, Flatten Transparency; graphic styles, links and libraries; CSS library export preserves fractional linear and radial gradient stop positions. Left: freeform gradients in SVG/PDF, mixed spot/process gradients in SVG and PDF gradients mixing a spot color with RGB or Lab stops export as stops or process colors; PDF export writes dithered gradient fills with spot stops, and Perceptual spot gradients with RGB or Lab stops other than white or more than 4,096 stops on their curve, as images in process colors; confirm the Scale Strokes & Effects default; Recolor model selection is available through the engine and MCP only (#2520); the dialog retains its existing controls | 4–8 |
| M4 | Files (native, SVG, PDF, raster, Export for Screens, clipboard interop) | Save refusal continuation (#6146): ordinary Save offers Save As; unchanged format keeps options, changed format opens its Save options; cancellation/automation isolation; focused UI regressions passed, native acceptance pending; PDF authored ActualText preserved once in editable import (#6165; first-glyph line/style, bounded input; estimates unchanged); PDF export keeps harakat and zero-advance Noto Arabic dot glyphs in one compatible batch per layout line, with per-glyph vertical offsets (#6309). `crates/pdf/src/tests/realtext.rs` (`mark_text_*`) checks decoded cluster mappings, mixed Arabic/Latin/digits, contour placement against outline export, vertical columns, differing path rotations and PDF/X-1a/3/4. Editable-text import preserves authored ActualText separately (#6165), with cross-line/style layout limits; separately styled or differently rotated clusters and external viewer search/copy remain outside this export-only evidence. `.ai`/EPS type from the text document keeps its paragraphs' tab stops (#6147); `.ai`/EPS type from the text document keeps auto leading per line, its hyphenation options, and may show more of the last lines the file sets in an area frame of its own, when growing the frame by its lines' deepest descent plus 0.05 pt shows them (#6137); ✅ done (M4.14–M4.98 on 2026-10-05): Save As/Save a Copy/Revert/templates, Data Recovery and background save, SVG import and export fidelity (units, text, symbols, filters, SVGZ, Preserve Editing, SVG Options), shared guarded PDF writer and windowed reader ([pdf-backend.md](pdf-backend.md)), PDF import (colours, layers, masks, editable text, security) and export (presets, marks, bleed, ICC and output intent, raster effects, subset fonts, PDF/X-1a, X-3 and X-4, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3), Place and Links, Package, EPS and DXF in and out (EPS legacy mesh corners, vector opacity masks and raster page comparison verified; closed SPLINE flags round-trip and circular paths export as native CIRCLE/ARC, #1299), EMF/WMF, TIFF/BMP/Targa/PSD/GIF/PNG-8 export, Print and print presets, slices, Save for Web, TIFF in Export for Screens and Asset Export, clipboard flavours, File Info; AF.1–AF.4 add Affinity import (native layers, artboards, vectors, text and images from Affinity 1–3 files; the embedded preview only as a warned fallback; AF.4 expands current `.af` fixtures and every-board round-trip validation); EPS Save and Save As keep an opened EPS as EPS with its page frame and complete editable VectorCraft document (2026-10-26; G9 import fidelity unchanged). Left: DWG (use DXF), PSD placement as layers, overprint read back from PDF, Affinity export, polish; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts | 3–6 (existing scope; Affinity export unestimated) |
| M4 | Files (native, SVG, PDF, raster, Export for Screens, clipboard interop) | Explicit editing-data import keeps oversized capped-page artboards (#6865; estimates unchanged); Save refusal continuation (#6146): ordinary Save offers Save As; unchanged format keeps options, changed format opens its Save options; cancellation/automation isolation; focused UI regressions passed, native acceptance pending; PDF authored ActualText preserved once in editable import (#6165; first-glyph line/style, bounded input; estimates unchanged); PDF export keeps harakat and zero-advance Noto Arabic dot glyphs in one compatible batch per layout line, with per-glyph vertical offsets (#6309). `crates/pdf/src/tests/realtext.rs` (`mark_text_*`) checks decoded cluster mappings, mixed Arabic/Latin/digits, contour placement against outline export, vertical columns, differing path rotations and PDF/X-1a/3/4. Editable-text import preserves authored ActualText separately (#6165), with cross-line/style layout limits; separately styled or differently rotated clusters and external viewer search/copy remain outside this export-only evidence. `.ai`/EPS type from the text document keeps its paragraphs' tab stops (#6147); `.ai`/EPS type from the text document keeps auto leading per line, its hyphenation options, and may show more of the last lines the file sets in an area frame of its own, when growing the frame by its lines' deepest descent plus 0.05 pt shows them (#6137); ✅ done (M4.14–M4.98 on 2026-10-05): Save As/Save a Copy/Revert/templates, Data Recovery and background save, SVG import and export fidelity (units, text, symbols, filters, SVGZ, Preserve Editing, SVG Options), shared guarded PDF writer and windowed reader ([pdf-backend.md](pdf-backend.md)), PDF import (colours, layers, masks, editable text, security) and export (presets, marks, bleed, ICC and output intent, raster effects, subset fonts, PDF/X-1a, X-3 and X-4, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3), Place and Links, Package, EPS and DXF in and out (EPS legacy mesh corners, vector opacity masks and raster page comparison verified; closed SPLINE flags round-trip and circular paths export as native CIRCLE/ARC, #1299), EMF/WMF, TIFF/BMP/Targa/PSD/GIF/PNG-8 export, Print and print presets, slices, Save for Web, TIFF in Export for Screens and Asset Export, clipboard flavours, File Info; AF.1–AF.4 add Affinity import (native layers, artboards, vectors, text and images from Affinity 1–3 files; the embedded preview only as a warned fallback; AF.4 expands current `.af` fixtures and every-board round-trip validation); EPS Save and Save As keep an opened EPS as EPS with its page frame and complete editable VectorCraft document (2026-10-26; G9 import fidelity unchanged). Left: DWG (use DXF), PSD placement as layers, overprint read back from PDF, Affinity export, polish; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts | 3–6 (existing scope; Affinity export unestimated) |
| M5 | Performance | 🟡 background render + caches + MT done; `vectorcraft-cli bench` and `vectorcraft-cli perf` (budget suite); opaque solid-fill knockout paths draw once in hard-edge/supersampled raster export, with analytical-AA compositing unchanged; file format v2 opens 3× faster (50k paths: 722 → 244 ms); raster effects (glows, shadows, blur, feather) no longer force the whole frame single-threaded (filtered offscreen per effect, verified equal to the single-threaded reference); Craquelure/Mosaic Tiles/Stained Glass distance searches optimized with pixel-invariance checks and the `texture_drag_perf` probe (#1257); effect-heavy demos need a clean-machine benchmark; dirty-region rendering, GPU backend spike pending | 15–25 |
| M6 | Path operations (Pathfinder, Shape Builder, offset…) | ✅ mostly done (live compound shapes: Make, Alt-click a Shape Mode, member modes, Release, Expand (#4522); single-result Pathfinder names and SVG IDs retained (#1293); Divide open-cutting-path geometry, appearance and history verified (#1307; existing segment cap remains); Simplify preview/counts and rewritten-path component selection verified (#1272; the slider opens at the Auto-Simplify level; a Direct Selection part of a path simplifies on its own); Compound appearance and member fill-rule edits verified (#1197); Boolean precision on almost-horizontal edges fixed and the property tests made deterministic; Shape Builder gap detection, large-offset bug open); Shape Builder Gap Detection shares Live Paint’s spatial gap finder and temporary merge bridges (#2526); Custom is capped at 72 pt in the existing options dialog | 3–6 |
| M7 | Type (point/area/path, editing, styles, OpenType, threading, glyphs) | Spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof and the custom dictionary and Ignore All commands and Auto Spell Check with its right-click suggestions, Ignore All and Add to Dictionary, and Spelling › Auto Spell Check in the context menu of type, and the Edit Custom Dictionary dialog (#4936; the Check Spelling dialog remains); Right-aligned area type sets the text after a line's last tab against the right edge, the text before it at its stops (#6147); A line a soft return ends is justified as a wrapped line is (#6137); Indian-language pattern hyphenation preserves syllables in both composers (#4947); 🟡 Hanging punctuation keeps its published amounts under Glyph Scaling, and Standard burasagari counts a hung line start as room; Fullwidth `!` / `?` keep an upright fallback in vertical type; Hyphenation… and Justification… per paragraph (#2310, #2755), Auto Leading also driving the Character panel's Leading, the Alt+↑/↓ leading nudge and Fill with Placeholder Text; Document-point character controls after object/group scaling (#1034; optional font-size preservation remains separate), Character/Paragraph Styles (explicit Auto-leading resets and local leading overrides), per-paragraph paragraph attributes, mixed paragraph alignment indication (headless regressions, full integration CI and visual review pass), Area Type Options (with Width/Height, Auto Size and Shrink Text to Fit; overflow reported to agents and vertical Align), type areas resized by their handles and reshaped with Direct Selection, threaded text, Fit Headline, Glyphs, OpenType panel (including font-defined Stylistic Sets), Find Font (Document, Recent and System replacement sources, #5217), installed system fonts in every font menu (fonts registered with Windows outside its font folders, the XDG and Flatpak host font folders on Linux, and fonts installed while the app runs, listed when it comes back to the front; searchable; the Character and Properties menus show each family's sample of the selected text, preview it live under the pointer or ↑/↓, show recently applied families first (#3515), filter by script and kind, list a family's styles, and star favourites; the Glyphs panel and Find Font's Replace With use the same menu without the live preview) and found by name on open/import (full-name aliases and Missing Fonts searches verified, #1258; Windows font-service QA pending), variable fonts' named instances as styles, CJK names in the UI, input methods (marked text in place, candidate window at the caret; Japanese IME), Text Wrap (offset, invert, both sides of an object; follows edits), Type on a Path effects and Options with draggable start/centre/end brackets (slide, flip), tab stops + Tabs panel (Above Text floats it over the selected text, its ruler at the canvas zoom so stops line up with the columns, #729), inline symbols in text (beyond parity), Hebrew/Arabic bidirectional text, Paragraph Direction, Every-line/Single-line Composer per object (ragged text too) done; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas tatweels on the base letter's paint (`tests::harakat`); tab leaders, spell check, vertical type pending | 45–60 |
| M7 | Type (point/area/path, editing, styles, OpenType, threading, glyphs) | Spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof and the custom dictionary and Ignore All commands and Auto Spell Check with its right-click suggestions, Ignore All and Add to Dictionary, and Spelling › Auto Spell Check in the context menu of type, and the Edit Custom Dictionary dialog (#4936; the Check Spelling dialog remains); Right-aligned area type sets the text after a line's last tab against the right edge, the text before it at its stops (#6147); A line a soft return ends is justified as a wrapped line is (#6137); Indian-language pattern hyphenation preserves syllables in both composers (#4947); 🟡 Hanging punctuation keeps its published amounts under Glyph Scaling, and Standard burasagari counts a hung line start as room; Fullwidth `!` / `?` keep an upright fallback in vertical type; Hyphenation… and Justification… per paragraph (#2310, #2755), Auto Leading also driving the Character panel's Leading, the Alt+↑/↓ leading nudge and Fill with Placeholder Text; Document-point character controls after object/group scaling (#1034; optional font-size preservation remains separate), Character/Paragraph Styles (explicit Auto-leading resets and local leading overrides), per-paragraph paragraph attributes, mixed paragraph alignment indication (headless regressions, full integration CI and visual review pass), Area Type Options (with Width/Height, Auto Size and Shrink Text to Fit; overflow reported to agents and vertical Align), type areas resized by their handles and reshaped with Direct Selection, threaded text, Fit Headline, Glyphs (feature-valued alternates and unencoded glyphs applied in one Undo step while keeping Arabic joining, #5514), OpenType panel (including font-defined Stylistic Sets), Find Font (Document, Recent and System replacement sources, #5217), installed system fonts in every font menu (fonts registered with Windows outside its font folders, the XDG and Flatpak host font folders on Linux, and fonts installed while the app runs, listed when it comes back to the front; searchable; the Character and Properties menus show each family's sample of the selected text, preview it live under the pointer or ↑/↓, show recently applied families first (#3515), filter by script and kind, list a family's styles, and star favourites; the Glyphs panel and Find Font's Replace With use the same menu without the live preview) and found by name on open/import (full-name aliases and Missing Fonts searches verified, #1258; Windows font-service QA pending), variable fonts' named instances as styles, CJK names in the UI, input methods (marked text in place, candidate window at the caret; Japanese IME), Text Wrap (offset, invert, both sides of an object; follows edits), Type on a Path effects and Options with draggable start/centre/end brackets (slide, flip), tab stops + Tabs panel (Above Text floats it over the selected text, its ruler at the canvas zoom so stops line up with the columns, #729), inline symbols in text (beyond parity), Hebrew/Arabic bidirectional text, Paragraph Direction, Every-line/Single-line Composer per object (ragged text too) done; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas tatweels on the base letter's paint (`tests::harakat`); tab leaders, spell check, vertical type pending | 45–60 |
| M8 | Transform & distort (Puppet Warp, Liquify tools, Envelopes, Blends, Perspective Grid) | ✅ done (M8.1–M8.20 on 2026-10-06, fidelity pass): Liquify Tool Options with one shared brush, pen pressure, hold-to-apply, a safe scope and incremental strokes; tool options that last; Puppet Warp rotation, multi-select, Control bar and a rest-shape session that follows Undo; Width tool on compound paths; Envelope dialogs with preview, Reset with Warp/Mesh, full Envelope Options, type/images/symbols/appearance/gradients/patterns distorted on canvas and in every export, warps that keep their frame, Edit Contents, mesh handles and Control bar; Blend Options, anchor-targeted Blend tool, editable spine, keys picked on the canvas, faithful interpolation and knockout; Anchor Point spine conversion/handle editing and selection priority (#2544), with #2425 Shift reshape preserved; Perspective Grid with Define Grid, presets, lock/snap/rulers, widgets, the 1–4 plane switch, movable planes, Perspective Selection scale/copy/perpendicular/Transform Again, and type and symbols truly projected with Edit Text. Left: Anti-Alias and Preserve Shape output, warp-envelope point editing, brush interpolation in blends, Free Transform/Scale/Reflect along a rotated box | 2–4 |
| M9 | Live effects (+ 3D & Materials) | 🟡 2D effects done incl. Effect → Pathfinder, Color Adjustments and Document Raster Effects Settings; PDF export warns when the pixel cap lowers raster-effect resolution; Artistic › Colored Pencil, Cutout, Dry Brush, Film Grain, Fresco, Neon Glow, Paint Daubs, Palette Knife, Poster Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting and Watercolor and Sketch › Halftone Pattern, Bas Relief, Charcoal, Conté Crayon, Photocopy, Stamp, Torn Edges and Water Paper done (57/57 Photoshop filters); Revolve with automatic smooth vector shading, default-on circular caps and continuous rims, Extrude & Bevel, Inflate and Rotate with one to ten colored directional lights and four original lighting presets; SVG Filters, remaining raster effects and 3D materials and shadows pending | 81–130 |
| M10 | Brushes, symbols, patterns, Repeat | 🟡 pattern swatches (5 tile types, Pattern Options, editing mode, SVG `<pattern>`/PDF export) and live Repeat (radial/grid/mirror) done; interactive selected mirror axis implemented (focused and native checks passed; full CI blocked by an unrelated text test); symbol libraries done (Window › Symbol Libraries, five generated libraries (Basic Shapes, UI Frames, Map Markers, Inventory Icons, Skill Icons), User Defined, Other Library…, Save Symbols); brush libraries done (Window › Brush Libraries, eight generated libraries including Bristle Brushes and Dry Media, User Defined, Other Library…, Save Brushes); symbol editing shows draggable 9-slice guides (#5280), with guides outside the artwork still unsupported; brushes/symbols in progress | 23–37 |
| M10 | Brushes, symbols, patterns, Repeat | 🟡 pattern swatches (5 tile types, Pattern Options, editing mode, SVG `<pattern>`/PDF export) and live Repeat (radial/grid/mirror) done; interactive selected mirror axis implemented (focused and native checks passed; full CI blocked by an unrelated text test); symbol libraries done (Window › Symbol Libraries, five generated libraries (Basic Shapes, UI Frames, Map Markers, Inventory Icons, Skill Icons), User Defined, Other Library…, Save Symbols); brush libraries done (Window › Brush Libraries, nine generated libraries including Ink and Wash, Bristle Brushes and Dry Media, User Defined, Other Library…, Save Brushes); symbol editing shows draggable 9-slice guides (#5280), with guides outside the artwork still unsupported; brushes/symbols in progress | 23–37 |
| M11 | Artboards & views (selected-set deletion shared by Artboards menu, trash and Properties, retaining artboard 1 when all are selected; Artboard deletion keys keep the first of an all-selected set and protect artwork; history prunes invalid indices while preserving valid multiselection (history tests and full workspace CI pass); click-to-fit objects with the Artboard tool (#1227), artboard escape routing and document setup modal handoff done; artboard panel/tool with Move Artwork for X/Y fields and Artboard Options, Trim View, middle-button pan, print tiling and the status bar's artboard navigator with synchronized Artboard tool selection done; Window › Arrange › Tile and Consolidate All Windows done; multiple windows, presentation polish) (cached canvas reprojection preserves scale during resize, rotation and DPI changes; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) (cached canvas dimensions match the renderer limit before cache-key creation, view construction and worker submission; regression-tested; estimates unchanged); status-bar pointer coordinates follow the active ruler origin and General units | 🟡; Artboard Options shares existing presets and orientation controls and can fit artwork without moving it; artwork fitting includes all drawn document art across artboards and off-board; artwork bounds are cached by active document and revision, and selection uses the shared bounds cache across artboards | 15–25 |
| M12 | Advanced color & art (CMYK/ICC, separations, Gradient Mesh, Live Paint, Image Trace, Graphs) | 🟡 Gradient Mesh, Live Paint, Image Trace (junction-returning pieces retain their positioned boundaries; a simpler contour walk with identical output; an edge's sub-pixel shift is the same from either side; notches read only their own piece; collapsed loops keep few segments; large images trace with a warning; Auto Color keeps rare distinct colours; quick presets trace at once and the Image Trace button opens the adjustment controls; Properties reopens them; tiny fitted contours retain islands and holes; Create Strokes: exact crop EDT, Guo–Hall thinning and maximal-disc graph simplification; 15 presets, the 14 documented ones each with an enhanced version; Ignore Color, Shapes, Auto Grouping and Gradients options; measured in `image-trace-quality.md`; closed outlines retain their interior after straightening; the Flat Logo mode places edges to a fraction of a pixel, restores sharp corners, snaps lines and circles, and refuses gradients; live trace result bounds and selection highlights exclude the retained bitmap except in source-image views), Recolor Artwork, colour management (ICC, soft proofing, separations preview), Graphs (all 9 tools, Graph Data/Type, group-selected series colours persist through regenerate/save/open) done; Graph Design and Marker designs, Transpose and Switch x/y, `\|` label line breaks, the pie options, drop shadows, the legend across the top, overlapping widths, First Row/Column in Front, filled lines, kept label formatting and the Graph Data worksheet done; graph Column designs pending; Vector Halftone Preview keeps the selection box without drawing dot selection marks (#6369); Live Paint Bucket fills faces crossed in one drag (#6373) | 6–10 |
| M13 | Automation (Actions ✅ record/playback, persisted; variables, scripting, batch) | 🟡 MCP `add_text` exposes existing vertical point, area and path type; `script.run` and File › Scripts › Other Script… run JavaScript files, and Window › Script Console shows their output; the script document model and batch work remain | 18–27 |
@@ -190,6 +195,8 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-11-12 | minor | Explicit editing-data import keeps a single artboard beyond a capped 14,400 pt PDF page (#6865), with a warning; automatic mode stays unchanged. Scores and estimates unchanged. |
| 2026-11-12 | minor | M7: Glyphs panel applies feature-valued alternates and unencoded glyphs while keeping Arabic joining (#5514). Scores and estimates unchanged. |
| 2026-11-12 | minor | M14: numeric fields keep the caret when a focus click is released more than 1 pt from its press; a tap within 1 pt still selects all (#6659, follow-up to #1326); estimates unchanged. |
| 2026-11-12 | minor | M12: Live Paint Bucket fills faces crossed in one drag (#6373). Scores and estimates unchanged. |
| 2026-11-12 | minor | Save refusal follow-up (#6146): changed format opens its Save options; unchanged format retains original options. Focused UI regressions passed, including the format-change case that failed before the fix; native panel acceptance pending. Scores and estimates unchanged. |
+1 -1
View File
@@ -9,7 +9,7 @@ Scribble over overlapping paths to make an editable Shaper Group:
- End the scribble outside the artwork to remove the touched fills and exposed boundary strokes. Shared edges along surviving filled areas remain, closing their contours. Compatible stroke pieces join at the new corners, so the selected stroke’s join style applies there.
- Scribble over an unfilled stroke segment to remove that piece. A line passing through a shape is split at the intersections, so its protruding ends can be removed separately.
Click once to select the group; click again to select its visible fill and change its color. Double-click with Shaper to enter construction editing and select an original shape. The Selection tool then moves or resizes it normally, and the visible composition updates. Escape leaves construction editing. The original live shapes and edit recipes survive native save/reopen. Outside construction editing, selection highlights and bounds show only the visible result. Outside construction editing, a marquee selects the group or its faces only where it reaches the visible result, and Direct Selection's clicks and marquee select only the anchor points of the visible result.
Click once to select the group; click again to select its visible fill and change its color. Double-click with Shaper to enter construction editing and select an original shape. With the group or one of its faces selected, a Shaper click on an original shape's outline also enters construction editing and selects that shape, even where the outline runs through a merged face ([help page](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/create-shapes-with-the-shaper-tool.html)). The click counts within 3 screen pixels of the outline, a VectorCraft value. The Selection tool then moves or resizes it normally, and the visible composition updates. Escape leaves construction editing. The original live shapes and edit recipes survive native save/reopen. Outside construction editing, selection highlights and bounds show only the visible result. Outside construction editing, a marquee selects the group or its faces only where it reaches the visible result, and Direct Selection's clicks and marquee select only the anchor points of the visible result.
While the group or one of its faces is selected (Face Selection mode) and the Shaper or Selection tool is active, an arrow widget sits beside the middle of the right side of the original shapes’ bounds. Click it to enter Construction mode: the original shapes are isolated and nothing is selected, and a click with Shaper selects the group’s original shape under the pointer. Click the widget again to leave Construction mode with the group selected. The arrow points right in Face Selection mode and left in Construction mode.
File diff suppressed because one or more lines are too long
+13
View File
@@ -51,6 +51,19 @@ pages describe it.
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.
- **Quick Actions › Expand as Wireframes** sits under the Object tab (under the first tab for Revolve and Rotate,
which have no Object tab). It replaces each selected object that has a 3D effect, the 3D (Classic) effects included, with a group of paths, the
edges of the faces the effect draws for the object as it is turned and projected now (`effect.expandAsWireframes`),
as the public [Render 3D vector artwork](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/render-3d-vector-artwork.html)
page describes for converting the wireframe into editable paths. Each edge appears once: edges that meet end to
end form one path, closed where it comes back to its start, and curved edges stay curves. A face partly covered
by another face gives its whole outline. Faces turned away from the viewer are included where the effect draws
them (Draw Hidden Faces and the Wireframe surface of Extrude & Bevel (Classic)). Geometry effects after a 3D
effect, on the object or on a fill or stroke, reshape the edges as they reshape the drawn art. Shadows are not
part of the wireframe. The paths have a 0.5 pt black stroke with round joins and no fill, which is VectorCraft's
own choice. The group keeps the object's place, id, name, opacity, blending mode, opacity mask, attributes
and text wrap and is selected; objects without a visible 3D effect do not change. It is one undo step, and an error changes nothing
when no selected object has a visible 3D effect.
- The Lighting tab also holds the Shadows switch and its settings ([Shadows](#shadows)). The Materials tab and
ray-traced rendering are not built (see below). The rotation and light rings on the artboard are part of the
options dialog only.
+9 -2
View File
@@ -2,6 +2,8 @@
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-11 · **Change:** minor (kerning at the Type tool's caret at the start of a line sets the space before the character after it, #5111; previously minor: type's strokes in an envelope paint along its bent glyph outlines at their own weight, Distort Appearance on or off, as users' `.ai` files draw them, #6644; previously minor: a centred or right-aligned line of tracked type is aligned without the tracking after its last character, as the page of users' `.ai` files aligns it, #6610; previously minor: tracking and manual kerning in horizontally scaled type are thousandths of the em as the Horizontal Scale draws it, as the page of users' `.ai` files spaces such type; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: Direct Selection shows and drags the direction handles of type on a path's path, #3565; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas base-letter paint; scores and estimates unchanged; custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: right-aligned area type sets the text after a line's last tab against the right edge; a soft return in justified type no longer keeps its line from being justified; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen) · **Target:** Adobe Illustrator 2026 (30.x)
Glyphs review (#6084): a positional variant no supported feature draws is accepted as a glyph choice. Entire Font respects the selection's variable axes and font version; the default cell can reset an unlisted choice without GSUB alternates. Contextual choices use the supplying face, including fallback. Feature-only boundaries preserve ligature clusters and Arabic mark attachment. Encoded characters insert unchanged except Arabic presentation forms with a joining base letter; normalization reports success only after insertion succeeds. Latest follow-up regressions await runtime verification. Scores and estimates unchanged.
Type is Illustrator's largest single area by use (weight 13 of 105 across "Type core" and "Type advanced" in
[target-app-parity.md](target-app-parity.md)). This checklist lists the features one by one. Status is from the
code and tests (`crates/text`, `crates/engine/src/cmd/type*.rs`, `textstyles.rs`, `crates/tools/src/text.rs`)
@@ -30,8 +32,9 @@ against Illustrator's public documentation; **estimated** unless noted.
| Copy or Cut selected characters, then paste as separate text art | done | A Type-range copy keeps the selected characters' font family, size and color when pasted on the canvas after leaving text editing, in the same document or another. On desktop the clipboard offers plain text to text consumers and rich art for canvas Paste; Paste Without Formatting while editing uses the destination text's style. `tests::clipboard::type_range_copy_stays_styled_when_pasted_as_art` (#5636) |
| Character size and horizontal scale after object/group scaling | done | #1034: fields and setters compensate the stored affine; point size follows the transformed em-height axis, not the glyph ink bounds. Range edits, keyboard increments, leading and baseline shift use document points; rotation, reflection and shear remain editable. |
| Preserve font sizes while scaling a group | missing | Separate enhancement; scaling still scales text with the other artwork. |
| OpenType panel, Glyphs panel, alternates | done | Highlight Alternate Glyphs preference (#394, off by default) paints pink behind OpenType substitutions on screen when on (required shaping such as Arabic joining forms and the lam-alef ligature, including a lam-alef older fonts file under `liga`, is not highlighted; an optional feature that changes the ligature is); stylistic sets ss01–ss20, stylistic and titling alternates, proportional and lining figures, superscript and subscript are character-style features (`text.setStyle {features}`, #1607); the OpenType panel's Position menu sets superscript, subscript, numerator or denominator glyphs (`sups`, `subs`, `numr`, `dnom`). The OpenType panel's Stylistic Sets menu lists the selected font's `ss01`–`ss20` features, using a font-supplied name where available and `Set N` otherwise; several sets can be active, and selecting an active one removes it. For a mixed-font selection it uses the first selected character's font, a VectorCraft design choice ([public help](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/add-stylistic-sets-to-selected-text.html), [OpenType feature specification](https://learn.microsoft.com/en-us/typography/opentype/spec/features_pt)). The Glyphs panel highlights the character selected with the Type tool (a range's first character, else the one before the caret) and scrolls it into view as the selection moves (#4908; `panels::glyphs` tests); its Show menu lists Unicode ranges and Alternates for Current Selection, the selected character's alternate glyphs from the font's `GSUB` table, one of which a double-click draws for that character (`text.setGlyphAlternate`); the glyph-cell pop-up of alternates is not there yet |
| Font menu: samples, live preview, filters, favourites, recent families; font matching by any name; Find Font | done | #3515: recently applied families appear first, once each; missing families are omitted and the existing Number of Recent Fonts setting applies; #339; script filter: Japanese, Latin, Thai; 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); Select Text by Font takes a scope: the document, chosen artboards or chosen layers; Find Font's Find In offers Document, Artboard (the active one) and Layers (the selected ones, else the current one) (#5529, a VectorCraft addition); Replace With Font lists family and style rows, one click setting both, with a search over them; its list scrolls on its own, apart from the document's font list (#4972). |
| OpenType panel, Glyphs panel, alternates | done | Highlight Alternate Glyphs preference (#394, off by default) paints pink behind OpenType substitutions on screen when on (required shaping such as Arabic joining forms and the lam-alef ligature, including a lam-alef older fonts file under `liga`, is not highlighted; an optional feature that changes the ligature is); stylistic sets ss01–ss20, stylistic and titling alternates, proportional and lining figures, superscript and subscript are character-style features (`text.setStyle {features}`, #1607); the OpenType panel's Position menu sets superscript, subscript, numerator or denominator glyphs (`sups`, `subs`, `numr`, `dnom`). The OpenType panel's Stylistic Sets menu lists the selected font's `ss01`–`ss20` features, using a font-supplied name where available and `Set N` otherwise; several sets can be active, and selecting an active one removes it. For a mixed-font selection it uses the first selected character's font, a VectorCraft design choice ([public help](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/add-stylistic-sets-to-selected-text.html), [OpenType feature specification](https://learn.microsoft.com/en-us/typography/opentype/spec/features_pt)). The Glyphs panel highlights the character selected with the Type tool (a range's first character, else the one before the caret) and scrolls it into view as the selection moves (#4908; `panels::glyphs` tests); its Show menu lists Unicode ranges and Alternates for Current Selection, the selected character's alternate glyphs from the font's `GSUB` table. A double-click applies the OpenType feature that draws a variant to that character alone, or uses a glyph choice when no supported feature draws it; see the Glyphs panel row below for Entire Font and the joining limits. The glyph-cell pop-up of alternates is not there yet |
| Glyphs panel: alternates as OpenType feature values, unencoded glyphs | done | #5514, as [Replace characters with alternate glyphs](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/replace-characters-with-alternate-glyphs.html) describes (Show › Alternates For Current Selection; replace the selected character by double-clicking the glyph). A character-style feature takes a value (`"salt=2"`, the font's alternate number; `ss01`–`ss20`, `swsh`, `titl` and `aalt` too) in `features` and `featureChanges`, in SVG `font-feature-settings` (`"salt" 2`) and in native files as the plain string. Show › Entire Font applies an unencoded glyph the same way when it is an alternate of the selected character in its word, applies one no feature reaches (`A.salt` in a font with no `salt`) to the selected character as a glyph choice (it may not join), and dims the others with the reason in the tooltip (e.g. `yehfarsi.fina.alt2 belongs to ی (U+06CC) at the end of a word; your selection is ي (U+064A)`); an Arabic presentation form picked there inserts its letter so the word joins. Runs that differ only in their features shape as one piece of text, each run's features over its own characters, so a variant on one letter of an Arabic word keeps its neighbours' initial, medial and final forms and the letters still touch. The panel lists the alternates of the glyph the shaper makes for the selected character in its word (a medial letter shows medial variants), and a double-click applies the OpenType feature that draws the variant (`salt=N` or `ssNN`) to that character alone, in one undo step (`text.setRangeStyle`); a variant no feature reaches falls back to a replacement glyph (`text.setGlyphAlternate`), which its tooltip says may not join. Show › Entire Font also lists the glyphs no character maps, with their name or `gid N` and the feature that gives them. The text crate reads each face's `GSUB` once (alternate sets, single substitutions, the feature that reaches a glyph). Not done: alternates reached through contextual or ligature lookups, and features of the font that the style has no tag for (`cv01`–`cv99`, `hist`). Evidence: `tests::glyph_alternates` in the text crate (a fixture font with a medial teh at `salt=1` to `3`, the neighbours' forms, no gap; Kufi Murabba PIIIX when it is installed), `features::tests::feature_values_are_parsed_validated_and_shaped`, `tests::textedit::feature_values_are_set_replaced_and_saved`, the `panels::glyphs` tests. |
| Font menu: samples, live preview, filters, favourites, recent families; font matching by any name; Find Font | done | #3515: recently applied families appear first, once each; missing families are omitted and the existing Number of Recent Fonts setting applies; #339; script filter: Japanese, Latin, Thai; 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); Select Text by Font takes a scope: the document, chosen artboards or chosen layers; Find Font's Find In offers Document, Artboard (the active one) and Layers (the selected ones, else the current one) (#5529, a VectorCraft addition); Replace With Font lists family and style rows, one click setting both, with a search over them; its list scrolls on its own, apart from the document's font list (#4972); wherever the font menu opens, its filters keep the same four rows, and it opens at least wide enough for them and for a family name up to 220 points wide with its sample (up to 60% of the window), each sample filling its row's height (a VectorCraft layout). |
| Missing glyphs set in an installed font made for their script (Thai, Lao, Khmer, Myanmar first; then any font that has them) | done | Thai: Leelawadee UI, Tahoma, Thonburi, Sukhumvit Set, Noto Sans Thai, TLWG |
| Full font-name matching (OpenType name ID 4) | done | #1258: loaded/cataloged faces and Missing Fonts searches; localized names and variable default faces; Windows font-service QA pending |
| Variable fonts | done | named instances as styles; Character panel › Variable Font sets each axis (`fontAxes`); the font menu's Font Technology › Variable lists only variable font families, with the script, kind and ★ filters ([Find, apply, and adjust variable fonts](https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/find-apply-and-adjust-variable-fonts.html)); a family with a variable file beside static ones counts as variable. Evidence: `font_menu::tests::the_variable_filter_lists_only_variable_families`, `tests_axes::a_family_with_variation_axes_is_variable`, `tests_sysfonts::a_family_with_a_variable_file_beside_static_ones_is_variable` |
@@ -81,6 +84,10 @@ Character Styles menu New/Redefine and footer New capture the first selected cha
| Date | Change | Summary |
|---|---|---|
| 2026-11-12 | minor | Glyphs review (#6084): validate contextual glyph choices against their supplying face, including fallback and overflow; keep feature-only boundaries inside ligatures and Arabic mark clusters while retaining drawing-attribute splits; preserve insertion failures instead of reporting normalization success. Deterministic engine, shaping and panel regression sources added; ligature range assertions use the fixture's unencoded glyph identity. Runtime proof pending. Scores and estimates unchanged. |
| 2026-11-12 | minor | Glyphs review (#6084): Entire Font resolves the selected font's version and variable axes; the default cell remains available to reset an unlisted choice with no listed alternates. Character insertion normalizes only Arabic presentation forms, preserving ordinary characters whose glyphs share Arabic substitution provenance. Add deterministic panel apply/reset/Undo regressions without OS-font dependencies. Scores and estimates unchanged. |
| 2026-11-09 | minor | Glyphs review (#6084): the engine accepts a positional alternate as a glyph choice when no supported feature draws it, not only an isolated character's alternates; both panel views exercise apply and Undo. OpenType style options preserve only a valid last on-value when enabling a stylistic set. Scores and estimates unchanged. |
| 2026-11-07 | minor | Glyphs panel (#5514): Alternates for Current Selection lists the alternates of the selected character's glyph in its word and a double-click applies the OpenType feature that draws one to that letter alone (`salt=2`; `features` and `featureChanges` take `tag=N`), so an Arabic word stays joined, and Entire Font lists the unencoded glyphs, applies the ones that are the selected letter's alternates, or that no feature reaches and the font's names do not give to another letter, and dims, with the reason, those that are not. Runs that differ only in their features shape as one piece of text. Scores and estimates unchanged. |
| 2026-11-11 | minor | Kerning at the start of a line (#5111): at the Type tool's caret before the first character of the text, of a paragraph or after a forced line break, the Kerning field, Alt+arrows and Reset Tracking and Kerning set the space before the character after the caret (`CharStyle::kerning_before`, `kerningBefore` in `text.setRangeStyle`), which moves it and the rest of its line, in horizontal, right-to-left and vertical type and in SVG export. They did nothing at the text's start and kerned the invisible line break at a paragraph's. Scores and estimates unchanged. |
| 2026-11-11 | minor | Type in envelopes: its strokes (its characters' and those on its own appearance) paint along the bent glyph outlines at their own weight, Distort Appearance on or off, as the drawn art of users' `.ai` files strokes them; with Distort Appearance they bent with the art as their filled outlines. The same holds in an envelope inside an envelope. Geometry effects on type's fills and strokes, and an opacity mask on type, still bend; Expand gives the bent outlines with the strokes on them. The visual bounds of envelopes, blends and repeats count type's character strokes as type's own visual bounds do. This changes how VectorCraft's own envelopes of stroked type draw (#6644). Score unchanged (estimated) |
| 2026-11-11 | minor | Aligned lines of tracked type: a centred or right-aligned line is as wide as its characters without the tracking after its last one (`LineInfo::trailing`), so a centred line sits half that tracking right of where it did and a right-aligned line's last glyph ends on its anchor or frame edge, the tracking after it reaching past. The page of users' `.ai` files (used locally, never committed) aligns tracked lines so: matched glyph by glyph with the page, 176 centred lines of point type in 51 files sat half their last character's tracking left of it (median 0.500 of it; 143 within 0.05 pt), 58 right-aligned lines in 5 files the whole tracking (median 1.001), and 182 left-aligned lines where the page has them; in a line whose last character is tracked 150 and the others −34, the offset is half the last character's tracking (0.498 of it). All those lines end a paragraph. The last character is the last the line draws before a paragraph's end, a forced line break or the spaces a wrap leaves (they hang past the line). Spaces or a tab typed before a paragraph's end or a forced line break are the line's last characters, and the letter before them keeps its tracking: in the 2 such lines in the files, the page counts the space and the letter's tracking and leaves out only the space's own (VectorCraft still leaves trailing spaces out of the width). Forced line breaks and wrapped lines of tracked type aren't in the files: they take the rule unverified, so that Convert To Point Type and Artwork Cleanup's Break Text, which turn wraps into paragraphs and forced line breaks, keep the lines where they were. A hyphenated line, one whose last character hangs, justified, vertical and right-to-left lines and type on a path keep the tracking, and so do right, centre and decimal tab stops, which still align the text after them with its last character's tracking. Manual kerning on the last character, line breaking and justification are unchanged (none in the files). The tracking stays in the last glyph's advance (the caret after it and a selection of it take it in), and Fit Headline still counts it, as line breaking does. Right-aligned area type sets the text after its last tab the same way. Convert To Area Type makes the frame end where the lines are aligned, with room for that tracking, so tracked type converted either way stays where it is (it moved by the tracking on each round trip). Affinity import aligns artistic text by the same rule, so centred or right-aligned tracked artistic text moves by half or all of its last character's tracking (unverified against Affinity). Centred and right-aligned tracked type lays out differently than before in every document. Tests `tests::an_aligned_line_leaves_its_last_characters_tracking_out_of_its_width`, `tests::which_lines_leave_the_tracking_after_their_last_character_out`, `tests::an_aligned_line_leaves_out_its_last_tracking_as_glyph_scaling_sets_it`, `tests::the_text_after_the_last_tab_of_right_aligned_type_leaves_its_last_tracking_out`, and the engine's `converting_tracked_type_between_point_and_area_keeps_it_in_place` and `break_tracked_area_type_keeps_its_aligned_lines_in_place`. Scores and estimates unchanged. (#6610) |
+8 -4
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (Remove Background in the Contextual Task Bar, Properties › Quick Actions and the Control bar; previously minor: Ruler origin: the corner drag snaps as a drawn point does; previously minor: Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: Outline view: images and gradient meshes are picked on what Outline draws, so a click inside them reaches the path behind, #6437; previously minor: a middle-click on a document tab closes it, #6506; previously minor: Blob Brush: a round tip's preview is the outline of the area it adds, round at both ends, as the stroke it commits (#4343); scores and estimates unchanged; previously:; ruler double-click adds a guide at the pointer, #5239; G5: Inventory Icons built-in symbol library (four built-ins); previously minor: files that record no units open in Preferences › Units › General, #5820; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; numeric fields step on wheel hover without taking keyboard focus, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Selection QoL: marquee modes and visible feedback, #5313; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Layers: Rename (`ui.rename`) opens the name field of the selected layer, group or object, #5324; Properties in Edit Artboards: Transform section above the Artboards section, #5219; scrubby labels: the Control bar's Stroke: and Opacity: links and the Properties panel's Stroke link scrub their fields, #4088; Properties: Quick Actions has Repeat Options for one selected repeat, #5181; Direct Selection: Shift-click picks several blend spine points, which drag together, #5285; Direct Selection: Cmd/Ctrl held lends the Selection tool and shows its bounding box, #3901; Join tool trims two crossing open paths and joins them at the crossing, #5844; Control bar: Paragraph: link and Align left/center/right while type is selected, #5372; shape tools: a press on the selection's centre point moves it and one in the rotate zone outside a box corner turns it, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-13 · **Change:** minor (icon-strip flyouts also resize from the top edge and the canvas-side top corner, keeping the moved top in workspaces (storytold/craft-libs#1094); scores and estimates unchanged; previously: macOS: the canvas cursor stays after the pointer moves, #6362; scores and estimates unchanged; previously minor: Remove Background in the Contextual Task Bar, Properties › Quick Actions and the Control bar; previously minor: Ruler origin: the corner drag snaps as a drawn point does; previously minor: Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: Outline view: images and gradient meshes are picked on what Outline draws, so a click inside them reaches the path behind, #6437; previously minor: a middle-click on a document tab closes it, #6506; previously minor: Blob Brush: a round tip's preview is the outline of the area it adds, round at both ends, as the stroke it commits (#4343); scores and estimates unchanged; previously:; ruler double-click adds a guide at the pointer, #5239; G5: Inventory Icons built-in symbol library (four built-ins); previously minor: files that record no units open in Preferences › Units › General, #5820; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; numeric fields step on wheel hover without taking keyboard focus, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Selection QoL: marquee modes and visible feedback, #5313; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Layers: Rename (`ui.rename`) opens the name field of the selected layer, group or object, #5324; Properties in Edit Artboards: Transform section above the Artboards section, #5219; scrubby labels: the Control bar's Stroke: and Opacity: links and the Properties panel's Stroke link scrub their fields, #4088; Properties: Quick Actions has Repeat Options for one selected repeat, #5181; Direct Selection: Shift-click picks several blend spine points, which drag together, #5285; Direct Selection: Cmd/Ctrl held lends the Selection tool and shows its bounding box, #3901; Join tool trims two crossing open paths and joins them at the crossing, #5844; Control bar: Paragraph: link and Align left/center/right while type is selected, #5372; shape tools: a press on the selection's centre point moves it and one in the rotate zone outside a box corner turns it, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
>
> Vector Halftone preview update (2026-11-10): temporary dot selection marks are hidden, and the bounding box stays available (#6369).
>
@@ -15,7 +15,7 @@ Numeric fields step on wheel hover without taking keyboard focus (#5949). Arrow
| 2026-11-06 | minor | Opening a file that takes longer than 0.4 s shows a progress window: the file name, a moving bar (reading reports no steps), how many more files wait, Cancel (Esc; the file doesn't open) and Continue in Background (`file.cancelOpen`, `file.openInBackground`); it never opens over another dialog or progress window (part of #5930); scores and estimates unchanged |
Raster rendering fits and preflattens curved stroke outlines more tightly only below four times the raster tolerance, retaining thin-band coverage without changing stroke weight or vector export geometry. Current cached character/object outlines, cropped long strokes, gradient clips and effect rasters share the helper; cropped-region reuse and guarded flattening remain. The unchanged `fractional_curved_strokes_retain_coverage_between_notches` regression covers fractional weights, rotation, phase, opacity and f32-rounded endpoints. This is a quality/cost tradeoff, not a speed claim. Scores and estimates unchanged.
Icon-strip flyouts resize from the canvas-side edge and bottom, keeping the icon-column edge anchored (#5713). Independent group heights and flyout widths persist in workspaces; saved heights override content fitting while untouched heights still fit content. Shared panel button bars and the Glyphs font row stay at the bottom after enlargement; the Artboards list grows into the extra space. Regressions: `panel_docking::tests::glyphs_and_artboards_flyouts_keep_user_sizes` and `expanded_panel_footers_stay_at_the_bottom_with_short_and_long_lists`. Scores and estimates unchanged.
Icon-strip flyouts resize from the canvas-side edge, the top, the bottom and the corners on the canvas side, keeping the icon-column edge anchored (#5713, storytold/craft-libs#1094); a top-edge resize keeps the bottom and saves the moved top per group in workspaces. Independent group heights and flyout widths persist in workspaces; saved heights override content fitting while untouched heights still fit content. Shared panel button bars and the Glyphs font row stay at the bottom after enlargement; the Artboards list grows into the extra space. Regressions: `panel_docking::tests::glyphs_and_artboards_flyouts_keep_user_sizes` and `expanded_panel_footers_stay_at_the_bottom_with_short_and_long_lists`. Scores and estimates unchanged.
**PR #3273 revision — 2026-11-04:** Thin curved stroke coverage ported to the current raster-only cache, cropped-long-stroke and effect paths with guarded flattening; vector export geometry unchanged. Scores and estimates unchanged.
@@ -62,6 +62,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard
| 2026-11-06 | minor | Shape tools: a press on the selection's centre point (Show Center) moves the selection, also where no art is painted, and a press in the rotate zone just outside a bounding-box corner turns it about the centre, both as the Selection tool's do (#5477); the Selection tool takes its centre point too, so an unfilled shape moves instead of starting a marquee; scores and estimates unchanged |
| 2026-11-06 | minor | Transform fields: one Tab moves from one numeric field to the next in the Transform panel, the Properties panel's Transform section and the Control bar — a field's scrub label was a second, invisible tab stop, and the W/H link between W and H is now made after the fields, so it stays a tab stop after them; scores and estimates unchanged |
| 2026-11-06 | minor | Scrubby labels: the Control bar's Stroke: and Opacity: and the Properties panel's Stroke, links to their panels, scrub the field after them as other labels do; a click still opens the panel (#4088); scores and estimates unchanged |
| 2026-11-12 | minor | Icon-strip flyouts also resize from the top edge and the canvas-side top corner; the moved top is kept per group in workspaces (storytold/craft-libs#1095). Scores and estimates unchanged. |
| 2026-11-06 | minor | Icon-strip flyouts resize on their free edges and retain sizes with workspaces (#5713); saved heights beat auto-fit; panel footers stay at the bottom after enlargement. Scores and estimates unchanged. |
| 2026-11-05 | minor | Direct Selection: Shift-click picks several points of a blend's spine and dragging one moves them all, with the key objects on them, in one undo step (#5285); scores and estimates unchanged |
| 2026-11-05 | minor | Direct Selection: Cmd/Ctrl held lends the Selection tool, whose bounding box shows while the key is down over objects selected whole; its handles scale and rotate, and a Cmd drag moves the whole object with its direct-selected anchors kept (#3901); scores and estimates unchanged |
@@ -163,6 +164,7 @@ tools, never checked side by side) isn't captured row by row.
| Each anchor snaps to Smart Guides and construction angles | done | `DrawSnap` |
| Curvature tool: click/drag points, double-click or Alt-click for a corner, temporary Direct Selection, point conversion | partial | circular point markers without direction handles or duplicate markers, Anchor Size and Hide Edges regression-tested (#1236, in part); curve geometry: each stretch of three points follows the circle through them (handles along its tangent, chord/(3·cos²(α/2)) long, shorter in a turn tighter than 30°), open ends curving in, the same rule for the rubber band, dragged, inserted and converted points (#1236, in part); neighbours of a point dragged on an existing path keep their tangents, and modifiers requested in #975 remain |
| Icon-strip flyout resizing (#5713) | done | Canvas-side edge and bottom; user size persists after reopening and workspace restore; Glyphs and Artboards pointer/cursor regression |
| Icon-strip flyout resizing from the top (storytold/craft-libs#1094) | done | Top edge (a 4 pt band over the tab strip) and the canvas-side top corner; the bottom and the icon-column edge stay; the moved top persists after reopening and workspace restore; `an_icon_flyout_grows_upward_from_its_top_edge_and_free_corner` |
| Layers panel lists a blend's spine (#4820) | done | an open blend shows a `<Path>` row below its key objects, where Release leaves the spine; a click selects the blend (Shift/Cmd toggles it) so Direct Selection edits the spine; a locked blend's row selects nothing; no row while searching or filtering (`a_blends_spine_is_a_row_below_its_keys_that_selects_the_blend`) |
| Layers panel: a layer whose Layer Options › Print is off shows its name italic (#5739) | done | the panel row and its drag ghost; a printing layer stays upright, and Print on again restores it; `panels::layers` test `a_layer_that_does_not_print_has_its_name_italic` |
| Blend spine Anchor Point conversion and handle editing (#2544) | implemented | symmetric handles, independent drag/retraction, visible-handle policy and selection/stacking covered by tool and engine regressions; Windows native canvas checks and screenshots reviewed in PR #2643 |
@@ -332,7 +334,7 @@ tools, never checked side by side) isn't captured row by row.
| Tool double-click opens its options | done | |
| Per-platform mapping (Cmd ↔ Ctrl, Option ↔ Alt) | done | |
| Eyedropper cursor; Use Precise Cursors | partial | original pipette at the sampling tip, distinct from the precise crosshair at five DPI scales (#1269); OS cursor display unverified; while Caps Lock is on, the tools that Use Precise Cursors changes show the crosshair, and the saved preference does not change (the desktop app reads the lock state on macOS, Windows and Linux X11, not on native Wayland; `view.capsLock` reads or sets it); a Use Precise Cursors command toggles the preference and can take a shortcut (#1663); cursors are painted into the window on Wayland (#1652, not run on Linux) |
| macOS canvas cursor delivery (#2292) | partial | The UI prepares the existing glyphs as aligned 1×/2× RGBA requests; the desktop host builds AppKit cursors and owns cursor rectangles for the root document window. Detached document viewports and requests without a usable native bitmap retain the visible system fallback; precise-cursor and UI-scale preferences retained. Headless regressions cover routing, pixels/hotspots and lifecycle; native focus/menu/handback timing, mixed displays, Spotlight and full-screen Spaces remain unverified. Wayland still paints its glyphs. |
| macOS canvas cursor delivery (#2292) | partial | The UI prepares the existing glyphs as aligned 1×/2× RGBA requests; the desktop host builds AppKit cursors and owns cursor rectangles for the root document window. While it owns the cursor, the host sets it again on every pass, so the tool's cursor stays when the pointer moves (#6362). Detached document viewports and requests without a usable native bitmap retain the visible system fallback; precise-cursor and UI-scale preferences retained. Headless regressions cover routing, pixels/hotspots and lifecycle; native focus/menu/handback timing, mixed displays, Spotlight and full-screen Spaces remain unverified. Wayland still paints its glyphs. |
| Every tool's modifiers checked against the shortcut reference | missing | this is the fidelity pass |
## Keyboard shortcuts
@@ -374,7 +376,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
|---|---|---|
| Effect > Warp Options: style previews, orientation choices and percent controls | done | #1760; focused input, mixed-style and history checks passed; original captures reviewed. No geometry or optional Properties changes. |
| Command search: Up and Down move through the results, Enter runs the highlighted one | done | #1798 (#1757) |
| Learn panel: Window › Learn and Explore tutorials on the app bar open it; For you holds a Quick skill guide card, Skills lists VectorCraft's own skills with tutorial counts and empty progress rings, a tutorial shows its numbered steps, Back goes up a level | done | [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `learn.list`, `learn.show`; `panels::learn::tests` |
| Learn panel: Window › Learn and Explore tutorials on the app bar open it; For you holds a Quick skill guide card, Skills lists VectorCraft's own skills with tutorial counts and empty progress rings, a tutorial shows its numbered steps, Back goes up a level; a tool's name in a step is a link that marks the tool where the toolbars show it (its button, its group's flyout, a torn-off strip, a toolbar made with New Toolbar or the All Tools drawer) without choosing it | done | [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `learn.list`, `learn.show`, `learn.locateTool`; the mark's look and 3-second lifetime are VectorCraft's own rules; `panels::learn::tests`, `toolbar::tests` |
| Pathfinder panel: a group selected alone counts its nested shapes, so an expanded stroked object keeps the Shape Modes enabled | done | #1642 (#1635) |
| Selected Grid Repeat shows row, column and corner handles and spacing sliders; bounding-box handles take priority where they meet | done | #2691, `tools/src/repeat_grid.rs`; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html); drags preview and commit `object.repeat.options` as one undo step, Escape cancels; Radial Repeat has no handles yet |
| Grid Repeat Options (Object › Repeat › Options…) has a Preview checkbox: the repeat on the canvas follows the fields while the dialog is open, Cancel puts the original back without a history step, OK keeps the previewed values as one undo step | done | #2651; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html) lists the Preview option for the grid options only, so Radial and Mirror Repeat Options have none; `dialogs/command.rs` tests |
@@ -410,6 +412,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Select › Start Global Edit / Stop Global Edit; Properties › Quick Actions with the Global Edit Options popover (Match: Appearance, Size; On Artboards: All Artboards or a range, Include Objects On Canvas; Result(s)); Escape stops it; the matched objects are highlighted in their layer colour while it runs; a Shift-click on a highlighted match leaves it out | partial | #3521, `select.globalEdit` / `select.globalEditOptions` / `select.globalEditExclude`, `crates/engine/src/tests/globaledit.rs`; paths, compound paths, symbol instances and groups of them (similar groups, #4939: edits inside one group reach the same object in the others; [public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-groups-with-similar-objects.html)); Shift-click to leave a match out ([public source](https://helpx.adobe.com/ph_fil/illustrator/how-to/global-editing-sensei.html)); the Control bar's Global Edit Options dropdown opens the same options; rotated copies and highlighting matches before the start remain ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-similar-objects.html)) |
| Contextual task bar | done | |
| Control bar with Transform and Stroke popovers | done | |
| Stroke panel: custom arrowheads after the built-in ones in both arrowhead menus, from the arrowheads file that the panel menu's Edit Arrowheads… opens; saving the file reloads the menus, and a stroke keeps a copy of its custom head's art | done | [Apply stroke on an object](https://helpx.adobe.com/illustrator/using/stroke-object.html) (Customize arrowheads); the file's layout (one symbol per head, pointing right, tip at the registration point, height drawn as wide as the built-in heads) is VectorCraft's own; `stroke.arrowheads.edit`, `stroke.arrowheads.list`, `stroke.arrowheads.reload`, `stroke.set` `{custom: name}`; engine `tests::arrowheads`, `stroke::tests_arrows::custom_heads_draw_their_own_art_at_the_tip` |
| Control bar: Paragraph: link and Align left/center/right while type is selected (#5372) | done | After the Character controls, as the public documentation has the Control panel format paragraphs ([Paragraph panel overview](https://helpx.adobe.com/illustrator/using/formatting-paragraphs.html), [Align text](https://helpx.adobe.com/illustrator/using/align-text.html)); the link opens the Paragraph panel in a popover, as Character: does with the Character panel. The bar trims from its right end: X/Y/W/H, Distribute, Align, area type vertical alignment, then these buttons, then the link. The three-button set is our choice for room; all seven alignments are in the popover. `panels/paragraph/tests.rs` headless tests (`the_control_bar_shows_the_paragraph_alignment_of_the_selected_type`, `the_control_bar_trims_its_paragraph_section_after_the_controls_to_its_right`) |
| Pathfinder shape results keep their source names and SVG IDs | done | #1293; Minus Front keeps the back name, Minus Back the front name; Unite/Intersect/Exclude use the backmost named input; compound output carries it once; geometry and paint unchanged |
| Attributes fill-rule controls with a compound member selected | done | Edits the compound's rule; direction still edits only the member; rendered-hole and Undo/Redo regression tests (#1197) |
@@ -526,6 +529,7 @@ before release (#6373). [Paint faces and edges](https://helpx.adobe.com/illustra
| 2026-11-12 | minor | Live Paint Bucket fills faces crossed in one drag (#6373). Scores and estimates unchanged. |
| 2026-11-12 | minor | Save refusal follow-up (#6146): changed format opens its Save options; unchanged format retains original options. Focused UI regressions passed, including the format-change case that failed before the fix; native panel acceptance pending. Scores and estimates unchanged. |
| 2026-11-11 | minor | Ruler origin: the corner drag snaps as a drawn point does, onto the art's and the artboards' anchors, centres, edges and paths, and with Snap to Point along the ruler guides; scores and estimates unchanged |
| 2026-11-11 | minor | macOS canvas cursor delivery (#2292): while the host owns the cursor it sets it again on every pass, so the tool's cursor stays after the pointer moves (#6362); `mac_cursor` ownership test updated; scores and estimates unchanged. |
| 2026-11-11 | minor | Eyedropper copies effective character dimensions between transformed text objects (#6404); scores and estimates unchanged. |
| 2026-11-11 | minor | Ordinary Save to a location refusing writes offers Save As (#6146); cancellation preserves the original path and modified state; automation receives READ_ONLY_LOCATION. Scores and estimates unchanged. Validation pending. |
| 2026-11-11 | minor | Outline view: a click on a path's outline selects it through an image or a gradient mesh in front, which Outline draws as a frame or mesh lines (#6437). |