Merge main into fix/trace-blur-rings

# Conflicts:
#	docs/image-trace-quality.md
This commit is contained in:
dotCooCoo
2026-11-13 10:06:37 -08:00
359 changed files with 26656 additions and 2546 deletions
+4 -1
View File
@@ -417,7 +417,10 @@ button (Add Tool…) opens a menu of every tool under the drawer's headings: a c
{tool, toolbar}`; the tools already in the toolbar are dimmed under their headings. The drawer has no Show section
there. `window.floatTools {tool, toolbar}` floats one of its groups, and a group torn off it shows and hides with
it. `window.toolbar.list` returns the toolbars (`ui.custom_toolbars` in `ui.inspect`: name, slots, shown flag,
two-column flag, top-left corner, the groups torn off it). They are kept with the workspace they were made in:
two-column flag, top-left corner, the groups torn off it, and `dock` and `dock_right` while docked or once docked).
`window.toolbar.dock {name, side?, order?}` docks a toolbar in the lane along the window's left or right edge, and
`window.toolbar.float {name, x?, y?}` floats it again; dropping its title bar on an edge or dragging it out of its
lane does the same (see the docking section in `docs/mcp.md`). They are kept with the workspace they were made in:
switching workspaces shows that workspace's toolbars, Reset Workspace keeps them, and New Workspace starts the new
workspace with a copy of them.
+1 -1
View File
@@ -126,7 +126,7 @@ On GNOME/Wayland, enable **Preferences › User Interface › System Title Bar**
The window runs natively on Wayland (eframe's `wayland` feature) and on X11. On machines with two GPUs it renders on the one the desktop runs on unless Preferences say otherwise; see [Desktop graphics processor](#desktop-graphics-processor) (#502). The system clipboard (`apps/vectorcraft/src/clipboard.rs`) is arboard with its `wayland-data-control` feature:
- **Clipboard:** under Wayland arboard talks to the compositor through the data-control protocol (wlroots compositors such as Sway and Hyprland, KDE Plasma), so bitmaps (`image/png`), SVG markup and text copied in any app paste (#398). Where the compositor lacks the protocol (GNOME's Mutter, [arboard#223](https://github.com/1Password/arboard/issues/223)), arboard falls back to the X11 clipboard through XWayland, which only holds what X11 apps copied. Where there is no X11 either (the Flatpak on a Wayland session: `--socket=fallback-x11` gives it none), Copy and Paste use the compositor's core data-device protocol on the window's own connection through smithay-clipboard: text only (a copy's text or its SVG markup), and only while the window has focus. smithay-clipboard waits for the owner's data with no limit, so its reads run on a worker thread and the UI thread waits 2 s at most: an owner that never sends its data costs one read 2 s, and later reads return at once until it answers. When a read gets no answer in its 2 s, such as a paste's check whether the clipboard still holds what VectorCraft copied, the paste waits on its background thread for that read's answer, up to 10 s after the read was sent, and pastes the text unless it is empty or what VectorCraft copied. A clipboard neither backend opens is tried again after 1 s, doubling to a minute (a Copy or Paste of the user's at most once a second), and logged once when it goes and once when it comes back: before, the background Paste check reconnected and logged a warning every 250 ms. egui's own text paste into fields goes through smithay-clipboard and works either way.
- **Clipboard owners that never answer (#510):** reading the clipboard waits on the app that owns it: arboard gives up on an X11 owner after 4 s for each format it tries, and a Wayland read waits for the owner to send its data. So on Linux the check that enables the Paste menu items runs on a background thread with a second clipboard handle (`crates/ui-egui/src/clipboard_probe.rs`, installed as `Services::clipboard_probe`), only while nothing is copied in the app and a document is open, and the window keeps drawing. Every arboard read on Linux and the BSDs, including the UI thread's (a paste, and a Copy or Cut checking what the clipboard held), runs on a worker thread with a handle of its own and waits 2 s at most: an owner that never sends its data costs one read that wait, and later reads fail at once until it answers. A paste's bitmap read waits up to 10 s on the paste's background thread, so the UI thread does not wait for it. When one of a paste's reads gets no answer in its 2 s, or a paste follows a read that has not been answered yet, such as the paste's own check whether the clipboard still holds what VectorCraft copied, the paste waits on that thread for that read's answer, up to 10 s after the read was sent, and pastes what it holds. When the answer is what VectorCraft copied, holds nothing to paste, or does not come, the paste takes the internal clipboard. On X11, quitting within 25 s of a read that got no answer waits for that read to end, so the app's own handle is the last one and hands what it copied to the clipboard manager. Windows and macOS keep the check on the UI thread: Windows only asks which formats the clipboard holds, macOS asks the pasteboard server.
- **Clipboard owners that never answer (#510):** reading the clipboard waits on the app that owns it: arboard gives up on an X11 owner after 4 s for each format it tries, and a Wayland read waits for the owner to send its data. So on Linux the check that enables the Paste menu items runs on a background thread with a second clipboard handle (`crates/ui-egui/src/clipboard_probe.rs`, installed as `Services::clipboard_probe`), only while nothing is copied in the app and a document is open, and the window keeps drawing. Every arboard read on Linux and the BSDs, including the UI thread's (a paste, and a Copy or Cut checking what the clipboard held), runs on a worker thread with a handle of its own and waits 2 s at most: an owner that never sends its data costs one read that wait, and later reads fail at once until it answers. A paste's bitmap read waits up to 10 s on the paste's background thread, so the UI thread does not wait for it. When one of a paste's reads gets no answer in its 2 s, or a paste follows a read that has not been answered yet, such as the paste's own check whether the clipboard still holds what VectorCraft copied, the paste waits on that thread for that read's answer, up to 10 s after the read was sent, and pastes what it holds. When the answer is what VectorCraft copied, holds nothing to paste, or does not come, the paste takes the internal clipboard. On X11, quitting within 25 s of a read that got no answer waits for that read to end, so the app's own handle is the last one and hands what it copied to the clipboard manager when one runs. That includes a read of the Paste menu check's handle: quitting waits up to 27 s for the background thread to drop it before the app drops its own, and quits after that wait even when the handle has not been dropped. The app hides its main window before these waits (`hide_before_teardown` in `apps/vectorcraft/src/app.rs`), so an X11 window, an XWayland one included, closes at once; winit cannot hide a native Wayland window, which stays on screen until the waits end. Windows and macOS keep the check on the UI thread: Windows only asks which formats the clipboard holds, macOS asks the pasteboard server.
- **Copied files:** files copied in a file manager (`text/uri-list`; `CF_HDROP` on Windows, file URLs on macOS) paste as the first one that is art (SVG, PDF, EPS, EMF/WMF or a bitmap; a `.ai` or `.ait` file from the editing data it holds, else as the PDF or PostScript it holds), before the clipboard's other formats (which include the files' paths as text).
- **Dropping files on the window does not work under Wayland:** winit 0.30, which eframe 0.37 runs on, has no Wayland drag and drop ([winit#1881](https://github.com/rust-windowing/winit/issues/1881), added in winit 0.31; [egui#1563](https://github.com/emilk/egui/issues/1563)), so no `dropped_files` arrive. Until eframe moves to winit 0.31: copy the files in the file manager and paste them, use File › Place or Open, or open the window through XWayland, where drops work: set **Preferences › Performance › Linux Display Server** to **X11 (XWayland)** and restart (#1151), or start the app with `WAYLAND_DISPLAY= vectorcraft`. The preference (`linuxDisplayServer`, read at startup in `apps/vectorcraft/src/main.rs`, shown on Linux only) has winit open the window on X11 (`EventLoopBuilderExtX11::with_x11`) when the session is Wayland, `DISPLAY` is set and the X11 libraries winit loads are installed (`apps/vectorcraft/src/xwayland.rs` reads `ldconfig -p` and the usual library directories); otherwise the window opens on Wayland and the log says why. Unlike clearing `WAYLAND_DISPLAY`, it leaves the clipboard on the compositor's own.
- **Pens and pen displays under Wayland (#491, #2028):** winit 0.30 doesn't bind the Wayland tablet protocol (`zwp_tablet_v2`; tablet input arrives in winit 0.31, [winit#4318](https://github.com/rust-windowing/winit/pull/4318), [egui#7731](https://github.com/emilk/egui/pull/7731)). Since Plasma 6.3, KWin no longer turns tablet input into pointer input for such apps ([kwin!6336](https://invent.kde.org/plasma/kwin/-/merge_requests/6336), commit [25c65abd](https://invent.kde.org/plasma/kwin/-/commit/25c65abddd95eb87fb892a8594faccbf509ff62f)), and GNOME's Mutter does not turn it into pointer input either (that commit's message): on KWin the pen moves the compositor's cursor over the window, but the app gets no motion or presses and shows none of its own cursors. Until eframe moves to winit 0.31, the app runs under XWayland, which supports the tablet protocol and hands the pen to X11 apps as a pointer: Preferences › Performance › Linux Display Server's Automatic (the default) and X11 (XWayland) When a Pen Is Attached (`x11WithPen`) open the window through XWayland when `/proc/bus/input/devices` lists a pen as the app starts, a device with the `BTN_TOOL_PEN` key and the `ABS_X` and `ABS_Y` axes, and log `pen found: opening the window through XWayland`, as PhotoCraft does (#4967; `VECTORCRAFT_NATIVE_WAYLAND=1` keeps the window on Wayland). For a pen attached after the app started, either run the app under XWayland (Preferences › Performance › Linux Display Server › X11 (XWayland); `WAYLAND_DISPLAY= vectorcraft`, or `env -u WAYLAND_DISPLAY vectorcraft`; for a desktop launcher, put `env WAYLAND_DISPLAY=` before the command in its `Exec=` line), or turn KWin's deprecated emulation back on for the whole session by setting `KWIN_WAYLAND_EMULATE_TABLET=1` in KWin's environment (for example in a file under `~/.config/environment.d/` or in `/etc/environment`) and logging in again. The pen then works as a mouse. Under XWayland the app also reads the pen's pressure (next item); with KWin's emulation on native Wayland the pen has no pressure. Other compositors that don't emulate a pointer for tablets behave the same way.
+6 -1
View File
@@ -16,7 +16,12 @@ The **Dimension Tool** is in the Modify section of the toolbar. Its strip offers
before the release; a longer drag drags out the dimension of the segment under the pointer. A
curved segment is measured from end to end. With Smart Guides on, both points also snap into line with other objects'
anchors and centres, and the second onto the construction guides through the first; the guides
show while hovering, before each click.
show while hovering, before each click. VectorCraft's own rules decide where a point lands: an
anchor within 8 pixels takes it first; a point on a guide stays on the guide where the pointer
is, except that it moves onto a segment within the Smart Guides snapping tolerance, or onto the
place where a construction guide crosses another object's alignment line within twice that
tolerance; a point that no anchor, guide, grid or pixel snapping places lands on the closest
point of a segment within 8 pixels.
A linear dimension is aligned, horizontal or vertical, as the
[linear dimensions help](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/plot-linear-dimensions-of-objects.html)
describes: after the second click, clicking again places an aligned dimension, and moving the
+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, 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)); the Materials tab's Graphics, document symbols mapped onto Extrude & Bevel's caps and onto a plane, moved with an artboard handle ([three-d.md](three-d.md#graphics)); no base materials, graphics on curved surfaces, 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)); the Materials tab's Graphics, document symbols mapped onto Extrude & Bevel's caps and onto a plane, moved with an artboard handle, and wrapped around Revolve's side ([three-d.md](three-d.md#graphics)); no base materials, graphics on Inflate or on Extrude & Bevel's walls and bevels, 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
+4 -3
View File
@@ -541,7 +541,7 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi
Ease Out In), Ramp 0–100% and independent color Shift; easing is disabled without a spine.
The original polynomial distributions approximate the public description. With the Selection or
Blend tool, on-canvas widgets on a blend with a spine set the color Shift and, when the steps follow
that spine, the Ramp; a blend made without a spine shows neither widget. The Blend panel shows the
that spine, the Ramp; a blend made without a spine shows the color Shift widget alone. The Blend panel shows the
values they set. [Public source](https://helpx.adobe.com/illustrator/desktop/manage-colors/apply-transparency-and-blending/blend-panel-overview.html).
- **Done:** gradient Dither (30.0): the Gradient panel's Dither box (`paint.editGradient` `dither`, also in `paint.setFill`'s
@@ -684,8 +684,9 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi
- **Done:** custom document ruler origin (#1212): corner drag with preview, double-click reset to the active artboard, relative X/Y inputs and native save/reopen.
- **Done:** View › Rulers › Change to Global / Artboard Rulers (`view.globalRulers`, Cmd+Alt+R): artboard rulers, a new document's, count from the active artboard, each artboard with its own zero point; rulers, X/Y fields and guide readouts follow the mode, which is saved with the document. Estimate unchanged.
- **Done (#2849):** View › Rulers › Show Video Rulers (`view.videoRulers`): the rulers show and count the active artboard in video pixels from its top-left corner, a pixel as many points wide as the artboard's Video Ruler Pixel Aspect Ratio and one point tall ([public source](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/set-video-display-options-for-artboards.html)); Hide Video Rulers returns them to the General unit and the ruler origin. Shift-snapping a new guide to ruler ticks still uses the General unit's ticks. Estimate unchanged.
- **Partial:** custom toolbars (#1759): Window › Toolbars › New Toolbar makes them, kept with the workspace they
were made in, and Window › Toolbars › Manage Toolbars renames, copies and deletes them; docking them is missing.
- **Done:** custom toolbars (#1759): Window › Toolbars › New Toolbar makes them, kept with the workspace they
were made in, and Window › Toolbars › Manage Toolbars renames, copies and deletes them; they dock in lanes along
the window's left and right edges (`window.toolbar.dock`, `window.toolbar.float`).
Inside the estimate below.
- **Done (#1753):** a saved workspace keeps the width of the dock column.
- **Done (#4135):** floating panels open in OS windows of their own on the desktop, so they can sit on another monitor; on the web and on Wayland they float inside the window.
+41
View File
@@ -184,6 +184,47 @@ Tests on code-drawn images: `tests::a_blurred_edge_is_no_ring_of_a_tone_between_
blurred into a 10 px ramp, 16 Colors), `tests::grayscale_settles_a_blurred_edge_between_its_own_two_grays`,
`tests::grayscale_settles_a_tinted_blurred_edge_by_its_lightness`,
`tests::the_photo_presets_keep_the_tones_of_a_blurred_edge` and `quantize::cut_edge::tests`.
## Staircases in smooth areas
Where a band border in a gradient or a shaded area runs at a slant, its pixels form a staircase whose
steps are two or more pixels long. One piece of outline can run along a sharp edge in the image and then
along such a border with no junction between them. That piece as a whole is not a contour (see above),
and the outline kept a corner at every step. `subpixel::outline` now reads a run of three or more edges of
1 to 8 px (`STAIR_MAX`), each turning one way where it starts and the other where it ends, with no
junction on it, as a staircase when the run's own cracks pass `subpixel::low_contrast`. A run is cut at
any step whose own cracks show half of the step between the two colors or more. The 8 px limit and the
one-half cut are VectorCraft's own values. The run's edges are then steps, as one-pixel steps already
were: the outline has no corner where two of them meet, and each gives one point, its middle, moved by the
mean of its cracks' shifts. Where a run meets an edge that is not a step, the corner between them stays,
so a straight edge longer than 8 px next to a staircase stays straight. A straight edge of 8 px or less
between two turns of opposite sign is read as one more step of the run. Inside a run, two one-pixel steps
between longer ones are read as part of the slope and do not make a notch. A staircase of longer steps,
and one whose cracks read as an edge, keep their corners, and Black and White traces do not use the rule.
The runs are the same from either side of the border.
Measured with the harness below, before and after the change: rows of the "flat colour art" inputs do not
change. On gradient and photo inputs, anchors change in 106 rows, 101 of them fewer, by −64% to +6.4%: for
example 390 to 142 (Shades of Gray, linear gradients), 1,860 to 1,537 (High Fidelity Photo, linear
gradients), 6,766 to 6,542 (High Fidelity Photo, gradient and photo) and 47 to 50 (High Fidelity Photo with
Gradients, blurred). On gradient and photo, 6 Colors, 6 Colors with Shapes, with Auto Grouping, without
Gradients and enhanced each leave a gap of 0.9 px (was 1.2) and an overlap of 1.0 px (was 0.8). Edge ΔE
of Shades of Gray on linear gradients goes from 8.36 to 8.40 (8.39 enhanced), and that of 6 Colors with
Gradients on a blurred input from 2.24 to 2.22; other ΔE and edge ΔE values change by 0.01 or less.
Lines % changes by −2 to +3, grouped % by −1 to +4, upright % by +1 in 3 rows (Shades of Gray on a blurred
input), and specks do not change. On synthetic photos of 800 and 4,000 px traced outside this harness,
the corners of rectilinear staircases per 100 px of outline fall from 0.57 to 0.46 and from 1.09 to 0.94
with High Fidelity Photo. On the 800 px photo, the 95th percentile of the pixel difference with High
Fidelity Photo rises from 6.33 to 6.67, and 16 Colors leaves 2 staircases of steps of 2 px or more where
it left none. The tables below are the previous measurement, not a rerun.
Tests on code-drawn outlines in `subpixel::tests`:
`a_staircase_of_longer_steps_in_a_smooth_area_becomes_the_slope_through_its_steps` (a piece with a
staircase of 2 × 3 px steps in a smooth area, a straight run and the same staircase on a sharp edge: the
first gives its steps' middles and none of its corners, the straight run keeps the corners at its ends, the
second staircase keeps its corners, both regions get the same points, and 10 px steps keep their corners
in a smooth area), `a_staircase_that_runs_on_from_a_smooth_area_into_an_edge_keeps_the_edges_corners`,
`a_staircase_whose_cracks_do_not_read_as_a_contour_keeps_its_corners` (two flat colors),
`a_staircase_step_moves_by_the_mean_shift_of_its_cracks`,
`a_staircase_of_mixed_one_pixel_and_longer_steps_is_one_slope_without_a_notch`,
`a_run_of_two_steps_keeps_its_corners`, `a_junction_on_a_step_ends_the_staircase_there` and
`a_step_whose_cracks_read_as_an_edge_ends_the_staircase_there`.
## 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
+12
View File
@@ -25,6 +25,13 @@ Hindi integer counts use the first plural form for 0 and 1 and the second for 2+
Both `@plural` and `@msgplural` rows have two forms. The latter translate status messages when
displayed; the engine, control channel and MCP continue to return English messages.
In diagnostics, "a number of points" is a numeric value in the point unit: write संख्या and
पॉइंट, never कई अंक ("many digits"). Keep every bound and alternative the English lists, and
every clause such as "all" or "what follows it". Type as a text object is टाइप (एरिया टाइप,
पॉइंट टाइप), not प्रकार, which stays for a kind. `hindi_rows_use_the_glossary_term_for_what_their_english_names`
in `crates/ui-egui/src/i18n/tests.rs` checks the save, swatch, crop, resolution and path terms
and the point unit in every row.
## Terminology
| English | Hindi |
@@ -42,6 +49,7 @@ displayed; the engine, control channel and MCP continue to return English messag
| Fill / Stroke | फ़िल / स्ट्रोक |
| Gradient / Mesh / Blend | ग्रेडिएंट / मेश / ब्लेंड |
| Pattern / Swatch | पैटर्न / स्वॉच |
| Type (text object) / Crop / Resolution | टाइप / क्रॉप / रिज़ॉल्यूशन |
| Appearance / Graphic style | अपीयरेंस / ग्राफ़िक स्टाइल |
| Symbol / Envelope | सिंबल / एन्वेलप |
| Clipping mask / Opacity mask | क्लिपिंग मास्क / ओपेसिटी मास्क |
@@ -61,6 +69,10 @@ The draft has 7,877 unique rows, including 2,537 `@msg`, 45 `@msgplural` and 38
Its source set combines the sibling catalogs with the current test scanner's UI/undo and error
message dumps, so it includes runtime labels outside the literal scanner. Row count is not a
percentage of the application's entire interface.
On 2026-11-13 the catalog has 8,732 rows, including 3,003 `@msg`, 47 `@msgplural` and 41
`@plural` rows. A wording pass on that date corrected 193 rows outside the plug-in
messages for meaning, dropped clauses, English left in place and the save, swatch, crop,
resolution, path and type terms. It is not a native proofread.
The comparison covers release `v0.21.1` (`f6aa4309`) and current `main` (`3c7d9e1f`). Their
non-English catalogs have no missing Hindi keys except the names deliberately kept in English.
+1 -1
View File
@@ -29,7 +29,7 @@ Chinese by `imachus`). Only Georgian is recorded as reviewed by a native speaker
| English | `en` | source, 100% | all | Latin | — | full | — |
| Simplified Chinese (Mandarin) | `zh-hans` | 5,102 rows, 791 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | all | CJK UI font from craft-fonts (BIZ UDPGothic) when embedded, else a system font; IME with marked text at the caret; vertical type partial ([type-parity.md](type-parity.md)) | no | full | — (review) |
| Spanish | `es` | 4,009 / 4,020 (99.7%) | all (604 messages) | Latin | no | full | — (review) |
| Hindi | `hi` | 7,877 rows (2026-11-06): 2,537 `@msg`, 45 `@msgplural`, 38 `@plural`; no gaps in the untranslated report | menus, dialogs, tooltips and scanned messages; contributor-approved loanword terminology; agent wording pass | UI and document text shape with HarfRust; installed desktop Devanagari fallback, or a `Deva` craft-fonts face; web font still needed | contributor terminology review; wider proofread welcome | complete catalog, machine-assisted draft | native review, layout and web font |
| Hindi | `hi` | 8,732 rows (2026-11-13): 3,003 `@msg`, 47 `@msgplural`, 41 `@plural`; no gaps in the untranslated report | menus, dialogs, tooltips and scanned messages; contributor-approved loanword terminology; agent wording pass | UI and document text shape with HarfRust; installed desktop Devanagari fallback, or a `Deva` craft-fonts face; web font still needed | contributor terminology review; 2026-11-13 wording pass on messages and longer labels, with a glossary-term test; wider proofread welcome | complete catalog, machine-assisted draft | native review, layout and web font |
| Arabic | `ar` | 0 | none | document text: bidi layout and Paragraph Direction; interface: no right-to-left layout, no Arabic shaping in egui | no | none | 25–40 |
| French | `fr` | 4,020 / 4,020 (100%) | all | Latin | no | full | — (review) |
| Portuguese | `pt-br` | 6,106 catalog entries (counted 2026-10-31 on `main` at `d26dd933a`): menus, `tl!` literals and messages complete when counted; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) |
+280 -60
View File
@@ -1,6 +1,6 @@
# VectorCraft MCP server
> **Last updated:** 2026-11-11 · **Change:** Save location-refusal error (#6146); existing response shapes retained.
> **Last updated:** 2026-11-13 · **Change:** Envelopes: a top object's side whose pieces are all one curve split at anchors follows that curve's parameter (#7097); existing response shapes retained. Previously: Save location-refusal error (#6146); existing response shapes retained.
`vectorcraft-cli mcp` runs a [Model Context Protocol](https://modelcontextprotocol.io) server on stdio
(newline-delimited JSON-RPC 2.0; see [Protocol](#protocol) for version negotiation). Agents use it to
@@ -630,7 +630,7 @@ sampler.
| `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`. |
| `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`. An app that does not send the image back writes a window capture without `path` to a file in a new folder inside the system's temporary folder (on Unix only the user can open it); the server reads the file and then removes the folder, also when the capture or the read fails. |
| `open_file` | `{path, importer?, params?}` | Opens any readable file as a new active document: `.vectorcraft`/`.drawcraft`, `.vctemplate`, `.svg`/`.svgz`, `.pdf`/`.ai`, `.ait`, `.eps`/`.epsf`, `.ps`, `.dxf`, `.emf`, `.wmf`, PICT (`.pct`/`.pic`/`.pict`, and a version 2 picture of any extension), HP-GL (`.plt`/`.hpgl`/`.hgl`/`.hpg`, and a file of an extension no other format reads that starts with plotter instructions), PNG (`.png`/`.pns`), JPEG, GIF, WebP, TIFF, BMP (`.bmp`/`.dib`/`.rle`), Targa (`.tga`/`.vda`/`.icb`/`.vst`), PCX, Pixar (`.pxr`), JPEG 2000 (`.jpf`/`.jpx`/`.jp2`/`.j2k`/`.j2c`/`.jpc`), HEIC/HEIF (`.heic`/`.heif`), JPEG XL (`.jxl`), PSD (`.psd`/`.pdd`)/PSB (an image opens as a document of its pixel size; a PSD or PSB file with its layers as layers, or as its merged image through `run_command document.open` with `layers: false`; a JPEG XL file as its first frame). Templates (`.vctemplate`, `.ait`) open as a new untitled document. PDF, `.ai` and SVG files saved with Preserve Editing reopen as the document they carry. The reply is `document.open`'s: its `warnings` say what didn't come in as it was (an EPS whose PostScript can't be read opens as its preview, and the warning names the PostScript error, the operator and the procedure). A file of an extension only an installed importer plug-in lists opens with that importer ([Plug-ins](#plug-ins)); `importer: "plugin.<id>"` opens the file with the importer it names, which must list the file's extension, also one a built-in format reads (in the app, as File › Open With does), and `params` gives that importer's parameters (their defaults when omitted). `run_command document.formats` lists the formats. |
| `save_file` | `{path?, compress?, version?, preview?, precision?, options?}` | Runs `document.save`: the document's own file in its own format (native `.vectorcraft` unless it was opened from or saved as SVG, PDF or a restorable `.ai`; then `warnings` say what that format loses). A path's extension picks the format (`.vectorcraft`, `.vctemplate`, `.pdf`, `.svg`, `.svgz`, `.ai`: a PDF carrying the native document, which reopens editable; `.eps`: by default one file whose bounding box fits the visible art, the first artboard when no art shows; `options.useArtboards: true` makes the chosen artboard the page and, with two or more chosen, the file at the path is the central file of the visible art and each artboard is also written to `<name>_<artboard>.eps` beside it, listed in the reply's `files`). For a native file, `compress: true` gzips it (default: the `useCompression` preference, Preferences › File Handling › Use Compression), `version` (1 to 4) writes it for an older VectorCraft (1 and 2 are never compressed, and `version: 2` with `compress: true` is an error), `preview: true` embeds a PNG of the first artboard, and `precision` (0 to 9) rounds path anchors and handles to that many decimal places for that save only (colors, transforms and type stay exact). `options` passes the format's save options as `run_command document.formats` lists them. When `compress`, `version`, `preview` or `precision` is given both at the top level and in `options` with different values, the call is an error (`` `compress: true` and `options.compress: false` disagree: give the option once ``) and nothing is written. A key in `options` that no save format takes is an error, and nothing is written (see Parameter keys). The save drops each option the written format doesn't read. The reply's `warnings` then name each such option given (for example `` `compress` was not used: SVG files don't read it `` for an `.svg` path, and `` `options.preview` was not used: … `` for a key in `options`; a `.ai` file reads `compress` but not `version`, `preview` or `precision`). For a `.ai` file, these warnings name the kind of `.ai` written: a legacy .ai (version 7) for `aiVersion: "7"`, a non-PDF-compatible .ai for `pdfCompatible: false`, and a PDF-compatible .ai otherwise, including a save that reuses the options the document was last saved with. `run_command document.save` and the other save commands refuse any option given beside the path and in `options` with different values, and give the same warnings for a top-level `compress`, `version`, `preview` or `precision` and for each key in `options` that the format doesn't read. The headless session and the app receive the same `app.save` params, and the app is also sent `wait: true`: the call returns once the file is written, or with the error (such as a path outside the write root), even with Background Save on. When the save asks first (the question before replacing a file that opened with losses, or on macOS and Linux the file dialog for an untitled document without `path`), the reply is `{pending}` or `"cancelled"` as before, and the save that runs after the answer uses the Background Save preference. Location refusals return `READ_ONLY_LOCATION: <reason>` without a fallback panel for automation; see [Save location errors](#save-location-errors). |
| `export` | `{path?, format?, scale?, artboard?, range?, selection?, outlineText?, options?}` | `svg`, `svgz`, `pdf`, `ai` (the PDF-compatible `.ai` that Save writes, which carries the document when every artboard is written in order), `eps`, `dxf`, `emf`, `wmf`, `hpgl` (a `.plt` plot file for plotters and cutters), `png`, `jpg`, `webp`, `gif`, `png8` (an indexed `.png`), `tiff`, `bmp`, `tga`, `psd` (layered), `txt` (the document's text), `vectorcraft` or `template` (a native template). The tool's `format` enum and `document.formats` list them, generated from the engine's format table. When `format` is omitted, it comes from the path's extension. PDF and `.ai` write one page per artboard: all of them, or `artboard` (0-based) / `range` (`"1-3, 5"`, 1-based). SVG and SVGZ write one file per chosen artboard. EPS writes one file of the visible art; with `artboard`, `range`, `options.artboards` or `options: {"useArtboards": true}` it writes one file per chosen artboard, after a central file of the visible art when there are two or more, and `useArtboards: false` keeps the one file. Rasters, DXF, EMF, WMF and HP-GL write one artboard, or one file per chosen artboard with `options: {"useArtboards": true}`. Rasters also take `options: {"rect": [x, y, width, height]}` (page points, y down): that area of the page at the given `ppi`/`scale`, without an artboard and without changing the document; it is refused with `artboard`, `artboards`, `range` or `useArtboards`, with a non-positive or non-finite size, and for non-raster formats. `options` carries more format options (e.g. `{"quality": 80}` for JPEG). `selection: true` exports the selected objects cropped to their bounds (the reply adds their `bounds`, and reports `format` and the encoder's `warnings` as a whole-document export does); `outlineText: true` writes SVG text as paths. Template layers are left out, live effects are kept, and exporting `vectorcraft` never changes the document's path. Without `path` the bytes come back as `dataBase64`. Both backends run the same `document.export` call. |
@@ -640,7 +640,7 @@ sampler.
| `transform` | `{ids?, dx?, dy?, rotate?, scale?, scaleX?, scaleY?, reflect?, shear?, origin?, copy?}` | Runs move, rotate, scale, reflect, shear in that order. `rotate` is in degrees counter-clockwise; a positive `shear` moves the top to the right. With `copy`, the first step duplicates. |
| `create_graph` | `{type?, x, y, width, height, csv? \| series?, categories?, rows?}` | The nine Illustrator graph types: column, stacked column, bar, stacked bar, line, area, scatter, pie and radar. An empty CSV cell or a `null` in `rows` is a blank value; a number in straight quotes is a label. Edit later with `graph.setData` / `graph.setType` via `run_command`. `graph.column {design, columnType: vertical\|uniform\|repeating\|sliding, each, fractions: chop\|scale}` draws the columns or bars of the series picked with Group Selection (or `seriesIndexes`, or all of them) with a graph design saved by `graph.design`; with no `design` it returns the series' current design and options. Type in the design that holds a `%` followed by two digits shows each column's value, with at least the first digit's places before the decimal point and the second digit's places after it. |
| `text_wrap` | `{ids?, offset?, invert?, release?}` | Area type below the objects (same layer) flows around them. |
| `undo` / `redo` | `{}` | Many calls that change nothing record no undo step, for example `object.align` with nothing to move, `object.move` by 0 without `copy` and `object.showAll` with nothing hidden. `undo` (`edit.undo`) after such a call undoes the step before it. `inspect_document` lists the undo steps as `history`. `run_command` with `edit.clearHistory` removes every undo and redo step of the active document and returns `{cleared}`, the number of steps removed; the document stays as it is, and the command records no undo step. `run_command` with `edit.newDocumentFromState` and `{index?, created?}` makes a new document holding the art as it was at that state and makes it the active document, and returns `{index, title}`. `index` is a row of the History panel, with 0 the oldest state kept, and an omitted `index` uses the current state. An `index` that names no state is an error and changes nothing. `created` is File Info's created date in Unix seconds or null, and defaults to now. The new document takes the next `Untitled-N` name, is marked unsaved, has nothing selected and has no steps to undo or redo. It is out of symbol, opacity-mask and pattern editing, and the art drawn in a pattern's tile is stored in the pattern, as `object.pattern.done` stores it. The source document and its steps stay as they are, except that typing or a drag in progress is first kept as a step. While the Type tool edits type, `undo` and `redo` put its caret, with no characters selected, at the end of the text the step changed: after the text it puts back or retypes, or where the text it takes out was. A step that changes no characters, such as a formatting change, leaves the caret and the selection where they are. |
| `undo` / `redo` | `{}` | Many calls that change nothing record no undo step, for example `object.align` with nothing to move, `object.move` by 0 without `copy` and `object.showAll` with nothing hidden. `undo` (`edit.undo`) after such a call undoes the step before it. `inspect_document` lists the undo steps as `history`. `run_command` with `edit.clearHistory` removes every undo and redo step of the active document and returns `{cleared}`, the number of steps removed; the document stays as it is, and the command records no undo step. `run_command` with `edit.newDocumentFromState` and `{index?, created?}` makes a new document holding the art as it was at that state and makes it the active document, and returns `{index, title}`. `index` is a row of the History panel, with 0 the oldest state kept, and an omitted `index` uses the current state. An `index` that names no state is an error and changes nothing. `created` is File Info's created date in Unix seconds or null, and defaults to now. The new document takes the next `Untitled-N` name, is marked unsaved, has nothing selected and has no steps to undo or redo. It is out of symbol, opacity-mask and pattern editing, and the art drawn in a pattern's tile is stored in the pattern, as `object.pattern.done` stores it. The source document and its steps stay as they are, except that typing or a drag in progress is first kept as a step. When a command keeps a drag whose preview Real-time Drawing off had not applied yet (`edit.undo`, `edit.redo`, `edit.clearHistory`, `edit.newDocumentFromState` and the other commands that end a drag first), and applying that preview gives a warning, the command's result carries it as `previewWarning`, and as `warning` too when the command gives no warning of its own; the app's status bar shows `warning`. While the Type tool edits type, `undo` and `redo` put its caret, with no characters selected, at the end of the text the step changed: after the text it puts back or retypes, or where the text it takes out was. A step that changes no characters, such as a formatting change, leaves the caret and the selection where they are. |
For example, create editable Japanese area type with the engine's vertical writing mode:
@@ -758,9 +758,31 @@ designs keep their own paint. A row that had no
paint before the command and has none after it does not change the paint the graph stores. `paint.setFill`,
`paint.setStroke`, `paint.none` and the commands that apply one paint kind to the other store the topmost row of the kind
even when it is hidden, and store a stroke set on a band. `stroke.set`, `paint.editGradient` and the recolor commands do
not store a series' paint. An `appearance.*` command or `graphicStyle.apply` given a series group's id, and
`graphicStyle.redefine` on a series group linked to the style, edit the group's own rows, and the graph does not store
them.
not store a series' paint.
`appearance.addFill`, `appearance.addStroke`, `appearance.setItem`, `appearance.removeItem`,
`appearance.duplicateItem`, `appearance.moveItem`, `appearance.clear`, `appearance.reduceToBasic`,
`appearance.showAllHidden`, `appearance.transfer` (as its `target`), `graphicStyle.apply`, and `transparency.set` with a
fill or stroke `item`, given a series group's id or the selected series group, edit the paths directly in the group as
if each had been given, and the group keeps no rows of its own. A series group with no path directly in it, for example
one drawn with a column design, and a series group that has a fill or stroke row of its own, as a document saved by an
earlier version can hold, are edited themselves by these commands, and `appearance.moveItem` with `from: "contents"`
moves the group's own Contents row. `appearance.clear`, `appearance.reduceToBasic`, `appearance.showAllHidden` and
`graphicStyle.breakLink` edit the group as well as its paths. `graphicStyle.apply` without `add` and
`appearance.transfer` onto the group clear the group's own effects, opacity, blend mode, isolation, knockout and style
link, and `graphicStyle.apply` with `add` unlinks the group. With `appearance.transfer`, a `source` that is one of the
target group's paths keeps its look on a move, and a `source` that is the group's only path is an error.
`graphicStyle.redefine` gives the style to the paths directly in a series group linked to it, whether or not the group
has rows of its own, links them, and clears and unlinks the group; the source and the paths linked to the style whose
look was edited keep their look. Read as a source, by `graphicStyle.new`, `graphicStyle.redefine` or
`appearance.transfer`, a series group that has no fill, stroke, effect, opacity, blend mode, isolation, knockout or
style link of its own gives the look and the style link of the topmost path directly in it, and a move with
`appearance.transfer` clears each of those paths; a series group that has one of them is read itself.
The other commands given a series group's id edit the group itself, and the graph does not store what they set on it:
`transparency.set` and `object.setProps` with an opacity or blend mode for the object, the `effect.*` commands and
`appearance.addEffect`, and the paint, stroke and gradient commands with a fill or stroke `item`, which is a row of the
group's own stack. The Appearance panel and `appearance.setActiveItem` read the selected group's own rows.
`text.setFormat` without `start` and `end`, on a graph or on its labels, stores the character and paragraph attributes
it sets for each kind of label it reaches (value axis numbers, category labels, legend labels), and every label of that
@@ -969,7 +991,11 @@ SVG writes one file per artboard of a `range`); `useArtboards: false` exports th
its effects paint, the effects of groups, type and images included. `artboard.fitToArt` measures the art the same
way. `document.exportPdf {useArtboards: false}` writes that one page as well, and ignores `artboard`, `artboards` and
`range`. A PDF of that page, from either command, carries no Preserve Editing data; when Preserve Editing is on (as it
is in VectorCraft Default), the export comes back with a warning that says so.
is in VectorCraft Default), the export comes back with a warning that says so. A `.ai` export with
`useArtboards: false` writes that one page too, without the native document and without the editing copy for other
apps (`editingData`), and always comes back with the warning `Preserve editing was left out: an .ai file of the art's
bounds would replace the document's artboards when reopened`; with `pdfCompatible: false` it is refused, whatever
`editingData` says. A legacy version 7 `.ai` (`aiVersion: "7"`) of the art's bounds is written as before.
Export As PDF continues in the Save PDF dialog with Use Artboards on or off; with it off, the dialog has no
Artboards section and the page is the bounds of the visible art.
@@ -1036,7 +1062,8 @@ and `flattener` options over it (as `object.flattenTransparency` takes them); im
transparency and rasterized areas are clipped to their regions. Layers the file leaves out (non-printing ones, unless
`includeNonPrinting` or `createLayers` is set) are left out of the flattening. A file that would still have transparency fails the
export. PDF/X-1a and PDF/X-3 files are PDF 1.3 files too.
A page with a side longer than 14,400 pt is written with a `UserUnit` (PDF 1.6): its longer side divided by 14,400,
In PDF/A-2b files, `.ai` saves and files written with `longPagesInPoints: false`, a page with a side longer than
14,400 pt is written with a `UserUnit` (PDF 1.6): its longer side divided by 14,400,
rounded up to the next thousandth, with the page's sizes and coordinates in those units, so that no side is longer than
14,400 units (ISO 32000-1, Annex C.2). A PDF 1.5 file with such a page is written as PDF 1.6, with a warning, and
keeps PDF 1.5's encryption (128-bit RC4). A PDF/A-2b file at PDF 1.4 with such a page is written as PDF 1.6 too, with a
@@ -1045,9 +1072,10 @@ unless the page is too narrow for that (below)
([veraPDF rule 6.1.13-11](https://github.com/veraPDF/veraPDF-validation-profiles/wiki/PDFA-Parts-2-and-3-rules#rule-6113-11);
PDF/A-2 files may declare PDF 1.0 to 1.7, [rule 6.1.2-1](https://github.com/veraPDF/veraPDF-validation-profiles/wiki/PDFA-Parts-2-and-3-rules#rule-612-1)).
PDF/X files and other PDF 1.3 and 1.4 files write such a page in points, and apps
that keep to the limit cut it to 200 in. `longPagesInPoints: true` (off by default; "Write pages longer than 200 in at
that keep to the limit cut it to 200 in. `longPagesInPoints` (on by default; "Write pages longer than 200 in at
full size in points" in the Save PDF dialog's General section) writes such a page in points at every PDF version, without
a `UserUnit`, and a PDF 1.5 file stays PDF 1.5; the warning then says the page is written in points. The option applies
a `UserUnit`, and a PDF 1.5 file stays PDF 1.5; the warning then says the page is written in points. Readers that
ignore `UserUnit` show such a page at its full size. The option applies
only to files without a standard: PDF/X files write such a page in points anyway, and PDF/A files keep the `UserUnit`, which
keeps their page boundaries within PDF/A-2's limit of 14,400 units (ISO 19005-2, 6.1.13), a PDF/A file at PDF 1.4
included (written as PDF 1.6). The dialog disables the
@@ -1434,8 +1462,10 @@ preferences (the folder the desktop app uses, so both see the same ones; `VECTOR
start, after the profiles above are registered (#6335). Saved settings that name a profile that is no longer
there, or a damaged file, leave the defaults (logged), and the next change writes over the file.
`edit.assignProfile` tags the document only: colors show and convert through the profiles it assigns, and the
working spaces and the file stay as they are. The `workingCmyk` target of `view.proofSetup` simulates the working
CMYK space in such a document too.
working spaces and the file stay as they are. The profiles in use are those of the document in front, after
`file.new`, `document.open`, `document.activate`, `file.close`, `file.revert`, `edit.undo`, `edit.redo` and a
failed `command.batch` too; with none assigned, or no document open, the working spaces are. The `workingCmyk`
target of `view.proofSetup` simulates the working CMYK space in such a document too.
Not remembered: the document, anything else. Nothing is read or written when
`VECTORCRAFT_NO_PREFS` is set, and a server confined with `--automation-read-root` or
@@ -1777,7 +1807,7 @@ vectorcraft-cli run --cmd file.new --params '{"width":600,"height":200}' --cmd
### Parameter schemas
Some commands have a machine-readable parameter schema: at present a small set including `view.capsLock`, `view.saved.new`,
`view.saved.edit`, `view.saved.list`, `view.transparencyGrid`, `view.snapToGlyph.options` and `engine.apiReference`
`view.saved.edit`, `view.saved.list`, `view.transparencyGrid`, `view.snapToGlyph.options`, `view.snapToPixel.options` and `engine.apiReference`
(most of the catalogue still documents params in prose only; schemas grow command by command). The engine's
`engine.commands` list gives such a command a `schema` field: an object schema in the shape of JSON Schema 2020-12
(https://json-schema.org/draft/2020-12). Its root has `$schema` (the 2020-12 dialect), `description` when the
@@ -1830,9 +1860,10 @@ as the English gives them.
An omitted value with a `default` takes it (`rotation` 0 in `view.saved.new`); the journal keeps the params as they
were given. Saved steps played from a journal or an action keep a key the schema does not name, unless its value nests
more than 16 levels deep, and the values of the parameters it names are checked as in a new call. The schemas of built-in commands belong to API level 3
more than 16 levels deep, and the values of the parameters it names are checked as in a new call. The schemas of built-in commands belong to API level 4
(`vectorcraft_engine::schemas::API_VERSION`); a change to any of them raises the level. Level 2 added
`engine.apiReference`; level 3 says that `rotation` in `view.saved.new` and `view.saved.list` turns clockwise. Commands that plug-ins add have no schema here.
`engine.apiReference`; level 3 says that `rotation` in `view.saved.new` and `view.saved.list` turns clockwise; level 4
adds `view.snapToPixel.options`. Commands that plug-ins add have no schema here.
`engine.apiReference {}` → `{markdown}` returns the command API reference, written from these schemas each time it
runs; no file of it is kept. It needs no open document and changes nothing. The reference has a `##` section for each
@@ -1947,8 +1978,10 @@ app through the global object `vc`, and writes to the Script Console and runs ot
file). Its preprocessor lines are read as the script's are. `timeout` is a number of milliseconds from 0; the file
is read at once and `timeout` is not waited on. The run's fuel and limits apply to the file. An empty path, a
missing file and a refused path throw errors the script can catch.
- `$.fileName` is the file being run, which is the file `$.evalFile` is running while it runs, and is empty for a
script given as `source`. `$.includePath` is the current `#includepath` list, separated by `;`. `$.engineName` is
- `$.fileName` is the file of the code that reads it: the script, a file it included with `#include`, or a file
`$.evalFile` ran, also when a function that file defined is called after `$.evalFile` returned. It is empty in code
from a script given as `source`. In code that `eval` or `Function` compiled it is the file being run, which is the
file `$.evalFile` is running while it runs. `$.includePath` is the current `#includepath` list, separated by `;`. `$.engineName` is
the name `#targetengine` gave the engine the script runs in (`VectorCraft` without the line) and is read-only.
- `alert(message[, title, errorIcon])`, `confirm(message[, noAsDflt, title])` and `prompt(message, preset[, title])`
open a dialog, described below. `vc.api.has("dialogs")` is true.
@@ -2018,7 +2051,7 @@ VectorCraft's own, and `vc.api.has("dollar")` is true where they are available:
| `global` | the global object |
| `hiresTimer` | the microseconds since the run last read it, or since the run started; `0` on the web |
| `level`, `memCache` | `0`; setting `memCache` throws |
| `line`, `stack` | the line of the code that reads it; the running code, outermost first, one a line as `name:line` (`[global]` for the script's own code, `[anonymous]` for a function without a name), at most 64 lines |
| `line`, `stack` | the line of the code that reads it, counted in the file that code is in (the script, a file it included or a file `$.evalFile` ran, in this run or an earlier run of its named engine); the running code, outermost first, one a line as `name@file:line` (`[global]` for the script's own code, `[anonymous]` for a function without a name; the file as `$.fileName` gives it, or the run's name for a script given as `source`), at most 64 lines. Code that `eval` or `Function` compiled keeps the line of the source they compiled, in the file `[eval]`; code whose place a named engine no longer keeps is at line `0` of `[unknown]` |
| `locale` | the interface's language as a locale such as `de` or `pt_BR` (`en` when the interface follows the system); a script can set `ll` or `ll_RR`, and `null` or `""` sets the interface's again |
| `localize` | `false`; while `true`, `String(object)` of a localization object gives its text for `$.locale` |
| `os` | `Windows`, `macOS`, `Linux`, `FreeBSD`, `Unix` or `Web`, without a version |
@@ -2092,6 +2125,9 @@ no engines, and the engines it left are dropped when their own thread next uses
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.
- `$.fileName` in code an earlier run compiled names the file that code is in, by the same places: the earlier run's
script, a file it included or a file its `$.evalFile` ran, not the calling run's file. It is empty in code from an
earlier script given as `source`, even when that run had a name, and in code whose place is gone.
- `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`,
@@ -2473,8 +2509,8 @@ them again with the key object. When the call changes nothing, it records no und
Other `object.setProps` calls leave the selection unchanged.
`object.unlockAll` and `object.showAll` unlock or show every locked or hidden object in the document and select
them → `{count}`. In isolation mode they take only the objects inside the isolated object. With `scope: "group"` they
take only the objects inside the groups that hold the selected objects:
them → `{count}`. Ruler guides locked or hidden on their own are counted, and are selected as guides. In isolation
mode they take only the objects inside the isolated object. With `scope: "group"` they take only the objects inside the groups that hold the selected objects:
for each selected object, the group nearest above it, or the object itself when it is a group. The objects of a
group inside another such group are counted once. With no selected object in a group, the call returns the error
"nothing selected is in a group" and changes nothing. `scope: "document"` is the default, and any other value is
@@ -2672,6 +2708,43 @@ kept with the workspace, and built-in workspaces and workspaces saved without it
{"name":"invoke_menu","arguments":{"command":"window.toolsBesideCanvas","params":{"on":true}}}
```
## Docking toolbars made with New Toolbar
A toolbar made with New Toolbar floats when it is made. `window.toolbar.dock {name, side?, order?}` (Dock to Left and
Dock to Right in the context menu of the toolbar's title bar, remote mode, `invoke_menu`) docks it in a lane along the
window's left or right edge. On the left, the lane is right of the Tools panel when the Tools panel is docked at the
window's edge. With Keep Tools Beside the Canvas on and the Tools panel docked on the left, the lane is right of the
Tools panel beside the canvas, and dropping a title bar on the Tools panel or on the lane docks the toolbar there. `side` is `left` or `right` (`start` and `end` also work); without it, the toolbar docks at the edge it
docked at last, or at the left the first time. Toolbars docked at one edge stack from the top. `order` is the
toolbar's place in that stack, 0 at the top, from 0 to the number of other toolbars docked there; without it, the
toolbar goes at the end, or stays where it is when it is already docked at that edge. Each toolbar takes an equal
share of the lane's height and its tools scroll in it, and the lane is as wide as its widest toolbar. When the lane
is too short to give each toolbar 88 points of height, the stack wraps into more columns, left to right, and the lane
widens, so no toolbar covers another. A lane takes at most half the width the canvas and panels would have (beside
the canvas, it also leaves the canvas at least 200 points, and the canvas keeps room for one column); when that
many columns still give a toolbar less than 88 points, the toolbars in those columns show only their title bars, which
still float, dock and show the context menu. A column too short for all of its title bars shows as many as fit, and
the mouse wheel over the column scrolls through the others, one toolbar per notch.
`window.toolbar.float {name, x?, y?}` (Float Toolbar in the same menu) floats it with its top-left corner at `x`, `y`
in screen points, or where it floated last when both are omitted. Both commands answer
`{toolbar, floating, side, order, pos}`: `side` is the edge the toolbar docks at (while it floats, the edge it docked
at last), `order` its place in that edge's stack (null while it floats) and `pos` where it floats (while it is docked,
where it floated last). A missing or unknown name, a `side` other than left or right, an `order` that is not a whole
number in range, or only one of `x` and `y` is an error and changes nothing. `window.toolbar.list` reports `floating`,
`side` and `order` for each toolbar.
With the pointer, dropping a floating toolbar's title bar on the window's left or right edge, or on that edge's lane,
docks it at the end of that edge's stack; the target lights up while the title bar is dragged over it. Dragging a
docked toolbar's title bar out of its lane floats it under the pointer. Escape, or the window losing focus, during
the drag puts the toolbar back where it was. Hiding a docked toolbar keeps its place, renaming it keeps its place, a
copy made with Manage Toolbars' + floats, and deleting one closes up the stack. Placement is kept with the workspace.
Toolbars saved before docking existed load floating. The lanes, the edge targets and the stacking are VectorCraft's
own layout rules.
```json
{"name":"invoke_menu","arguments":{"command":"window.toolbar.dock","params":{"name":"Toolbar 1","side":"left","order":0}}}
```
## Learn panel
Window › Learn and the Explore tutorials button beside the search icon at the right end of the app bar show and hide
@@ -2683,13 +2756,34 @@ toggles it (`show` omitted or null), and answers whether it shows; Window › Le
The panel counts as hidden while the dock is hidden or a full screen mode hides the panels. Showing it shows a hidden
dock; in a full screen mode it makes Learn the front panel, which shows when the mode ends, and answers false. Any
other value of `show` is an error and changes nothing. `learn.list {}` → `{skills: [{id, title, tutorials: [{id, title, steps}]}]}`, in the order the Skills tab
lists them, where `steps` is the number of steps. `learn.show {tutorial}` shows the panel on its Skills tab with that
lists them, where `steps` is the number of steps. `learn.skills {}` shows the panel on its Skills tab with the list of
skills, with no skill and no tutorial open, as the Quick skill guide card and Back to skills do, and answers what
`learn.list` answers. `learn.show {tutorial}` shows the panel on its Skills tab with that
tutorial's steps → `{skill, tutorial, title, steps: [text]}`. A missing, non-string or unknown `tutorial` is an error
and changes nothing. Behavior reference:
and changes nothing.
Under a tutorial's steps, View more opens the tutorial's practice sample, a small drawing of VectorCraft's own with the
art its steps work on, as a new untitled document that becomes the active one; Save asks where to save it. Once the
sample is open, Finish tutorial marks the tutorial finished. A finished tutorial shows Next Up with the title of the
next tutorial in the same skill, which Next Up shows without opening its sample. A skill's last tutorial, finished,
says so and shows Back to skills instead; Next Up never moves to another skill. Which tutorials are finished is kept
while the app runs, and the skill rings and counts do not show it yet. These are VectorCraft's own rules; the help
page also plays a short video, which VectorCraft does not have. `learn.openSample {tutorial}` runs View more →
`{tutorial, index, title}` of the new document, and moves the Learn view to that tutorial; each call opens another
copy and leaves the open documents as they are. `learn.finish {tutorial}` runs Finish tutorial →
`{tutorial, finished: true, next}`, where `next` is the next tutorial's id in the skill, or null after the skill's
last one; the tutorial's sample must have been opened, and finishing it again keeps one entry. `learn.next {tutorial}`
runs Next Up on a finished tutorial and shows the panel on the next one → `{skill, tutorial, title}`; on a tutorial
not finished, or a skill's last tutorial, it is an error. For all three, a missing, non-string or unknown `tutorial`
is an error, and an error changes nothing. Behavior reference:
[Quick skill guide](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html).
```json
{"name":"invoke_menu","arguments":{"command":"learn.show","params":{"tutorial":"pen-paths"}}}
{"name":"invoke_menu","arguments":{"command":"learn.openSample","params":{"tutorial":"pen-paths"}}}
{"name":"invoke_menu","arguments":{"command":"learn.finish","params":{"tutorial":"pen-paths"}}}
{"name":"invoke_menu","arguments":{"command":"learn.next","params":{"tutorial":"pen-paths"}}}
{"name":"invoke_menu","arguments":{"command":"learn.skills","params":{}}}
```
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
@@ -2721,12 +2815,22 @@ vertical guide, the y of a horizontal one, in points) → `{index}`. With `artbo
artboard guide: it runs across that artboard only, moves with it (`artboard.move`, a pure move in
`artboard.setProps`, `artboard.rearrange`), scales with it (`artboard.setProps {scaleArt}`), is copied with it (`artboard.duplicate`, `artboard.move {copy}`) and is
deleted with it (`artboard.delete`); without, a canvas guide runs across the whole canvas. `guide.list` →
`[{index, vertical, pos, selected, artboard?}…]` (`artboard` only for artboard guides).
`[{index, id, vertical, pos, selected, artboard?, layer?, hidden, locked, shown, editable}…]` (`artboard` only for
artboard guides; `id` is the guide's node and `layer` the layer it is on; `hidden` and `locked` are its own state,
`shown` and `editable` also take its layer into account). `layer.setProps {ids: [id…], visible?, locked?}` hides or
locks guides on their own, as the eye and lock on a guide's row in the Layers panel do; a hidden guide is not drawn
and does not snap, and a locked one is drawn and snaps but can't be selected, moved or deleted.
`guide.select {indexes: [index…], toggle?}` selects guides on their own (the art is deselected; `toggle` adds or
removes them) → `{selected}`. `guide.move {index, pos}` puts one guide somewhere, `guide.move {dx?, dy?, copy?}`
moves the selected ones (vertical guides by `dx`, horizontal ones by `dy`; `copy` leaves them and selects the
moved copies), and `guide.remove {index?}` deletes one guide or the selected ones → `{count}`; each is one undo
step. With guides selected, `edit.clear` (Delete) deletes them and `object.nudge` (the arrow keys) nudges them.
moved copies), and `guide.remove {index?, indexes?}` deletes one guide, the guides `indexes` (not with `index`; an
index given twice counts once; a hidden or locked guide among them is an error and nothing is deleted) or the
selected ones → `{count}`; each is one undo step. An `indexes` list of `guide.select`, `guide.remove` or
`guide.setLayer` holds at most 10,000 entries; a longer one is an error. With guides selected, `edit.clear` (Delete) deletes them and `object.nudge` (the
arrow keys) nudges them. Object › Lock › Selection (`object.lock`) and Object › Hide › Selection (`object.hide`) lock
or hide the selected guides on their own, with the selected art, in one undo step, except in isolation mode.
`object.unlockAll` and `object.showAll` with the default scope, outside isolation mode, also unlock or show the
guides locked or hidden on their own and select them as guides.
View › Guides › Lock Guides (`view.guides.lock`) deselects them and the paths made into guides, in every open
document, and refuses these commands until unlocked. While it is on, a command that selects art, such as `edit.undo`,
`edit.redo`, `object.exitIsolation`, `object.unlockAll`, `object.showAll` or `edit.paste`, leaves the guides out.
@@ -2916,6 +3020,13 @@ already walks Image Trace. Short agent entry points:
Tilt goes from its first value with the pen lying flat to its second upright, and on Bearing or Rotation from its
first value at −180° to its second at 180°. A stroke without that input draws the brush's own value (a Scatter
brush's value half way between the two).
- Freehand Fidelity: the `fidelity` of `path.freehand` and `brush.freehand` is in points, 1.5 pt when left out. A
value must be from 0.000015625 pt (0.01 / 640) to 100 / 0.0313 pt (about 3194.89 pt), and a value outside that range
is rejected with an error. The Pencil and the Paintbrush give Fidelity in screen pixels, 0.01 to 100 px, and send it
divided by the zoom (0.0313 to 640) and kept within that range, so a stroke is fitted at the same tolerance on screen
at every zoom. A stroke
that continues a path (`extend`) closes it when it ends within four times the Fidelity of the path's other end, at
least 6 pt and at most 400 pt. The Blob Brush's `path.blob` Fidelity stays in points, 0.01 to 100 pt.
- Variables: `variable.list`, `variable.define {name, kind: text|visibility|image|graph}`, `variable.bind {variable, ids?}`,
`variable.makeTextDynamic`, `variable.makeVisibilityDynamic`, `variable.makeImageDynamic`, `variable.makeGraphDynamic`
(`variable.bind` and `variable.makeTextDynamic` refuse type that already has another text variable and name it),
@@ -3016,15 +3127,18 @@ already walks Image Trace. Short agent entry points:
the file at `path`, else a picked one, or the file's text `data`, adds its set after the other sets and returns
`{set, name, actions}`. A set named like one already in the panel gets the next free number (`Print 2`), and the
other sets and the current set stay as they are. An action set file (`.vcactions`) is JSON:
`{"format": "vcactions", "version": 1 to 5, "name", "actions": [{"name", "steps": [{"command", "params", "excluded"?}]}]}`, each step
`{"format": "vcactions", "version": 1 to 6, "name", "actions": [{"name", "steps": [{"command", "params", "excluded"?}]}]}`, each step
a command id and its params as `command.batch` takes them, or a stop: the command `action.stop` with
`{message, allowContinue}`. A Select Object step is the command `select.byNote` with `{note, caseSensitive,
wholeWord}`, and a Select Path step is the command `select.storedPath` with `{id, fingerprint}`. `excluded: true` preserves a step but omits it from playback;
an absent or false flag includes the step. Files with Select Path steps use version 5, files with Select Object steps
and no Select Path step use version 4, files with stops and neither use version 3, and files with excluded steps and
none of these use version 2, so older builds refuse them instead of playing the excluded steps or failing at a stop, a
Select Object step or a Select Path step; other files use version 1, and version 1 to 4 files still load. A file with another `format` or a `version` other than
1 to 5, a stop whose message is not text or whose `allowContinue` is not true or false, a Select Object step whose params `ui.action.insertSelectObject` would refuse, 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
wholeWord}`, and a Select Path step is the command `select.storedPath` with `{id, fingerprint}`. A View menu step is
the id of a View menu command that the app runs itself (`view.outline`, `view.zoomIn`, `view.rotate` and the others
`ui.action.insertMenuItem` lists) with the item's params. `excluded: true` preserves a step but omits it from playback;
an absent or false flag includes the step. Files with View menu steps use version 6, files with Select Path steps and
no View menu step use version 5, files with Select Object steps and neither use version 4, files with stops and none
of these use version 3, and files with excluded steps and none of these use version 2, so older builds refuse them
instead of playing the excluded steps or failing at a stop, a Select Object step, a Select Path step or a View menu
step; other files use version 1, and version 1 to 5 files still load. A file with another `format` or a `version` other than
1 to 6, a stop whose message is not text or whose `allowContinue` is not true or false, a Select Object step whose params `ui.action.insertSelectObject` would refuse, a View menu step whose params are not an object, 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. 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
@@ -3063,7 +3177,9 @@ already walks Image Trace. Short agent entry points:
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
`ui.action.stop` leaves its step out. So does a `script.run` whose commands, or a tool's or panel's edit whose
step, takes the recording past a limit: the status bar names the limit when the run or the edit ends, and
`ui.action.stop` keeps the steps that fit. 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
@@ -3089,7 +3205,12 @@ already walks Image Trace. Short agent entry points:
here and in Batch…. An action with an included stop plays up to the stop at any speed and then waits, with its
message box open, until `ui.action.continue` or `ui.action.stop`. At Accelerated the steps between two stops are one
undoable batch each. The result is then `{results, pending, atStop?}`, with `atStop: true` when the action already
waits. Batch… refuses an action with an included stop, and a stop that is excluded does not hold playback.
waits. Batch… refuses an action with an included stop, and a stop that is excluded does not hold playback. A View
menu step (`ui.action.insertMenuItem`) plays at every speed in its place among the steps, as its menu item runs, and
adds no undo step. At Accelerated the steps before it and the steps after it are one undoable batch each, and the
result is `{results}` with every step's result in order. A step that fails ends the playback with its error: the
batch it is in changes nothing, and the steps before that batch stay done. While another action records, the View
menu step is recorded too. Batch… refuses an action with an included View menu step.
- Actions: `ui.action.insertStop {set?, action?, step?, message?, allowContinue?}` inserts a stop. With `set` and
`action`, the stop goes into that action, after step `step`, else after its last step, and the command returns
`{set, action, step}`. Without them it goes into the recording in progress, after the steps recorded so far, and
@@ -3103,11 +3224,17 @@ already walks Image Trace. Short agent entry points:
(`message`, `allowContinue`, `set`, `action`, `step`), whose OK runs this.
- Actions: `ui.action.insertMenuItem {set?, action?, step?, command, params?}` inserts a menu item's command as a step
without running it. `command` and `params` (default: `{}`) must be those of an item in the menus that can be a step:
an item that runs a document command without opening a dialog first, outside the File, Window and Help menus, and
not Undo, Redo, a Paste command, Object › Align (its items add the Align To choice when clicked), Object › Image
an item that runs a document command, or a View menu item that the app runs itself (Outline, Pixel Preview, Trim
View, Presentation Mode, Screen Mode, the zoom, fit and Rotate View items, and the show, hide and snap items), without
opening a dialog first, outside the File, Window and Help menus, and not Undo, Redo, a Paste command, Object › Image
Trace › Make or Make and Expand (the menu traces off the UI thread, with progress and Cancel), Object › Pattern ›
Make or Edit Pattern (the menu also opens the Pattern Options panel), a preference
toggle, an Actions command or a recent-font, saved-selection or saved-view slot. Disabled items are listed, and playback checks the step when it runs. With `set` and `action`, the
toggle, an Actions command, a Perspective Grid preset of the preferences or a recent-font, saved-selection or saved-view slot. Disabled items are listed, and playback checks the step when it runs. An Object › Align item's step
keeps the `to` its click sends at insertion (`key` while the selection has a key object or key guide, `artboard` for one selected
object or with Align to Artboard, else `selection`), so a later Align To choice does not change it, and a step that
aligns to the key object fails in playback when the selection has no key then. With anchor points selected the click
sends no `to`, and neither does the step. These are VectorCraft's own rules. A
View menu step plays as `ui.action.play` describes. With `set` and `action`, the
step goes into that action, after step `step`, else after its last step, becomes the panel's selected step, and the
command returns `{set, action, step}`. Without them it goes into the recording in progress and returns
`{recording: true}`. A command no such item runs, params that are not an object, an index past the last set, action
@@ -3528,12 +3655,16 @@ effect of each selected object, in one undo step: `face` is `front` (default) or
offset in points of the graphic's center from the center of the object's bounds in the face's plane, `scale` is a
percentage of the symbol's size (1..10000; omitted, 100 or less so the graphic fits inside the object) and
`rotation` is in degrees counterclockwise (-360..360). Without `index` each object's first 3D effect takes it, and `item` is refused.
Every object's 3D effect must be Extrude & Bevel with Cap on (front only at a depth of 0) or Rotate (the Plane type,
front only), and `symbol` must name a document symbol; otherwise the command is refused and nothing changes. It returns `{ids, graphic}`.
Every object's 3D effect must be Extrude & Bevel with Cap on (front only at a depth of 0), Rotate (the Plane type,
front only) or Revolve (front only: its swept side), and `symbol` must name a document symbol; otherwise the command is refused and nothing changes. It returns `{ids, graphic}`.
On Revolve, `x` and `y` are measured on the side unrolled flat from its middle, and a graphic that does not fit
starts scaled to fit the half of the side seen from the front ([three-d.md](three-d.md#graphics)).
The graphics are the `graphics` parameter of `threeD.extrude` and `threeD.rotate`, which `effect.setParams` also
The graphics are the `graphics` parameter of `threeD.extrude`, `threeD.rotate` and `threeD.revolve`, which `effect.setParams` also
edits: an array of up to 64 `{symbol, face, x, y, scale, rotation}` objects, drawn on the face turned,
foreshortened and shaded with it and clipped to it ([Map artworks on 3D object](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/map-artwork-on-3d-objects.html)).
Revolve takes graphics on its swept side and shades them with the object's light ([the archived reference, page 185](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf#page=185));
how a graphic wraps around the side is VectorCraft's own rule.
A graphic naming a symbol the document does not have is refused, unless the effect already holds a graphic of it.
The drawing rules are VectorCraft's own; see [three-d.md](three-d.md#graphics).
@@ -3769,6 +3900,32 @@ excluded take the palette as their new colors in row order, the colors repeating
{"name":"run_command","arguments":{"command":"recolor.pickTheme","params":{"picks":[{"at":[120,80]},{"rect":[200,40,320,140]}],"colors":4}}}
```
`recolor.weigh {map, weights?, ids?}` is the dialog's Prominent Colors bar. It takes rows as `recolor.apply` does and
returns them as `map`, each with `from` as a list, and `shares`: for each row, its percent of the uses (the `count` of
`recolor.colors`) of the current colors of the rows that are not excluded, and 0 for an excluded row. A use counts for
the row that `recolor.apply` recolors it with (rows with a new color first, an exact color before a hex color, then
the first row that lists it), and otherwise for the first other row that is not excluded and lists it. A current
color that no art uses counts nothing. Without `weights` the
rows come back unchanged. `weights` holds one number of 0 or more per row; an excluded row's weight is ignored, and
at least one other row needs a weight above 0. As VectorCraft's own rule, current colors then move one at a time
between the rows that are not excluded and have a new color (`to`), as long as a move brings each row's share of the uses nearer its share of the
weights. Of those moves, it takes first the color nearest in Lab to the first color of the row it goes to, compared
with the row it leaves. A row keeps its first color, and the tints of one global swatch in a row move together. A
color that the first color of another row with a new color also lists stays where it is. When a color moves, other
rows with a new color drop their own entries for it, so `recolor.apply` recolors it with the row it moved to. The
colors that can move are compared with the rows at most 50,000,000 times in all; when those colors times the rows
that are not excluded pass that, no color moves. Colors that stay in a row keep their order, and the colors that come
to it follow them. The command changes nothing; `recolor.apply` with the returned `map` recolors the art. In the Recolor Artwork dialog the
bar sits under the Edit tab's wheel: each row with a share is a part as wide as that share, in the row's new color,
the largest first. Dragging the edge between two parts moves width between them, and when the drag ends the widths
become the `weights` field, which moves the rows' current colors and previews the result. The New Color Group menu
beside the link button runs `swatch.newGroup` right away, as its own undo step: Save All Colors saves the new colors
of the rows that are not excluded, in row order, and Save Prominent Colors saves the bar's colors in the bar's order.
```json
{"name":"run_command","arguments":{"command":"recolor.weigh","params":{"map":[{"from":["rgb 255 0 0","rgb 128 0 204"],"to":"#00ff00"},{"from":["rgb 0 0 255"],"to":"#ffff00"}],"weights":[1,1]}}}
```
The `recolor.*` commands refuse parameters of the wrong form. A `limitTo` or `method` that is not a string gets the
error for an unknown library or method. Other wrong-form parameters get an error that names them.
`includeImages`, `includePatterns`, a row's `exclude`, `order`, `saturationBrightness` and the `white`, `black` and
@@ -4306,6 +4463,13 @@ and `layerRows`, the rows highlighted in the panel.
draws and is clicked in outline, Ctrl/Cmd-click its eye), `dimImages` (0–100, or false) and `color` (an index 0–26,
`#rrggbb` or a preset name; a hex color with an alpha part, `#rrggbbaa` or `#rgba`, is an error, and so it is in
`layer.new` and `layer.newSublayer`). Several rows change in one undo step (a drag down the eye or lock column).
A ruler guide's row has its own eye and lock: `ids` may name ruler guides (their `id` in `guide.list`), which take
`visible` and `locked`. They can be changed while the guide's layer is hidden or locked, and the guide shows
or can be edited only when its layer allows it as well. Alt-click on a guide row's eye or lock acts on the other
layers, as on any row in that layer. A click on a guide row selects the guide (`guide.select`) and clears the
highlighted rows (`layer.highlight {ids: [], mode: "set"}`), so Delete then runs `edit.clear` on the guide; with no
row highlighted, the trash button runs `edit.clear` on the selected guides and art, and guide rows dropped on the
trash are deleted (`guide.remove {indexes}`).
- **Dragging rows**: `layer.move {ids, target, place?: above|below|inside, copy?}` moves rows beside a row or into a
layer or group (on top of its contents), keeping their stacking order; `copy` (Alt-drag) moves copies. Layers go
only in layers or at the top level and objects in layers and groups (an object placed beside a top-level layer goes
@@ -4603,7 +4767,9 @@ and those of the object shape the outline of the clip group, the gradient fits t
box, as the canvas paints the fill; the expanded art does not keep them. With `fill: false` gradient and pattern fills
stay live. `object.expand.info` answers which options have something to expand in the selection
(`{object, fill, stroke, gradient, appearance, symbolArt?}`; `gradient` says whether Fill has a gradient fill, which Expand
Gradient To applies to; `appearance` whether the selection has appearance attributes Expand Appearance expands first,
Gradient To applies to, that it turns into art: it is false when every such fill repeats or reflects in more than 256
runs across the box it fills, which for type, symbol art included, is the box of each outline Object makes of a character,
ruby character, underline or strikethrough; `appearance` whether the selection has appearance attributes Expand Appearance expands first,
for which Object → Expand… is dimmed in the menus; `symbolArt: {fill, stroke, gradient}`, present when the selection
holds symbol instances that `object` expands, whether only their art holds it, which Fill and Stroke reach only with
`object` on), and `object.mesh.create` on a gradient-filled object colours the
@@ -4923,9 +5089,10 @@ round, such as a rectangle, stays as it is. Closed paths stay closed and open pa
{"name":"run_command","arguments":{"command":"object.path.smooth","params":{"level":80}}}
```
In the desktop app, Object › Path › Smooth opens the slider (`ui.menuDialog {command: "object.path.smooth"}`, dialog
`smoothSlider` with field `level`) at the Auto-Smooth level and previews the result until `ui.dialog.confirm`;
`ui.smoothAuto` moves the open slider back to the Auto-Smooth level.
In the desktop app, Object › Path › Smooth and the canvas context menu's Smooth… (listed while the selection holds a
path) open the slider (`ui.menuDialog {command: "object.path.smooth"}`, dialog `smoothSlider` with field `level`) at
the Auto-Smooth level, and the slider previews the result until `ui.dialog.confirm`; `ui.smoothAuto` moves the open
slider back to the Auto-Smooth level.
## Editing a path with the Curvature tool
@@ -5381,8 +5548,12 @@ the selected artboards (those the Artboard tool has selected, else the active on
`artboard.fitToSelection` calls it runs without `index`. Both include MCP's `run_command` calls in remote mode. The
Artboards panel menu's Fit to Artwork Bounds also passes the selected artboards. A batch passes its `artboard` on to
each of their steps that names no `index`, as the step's `index`. When the document a step runs in has no artboard at
that index, the step gets that document's last artboard. An action recorded in the Actions panel leaves `index` and
`indices` out of these steps, and they resize the artboard that is active when the action plays.
that index, the step gets that document's last artboard. An `artboard.fitToArt` step that names neither `index` nor
`indices`, while the Artboard tool is chosen with several artboards selected when the step runs, gets those artboards
as its `indices` instead, and the batch's journal entry records them, so a replay of the journal fits the same
artboards. A step's own `index` or `indices` is kept. An action recorded in the Actions panel leaves `index` and
`indices` out of these steps: when the action plays, Fit to Artwork Bounds resizes the artboards the Artboard tool then
has selected (two or more), else the active artboard, and Fit to Selected Art resizes the active artboard.
```json
{"name":"run_command","arguments":{"command":"object.align","params":{"horizontal":"left","to":"artboard","artboard":1}}}
@@ -5644,6 +5815,23 @@ know or a value that isn't a boolean is refused and changes nothing.
{"name":"run_command","arguments":{"command":"view.snapToGlyph.options","params":{"angular":false,"anchors":false}}}
```
The Pixel Snapping Options (the desktop app's Control bar › Snapping menu › Pixel Snapping Options…, described on the
help page,
<https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/draw-pixel-perfect-art.html>):
`view.snapToPixel.options {drawing?, moving?,
scaling?}` (or `prefs.get`/`prefs.set` with key `pixelSnap`, a partial object) reads and sets which edits View › Snap to
Pixel lands on whole pixels. All three are on by default, which is VectorCraft's own default. `drawing` covers the
points the drawing tools place, the rulers' origin and measured points; `moving` covers the selection, its anchors and
segments, artboards and ruler guides moved by dragging, and the Rotate, Reflect and Shear tools' drags; `scaling`
covers the edges a bounding-box, Free Transform, artboard or slice handle drags, and the Scale tool's drag. An edit
whose option is off snaps as it does with Snap to Pixel off. With Snap to Pixel off, the options do nothing. `{}` reads
them; a key it doesn't know or a value that isn't a boolean is refused and changes nothing. Align New Objects to Pixel
Grid and the objects' pixel-aligned property (see Align to Pixel Grid) do not depend on these options.
```json
{"name":"run_command","arguments":{"command":"view.snapToPixel.options","params":{"scaling":false}}}
```
With Smart Guides on, a bounding-box handle dragged with the Selection or Free Transform tool (`mods.shift`
proportional, `mods.alt` from the centre) lands on another object's anchor or centre (labelled "anchor" or
"center"), else its corners and side line up with the edges and centres of the other objects and the artboards (a
@@ -5871,8 +6059,8 @@ Preferences. `prefs.reset` resets the preferences at once, without the question.
`prefs.export {path?}` writes the preferences as a JSON file, `{"format": "vectorcraft-preferences", "version": 1,
"prefs": {…}}`, and replies `{path, count}`; without `path` it replies `{data, count}` with the file's text. The file
holds every preference `prefs.list` lists and the `eyedropper`, `perspectiveWidget`, `pathfinder` and `glyphSnap`
groups. It leaves out the folder preferences (`fontsFolder`, `pluginsFolder`, `recoveryFolder`, `templatesFolder`) and
holds every preference `prefs.list` lists and the `eyedropper`, `perspectiveWidget`, `pathfinder`, `glyphSnap` and
`pixelSnap` groups. It leaves out the folder preferences (`fontsFolder`, `pluginsFolder`, `recoveryFolder`, `templatesFolder`) and
saved presets. An export over 1 MB, the most `prefs.import` reads, is an error and writes nothing.
`prefs.import {path | data}` applies such a file and replies `{imported}`, the number of preferences it
set. It checks every value first, as `prefs.set` does: a file over 1 MB, malformed JSON, another `format`, a newer
@@ -6560,8 +6748,8 @@ path isn't followed) when it isn't. The desktop app also checks the active docum
comes back to the front, following the same preference. `links.check` reports every link's `status` (`ok`, `modified`,
`missing`), `links.update {ids?}` reads modified files again and `links.relink {ids?, path | folder}` points images at
another file (or each at the file of its name in a folder); images keep their bounds, one undo step each.
`links.relink`'s `scope` picks which links take the new file (Relink's Replace choice): `selected` (the default) relinks `ids`; `all` also relinks every other object in the layers linked to the same file as
one of them; `missingInFolder` relinks `ids` to `path`, then every other missing link to the file of its name in
`links.relink`'s `scope` picks which links take the new file (Relink's Replace choice): `selected` (the default) relinks `ids`; `all` also relinks every other stored object linked to the same file as
one of them, in the layers, opacity-mask art, symbols, graph designs and patterns; `missingInFolder` relinks `ids` to `path`, then every other missing link to the file of its name in
`path`'s folder, and lists the names not there in `notFound` (with `copy`, only missing images are looked for); `each` takes `paths: [{ids, path}]` in place of `ids`,
`path` and `folder` and relinks each link to its own file. An unknown scope, `paths` without `each`, `each` with `ids`,
`path` or `folder`, an entry without `path` or `ids`, an id in two entries and `missingInFolder` with `folder` are
@@ -6569,8 +6757,10 @@ errors, and nothing is relinked. Each call is one undo step. The Links panel's R
(`ui.relinkDialog`) with a Replace menu of these four choices. A PNG, JPEG,
another file (or each at the file of its name in a folder); images keep their bounds, one undo step each.
`links.update`, and `document.open` when it reads a modified file, also read the file again for the images and placed
documents inside symbols, graph designs, patterns and opacity masks that show the same content (by default also for
those that only that art shows); they have no ids, so the results do not list them. Saving a document also reads it
documents inside symbols, graph designs, patterns and opacity masks that show the same content. Those objects have ids
of their own: `links.list`, `links.check`, `links.findFiles`, the `missingLinks`, `modifiedLinks` and `updatedLinks`
rows and the link watch list them, and every `links.*` command takes their ids. Opening a document gives an object
there whose id is 0 or already used by another object a new id, and the next save writes it. Saving a document also reads it
again for the placed documents of it inside that art in the other open documents. A PNG, JPEG,
GIF or WebP file whose pixels can't be read is not shown: `links.relink` fails with "the image's pixels can't be
read", and `links.update`, or `document.open` when it reads the file, lists it as missing, its images keeping what they
@@ -6810,8 +7000,11 @@ listed in `warnings`. Binary DXF and DWG files answer an error saying to save th
## Links panel
`links.list {show?: all|missing|modified|embedded, sort?: name|kind|status}` lists every image in the layers, top
first, as the Links panel does: `{id, name, linked, status: ok|modified|missing|embedded, format, pixelWidth,
pixelHeight, path?, found?, page?, preview?, note?}` (`note`: the image takes more than the decode allowance, 2 GiB
first, as the Links panel does, then every image and placed document in opacity-mask art and in the art of symbols,
graph designs and patterns, one row per stored object (never one per symbol instance or pattern tile): `{id, name,
linked, status: ok|modified|missing|embedded, format, pixelWidth, pixelHeight, path?, found?, page?, preview?, note?,
owner?}` (`owner`, for an object outside the layer tree: `{kind: layers|symbol|graphDesign|pattern, name?, maskOf?}`,
`maskOf` naming the object whose opacity mask holds it; `note`: the image takes more than the decode allowance, 2 GiB
or 512 MiB in the web app, as RGBA or as decoded, and it is drawn at the largest size within the allowance ("shown at
reduced size"; the document keeps the whole file) or, when it can't be read that way or has more pixels than the
allowance has bytes, drawn as a crossed gray box on the canvas and left out of exports ("too large to draw"); `document.open` and `file.place` say the same in their `warnings`). `links.info {id?}` is the Link Info: `image.info`'s fields plus
@@ -6828,7 +7021,8 @@ bounds of another visible object that has one of those or a gradient with a tran
a character's fill or stroke); a DCS image with transparency of its own counts as such an object. Bounds are cut to the
bounds of the clipping paths of the clip groups and clipping layers holding an object. The check looks at
every object in the layers each time the list is read. A `dcsTransparency` that is not true or false is an error.
`links.goTo {id}` selects an image; `links.embed {ids?}` keeps linked files' pixels in the document (an image whose
`links.goTo {id}` selects an image (`onCanvas: true`); for an object outside the layer tree it clears the selection and
returns `{id, onCanvas: false, owner}`, and the panel says the link has no place on the canvas. `links.embed {ids?}` keeps linked files' pixels in the document (an image whose
file is missing stays linked: relink it first); `links.unembed {id, path}` writes an embedded image to a file
(another extension converts it) and links to it, and without `path` returns `{name, dataBase64}`.
`links.placementOptions {ids?, preserve?: transforms|bounds|fileDimensions|fit|fill, align?: topLeft…bottomRight,
@@ -9525,7 +9719,12 @@ outline. On a tie, the anchor met first along the outline in the order it was dr
Without such an anchor, the corner is the outline's point nearest the box corner, moved onto the anchor at the end of
its segment when within 0.5% of the segment's parameter. Each piece of a side between anchors and corners takes a
share of the content's width or height in proportion to the length of its control polygon, spread along the piece by
its curve parameter, so content spreads evenly along a segment without handles. An envelope opened from an `.ai` file
its curve parameter, so content spreads evenly along a segment without handles. A side whose pieces, from corner to
corner, are all one cubic curve split at anchors (each within 0.02 pt of that curve's piece) follows that curve's
parameter instead, so splitting such a side's curve without changing its shape (Add Anchor Points, or a file that
saves a curve in pieces) leaves how the side shares out the content as it was. Corners found from the outline can
still move, since one at the outline's nearest point snaps onto an anchor within 0.5% of its segment's parameter. A
side only partly made of one curve keeps the polygon shares. An envelope opened from an `.ai` file
keeps the corners the file saves, and they stay on the same anchors when the envelope is moved, rotated or
reflected. These are VectorCraft's own rules, and existing top-object envelopes redraw by them.
A warp bends the content's bounds as one Bézier patch. The style sets the patch's control points from the bend and
@@ -9777,8 +9976,9 @@ 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:
during the drag (ten with Shift). A blend made without a spine shows the upper widget alone, placed against the
straight lines between its key centers that its steps are laid out along, which are not drawn. 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}}}
@@ -9896,11 +10096,26 @@ order along the path changes: Space, the start, Pivot, Rotate and the children's
new place along the path counted from 0. `object.objectsOnPath.selectMember {member}` takes the same kind of `member`
and returns `{group, member}`.
`object.objectsOnPath.detach {member?}` takes one object off the path: `member`, or without it the object chosen with
the Select/Move widget of the one selected group (otherwise it fails with "choose the object to detach with its
Select/Move widget"). `member` takes the same kind of object as `moveMember`, and neither it nor the group may be
hidden or locked. The object leaves the group and goes just above it in the group's layer or group, staying where it
is drawn, and becomes the selection; when the group is itself an object on another group's path, the object goes just
above that outer group, so it does not join its path. The objects after it move up the path in their order: with Auto Space they spread
evenly again (on an open path the first and last on its ends while two or more are left), and a Space value and the
start are kept, drawn as close as the path has room for. Hidden and locked objects left in the group move up too. The
group, its path and its options stay even with one object or none left, and an object moved back into it joins the
path. It is one undo step, and undo puts the object back on the path with the choice it had. It returns
`{group, member}`. In the desktop app, Detach in the Properties panel's Objects on Path Options section sends it, and
is dimmed until one group is selected with an object chosen. To take several objects off at once, drag their rows out
of the group in the Layers panel (`layer.move`).
```json
{"name":"run_command","arguments":{"command":"tool.setOption","params":{"tool":"objectsOnPath","values":{"pivot":7,"rotate":30}}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.attach","params":{"path":30,"ids":[12,15,18]}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.options","params":{"pivot":7,"rotate":15}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.options","params":{"spacing":24,"start":0}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.detach","params":{"member":15}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.expand","params":{}}}
```
@@ -9913,8 +10128,12 @@ justified text gets even word spacing, ragged text (left, centre or right aligne
that fits to the next line to avoid a lone short word on the last line or a full line next to a short one.
Single-line fills each line as far as it goes. Point type and type on a path have no line width, so both composers
give the same result there. A paragraph whose characters differ in line metrics (type size, font, leading or vertical
scale, or an inline graphic taller than the text) is composed with Every-line in rectangular area type, rows and
columns included; in area type of another shape, or with text wrap, it falls back to Single-line.
scale, or an inline graphic taller than the text) is composed with Every-line in area type of any shape, rows,
columns and text wrap included. In a frame that is not a rectangle, or with text wrap, each line it tries is measured
where the lines above it put it, across the line's own height, so a taller line that reaches where the frame is
narrower, or beside a wrap object, gets the room there. Such a paragraph falls back to Single-line when the frame has
no room for all of it, or when the search for its breaks passes VectorCraft's own limits (1,000,000 lines tried and
2,000,000 baseline steps for one paragraph, 2,000,000 lines and 4,000,000 steps for all the paragraphs of a frame).
The UI command `type.lineComposer` (Cmd+Alt+Shift+C, in remote mode `press_key {key: "C", mods: {cmd: true, alt: true,
shift: true}}` or `invoke_menu`) sets `singleLine` when every paragraph of the selected type (or every paragraph the
Type tool's selection touches) uses Every-line, else `everyLine`, and returns the composer it set.
@@ -11007,4 +11226,5 @@ the error text. The interactive fallback applies only to ordinary File > Save, n
| Date | Change |
|---|---|
| 2026-11-13 | Envelopes: a top object's side whose pieces are all one curve split at anchors follows that curve's parameter (#7097). |
| 2026-11-11 | Document READ_ONLY_LOCATION for saves (#6146); no response-shape change. |
+12 -1
View File
@@ -264,7 +264,13 @@ capability model proposed for PhotoCraft plug-ins
hexadecimal digits covers nothing. `plugin.remove` forgets the grant, and so does a successful `plugin.install` of a
module that lists no permissions under that id, so the earlier bytes need a new approval. When `plugin.remove` can't
remove the grant from `grants.json`, it returns an error, the plug-in stays installed, and a second `plugin.remove`
tries again. A refused install leaves the grant as it is. When an install of a module that lists no permissions can't
tries again. A refused install leaves the grant, the plug-in installed with the id, the `Plug-ins` folder and the
list of disabled plug-ins as they are. When another session installs a plug-in with the id or changes its grant
while `plugin.install` checks a module, the install is refused, before anything is written to the `Plug-ins` folder,
if the other plug-in would need `replace: true` or the changed grant no longer approves the module. A
`plugin.remove` in another session waits until the install has written the `Plug-ins` folder, and then removes the
module and its grant. Loading the `Plug-ins` folder or a plug-ins folder in another session waits for it too. When
an install of a module that lists no permissions can't
write the module to the `Plug-ins` folder, or can't remove the grant from `grants.json`, the folder keeps the module
it held and `grants.json` keeps the grant. The new module runs until the session ends. When the grant can't be
removed and the module already stored can't be read back or written back, the new module stays in the folder, and
@@ -773,6 +779,11 @@ is a piece drawn by itself, in the order returned, with paints of its own:
starts at 1 pt, miter join, butt cap, miter limit 10.
- An effect on a fill (`_context.item` `{"fill": i}`) returns only `fills`, which replace that fill on each piece; an
effect on a stroke returns only `strokes`. The object's other fills and strokes stay where they are.
- Each fill or stroke such an effect returns keeps the effects after it in that fill's or stroke's stack. A later
live effect, and a later geometry effect on strokes, runs once on each of them in paint order: it is given the
piece's `fills` and `strokes` with `_context.item` naming that one (`{"fill": 1}` for the second fill on the
piece), and its result replaces only that fill or stroke. A run that fails, is refused, is deferred or would pass
the budget leaves its own fill or stroke as it was, and the others still run.
- The effects after it in the stack apply to the pieces together, the reference box being the bounds of all of them.
Bounds, raster effects, Pathfinder effects, compound shapes and Envelope Options › Distort Appearance use the pieces
as they are drawn. Outside outline mode, an image is also hit where the pieces of its own fills and strokes paint; a
File diff suppressed because one or more lines are too long
+59 -22
View File
@@ -117,11 +117,20 @@ The rest is our reading:
each side face of the fill's solid that is turned toward the viewer is drawn before every face of the same
shape's strokes, turned toward the viewer, whose bounds overlap it, so the stroke's walls cover the fill's walls
at concave corners. This applies to strokes on closed paths without dashes, a width profile or a brush, painted
with a solid color or a gradient without transparent stops; with other strokes, and without caps, the faces keep
their depth order. Otherwise
each solid is drawn on its own with its opacity and blending, the lower item's first. With a light that is not
white, each solid is also drawn on its own when one of them is a puffed, shaded Inflate surface in a solid color
(drawn as one composite) or is painted with a gradient that has transparent stops (drawn with an opacity mask).
with a solid color or a gradient without transparent stops, under Extrude & Bevel (both kinds). Under Inflate
the fill can rise in front of such a stroke, so it is drawn like the other strokes. Other strokes (dashed, with a
width profile or a brush, on an open subpath, or in a paint that may show through) leave parts of the fill's edge
uncovered, such as the gaps between dashes. Each face of such a stroke that passes through a face of the fill's
solid, or through a face of another of the shape's strokes, where the two overlap in the drawing is cut where
their depths meet, and the faces of the shape's fill and strokes that overlap are drawn in the order of their
depths where they overlap, with caps on or off. A fill wall in a gap stays in its own depth order. Inflate's
curved surfaces follow this only as closely as their flat facets do. A solid painted with a gradient that has
transparent stops under a light that is not white is drawn with an opacity mask: its faces keep their places in
this order, in runs that each carry the mask, and so do its graphics. A member of a 3D group with its own opacity
mask or blending mode draws its fill's and strokes' faces in this order inside that mask or blending mode when
they are opaque with Normal blending. Otherwise each solid is drawn on its own with its opacity and blending, the
lower item's first. With a light that is not white, each solid is also drawn on its own when one of them is a
puffed, shaded Inflate surface in a solid color (drawn as one composite).
- The solids turn, twist and taper about the center of the bounds of all their shapes, Inflate's grid covers
those bounds, and with Perspective one eye sees every solid. A stroke too narrow to hold a point of Inflate's
grid stays flat.
@@ -150,8 +159,11 @@ them all. The members stay editable: moving or editing one reshapes the solids.
inside the 3D group applies to the solids of that group's members together. An opacity mask on
a member masks its solids, and one on a group inside the 3D group masks the solids of that group's members
together. The mask stays where it is on the page; it does not turn with the solids. A disabled mask is ignored.
- When a member, or a group inside the 3D group, has an opacity mask or a blending mode other than Normal, each
solid is drawn on its own, the backmost member's first, as when a solid is transparent.
- When a member, or a group inside the 3D group, has an opacity mask or a blending mode other than Normal, the
members' faces are not drawn in one shared order. Each member whose fills and strokes are opaque with Normal
blending draws their faces in one order of its own inside its masks and blending modes, as described under
[Fills and strokes](#fills-and-strokes). Otherwise each solid is drawn on its own, the backmost member's first, as
when a solid is transparent.
- When every solid is opaque, all members' faces are drawn in one back-to-front order; otherwise each solid is drawn
on its own, the backmost member's first.
- Hidden members and guides are left out. A group holding a live object other than a blend (an envelope, a mesh, a
@@ -308,8 +320,9 @@ Our reading:
The public [Map artworks on 3D object](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/map-artwork-on-3d-objects.html)
page describes graphics: art kept with the document and placed on single surfaces of a 3D object, and the
[3D and Materials panel options](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/3d-materials-panel-options.html)
page lists Graphics and Add Materials and Graphics on the Materials tab. VectorCraft maps graphics onto planar faces:
Extrude & Bevel's front and back caps, and the plane of the Plane type (Rotate).
page lists Graphics and Add Materials and Graphics on the Materials tab. VectorCraft maps graphics onto
Extrude & Bevel's front and back caps, the plane of the Plane type (Rotate), and the swept side of Revolve, which the
public page's example of a label on a bottle describes.
- The panel's **Materials** tab shows the document's graphics as thumbnails. A graphic is a document symbol, as
the public page allows existing symbols to be used. **Add as Single Graphic** keeps the selected art as a new
@@ -318,7 +331,8 @@ Extrude & Bevel's front and back caps, and the plane of the Plane type (Rotate).
- Clicking a thumbnail maps that graphic onto the front of the 3D effect of each selected object
(`threeD.mapGraphic`), in one undo step. A graphic larger than the object starts scaled down to fit inside it.
- Below the thumbnails the tab lists the graphics mapped onto the first selected object. The selected one shows
its Face (Front or Back; Plane has only a front), X and Y (the offset of its center from the center of the
its Face (Front or Back; Plane and Revolve show no Face, as a plane has only a front and Revolve's graphics go
on its side), X and Y (the offset of its center from the center of the
object's bounds, in the face's plane), Scale (a percentage of the symbol's size) and Rotation
(counterclockwise), and **Remove Graphic** takes it off. Each change is one undo step.
- While the tab is open, a blue handle sits on the center of each mapped graphic whose face is turned toward the
@@ -337,6 +351,26 @@ These rules are VectorCraft's own:
the viewer is not drawn.
- On the back cap, X runs to the right as the back is seen from behind, so the graphic reads the right way round
there.
- On Revolve a graphic lies on the swept side, which has one face (Front), unrolled flat. X runs around the axis:
the angle from the middle of the side times the profile's distance from the axis at that point, so a graphic
keeps its size at every distance and wraps farther around the axis where the profile comes nearer to it. Y runs
along the profile, toward its lower end, measured from the middle of its length; a profile's edges on the axis
sweep nothing and add no length. On a closed profile that does not touch the axis, Y is measured from where its
outer side crosses the middle of its height, and the graphic is cut on its inner side. The middle of the side
is the middle of the part of the sweep that faces the viewer before the object is turned, which is the part at
90° with the axis on the left and at 270° with it on the right (the middle of the sweep when no part faces the
viewer). On a compound profile the graphics go on its longest contour. Each face
of the side draws the part of the graphic that falls on it, shaded as the face is shaded, so the graphic narrows
toward the silhouette and is not drawn where the side's outside faces away or past the ends of the sweep; nearer
faces drawn after it cover it. A full turn cuts the graphic at the back, on the edge between two segments nearest
half a turn from the middle, and the end caps take no graphic; a part
that runs past the end of the profile reaches the curved rim of the cap and stops there. On a group or layer the
graphics go once, on the side of the member with the longest profile. Curves
are cut into straight pieces within 0.05 pt before they are mapped, and an opaque part takes a 0.3 pt stroke of
its paint over the seams between faces; a part filled with a pattern, or whose gradient has a transparent stop
or point, takes none. A graphic larger than the half of the side seen from the front starts
scaled to fit it. The panel shows no handle for a Revolve's graphics; X and Y move them. Expand Appearance keeps
every face of a surface with graphics.
- The symbol's fills and strokes are drawn as filled areas, a stroke as its outline, and their gradients and
patterns are placed with the graphic. Images, hidden objects,
guides and clipping paths in the symbol are left out, and clipping groups draw their art unclipped. Instances
@@ -345,12 +379,15 @@ These rules are VectorCraft's own:
- Without perspective, the graphic maps exactly. With perspective, straight lines stay straight and each curve is
cut into 8 cubic pieces before it is mapped. The part of a graphic at or behind the eye is left out.
- An object takes up to 64 graphics. One graphic draws up to 4,096 fills and strokes, and the graphics of one 3D
effect draw up to 400,000 path elements together. Commands refuse a graphic past either limit. A loaded file
effect draw up to 400,000 path elements together (on Revolve, 400,000 points after the curves are cut, and up to
2,000,000 once they are cut at the faces, with up to 64,000,000 points cut together, and the copies of their
paints in the parts take up to 256 MiB per object, apart from those of the surfaces). Commands refuse a graphic
past either limit. A loaded file
draws the object without a graphic past the first limit, and without all of its graphics past the second.
- A hollow Extrude & Bevel has no caps, so its graphics are not drawn until Cap is turned on again, and
`threeD.mapGraphic` refuses to map a new one onto it. Changing the type to Revolve or Inflate
drops the graphics, and changing between Extrude and Plane keeps them, except that Plane drops the graphics
on the back cap.
`threeD.mapGraphic` refuses to map a new one onto it. Changing the type to Inflate drops the graphics, and
changing between Extrude, Plane and Revolve keeps them, except that Plane and Revolve drop the graphics on the
back cap; on Revolve the front's graphics go on its side.
- `effect.apply` and `effect.setParams` refuse a graphic that names a symbol the document does not have, unless
the effect already holds a graphic of that symbol. A graphic whose symbol is deleted is no longer drawn. Renaming a symbol renames it in the graphics that use it,
in the layers, in the art of symbols, patterns, graph designs, brushes and opacity masks, and in graphic
@@ -364,9 +401,9 @@ These rules are VectorCraft's own:
uses: `rotationX`, `rotationY`, `rotationZ`, `perspectiveAngle` (degrees), `shade`, `lightColor` (`#rrggbb`, default `#ffffff`), `lightIntensity` (%), `lightRotation`,
`lightHeight` (degrees), `lightSoftness` (%), `ambient` (%, to 200), `lightBehind`. Lengths are points and scale with the object (Scale Strokes & Effects).
Commands validate the shape and the parameters before the document changes. All four also take `lights`: null (the default, use the earlier single-light fields), or an array of one to ten objects. Each object takes `color` (opaque `#rrggbb`, white), `intensity` (0..100%, 80), `rotation` (-180..180 degrees, 125.26), `height` (0..90 degrees, 30), `softness` (0..100%, 0), and `behind` (boolean, false). Omitted settings use those defaults. An explicit list replaces the earlier single light; `ambient` and `shade` remain shared. Set `lights` to null to return to the single-light fields. Invalid lists, counts, keys and values are rejected before editing; malformed loaded lists fall back to the earlier light safely. All four take `shadow` (boolean, false), `shadowPosition` (`behind` or `below`), `shadowDistance` (0..100%, 0) and `shadowBounds` (10..400%, 100) ([Shadows](#shadows)); values out of range are rejected before editing.
`threeD.extrude` and `threeD.rotate` take `graphics` ([Graphics](#graphics)): an array of up to 64 objects, each
with `symbol` (the name of a document symbol), `face` (`front` or `back`, default `front`; `threeD.rotate` takes
`front` only), `x` and `y` (points, default 0), `scale` (1..10000%, default 100) and `rotation` (-360..360 degrees,
`threeD.extrude`, `threeD.rotate` and `threeD.revolve` take `graphics` ([Graphics](#graphics)): an array of up to 64 objects, each
with `symbol` (the name of a document symbol), `face` (`front` or `back`, default `front`; `threeD.rotate` and
`threeD.revolve` take `front` only), `x` and `y` (points, default 0), `scale` (1..10000%, default 100) and `rotation` (-360..360 degrees,
default 0). Other keys, values out of range and graphics on the other 3D effects are rejected before editing.
## What differs from the public documentation
@@ -376,11 +413,11 @@ and [Create 3D objects](https://helpx.adobe.com/illustrator/desktop/special-effe
pages, and the archived Extrude & Bevel option list (depth 0–2,000, cap on/off, bevel height, bevel extent in or
out, perspective). No Adobe software, assets, presets or bevel shapes were used.
- **Materials** (base and Adobe Substance), graphics on curved surfaces (Revolve, Inflate and Extrude & Bevel's
walls and bevels), soft shadows, shadows of lights after the first, and **ray-traced rendering** are not built:
shading uses directional lights, in the paint of the fill or stroke the solid is built for
([Fills and strokes](#fills-and-strokes)), and the shadow is hard-edged ([Shadows](#shadows)). Graphics map
onto planar faces only ([Graphics](#graphics)).
- **Materials** (base and Adobe Substance), graphics on Inflate and on Extrude & Bevel's walls and bevels, soft
shadows, shadows of lights after the first, and **ray-traced rendering** are not built: shading uses directional
lights, in the paint of the fill or stroke the solid is built for ([Fills and strokes](#fills-and-strokes)), and
the shadow is hard-edged ([Shadows](#shadows)). Graphics map onto planar faces and Revolve's side only
([Graphics](#graphics)).
- The bevel shapes and their names are our own; custom bevel paths are not supported.
- Visibility is a painter's algorithm: faces of a closed solid turned away are left out, and the rest are drawn
back to front, with a flat cap or flat back turned toward the viewer drawn after all of them. Concave solids
+3 -3
View File
@@ -25,10 +25,10 @@ against Illustrator's public documentation; **estimated** unless noted.
| Native font-family labels | partial | With Show Font Names in English off, localized legacy names are used when typographic names contain no native label (#2208); loaded-font and catalog-only regressions. Choosing among multiple native languages is unchanged. |
| Point, area and type-on-a-path type; vertical variants | done / partial | vertical point, area and path type have initial support; fullwidth `!` / `?` keep the nominal glyph upright when no vertical alternate exists (Unicode Tu); in a non-rectangular area a word too wide for the top lines moves down to the first line that holds it (#1615); Escape while typing ends the edit, keeps the type selected and switches back to the tool whose double-click on type chose the Type tool (kept until another tool is chosen or another document or window becomes active, so later edits with the Type tool return there too), or to the Selection tool when there is no such tool, as when the Type tool was chosen from the toolbar or after such a switch (#1802), and Cmd+Enter (Ctrl+Enter on Windows and Linux) does the same unless a command is bound to it; `text.create` and `text.createInPath` take `justify` (#1752); MCP `add_text` forwards `vertical` to both commands; Type › Type Orientation sets every frame of a thread, and a thread's frames share one writing direction; the Type and Vertical Type tools make area type when a closed path's outline is clicked and type on a path when an open one is, with pointers that say which, as the public help describes ([Create text](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html), [Create type on a path](https://web.archive.org/web/20220127090635/https://helpx.adobe.com/illustrator/using/creating-type-path.html)) |
| Area Type Options (rows, columns, inset, first baseline, Text Flow by rows or by columns, Auto Size, Shrink Text to Fit) | done | Text Flow fills the cells down each column first (By Columns, the default, as files from before it open) or across each row first (By Rows), in the dialog and `text.areaOptions {flow}` (#4978); the commands that make or restyle type say when it does not fit its frame; MCP `add_text` takes Auto Size and Shrink Text to Fit (#1471) |
| Threaded text, Fit Headline, Text Wrap | done | Fit Headline sees wrapping in a frame one line tall (#1444); a thread holds area type and type on a path in any mix, as Illustrator links type in an area or along a path ([help](https://helpx.adobe.com/illustrator/using/manage-text-area.html)) |
| Threaded text, Fit Headline, Text Wrap | done | Fit Headline sees wrapping in a frame one line tall (#1444); a thread holds area type and type on a path in any mix, as Illustrator links type in an area or along a path ([help](https://helpx.adobe.com/illustrator/using/manage-text-area.html)); a frame's empty last paragraph keeps a format given to it when the story flows again |
| Threading from the ports: selected area type and type on a path show an in port and an out port, empty, with an arrow on a side where the frame is threaded, or with a red "+" in the out port when the text doesn't fit; a click on either port loads the text; a click on a closed path, area type or type on a path threads into it, a click on empty canvas makes a frame of the same kind, shape and size there, a drag draws a rectangular frame; the frame placed comes after the clicked one from its out port and before it from its in port (the story then starts there); Escape or another tool cancels; a double-click on a threaded port breaks the thread there | partial | #2572: the Selection tool, `text.thread.add {from, to?, rect?, at?, before?}`, as the reference app's help describes ports and threading ([archived help, Threading text between objects](https://web.archive.org/web/20210119051048/https://helpx.adobe.com/illustrator/using/creating-text.html), [create text threads](https://helpx.adobe.com/illustrator/desktop/design-with-text/add-manage-text/create-text-threads-between-objects.html)); area type's in port sits on the left side below the top-left corner, type on a path's just before its start bracket; Show Text Threads draws its lines between the same ports; with more than 1,000 such objects selected no ports are drawn. a double-click on a port where the frame is threaded breaks the thread there, the story flowing into the frames before the break and the frames after it left empty, still threaded with each other when two or more (`text.thread.break`, [archived help](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html)). Remaining: the ports only take clicks with the Selection tool, and the loaded cursor borrows the I-beam until it's drawn |
| Type on a Path effects and options; brackets | done | #429; Type on a Path Options has a Preview checkbox (#5360, as public tutorials of the dialog show); Direct Selection shows the anchors and direction handles of selected type on a path's path and drags both, the brackets keeping their fractions of the path and the text reflowing along it (#3980, #3565; `text.reshapePath`, `text.setPathHandle`), as the [Direct Selection page](https://helpx.adobe.com/illustrator/using/tool-techniques/direct-selection-tool.html) describes for a path; Type › Dynamic Text (`type.dynamicText`, also on the Contextual Task Bar) sizes type on a path to fill the span between its brackets and fits it again after each edit, as the [Dynamic Text overview](https://helpx.adobe.com/photoshop/desktop/text-typography/work-with-dynamic-text/dynamic-text-overview.html) describes; the 0.1 pt steps, the 0.1 to 1,296 pt range and keeping the drawn sizes when it is turned off or threaded are VectorCraft's own rules (`crates/text/src/tests/fit.rs`, `crates/engine/src/tests/dynamic_text.rs`) |
| Character and Paragraph panels, styles with overrides | done | Character-style Auto-leading resets, partial definitions, local leading overrides, override markers, Undo/Redo and native serialization have regression coverage; differing paragraph alignments leave the shared alignment buttons unselected (headless regressions, full integration CI and visual review pass); mixed-value display for other paragraph fields remains missing. Double-clicking a user character style's row opens its Character Style Options, and the dialog keeps a fixed width while its sections scroll when the window is shorter, so its buttons stay on screen; a paragraph style's row opens its Paragraph Style Options, the rename dialog the panel menu opens. Evidence: `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options`, `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list` |
| Character and Paragraph panels, styles with overrides | done | Character-style Auto-leading resets, partial definitions, local leading overrides, override markers, Undo/Redo and native serialization have regression coverage; differing paragraph alignments leave the shared alignment buttons unselected (headless regressions, full integration CI and visual review pass); mixed-value display for other paragraph fields remains missing. Double-clicking a user character style's row opens its Character Style Options, and the dialog keeps a fixed width while its sections scroll when the window is shorter, so its buttons stay on screen; a paragraph style's row opens the same Paragraph Style Options dialog as the panel menu, with General, Basic Paragraph Formats, Tabs, Composition, Hyphenation, Justification and East Asian Composition Settings. Evidence: `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options`, `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`, `dialogs::para_style_options::tests::every_section_draws_its_fields`, `dialogs::para_style_options::tests::tabs_section_edits_the_style_stops` |
| 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. |
@@ -48,7 +48,7 @@ against Illustrator's public documentation; **estimated** unless noted.
| Hyphenation… and Justification… options | done | Eleven Indian languages use attributed patterns and preserve syllables in both composers (#4947). Hyphenation… (`ui.hyphenation`, `text.setFormat`) sets each paragraph's Words Longer Than, After First, Before Last, Hyphen Limit, Hyphenation Zone (Single-line Composer only) and Hyphenate Capitalized Words, and paragraph styles carry them (#2310). Justification… (`ui.justification`, `text.setFormat`, #2755) sets Word Spacing, Letter Spacing and Glyph Scaling (Minimum, Desired, Maximum), Auto Leading and Single Word Justification; Desired applies to every line, justified lines and the Every-line Composer use the ranges. Vertical type isn't glyph scaled; type on a path doesn't use them. Evidence: `crates/text/src/tests/justify.rs`, `dialogs::justification::tests` |
| Roman Hanging Punctuation | done | Paragraph panel menu › Roman Hanging Punctuation, `text.setFormat {romanHangingPunctuation}` (#2756): per paragraph, Optical Margin Alignment's punctuation and amounts without the letters; paragraph styles carry it. Evidence: `tests::roman_hanging_punctuation_hangs_punctuation_but_not_letters`, `typecmd::area_tests::roman_hanging_punctuation_is_set_per_paragraph_and_saved_only_when_on` |
| Hyphenation… and Justification… options | done | Eleven Indian languages use attributed patterns and preserve syllables in both composers (#4947). Hyphenation… (`ui.hyphenation`, `text.setFormat`) sets each paragraph's Words Longer Than, After First, Before Last, Hyphen Limit, Hyphenation Zone (Single-line Composer only) and Hyphenate Capitalized Words, and paragraph styles carry them (#2310). Justification… (`ui.justification`, `text.setFormat`, #2755) sets Word Spacing, Letter Spacing and Glyph Scaling (Minimum, Desired, Maximum), Auto Leading and Single Word Justification; Desired applies to every line, justified lines and the Every-line Composer use the ranges. Vertical type isn't glyph scaled; type on a path doesn't use them. Auto Leading also sets the Leading the Character panel shows for Auto type, the leading Alt+↑/↓ (`type.step`) steps from, per paragraph, and the lines Fill with Placeholder Text counts. Evidence: `crates/text/src/tests/justify.rs`, `dialogs::justification::tests`, `tests_prefs::auto_leading_steps_and_fills_from_the_paragraph_auto_leading`, `panels::character` test `leading_field_shows_the_paragraph_auto_leading` |
| Tab stops and leaders; Tabs panel | partial | Tabs panel and stops with leaders in the model (`text.tabs.set`); a stop drags from where the press landed (#1458); leaders fill the tab's gap, repeated on a grid so they line up from line to line (#1877); Ctrl-dragging a stop (Command on macOS) moves all the stops together; in right-aligned area type the text before a line's last tab sits at its stops, from the left edge, and the text after it against the right edge, as Illustrator's own layout in a user's `.ai` sets a price list (evidence: `tests::right_aligned_area_type_sets_the_text_after_its_last_tab_against_the_edge`; centred and justified lines are still aligned as a whole) |
| Tab stops and leaders; Tabs panel | partial | Tabs panel and stops with leaders in the model (`text.tabs.set`); a stop drags from where the press landed (#1458); leaders fill the tab's gap, repeated on a grid so they line up from line to line (#1877); Ctrl-dragging a stop (Command on macOS) moves all the stops together; in right-aligned area type the text before a line's last tab sits at its stops, from the left edge, and the text after it against the right edge, as Illustrator's own layout in a user's `.ai` sets a price list (evidence: `tests::right_aligned_area_type_sets_the_text_after_its_last_tab_against_the_edge`; centred lines are still aligned as a whole); in justified area type the text up to a line's last tab keeps its spacing and its tabs their stops, and the text after the last tab takes the line's room to the far edge, VectorCraft's own rule (evidence: `tests::justify::a_justified_line_with_a_tab_spreads_the_text_after_it_to_the_edge` and the other tab tests in `crates/text/src/tests/justify.rs`) |
| Hidden characters (Show Hidden Characters) | done | `type.hiddenCharacters` |
| Spell check (Check Spelling, Auto Spell Check, custom dictionary) | partial | the engine (#4936): `text.spelling.check` lists the unknown words of the document's type, each run in its Language, at the bytes `text.editRange` changes, and `text.spelling.suggest` gives suggestions, both from craft-proof, each paragraph on its own (English from the first dictionary it finds: a Hunspell dictionary in the user's dictionaries folder, the SCOWL dictionary built in from craft-i18n, a system Hunspell dictionary, and last the public-domain Moby word list built into it; other languages from the dictionaries craft-proof finds for them). The custom dictionary commands are done: `text.spelling.addWord`, `removeWord` and `changeWord` (Add, and Edit Custom Dictionary's Add, Delete and Change), and `text.spelling.dictionary` lists the words, saved to `Custom Dictionary.txt` beside the preferences; Ignore All: `text.spelling.ignoreAll` skips a word in the active document while it is open. Auto Spell Check is done: Edit › Spelling › Auto Spell Check (`edit.autoSpellCheck`, off at first, saved with the view settings) draws a red wavy line under the unknown words of the visible type, leaves out type with effects, and follows edits, the custom dictionary and Ignore All. A right-click on an underlined word lists the words its dictionary suggests (each replaces it in one undo step, `text.editRange`), or No Suggestions, then Ignore All and Add to Dictionary, and a right-click on type has Spelling › Auto Spell Check, checked while it is on. Edit › Edit Custom Dictionary… lists the custom dictionary's words, with an Entry field and Add, Delete and Change, applied on Done (Cancel leaves the dictionary as it was). The Check Spelling dialog remains. Evidence: `crates/engine/src/tests/spelling.rs`, `crates/ui-egui/src/canvas/tests.rs` (`auto_spell_check_*`), `crates/ui-egui/src/tests/contextmenu.rs` (the right-click), `crates/ui-egui/src/dialogs/custom_dictionary.rs` (the dialog). |
| Smart Punctuation, Change Case, Find & Replace | done | |
+9 -6
View File
@@ -161,7 +161,8 @@ tools, never checked side by side) isn't captured row by row.
| Node-editing cursors: Direct Selection's white arrow with a hollow or filled square over an anchor (path unselected or selected); the Reshape Segment cursor over a curved segment (Direct Selection, Anchor Point, the Pen with Alt); the Anchor Point caret for the Pen with Alt; Group Selection's plus (also Direct Selection with Alt); a filled arrow while dragging that turns hollow on a snap; a double arrowhead for an Alt-drag copy | partial | as the public manual describes them, drawn in code; no cue over handles (none documented) |
| Anchor Point and Curvature tools' own cursors | done | the caret on the anchor it converts; the pen nib with a curve badge |
| Free Transform's Cmd/Ctrl side-shear cursor | done | two arrows passing along the side (ShearH, ShearV), instead of the resize arrow across it |
| Cursors of their own for Lasso, Magic Wand, the freehand tools, Shaper, Live Paint Bucket, Measure, Print Tiling, the symbolism tools, vertical type, Scissors (over a segment and away), Mesh and Puppet Warp | partial | each tool returns its own cursor and Use Precise Cursors turns them into the crosshair; until they're drawn they borrow the crosshair, the I-beam, the Pen's add cursor or not-allowed |
| Live Paint Bucket cursors | partial | a filled bucket over a face a click fills, a brush over an edge it paints, an empty bucket where it paints nothing, following Paint Fills, Paint Strokes and Shift; the Eyedropper with Alt; the crosshair with Use Precise Cursors. No cursor swatch preview |
| Cursors of their own for Lasso, Magic Wand, the freehand tools, Shaper, Measure, Print Tiling, the symbolism tools, vertical type, Scissors (over a segment and away), Mesh and Puppet Warp | partial | each tool returns its own cursor and Use Precise Cursors turns them into the crosshair; until they're drawn they borrow the crosshair, the I-beam, the Pen's add cursor or not-allowed |
| Blob Brush and Eraser: the tip's outline at the pointer | done | before a press and while painting, the outline of the tip (its size, angle and roundness, at the zoom) follows the pointer, black on a white halo, round a small centre dot; `[` / `]` and Tool Options change it at once; precise cursors (Caps Lock) show only the crosshair; it goes when the pointer leaves the canvas, while Cmd/Ctrl lends a selection tool and while Alt lends the Blob Brush the Smooth tool (#4977); `crates/tools/src/draw2/gesture.rs` and its tests While the button is down a round Blob Brush shows the outline of the area it will add, rounded at both ends, not a line as wide as the brush that ends square (#4343). |
| Busy cursor during long operations | done | Background open, save and export show the system progress cursor (gap G1) |
| Each anchor snaps to Smart Guides and construction angles | done | `DrawSnap` |
@@ -249,7 +250,7 @@ tools, never checked side by side) isn't captured row by row.
| A double-click on a ruler adds a guide at the pointer: a vertical one on the top ruler, a horizontal one on the left ruler (swapped in a view turned 90°, where the rulers read the other document axis; the nearer axis in between), exactly where the pointer is (no snapping), shown even with guides hidden, one undo step (`guide.add`); both clicks of the pair must be on that ruler, and the first adds nothing. The [public page on rulers](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html) doesn't describe this gesture, so it follows the request in #5239 | done | #5239; an artboard guide with the Artboard tool, as a drag from a ruler makes one; `canvas/tests.rs` `double_clicking_a_ruler_adds_a_guide_at_that_position`, `a_canvas_click_then_a_ruler_click_adds_no_guide`, `a_ruler_double_click_follows_a_rotated_view` |
| Escape cancels an existing ruler-guide press, move or copy | done | Selection and Direct/Group Selection; app-input regressions cover preselected guides, document/interaction-start selection, Undo/Redo and saved state, later on-canvas motion/release and temporary borrowing from idle Pen. All eight checks and the Direct Selection Escape control passed in current full CI. [Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area) |
| Ruler origin: corner drag with preview, snapped as a drawn point is (Smart Guides, Snap to Point and the ruler guides, pixels, the grid), double-click reset to the active artboard, relative X/Y fields, native save/reopen | done | #1212; `document.setRulerOrigin`, `document.resetRulerOrigin`; geometry and command coordinates stay absolute |
| Ruler guides as Layers rows: shown, hidden, locked and deleted with their layer; rows dragged to another layer | done | `guide.setLayer`; guides from older files go on a layer |
| Ruler guides as Layers rows: shown, hidden, locked and deleted with their layer; rows dragged to another layer; each row's own eye and lock; a row dropped on the trash deletes its guide | done | `guide.setLayer`; guides from older files go on a layer; `layer.setProps` on a guide's id hides or locks it alone; Object › Lock/Hide › Selection, Unlock All and Show All reach guides; `guide.remove {indexes}` |
| Objects made into guides: drawn in the guide colour and style of Preferences; Lock Guides locks them too (no select, move or delete); a dragged selection snaps to ruler guides with Smart Guides on, by the nearest point of the path when pressed on its stroke or beside its edge, so the path lands exactly on the guide | done | #1534 (#1543, #1583); #3701 |
| A selected ruler guide's X or Y in the Transform panel; typing a value moves it | not scored | #1659 (part of #1597); several guides move together; not in Properties or the Control bar. Not established by the documentation we could find: the help pages on guides, rulers and the Transform, Control and Properties panels (read again on 2026-10-14) don't describe it, and the one public statement is a user's [community answer](https://community.adobe.com/questions-652/adding-a-new-custom-guide-785586), which is not documentation. Kept as a convenience pending the owner's decision; no parity claim |
| Alt swaps a ruler guide's orientation while dragging | missing | Alt-drag copies the selected guides ([Use rulers](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html)); `guide.move` `{copy: true}` |
@@ -382,7 +383,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; 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` |
| 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; View more opens the tutorial's practice sample as a new untitled document, Finish tutorial then marks it finished, and Next Up shows the next tutorial in the same skill (no video) | done | [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `learn.list`, `learn.skills`, `learn.show`, `learn.locateTool`, `learn.openSample`, `learn.finish`, `learn.next`; 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 |
@@ -424,7 +425,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| 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) |
| Make Compound Path on a group (such as outlined type) keeps its backmost path's paint; compound paths joined into one keep their holes | done | #1537 |
| Symbol libraries: Window › Symbol Libraries (Basic Shapes, UI Frames, Map Markers, Inventory Icons, Skill Icons, User Defined, Other Library…, Save Symbols…) and the Symbols panel's Symbol Libraries Menu button and its menu's Open Symbol Library / Save Symbol Library…; a click on a library symbol adds it to the document's Symbols (one undo step, it becomes the current symbol), Shift/Cmd-clicks select for Add to Symbols, and a symbol dragged onto the canvas is added and placed where it is dropped | done | [public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-or-import-symbol-libraries.html), [Symbols panel options](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/symbols-panel-options.html), [placing from a library](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-and-place-symbols.html); `window.symbolLibrary`, `window.symbolLibrary.other`, `ui.saveSymbolLibrary` (dialog `saveSymbolLibrary`), `window.userSymbolLibrary1`…`10`; `panels::tests_symbol_libraries`; five built-in libraries (Basic Shapes, UI Frames with default 9-slice, Map Markers, Inventory Icons, Skill Icons; original, generated in code) |
| Brush libraries: Window › Brush Libraries (Art Ribbons, Calligraphic Nibs, Pattern Borders, Scatter Shapes, User Defined, Other Library…, Save Brushes…) and the Brushes panel's Brush Libraries Menu button and its menu's Open Brush Library / Save Brush Library…; a click on a library brush adds it to the document's Brushes (one undo step, it becomes the current brush, and selected paths take it in the same step), Shift/Cmd-clicks select for Add to Brushes, a brush dragged onto a path is added and applied to it and one dragged onto the Brushes panel is added; thumbnails render with the library's patterns and symbols | done | [public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/create-new-brush-libraries.html), [import brushes](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/import-brushes-from-brush-libraries-or-other-files.html), [Brushes panel overview](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/brushes-panel-overview.html); `window.brushLibrary`, `window.brushLibrary.other`, `ui.saveBrushLibrary` (dialog `saveBrushLibrary`), `window.userBrushLibrary1`…`10`; `panels::tests_brush_libraries`; four built-in libraries (original, generated in code) |
| Brush libraries: Window › Brush Libraries (Art Ribbons, Ink and Wash, Calligraphic Nibs, Bristle Brushes, Pattern Borders, Scatter Shapes, Game Trails, Energy Beams, Dry Media, User Defined, Other Library…, Save Brushes…) and the Brushes panel's Brush Libraries Menu button and its menu's Open Brush Library / Save Brush Library…; a click on a library brush adds it to the document's Brushes (one undo step, it becomes the current brush, and selected paths take it in the same step), Shift/Cmd-clicks select for Add to Brushes, a brush dragged onto a path is added and applied to it and one dragged onto the Brushes panel is added; thumbnails render with the library's patterns and symbols | done | [public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/create-new-brush-libraries.html), [import brushes](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/import-brushes-from-brush-libraries-or-other-files.html), [Brushes panel overview](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/brushes-panel-overview.html); `window.brushLibrary`, `window.brushLibrary.other`, `ui.saveBrushLibrary` (dialog `saveBrushLibrary`), `window.userBrushLibrary1`…`10`; `panels::tests_brush_libraries`; nine built-in libraries (original, generated in code) |
| Symbols panel: New Symbol opens Symbol Options (Name) before making the symbol; Alt-click skips it | partial | #1560 (#1566); Registration 9-point grid in Symbol Options (`symbol.new` / `symbol.place`); Export Type (Graphic / Movie Clip) and Enable Guides for 9-Slice Scaling (scaled instances keep their corners; in symbol editing mode the guides are drawn over the art and the Selection tool drags them); Symbol Type (Dynamic Symbol / Static Symbol, new symbols dynamic; #4938) |
| Dynamic symbols: an instance changes the fill, stroke, opacity and blend of objects in its symbol's art and stays linked; Reset in the Properties panel's Quick Actions and the Symbols panel menu (Reset Symbol Instance) | partial | #4938; `symbol.instance.setAppearance` (by child-index path) and `symbol.instance.reset`; redefining keeps the changes, Break Link bakes them; Direct Selection of a path, compound path or type object inside an instance, with the Fill/Stroke controls setting its solid fill or stroke, is on canvas; on-canvas editing beyond a solid fill and stroke, and `.ai` read and write, remain |
| Pathfinder Trim, Merge and Crop remove strokes; Merge joins same-fill objects whatever their strokes | done | #1570 (#1572) |
@@ -436,7 +437,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Canvas context menu | done | Layers rows, panels and type have none (gap) |
| SVG Interactivity panel: JavaScript for each object's events and the document's linked JavaScript files, written to SVG and read back as text | done | `svgInteractivity.*`, `ui.javascriptFilesDialog` |
| Retype, Comments, Version History panels | missing | the last two are cloud features (out of scope) |
| Blend panel (30.7) | partial | Window › Blend provides Type (Ease In, Ease Out, Ease In Out, Ease Out In), Ramp 0–100% and independent color Shift, alongside the existing blend controls. Easing is disabled without a spine. Original polynomial distributions approximate the publicly documented behavior; on-canvas easing handles remain missing. [Public source](https://helpx.adobe.com/illustrator/desktop/manage-colors/apply-transparency-and-blending/blend-panel-overview.html) |
| Blend panel (30.7) | partial | Window › Blend provides Type (Ease In, Ease Out, Ease In Out, Ease Out In), Ramp 0–100% and independent color Shift, alongside the existing blend controls. Easing is disabled without a spine. Original polynomial distributions approximate the publicly documented behavior. On-canvas Appearance and Easing widgets set Shift and Ramp; a blend without a spine shows the Appearance widget alone. [Public source](https://helpx.adobe.com/illustrator/desktop/manage-colors/apply-transparency-and-blending/blend-panel-overview.html) |
| Gradient panel: Dither (30.0) | done | Dither box under the stops (`paint.editGradient` `dither`); dimmed behind the Stroke proxy, fills only; `panels::gradient` test |
| Gradient panel: Method, Classic or Perceptual (30.0) | done | Method menu under the Stroke buttons for linear and radial gradients (`paint.editGradient` `method`); [public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-perceptual-interpolation-in-gradients.html); `panels::gradient` test |
| Gradient panel: Presets (30.0) | done | Presets menu by library (theme) plus Saved: applies a preset to the gradient behind the active proxy, keeping its placement, one undo step; panel menu Save Gradient Preset…, Delete Gradient Preset, Hide/Show Presets (`gradient.presets.*`); `panels::gradient` tests |
@@ -479,7 +480,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Color Effects add-on (VectorCraft addition): Object › Plug-ins › Color Effects › Find Nearest Swatch… picks a Library (Document Swatches, a built-in library, or a library or colour book the user saved or loaded) and Fill and Stroke, lists each colour of the selection with its three closest swatches (chip, name, ΔE 2000), and Apply gives each colour its nearest swatch (global and spot swatches linked; a library's swatches added to the document first), one undo step | done | #4923; `addon.color.nearestSwatch`, `addon.color.applyNearestSwatch`, `crates/engine/src/cmd/addons/color/nearest.rs`, `crates/ui-egui/src/addons/color/nearest.rs` and their tests; not a feature of the reference app, so it is not scored |
| Selection Tools add-on (VectorCraft addition): Object › Plug-ins › Selection Tools › Select by Artboards… lists every artboard (number, name, width × height in the document's units), filters them by name, chooses several (click, Shift-click for a range, Cmd/Ctrl-click to toggle; Select All / None act on the rows shown; a count of those chosen), fits a row's artboard in the window on double-click and makes it active, and on OK selects the objects overlapping, fully inside or fully outside the chosen artboards (Overlapping is All on Active Artboard's test) | done | #5525, #5548, #5549; `addon.select.byArtboards`, `crates/engine/src/cmd/addons/select.rs`, `crates/ui-egui/src/addons/select.rs` and their tests; not a feature of the reference app |
| Selection Tools add-on (VectorCraft addition): Select Linked, Select Embedded, Select Missing and Select Modified in Object › Plug-ins › Selection Tools and the Links panel menu (off when no image has that status; Shift adds to the selection) select the images and placed documents with that link status, leaving out hidden, locked, template-layer and outside-isolation ones, which the status bar counts (e.g. "Selected 9 missing images; 3 more are hidden or locked"); Select by Link Status… lists them with name, status and format, searches by name, filters by status, picks with checkboxes and All / None, and selects with Zoom to Selection on by default | done | #5526; `addon.select.byLinkStatus`, `crates/engine/src/cmd/addons/select.rs`, `crates/ui-egui/src/addons/select/link_status.rs` and their tests; not a feature of the reference app |
| Style panels: a double-click on a user character style's row opens its Character Style Options, and on a paragraph style's row its Paragraph Style Options (rename), and the Character Style Options dialog keeps a fixed width, its sections scrolling when the window is shorter so OK and Cancel stay on screen | done | `ui.charStyleOptions`, `ui.paramDialog` (`paraStyle.rename`); `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options`, `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list` |
| Style panels: a double-click on a user character style's row opens its Character Style Options, and on a paragraph style's row opens the same Paragraph Style Options as the panel menu, with General, Basic Paragraph Formats, Tabs, Composition, Hyphenation, Justification and East Asian Composition Settings; the Character Style Options dialog keeps a fixed width, its sections scrolling when the window is shorter so OK and Cancel stay on screen | done | `ui.charStyleOptions`, `ui.paraStyleOptions` (`paraStyle.edit`); `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options`, `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`, `dialogs::para_style_options::tests::every_section_draws_its_fields`, `dialogs::para_style_options::tests::tabs_section_edits_the_style_stops` |
## Look and feel
@@ -489,6 +490,8 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
- [x] Recolor Artwork › Color Theme Picker: with the button beside the tabs on, a click on the canvas takes the color palette of an object or image, a drag takes the palette of the art inside its marquee, and Shift adds more picks; the rows that aren't excluded take the palette as their new colors and the art previews them ([Recolor artwork](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/recolor-artwork.html)). The number and order of the picked colors are VectorCraft's own rule (`recolor.pickTheme`). Tests: `theme_picks_give_the_rows_the_palette_of_the_art_picked` and `the_theme_picker_takes_clicks_and_shift_drags_on_the_canvas` in `crates/ui-egui/src/dialogs/recolor.rs`, `theme_picker_*` in `crates/engine/src/tests/recolor.rs`.
- [x] Recolor Artwork › Prominent Colors: with art, a bar under the Edit tab's wheel shows the new colors, each as wide as its share of the art; dragging the edge between two parts raises one color's weight and lowers the other's, and the art previews the result; the New Color Group menu has Save All Colors and Save Prominent Colors ([Recolor options overview](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/recolor-options-overview.html)). How a share is measured and how a weight moves colors between rows are VectorCraft's own rules (`recolor.weigh`). Tests: `weights_move_the_rows_colors_and_the_art_previews_them`, `dragging_a_prominent_edge_moves_colors_between_rows` and `save_all_and_prominent_colors_make_color_groups` in `crates/ui-egui/src/dialogs/recolor.rs`, `weigh_*` in `crates/engine/src/tests/recolor.rs`.
- [x] Recolor Artwork Edit color chips scroll within the screen, preserving the wheel and actions (#3416); 5, 20, 200 and 400 colors, resizing and UI scaling, final-chip editing and preview/history covered by egui_kittest.
- [x] Status messages and bounded readouts remain stable as values, canvas hover and background jobs change; ordinary values fit all registered languages and units, and optional groups yield in narrow windows (regressions; contributor rebuilt and confirmed the original-file status-bar scenario on 2026-10-19). Sample widths are cached per UI and refreshed only when language, document units or pixels per point changes.