mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 11:55:23 +00:00
Merge main into feat/character-anti-aliasing-method
This commit is contained in:
@@ -51,7 +51,8 @@ A request the app hasn't answered after **60 s** gets `{"ok": false, "error": "t
|
||||
app had not started it (it was busy, or its window wasn't being drawn), the request is withdrawn
|
||||
and never runs, and the error says "will not run": retrying is safe. If the app was already
|
||||
running it, the error says "still running": it may yet complete, so check the document before
|
||||
retrying.
|
||||
retrying. On macOS the app keeps itself out of App Nap while the control channel is on, so it keeps
|
||||
answering with its window hidden, minimized or covered, or with the screen locked.
|
||||
|
||||
## Confining file access
|
||||
|
||||
@@ -95,15 +96,15 @@ the two can still win that race.
|
||||
| `ui.menu.list` / `ui.menu.invoke` | `{command, params}` | the full menu tree / invoke an item |
|
||||
| `ui.contextMenu.list` | | the canvas context menu for the current selection, flattened like `ui.menu.list` (`path` holds its submenus). `ui.click {x, y, button: "right"}` on the canvas opens it, after selecting the object there unless it is already selected. While a menu opened over locked objects is open, it has an `Unlock` submenu with one item for each locked object under the pointer, topmost first and at most 20, labeled with the object's name, which runs `layer.setProps {ids: [id], locked: false}`. A locked group gets one item, and the locked objects inside it get none. Hidden objects, objects on hidden, locked or template layers, and in isolation mode objects outside the isolated group get no item |
|
||||
| `ui.tool.select` / `ui.tool.list` | `{tool}` | |
|
||||
| `ui.pointer` | `{events:[{kind: down|drag|up|move|doubleclick, x, y, space?: "doc"|"screen", mods?}]}` | drive the active tool exactly like the mouse: `mods.cmd` held at a press with any tool but a selection tool drags with the selection tool chosen last (Direct Selection with the Pen until one is), and the release gives the tool back as it was; the reply lists `selection`, `key` (the key object, null without one) and `tool` |
|
||||
| `ui.pointer` | `{events:[{kind: down|drag|up|move|doubleclick, x, y, space?: "doc"|"screen", mods?}]}` | drive the active tool exactly like the mouse: `mods.cmd` held at a press with any tool but a selection tool drags with the selection tool chosen last (Direct Selection with the Pen until one is), and the release gives the tool back as it was; the reply lists `selection`, `key` (the key object, null without one) and `tool`. With the Shape Builder or the Live Paint Bucket and a selection of more than 128 segments, the app finds the regions off the UI thread, so a `move` replies before the region under the pointer is highlighted, and the highlight shows in the first frame after the regions are found |
|
||||
| `ui.key` / `ui.text` | `{key, shift?, alt?, cmd?}` / `{text}` | synthetic keyboard input |
|
||||
| `ui.move` | `{x, y}` | screen-point pointer move |
|
||||
| `ui.click` / `ui.drag` | `{x, y, button?, count?, shift?, alt?, ctrl?, cmd?}` / `{x, y, toX, toY, steps?, count?, button?, shift?, alt?, ctrl?, cmd?}` | egui pointer input; see synthetic input budgets above |
|
||||
| `ui.wheel` | `{x, y, dy?, dx?, unit?: "line"\|"point", shift?, alt?, cmd?}` | a mouse wheel turn over screen point (x, y): `dy` notches up (+) or down, `dx` sideways. Over the canvas the wheel scrolls and Cmd- or Alt-wheel (Option on the Mac) zooms about the pointer; with the `zoomWithMouseWheel` preference the wheel and Alt-wheel zoom about the pointer, Shift-wheel scrolls up and down and Cmd-wheel (Ctrl on Windows and Linux) sideways. Over a focused numeric field (click it first) each notch steps its value as Up/Down do (Shift: ten, Cmd/Ctrl: a tenth) and the panel stays put; over anything else in a panel the wheel scrolls it |
|
||||
| `ui.set` | `{brightness?, panel?, rulers?, outline?, grid?, smartGuides?, boundingBox?, controlBar?}` | |
|
||||
| `ui.set` | `{brightness?, panel?, rulers?, outline?, grid?, smartGuides?, boundingBox?, controlBar?, imageTracePreview?}` | `imageTracePreview`: the Image Trace panel's Preview (`ui.inspect` shows it as `ui.image_trace_preview`) |
|
||||
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value}` | fill and submit the open dialog |
|
||||
| `ui.screenshot` | `{path?, data?}` | capture the window (PNG), written to `path`; `data: true` sends it back as `pngBase64` too. Needs a presented frame: with the screen locked or the window minimized/covered it fails after ~8 s with an explanatory error |
|
||||
| `ui.render` | `{path?, scale?, artboard?, data?}` | render an artboard headlessly (PNG; `artboard`: its 0-based index, default the first): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
|
||||
| `ui.render` | `{path?, scale?, artboard?, data?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
|
||||
| `ui.resize` / `ui.focus` | | |
|
||||
| `app.open` / `app.save` / `app.export` / `app.quit` | `{path}` / `{path?}` / `{path?, format?, artboard?, range?, scale?, …}` | `app.open` reads every format `document.open` reads (see `document.formats`) and returns its result (`{index, title, format, warnings, …}`; `warnings` say what didn't come in as it was, such as an EPS shown as its preview image and why its PostScript couldn't be read), or null when a dialog asks first or a library loads. `app.export` encodes through the engine's `document.export` (same options; the document keeps its path) and writes `path` through the host; without `path` it returns `{dataBase64, format, bytes}`, as headless mode does. `app.quit`, `file.close` and `file.closeAll` first open a `saveChanges` dialog for each modified document (they return `{"pending": "saveChanges"}`): `ui.dialog.confirm` saves, `ui.dialog.set {field: "discard", value: true}` then confirm discards, `ui.dialog.cancel` cancels the whole close or quit |
|
||||
| `app.export` with `useArtboards` | `{path, useArtboards: true, range? \| artboards?, …}` | Export As: PNG, JPEG and WebP write one file per chosen artboard (default all; SVG does so for a `range`), `<path stem>-<artboard>.<ext>`, and return `{path, files: [path…]}`; a PDF keeps them as pages of `path`. `useArtboards: false` covers the bounds of the visible art. The menu's Export As… is `file.exportAs`: the `exportAs` dialog (`format`, `useArtboards`, `all`, `range`), then a save dialog and the format's options: `pngOptions` / `jpgOptions` / `webpOptions`, `svgOptions`, or `savePdf` for a PDF with Use Artboards |
|
||||
@@ -118,7 +119,7 @@ the two can still win that race.
|
||||
| `app.export` Text and WebP | `{path?, format: txt, encoding?: utf8 \| utf16, lineEndings?: lf \| crlf, selectionOnly?}` / `{format: webp, lossless?, quality?}` | Text writes the stories in stacking order (back to front; a thread once; hidden objects and template layers left out), each line ended by `lineEndings`; `utf16` is little-endian with a byte order mark. Export As → Text opens `txtOptions` (fields `path`, `encoding`, `lineEndings`, `selectionOnly`; OK writes the file). WebP is always lossless: `lossless: false` adds a warning |
|
||||
| `engine.execute` `document.exportForOffice` | `{}` / `{path?, ppi?: 72 \| 150 \| 300, transparent?, artboard?}` | File → Save for Office Documents…: without params opens `saveForOffice` (fields `ppi`, `transparent`, `artboard`; OK picks the file); with params writes the artboard as a PNG (`path`, else a picked file) → `{path}`, and the status bar shows the first of the export's `warnings` as Save As shows a save's notes. Headless it returns `{dataBase64, width, height, warnings}` |
|
||||
| Native file dialogs | any command with no `path` that needs one (`file.open`, `file.saveAs`, `file.place`, exports…) | on macOS (a sheet on the window) and Linux the dialog doesn't hold the window: the call returns `"cancelled"` at once, `ui.inspect` reports `fileDialog`, and when the person at the computer picks a file the command runs again on the document it was asked for. While it is open another dialog is refused: that call returns `"cancelled"` too, and the status bar (`ui.inspect` → `ui.status`) says `"A file dialog is open: choose there first"`; on macOS `app.quit`, `file.close` and `file.closeAll` return that message and the menu bar does nothing (#592, #867). On Windows the dialog is shown in line: the call returns once it closes. Pass `path` to skip the dialog |
|
||||
| Background Save / Export | `app.save`, `app.export {path}`, `file.save`, `file.saveAs`, `file.saveCopy`, `file.exportForScreens {folder, …}` | with the `backgroundSave` / `backgroundExport` preferences on (the default) the desktop app writes the file from a snapshot on a worker thread: the call returns `{path, background: true, status}` at once (`file.exportForScreens`: `{background: true, status}`, and the files are written into the folder from a snapshot taken at the call), `ui.inspect` lists the jobs still running in `background` (labels such as `"Saving a.vectorcraft"`), and the document counts as saved (as of the snapshot) once its file is written. Quit and Close wait for them. With the preferences off (`prefs.set`), saves return once written |
|
||||
| Background Save / Export | `app.save`, `app.export {path}`, `file.save`, `file.saveAs`, `file.saveCopy`, `file.exportForScreens {folder, …}` | with the `backgroundSave` / `backgroundExport` preferences on (the default) the desktop app writes the file from a snapshot on a worker thread: the call returns `{path, background: true, status}` at once (`file.exportForScreens`: `{background: true, status}`, and the files are written into the folder from a snapshot taken at the call), `ui.inspect` lists the jobs still running in `background` (labels such as `"Saving a.vectorcraft"`), and the document counts as saved (as of the snapshot) once its file is written. Quit and Close wait for them. A job that fails reports the error in the status bar only (`ui.inspect` → `ui.status`), a path outside `--automation-write-root` included. With the preferences off (`prefs.set`), saves return once written. `app.save {path?, wait: true}` and `app.export {path, wait: true}` wait for the jobs already running, write the file before they reply whatever the preferences are set to, and return its result or the error. 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 preferences. A `wait` that is not `true`, `false` or `null` is refused with "`wait` must be true or false", and nothing is written |
|
||||
| `engine.execute` `file.exportForScreens` | `{}` / `{…document.exportForScreens params}` | without params opens `exportForScreens` on the settings the document last exported with (`document.exportSettings`). While the Artboard tool is in use, the artboards it has selected (its `selected` option, the rows the Artboards panel highlights) are checked instead, and `select` is `range`, or `all` when every artboard is selected. Fields: `tab` (`artboards` \| `assets` \| `layers` \| `selection`; OK reads Export Layer or Export Selection on those), `layers` ([layer id…] checked), `selectionMode` (`one` \| `each`), `name` (the file name pattern of `document.exportForScreens`), `counterStart`, `folders` (`none` \| `rows` \| `format` \| `item` \| `itemFormat`), `exportPreset` (the preset applied last), `__savePreset` (the name typed to save one), `select` (`all` \| `range` \| `full`), `range` ("1-3, 5"), `boards` (a bool per artboard; checking them sets `select` and `range`), `includeBleed`, `folder` (desktop; default the Desktop, else home), `openLocation`, `subfolders`, `prefix`, `preset` (`""` \| `mobile` \| `density`), `formats` (rows `{format, scale: "2x" \| "100w" \| "100h" \| "72ppi", suffix, quality?, folder?}`), `settings` (`{png \| png8 \| jpg \| webp \| gif \| svg \| pdf: {…options}}`, the gear's Format Settings; `__settings` names the format shown). `ui.dialog.confirm` exports (a bad range or no folder keeps it open); on the web OK reads "Download" and the files download (several as one `.zip`). With params it runs `document.exportForScreens`: no `folder` returns the files (or with `zip: true` one store-only zip) as `dataBase64`; with `openLocation` the first file is shown in the file manager. When `warnings` is not empty, an export into a folder or a download shows the first in the status bar as Save As shows a save's notes |
|
||||
| Export for Screens' Assets tab, Asset Export panel | `ui.dialog.set` `tab: "assets"`, `assets: [asset id…]`; `window.panel {panel: "assetExport"}` | on the Assets tab of `exportForScreens` the `assets` field lists the checked asset ids (all of them when it opens); `ui.dialog.confirm` runs `document.exportForScreens {assets}` (nothing checked keeps it open) and OK reads "Export Asset". The Asset Export panel acts through `assets.*` and shares the format rows with the dialog (`assets.settings.set`) |
|
||||
| `engine.execute` `file.exportSelection` | `{multiple?}` | File › Export Selection…: collects the selected objects as one asset (`assets.add`; one per object with `multiple: true`) and opens `exportForScreens` on its Assets tab with just them checked → `{assets}`. `document.exportSelection` still exports the selection in one go, without the dialog |
|
||||
@@ -655,7 +656,9 @@ EPS Options: Export As… with the EPS format (`file.exportAs {format: "eps"}`,
|
||||
(`preserve`, `discard`), `flattenerPreset` (a built-in or saved preset's name), `includeLinkedFiles`, `thumbnails`,
|
||||
`cmykPostScript`, `compatibleGradients`, `level` (`2`, `3`), plus `path` and the artboard choice (`useArtboards`,
|
||||
`range`). `ui.dialog.confirm` checks them (a bad value or an unknown preset keeps the dialog open), remembers them for
|
||||
the next EPS Options and writes the file(s), asking for a path when there is none.
|
||||
the next EPS Options and writes the file(s), asking for a path when there is none. With Use Artboards and two or more
|
||||
artboards, it writes a central file of the whole document under the chosen name and one `{stem}_{artboard}.eps` per
|
||||
artboard.
|
||||
|
||||
Data Recovery: at launch, copies left behind by a crash open the `recovery` dialog (never copies another running
|
||||
instance keeps, so instances started side by side for screenshots don't get it). Fields: `copies` (`[{file, title,
|
||||
|
||||
+3
-1
@@ -5,7 +5,9 @@ The **Dimension Tool** is in the Modify section of the toolbar. Its strip offers
|
||||
|
||||
- **Linear:** hover a path segment and drag outwards: releasing places the dimension. Or click two
|
||||
points (anchors snap, Shift constrains to 45°), then click to place. A curved segment is measured
|
||||
from end to end.
|
||||
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.
|
||||
- **Angular:** hover a corner where two straight segments meet and drag outwards. Or click two
|
||||
straight lines that meet (or would, extended), then click to place.
|
||||
- **Radial:** hover a circle, or a segment that is a perfect arc, and drag: outwards gives its
|
||||
|
||||
File diff suppressed because one or more lines are too long
+15
-14
File diff suppressed because one or more lines are too long
@@ -36,7 +36,14 @@ its own palettes are chosen; this is VectorCraft's own method (see
|
||||
slowly, and the trace says so: `imageTrace.make` returns the note in `warnings` and the status bar
|
||||
shows it (the [results optimization](https://helpx.adobe.com/illustrator/using/image-trace-results-optimization.html)
|
||||
page describes the same warning for images over 1500 px). An image of more than 64 megapixels (up to
|
||||
192) is traced from a copy scaled down to 64 megapixels, and the trace says that too. A trace that would
|
||||
192) is traced from a copy scaled down to 64 megapixels, and the trace says that too. Flat Logo traces at
|
||||
most 1024 pixels on a side: a larger decoded raster is averaged down in whole-number blocks first (a 1025-pixel
|
||||
image is traced at 513, and a 16,000 × 12,000 image, decoded at 9,459 × 7,094, is traced at 946 × 710), and the trace names both sizes in `warnings`. Flat Logo gives that note in place
|
||||
of the slower-trace note for an image over 1500 pixels and in place of the 64-megapixel note. An image at most
|
||||
1024 pixels on a side is traced at its own size, also when the named `logoColors` differ slightly from the
|
||||
image's colors. Only an image with more than 150,000 different pixel values that are not exactly one traced
|
||||
color is averaged down further: the factor doubles until at most 150,000 remain or the working copy has at
|
||||
most 16,384 pixels (at most six times). A trace that would
|
||||
make more than 8,000,000 anchor points (2,000,000 on the web) is refused to protect memory, with advice:
|
||||
raise Noise, lower Paths or Colors, or pick a lower-fidelity preset. A trace keeps
|
||||
about 90 bytes an anchor; a 4000 × 3000 photo at High Fidelity Photo makes about 1.9 million.
|
||||
|
||||
@@ -57,7 +57,8 @@ nothing changes: 14, 72, 199 and 98 colours, the same paths, anchors and rendere
|
||||
|
||||
|
||||
## Large images
|
||||
Images over 1500 px on a side are traced with a warning that they trace more slowly (see
|
||||
Images over 1500 px on a side are traced with a warning that they trace more slowly (Flat Logo names
|
||||
the size it traced at instead; see
|
||||
[image-trace-adjustments.md](image-trace-adjustments.md)), and the anchor limit that guards memory is
|
||||
8,000,000 (2,000,000 on the web) instead of 2,000,000. Measured through `imageTrace.make` (release
|
||||
build, 4 cores, one trace at a time; memory is the peak allocated above the placed image, and what the
|
||||
|
||||
+162
-59
@@ -9,7 +9,7 @@ It has two backends:
|
||||
| Mode | How | What works |
|
||||
|---|---|---|
|
||||
| **Remote** | `vectorcraft-cli mcp --connect 127.0.0.1:7979` (the app must run with `vectorcraft --control 7979`) | Everything. Tool calls are forwarded over the [control protocol](control-protocol.md), so you watch the app change live. |
|
||||
| **Headless** | `vectorcraft-cli mcp --headless` | An in-process engine session with a CPU renderer. Everything except the UI-only tools (`inspect_ui`, `type_text`, `open_panel`, `screenshot {window:true}`). |
|
||||
| **Headless** | `vectorcraft-cli mcp --headless` | An in-process engine session with a CPU renderer. Everything except the UI-only tools (`inspect_ui` or `ui_inspect`, `type_text`, `open_panel`, `screenshot {window:true}` or `ui_screenshot`). |
|
||||
|
||||
With no flag, the server tries `127.0.0.1:7979` and falls back to headless. Logs go to stderr; stdout carries
|
||||
only protocol messages.
|
||||
@@ -69,6 +69,25 @@ claude mcp add vectorcraft -- "C:\Program Files\VectorCraft\vectorcraft-cli.exe"
|
||||
claude mcp add vectorcraft -- vectorcraft-cli mcp
|
||||
```
|
||||
|
||||
For a live session, start the installed app with the control port before the MCP client starts the server:
|
||||
|
||||
```sh
|
||||
# macOS: quit VectorCraft first, since `open -a` passes no arguments to an app that is already running
|
||||
open -a VectorCraft --args --control 7979
|
||||
# Windows (PowerShell), default install folder
|
||||
& "C:\Program Files\VectorCraft\vectorcraft.exe" --control 7979
|
||||
# Linux (deb, rpm)
|
||||
vectorcraft --control 7979
|
||||
```
|
||||
|
||||
The Dock, Start menu and desktop launchers start the app without `--control`, so the app opens no control
|
||||
port unless `VECTORCRAFT_CONTROL_PORT` is set. `vectorcraft-cli mcp` with no flag tries `127.0.0.1:7979`
|
||||
once, when it starts: a server that started before the app stays headless until the MCP client restarts
|
||||
it. `open -a` and PowerShell's `&` return before the app opens the port, so wait until the VectorCraft
|
||||
window is open before you start the MCP client. With `--connect`, a server that finds no app stops and
|
||||
prints `vectorcraft-cli: cannot connect to 127.0.0.1:7979: ` followed by the system's connection error, for
|
||||
example `connection timed out`.
|
||||
|
||||
## Confining file access
|
||||
|
||||
```sh
|
||||
@@ -107,6 +126,10 @@ the suite) keep the agent to the folders you give it:
|
||||
The result is compared with the root by whole folder names: `/work2/x` is not inside `/work`. On
|
||||
Windows case is ignored, UNC paths (`\\server\share\…`) are resolved like drive-letter ones, and
|
||||
device names (`CON`, `NUL`, `COM1`…) are refused.
|
||||
- With `--connect`, the server makes the paths of `open_file`, `save_file`, `export` (`path` or
|
||||
`options.path`) and `screenshot {path}` absolute against its own working directory before it sends
|
||||
them to the app. Paths inside `run_command` and `command_batch` params go to the app as given, and the
|
||||
app resolves them against its own working directory.
|
||||
- A refused path is an ordinary tool error the agent can read and correct, for example
|
||||
`automation path rejected: outside the write root /work/project/out: /etc/x.png` (with several,
|
||||
`outside the write roots /work/project/out, /tmp/scratch: …`) or
|
||||
@@ -215,7 +238,8 @@ node (layers and groups too) reports `opacity` when it is below 1 and `blend` wh
|
||||
|
||||
On a large document, drill instead of dumping: `inspect_document {depth: 0}` (or `run_command document.inspect
|
||||
{depth?, childLimit?}`, same rules) is the skeleton (artboards with top layers and counts), `run_command
|
||||
document.find {name?, kind?, text?, limit?}` locates nodes across the whole tree, and `document.node` reads the
|
||||
document.find {name?, kind?, text?, within?, intersects?, minArea?, …, select?, limit?}` locates nodes across the
|
||||
whole tree, and `document.node` reads the
|
||||
interesting ones sliced. `document.inspect` and the `doc_inspect` / `inspect_document` tools also scope the
|
||||
reply themselves: `ids: [id…]` lists just those nodes (any level, in the order given, an unknown id is an error,
|
||||
at most 10000), `kind` lists every node of one panel label (ignoring case; within the subtrees of `ids` when both
|
||||
@@ -226,9 +250,21 @@ top layers (`layers` and `total`). Without these options the reply is the whole
|
||||
`document.find` needs at least one filter and all given filters must match; matching
|
||||
ignores case, `name` is a substring of the Layers panel name, `text` a substring of type content and `kind` the
|
||||
exact panel label (`Layer`, `Group`, `Clip Group`, `Path`, `Compound Path`, `Type`, `Image`, `Rectangle`, ...).
|
||||
The geometric filters read each object's geometric bounds, in the document points `document.inspect` reports
|
||||
(groups included; layers, sublayers and a node without bounds do not match): `within: [x, y, width, height]` keeps
|
||||
objects inside that rectangle (edges included), `intersects: [x, y, width, height]` objects touching or overlapping it,
|
||||
`minWidth` / `maxWidth` and `minHeight` / `maxHeight` limit the bounds' size, `minThickness` / `maxThickness`
|
||||
the smaller of width and height (thin slivers of either orientation), and `minArea` / `maxArea` width × height.
|
||||
`minAnchors` / `maxAnchors` count a path's anchor points, a compound path's members together; other nodes do not
|
||||
match them. Limits are inclusive, not negative, and a minimum above its maximum is an error. `select: true`
|
||||
also selects the matches, leaving out hidden and locked ones, those inside another match, and a compound path's
|
||||
members unless the compound path is isolated; Select › Reselect repeats the find, and an action being recorded
|
||||
records it as a step (a find without `select` is not recorded). For example,
|
||||
`document.find {maxThickness: 1, kind: "path", select: true}` followed by `edit.clear` deletes hairline slivers.
|
||||
It answers `{matches: [{id, name, kind, path}], total}` top of the stack first, where `path` is the ancestor
|
||||
chain (layer first) and `limit` caps `matches` (default 100; `0` only counts). `document.json` stays whole: it is
|
||||
the fidelity path, not the way to look around.
|
||||
chain (layer first) and `limit` caps `matches` (default 100; `0` only counts); with `select` the reply adds
|
||||
`selected`, the number of objects selected. `document.json` stays whole: it is the fidelity path, not the way to
|
||||
look around.
|
||||
|
||||
### Completions
|
||||
|
||||
@@ -314,12 +350,14 @@ The older names below remain listed because existing workflows use them.
|
||||
|
||||
| Tool | Arguments and result |
|
||||
|---|---|
|
||||
| `command_list` | Optional `filter` (or `query`), `menu` (a menu path substring, levels joined with ` > `), `enabled_only`, `brief` (no `params` text); returns the command array. `list_commands` lists the same commands; it takes `enabledOnly` and returns `{count, commands}` |
|
||||
| `command_run` | `id`, optional `params`; runs the command through the existing backend. An unknown key in `params` is an error that names it and the nearest key the command takes (`invalid parameters for `file.new`: unknown parameter `widht`; did you mean `width`?`), so a misspelt key no longer runs the command with its default (see [Parameter keys](#parameter-keys)). `run_command` runs a command the same way and takes its id as `command` |
|
||||
| `command_batch` | `steps: [{id, params?}]` (at most 256), optional `stop_on_error` (default true), `atomic` and `label`; returns `completed`, `failed`, `results: [{ok, result\|error}]` and `created` (the ids the `draw_path` / `draw_shape` steps made, in order). A step's `id` is a command id, or `draw_path` / `draw_shape` with that tool's arguments as `params` (checked as `tools/call` checks them; the step's result is the tool's, so `"$N.id"` is the new object). Each edit has its own undo step, or with `atomic: true` the batch's edits are one undo step named `label` (else after its first edit; `edit.undoGroup.begin`/`end` underneath), and the edits made in a document that a step opens or creates (`document.open`, `file.new`) are one undo step there, named the same, also when no document was open before the batch. In a document that was open before the batch and that a step switches to (`document.activate`), each edit stays its own undo step; failures set `isError`. A param string `"$N.path"` or `"$last.path"` takes a value from an earlier step's result (see `run` below). Kept step results total at most 8 MiB; past that the batch stops with the reason. This is the MCP tool, not the engine command `command.batch` (that one takes `commands: [{command, params}]` and is what the Actions panel plays); use `command_run` with `command.batch` when replaying a recorded action |
|
||||
| `command_list` | Optional `filter` (or `query`), `menu` (a menu path substring, levels joined with ` > `), `enabled_only`, `brief` (no `params` text); returns the command array |
|
||||
| `command_run` | `id`, optional `params`; runs the command through the existing backend. An unknown key in `params` is an error that names it and the nearest key the command takes (`invalid parameters for `file.new`: unknown parameter `widht`; did you mean `width`?`), so a misspelt key no longer runs the command with its default (see [Parameter keys](#parameter-keys)) |
|
||||
| `command_batch` | `steps: [{id, params?}]` (at most 256), optional `stop_on_error` (default true), `atomic` and `label`; returns `completed`, `failed`, `results: [{ok, result\|error}]` and `created` (the ids the `draw_path` / `draw_shape` steps made, in order). A step's `id` is a command id, or `draw_path` / `draw_shape` with that tool's arguments as `params` (checked as `tools/call` checks them; the step's result is the tool's, so `"$N.id"` is the new object). Each edit has its own undo step, or with `atomic: true` the batch's edits are one undo step named `label` (else after its first edit; `edit.undoGroup.begin`/`end` underneath); failures set `isError`. A param string `"$N.path"` or `"$last.path"` takes a value from an earlier step's result (see `run` below). Kept step results total at most 8 MiB; past that the batch stops with the reason. This is the MCP tool, not the engine command `command.batch` (that one takes `commands: [{command, params}]` and is what the Actions panel plays); use `command_run` with `command.batch` when replaying a recorded action |
|
||||
| `doc_inspect` | Optional `depth`, `childLimit`, `ids`, `kind`, `limit`, `offset`; same summary as `inspect_document` |
|
||||
| `render_preview` | Optional `max_side` (1–4096, default 1024) and `artboard` (0-based, default 0); inline PNG of that artboard without editing it. Artboards too large to render within the allocation bound return a tool error |
|
||||
| `ui_inspect`, `ui_screenshot` | Connected desktop state/window capture; tool errors in headless mode |
|
||||
| `render_preview` | Optional `max_side` (1–4096, default 1024) and `artboard` (0-based, default 0); inline PNG of that artboard without editing it, drawn as `export` draws a PNG with `background: "white"` at the scale that fits `max_side` (at most 1). Artboards too large to render within the allocation bound return a tool error |
|
||||
| `ui_inspect`, `ui_screenshot` | Connected desktop state/window capture; tool errors in headless mode. `ui_inspect` is the same call as `inspect_ui`, and `ui_screenshot` is `screenshot` with `window: true`. `ui_screenshot` takes an optional `max_side` (1–4096), as `screenshot {window: true}` does: a capture whose longer side is larger is scaled down to `max_side` pixels on that side, and the reply's text gives the returned size as `width`/`height` and the capture's as `capturedWidth`/`capturedHeight`. Without `max_side` the full capture comes back |
|
||||
|
||||
Every tool declares read-only, destructive, idempotent and open-world hints. File-writing
|
||||
`screenshot`, `save_file` and `export` are conservatively marked as mutations. Unknown top-level
|
||||
@@ -331,7 +369,16 @@ currently describes them in prose, so MCP does not guess schemas or silently rem
|
||||
Coordinates are points in document space: y points down, the origin is the first artboard's top-left, and a new
|
||||
document is 612 × 792 (US Letter). New objects become the selection. Most commands act on the selection or on
|
||||
explicit `ids`. A command that leaves out an object selected inside a selected group also leaves it out when `ids`
|
||||
list it together with a group that holds it.
|
||||
list it together with a group that holds it. The commands that select their `ids` first (`object.group`, the
|
||||
`object.arrange.*` commands, `object.move`, the Pathfinder commands and the others like them) refuse an id that names a
|
||||
layer or sublayer, names no object or is not a non-negative integer, with an error that names the command that was run,
|
||||
and leave the document and the selection unchanged; `layer.move` reorders layers.
|
||||
explicit `ids`, which stand in for the selection, so they work with nothing selected. Where a command's params say
|
||||
`ids?|id?` (or `id?, ids?: … (instead of id)`), `id` is `ids` of one object: `{"id": 7}` does what `{"ids": [7]}`
|
||||
does, with or without a selection, and an `id` or `ids` that is malformed (not a non-negative integer) or names no
|
||||
object is refused under the command's name, never replaced by the selection (`effect.apply {"id": "warp.arc"}`
|
||||
without `effect` names the effect). A command that leaves out an object selected inside a selected group also
|
||||
leaves it out when `ids` list it together with a group that holds it.
|
||||
|
||||
Paint values (`fill`, `stroke`) accept `"#rrggbb"`, `"none"`, `[r,g,b]` (0..1), `{"c","m","y","k"}`, `{"gray"}`,
|
||||
or a full `paint.setFill` params object (`{"gradient": …}`, `{"swatch": "name"}`).
|
||||
@@ -363,7 +410,7 @@ objects' fills or strokes differ (`fillMixed` / `strokeMixed`, drawn as a "?" pr
|
||||
| `inspect_document` | `{depth?, childLimit?, ids?, kind?, limit?, offset?}` | Artboards (with `color`, `#rrggbb`, for one with a background color), layer tree (ids, kinds, bounds, paint), selection, history, tool. A type node's `fill`, `stroke` and `strokeWidth` are the paint its characters show (its first run's); fills and strokes of the type object itself come as `objectFill`, `objectStroke` and `objectStrokeWidth`. Paint comes as a label (`"#ff0000"`, `"None"`, `"G1 40% (#ff9999)"` for a swatch at a tint); `paint.proxies` gives it as an object. |
|
||||
| `inspect_ui` | `{}` | UI state. Remote mode only. |
|
||||
| `select_tool` | `{tool}` | `selection`, `directSelection`, `pen`, `rectangle`, `ellipse`, `polygon`, `star`, `lineSegment`, … |
|
||||
| `pointer_gesture` | `{events:[{kind,x,y,mods?}], tool?, mods?}` | `kind` is one of `down`, `drag`, `up`, `move`, `doubleclick`. Events go through the same path as the mouse. |
|
||||
| `pointer_gesture` | `{events:[{kind,x,y,mods?}], tool?, mods?}` | `kind` is one of `down`, `drag`, `up`, `move`, `doubleclick`. Events go through the same path as the mouse. Headless, a `move` replies after the tool's highlight under the pointer is ready, including the Shape Builder's and the Live Paint Bucket's regions of a large selection, which the desktop app finds off the UI thread and highlights after the reply. |
|
||||
| `draw_path` | `{points \| d, closed?, fill?, stroke?, strokeWidth?, dash?, cap?, join?, opacity?, name?, effects?}` | `points` is `[[x,y],…]` or `[{x,y,in?,out?,smooth?},…]`. `d` is SVG path data. The options are below. |
|
||||
| `draw_shape` | `{shape, …geometry, fill?, stroke?, strokeWidth?, dash?, cap?, join?, opacity?, name?, effects?}` | `rectangle`/`ellipse`: `x,y,width,height` (plus `radius` for corners). `polygon`: `cx,cy,radius,sides`. `star`: `cx,cy,radius1,radius2,points`. `line`: `x1,y1,x2,y2`. |
|
||||
| `set_paint` | `{fill?, stroke?, strokeWidth?, ids?}` | Applies to the selection (or `ids`) and becomes the default for new art. |
|
||||
@@ -371,13 +418,11 @@ objects' fills or strokes differ (`fillMixed` / `strokeMixed`, drawn as a "?" pr
|
||||
| `type_text` | `{text}` | Remote only. |
|
||||
| `invoke_menu` | `{command, params?}` | Invokes a menu item by command id. Includes UI commands such as `view.*` and `window.*` in remote mode. |
|
||||
| `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?}` | Returns MCP image content (`image/png`, base64) plus a text block. Renders the artboard; `window:true` captures the app window (remote only). With `path` the backend (the headless session or the app) writes the PNG there too. |
|
||||
| `screenshot` | `{path?, scale?, artboard?, window?, max_side?}` | 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; `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`. |
|
||||
| `open_file` | `{path}` | Opens any readable file as a new active document: `.vectorcraft`/`.drawcraft`, `.vctemplate`, `.svg`/`.svgz`, `.pdf`/`.ai`, `.ait`, `.eps`/`.epsf`, `.ps`, `.dxf`, `.emf`, `.wmf`, PNG, JPEG, GIF, WebP, TIFF, BMP, Targa (`.tga`/`.vda`/`.icb`/`.vst`), PSD/PSB (an image opens as a document of its pixel size; a Photoshop file as its merged image). 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). `run_command document.formats` lists the formats. |
|
||||
| `save_file` | `{path?}` | 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). |
|
||||
| `export` | `{path?, format?, scale?, artboard?, range?, selection?, outlineText?, options?}` | `svg`, `svgz`, `pdf`, `eps`, `dxf`, `emf`, `wmf`, `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 writes one page per artboard: all of them, or `artboard` (0-based) / `range` (`"1-3, 5"`, 1-based); the other formats write one artboard. `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. |
|
||||
| `add_text` | `{text, x?, y?, width?, height?, fit?, fitMinPercent?, path?, mode?, pathEffect?, size?, font?, color?, justify?, vertical?}` | Point type at (x, y) (`justify`: `left`, `center` or `right` starts, centres or ends each line on x); area type with `width`/`height` (`fit`: `none`, `autoHeight`, `shrinkText`, as `text.areaOptions`); or `path` + `mode` (`area`/`onPath`) to flow text in or along a path, with `pathEffect` (`rainbow`, `skew`, `3dRibbon`, `stairStep`, `gravity`); `justify` (also `auto`, `justifyAll`) aligns the paragraphs of every kind, as `text.setStyle` does later. `vertical: true` uses the existing vertical writing mode for every kind (default `false`, horizontal). Returns `{id}`, with `overflow: true` when the text doesn't fit its frame or path. |
|
||||
| `save_file` | `{path?, compress?, version?, preview?, 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). 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), and `preview: true` embeds a PNG of the first artboard. `options` passes the format's save options as `run_command document.formats` lists them. When `compress`, `version` or `preview` 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. 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` or `preview`). `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 warning for a top-level `compress`, `version` or `preview`. 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. |
|
||||
| `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`, `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, SVGZ and EPS write one file per chosen artboard. Rasters, DXF, EMF and WMF 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`, `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. |
|
||||
| `add_text` | `{text, x?, y?, width?, height?, fit?, fitMinPercent?, path?, mode?, pathEffect?, size?, font?, color?, justify?}` | Point type at (x, y) (`justify`: `left`, `center` or `right` starts, centres or ends each line on x); area type with `width`/`height` (`fit`: `none`, `autoHeight`, `shrinkText`, as `text.areaOptions`); or `path` + `mode` (`area`/`onPath`) to flow text in or along a path, with `pathEffect` (`rainbow`, `skew`, `3dRibbon`, `stairStep`, `gravity`); `justify` (also `auto`, `justifyAll`) aligns the paragraphs of every kind, as `text.setStyle` does later. Returns `{id}`, with `overflow: true` when the text doesn't fit its frame or path. |
|
||||
| `add_text` | `{text, x?, y?, width?, height?, fit?, fitMinPercent?, path?, mode?, pathEffect?, size?, font?, color?, justify?, vertical?}` | Point type at (x, y) (`justify`: `left`, `center` or `right` starts, centres or ends each line on x); area type with `width`/`height` (`fit`: `none`, `autoHeight`, `shrinkText`, as `text.areaOptions`); or `path` + `mode` (`area`/`onPath`) to flow text in or along a path, with `pathEffect` (`rainbow`, `skew`, `3dRibbon`, `stairStep`, `gravity`); `justify` (also `auto`, `justifyAll`) aligns the paragraphs of every kind, as `text.setStyle` does later. `vertical: true` uses the existing vertical writing mode for every kind (default `false`, horizontal). Returns `{id}`, with `overflow: true` when the text doesn't fit its frame or path. |
|
||||
| `apply_effect` | `{effect?, params?, ids?}` | Appends a live effect. Without `effect`, returns the effect catalogue with parameters and defaults. |
|
||||
| `pathfinder` | `{operation, ids?}` | `unite`, `minusFront`, `intersect`, `exclude`, `divide`, `trim`, `merge`, `crop`, `outline`, `minusBack`. For a live version, apply the `pathfinder.*` effect to a group (`effect.apply` groups several loose selected objects first). |
|
||||
| `transform` | `{ids?, dx?, dy?, rotate?, scale?, scaleX?, scaleY?, reflect?, shear?, origin?, copy?}` | Runs move, rotate, scale, reflect, shear in that order. With `copy`, the first step duplicates. |
|
||||
@@ -554,6 +599,8 @@ their content). `version: 2` or `1` writes a file older VectorCraft versions ope
|
||||
compressed), and `preview: true` embeds a PNG of the first artboard, at most 256 px on its longer side. A save writes
|
||||
a temporary file and renames it over the old one, so a failed save never damages the file; only the images the
|
||||
document uses are written, and document keys written by a newer version are kept.
|
||||
An uncompressed file is indented JSON when the document has at most 2,000 layers and objects and the indented
|
||||
document, as the chosen version writes it, is at most 1 MiB; otherwise the file is compact JSON.
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -1447,7 +1494,8 @@ already walks Image Trace. Short agent entry points:
|
||||
format's first extension (`png8` gives `.png`, `template` gives `.vctemplate`). A step without `format` keeps the
|
||||
source file's extension (`.vectorcraft` over data sets).
|
||||
- Image Trace: `imageTrace.presets`, `imageTrace.make {id?, preset?, params?, view?}` (or `imageTrace.makeAndExpand`),
|
||||
`imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`.
|
||||
`imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`. A key inside `params` that is not one of the trace
|
||||
settings, such as `colours`, is an error that names it and the nearest setting (`colors`), as for a command's own keys.
|
||||
- Live Paint: `livePaint.make`, `livePaint.merge`, `livePaint.fill {group?, point}`, `livePaint.strokeEdge`,
|
||||
`livePaint.info {group}`, `livePaint.release`, `livePaint.expand`, and `livePaint.gapOptions {ids?, detect?, size?,
|
||||
color?, closeGaps?}` (Object › Live Paint › Gap Options). With `detect: true`, paint stops at the gaps that run
|
||||
@@ -1655,7 +1703,10 @@ patterns out. The Blend commands also grade gradient meshes, which keep their sh
|
||||
Recolor Artwork keys colours by identity, not hex: `recolor.colors` lists each colour's `key` (`"rgb 255 0 0"`,
|
||||
`"cmyk 0 100 100 0"`, `"gray 40"`, `"lab 55 60 -40"`; RGB in 0–255 levels, CMYK and Gray in percent), so CMYK,
|
||||
Lab and RGB colours that look alike stay apart, and every colour parameter also accepts a key.
|
||||
`recolor.reduce {colors?, method?, preserve?, limitTo?}` groups the selection's colours into rows of similar colours (k-means in Lab, weighted by use;
|
||||
`recolor.colors` and `recolor.reduce` read the objects in `ids`, else the selection. With nothing selected they read
|
||||
the objects `select.all` takes, which leaves out hidden and locked art, and the selection is left unchanged; hidden
|
||||
and locked objects inside a group are left out too, also in isolation mode.
|
||||
`recolor.reduce {colors?, method?, preserve?, limitTo?, ids?}` groups those colours into rows of similar colours (k-means in Lab, weighted by use;
|
||||
tints of a global swatch share its row; White and Black are preserved by default) and returns a map of rows
|
||||
`[{from: [keys], to: key}]` to edit and pass to `recolor.apply {map, method?, model?, limitTo?, group?, groupColors?, rename?}`.
|
||||
For scripts and MCP, `model` selects the replacement color model: `keep` (default) retains each
|
||||
@@ -1671,7 +1722,12 @@ the others turn by the same hue). `limitTo` snaps new colours to a swatch librar
|
||||
`recolor.randomize {map, order?, saturationBrightness?, seed?}` shuffles or varies new colours; a row with
|
||||
`exclude: true` keeps its colours. `swatch.editGroup {group, colors, rename?}` rewrites a colour group in one undo
|
||||
step (art linked to its global swatches follows), and `recolor.apply` with `group` recolours the art and rewrites the
|
||||
group together.
|
||||
group together. Art linked to one of the group's global swatches stays linked when the swatch is rewritten with its
|
||||
row's new color (the default without `groupColors` when the rows follow the group's order), so a later `swatch.edit` still
|
||||
reaches it; the reply's `relinked` counts the other linked paints that took the group's new colors. Art in a row that
|
||||
is linked to a swatch rewritten with another color keeps its row's new color, even an unchanged one, and is no longer
|
||||
linked to the swatch, as does art whose color the row converts into the art's own color model. Without `group`,
|
||||
the swatch keeps its color and the recolored art is no longer linked to it.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"recolor.reduce","params":{"colors":2,"preserve":{"grays":true}}}}
|
||||
@@ -2445,7 +2501,9 @@ always tagged, whatever `profiles` says). `outputIntent` embeds a profile as the
|
||||
`/GTS_PDFX` output intent with `outputCondition`, `outputConditionId` and `registry`, and `trapped` sets `/Trapped`
|
||||
(PDF/A files keep their own output intent). Type is written as selectable, searchable text in embedded subset fonts
|
||||
with a ToUnicode map (fonts whose licence forbids embedding stay outlines, with a warning); `advanced: {outlineText:
|
||||
true}` writes every glyph as an outline instead. Type that Transform effects only move and copy (no scale, rotation,
|
||||
true}` writes every glyph as an outline instead. The hyphen that ends a line broken inside a word (`hyphenate`, or a
|
||||
soft hyphen) is written as text where the line draws it: the character `-`, or U+2010 when the font gives the hyphen's
|
||||
glyph to U+2010 and not to `-`. Kashidas are drawn as paths. Type that Transform effects only move and copy (no scale, rotation,
|
||||
reflection or Random) is written as text once in each place when its characters are in solid colors without underline
|
||||
or strikethrough, the type object has no fills or strokes of its own, no inline graphics and no perspective, its
|
||||
transform does not scale or shear stroked characters, and the copies do not overlap. When the effect is on the type
|
||||
@@ -3176,6 +3234,9 @@ packet (`dc:creator`, `dc:description`, `dc:subject` or `pdf:Keywords`, `xmpRigh
|
||||
`Subject`, `Keywords`, `CreationDate`, `ModDate`) give, the XMP packet's value where both give one. A value that
|
||||
`file.info` would refuse is left out, and the title is set as for any other open (the file name, or a new untitled
|
||||
name for a template).
|
||||
`document.inspect` (`inspect_document`) and the `document.open` (`open_file`) reply report the File Info title as
|
||||
`fileInfoTitle`. Their `title` is the document's tab name, which comes from the file name once the document is saved
|
||||
to or opened from a file (with ` [Converted]` or ` [Recovered]` where that applies).
|
||||
`file.new {created}` and a save to a file (`document.save` or `file.saveAs` with
|
||||
`{path, modified}`) use the given date (Unix seconds, or `null` for none) instead of now. Their journal entries always
|
||||
record the date they used, so replaying the journal makes the same document whenever it runs (an action recorded in
|
||||
@@ -3516,14 +3577,18 @@ numbered left to right and top to bottom: `{number, id (null for auto slices), s
|
||||
width, height, options, selected}`, plus `clipToArtboard`, `hidden` and `locked`.
|
||||
|
||||
The Object → Slice commands act on the selected slices: the ones `select.object.slices` (Select → Object → Slices)
|
||||
selects, and the selected objects that are object slices. `object.slice.release` (an object slice leaves its object,
|
||||
a user slice becomes an unpainted rectangle), `object.slice.fromGuides` (the grid the ruler guides cut the artboards
|
||||
into), `object.slice.fromSelection`, `object.slice.duplicate {dx?, dy?}`, `object.slice.combine`,
|
||||
`object.slice.divide {rows? | rowHeight?, columns? | columnWidth?}`, `object.slice.deleteAll`,
|
||||
`object.slice.options {kind?: image|noImage|htmlText, name?, url?, target?, message?, alt?, text?, background?:
|
||||
""|matte|#rrggbb, hAlign?, vAlign?}` and `object.slice.clipToArtboard {on?}` are each one undo step.
|
||||
`view.slices.hide {hidden?}` and `view.slices.lock {locked?}` are session view state; the canvas draws the slices in
|
||||
the `sliceLineColor` preference, numbered while `showSliceNumbers` is on.
|
||||
selects, and the selected objects that are object slices. `slice.list` reports both kinds as `selected`.
|
||||
`object.slice.release` (an object slice leaves its object, a user slice becomes an unpainted rectangle),
|
||||
`object.slice.fromGuides` (the grid the ruler guides cut the artboards into), `object.slice.fromSelection`,
|
||||
`object.slice.duplicate {dx?, dy?}`, `object.slice.combine`, `object.slice.divide {rows? | rowHeight?, columns? |
|
||||
columnWidth?}`, `object.slice.deleteAll`, `object.slice.options {kind?: image|noImage|htmlText, name?, url?, target?,
|
||||
message?, alt?, text?, background?: ""|matte|#rrggbb, hAlign?, vAlign?}` and `object.slice.clipToArtboard {on?}` are
|
||||
each one undo step. `view.slices.hide {hidden?}` and `view.slices.lock {locked?}` are session view state; the canvas
|
||||
draws the slices in the `sliceLineColor` preference, numbered while `showSliceNumbers` is on. Locking or hiding
|
||||
deselects the slices in every open document; unlocking or showing them again leaves them deselected and does not
|
||||
restore the selection that was cleared. While the slices are locked or hidden, a command that selects slices, such as
|
||||
`select.object.slices` (which replies `count: 0`), `object.slice.select`, `object.slice.create`, `edit.undo` or
|
||||
`edit.redo`, leaves them out; a selected object that is an object slice stays selected.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"object.slice.make","params":{}}}
|
||||
@@ -3588,7 +3653,9 @@ Batch exports (#1935):
|
||||
(`%!PS-Adobe-3.0 EPSF-3.0`, `%%BoundingBox`, `%%HiResBoundingBox`, `%%LanguageLevel`, spot colours as
|
||||
`%%DocumentCustomColors`), then the page. Without `useArtboards` the file covers the visible art, its bounding box y up
|
||||
from the first artboard's bottom-left corner; `useArtboards: true` (or `range`, `artboards`, `artboard`) writes one file
|
||||
per artboard, `{stem}_{artboard}.eps`, each bounded by its artboard. `level: 3` (default) writes gradients as smooth
|
||||
per artboard, `{stem}_{artboard}.eps`, each bounded by its artboard. When it writes two or more artboards, it also
|
||||
writes a central file first under the path's own name, `{stem}.eps`: the whole document, bounded by the visible art as
|
||||
without `useArtboards` (no central file when no art shows). `level: 3` (default) writes gradients as smooth
|
||||
shadings and masks transparent image pixels out; `level: 2` writes gradients as bands of colour (as does
|
||||
`compatibleGradients`) and transparent pixels white. Type is always glyph outlines, so no fonts are needed (`embedFonts`
|
||||
is accepted); spot colours are Separation colour spaces, and overprinting fills and strokes overprint unless
|
||||
@@ -4141,10 +4208,25 @@ that doesn't exist, a `from` that is point type, a `to` that is an open path, `f
|
||||
or already in a thread (`from` may be threaded already: the frame goes in beside it), coordinates that aren't finite
|
||||
numbers and a `before` that isn't true or false.
|
||||
|
||||
In every thread, a paragraph that continues from one frame into the next has its first line, first-line indent and
|
||||
bullet or number in the frame where it starts. In the next frame its first line starts where its wrapped lines start,
|
||||
and it has no bullet or number. In a frame that is no longer threaded (after `text.thread.releaseSelection` or
|
||||
`text.thread.remove`), in a copy of a frame and in point type converted from one, the first paragraph has its first-line
|
||||
indent and bullet or number again.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"text.thread.add","params":{"from":42,"at":[300,120]}}}
|
||||
```
|
||||
|
||||
`type.createOutlines` (Type › Create Outlines) and `effect.expandAppearance` (when it outlines type, as Expand
|
||||
Appearance describes) take an outlined frame of threaded text out of its thread with the text it shows, and the other
|
||||
frames keep the text they hold. When the thread's last frame is outlined, the text it has no room for, which its
|
||||
outlines do not draw, goes after the text of the last frame that stays. A thread left with one frame is no longer a
|
||||
thread. When `effect.expandAppearance` keeps a frame as type in a group (type with no fills or strokes of its own whose only
|
||||
effects are shadows, outer glows or Crop Marks), the type stays in the thread in the frame's place. Object › Expand
|
||||
(`object.expand`) outlines type with these two commands. `object.flattenTransparency` takes each frame of threaded text
|
||||
that it outlines (`textToOutlines`) or turns into flat regions or an image out of its thread in the same way.
|
||||
|
||||
## Converting between point type and area type
|
||||
|
||||
With the Selection tool, a single selected point or area type object shows the type widget: a small circle beside
|
||||
@@ -4308,46 +4390,67 @@ The menu item is checked while the paragraph the Paragraph panel shows has it on
|
||||
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"romanHangingPunctuation":true}}}
|
||||
```
|
||||
|
||||
## Bulleted lists
|
||||
## Bulleted and numbered lists
|
||||
|
||||
`text.setFormat {list: "none"|"bullet", bullet: "none"|"dot"|"dash"|"circle"|"square"}` (Paragraph panel › Bullets)
|
||||
makes the paragraphs of the selected type (or `ids`, or the paragraphs a `start` / `end` range touches) a bulleted list
|
||||
or takes them out of one. `bullet` sets the bullet: a filled dot (•), a dash (–), a hollow circle (◦) or a filled
|
||||
square (▪). `list: "bullet"` keeps the bullet of a paragraph that is already in a list and gives the others the dot.
|
||||
`"none"` in either key turns the list off and leaves the indents as they are. An unknown value is an error and changes
|
||||
nothing. A paragraph that joins a list while its left and first-line indents are both 0 gets a left indent of 18 pt and a
|
||||
first-line indent of -18 pt, unless the same call sets `leftIndent` or `firstLineIndent`; other indents stay as they are.
|
||||
In a horizontal right-to-left list paragraph the indents are mirrored: the left and first-line indents are measured
|
||||
from the right edge, and the right indent from the left edge. Its bullet starts the first line at the right, and the
|
||||
lines it wraps to start at the right where the first line's text starts after the bullet. A right-to-left paragraph
|
||||
without a list keeps its left and first-line indents at the left, and vertical type keeps its indents as they are. In
|
||||
threaded type, a list paragraph that continues into the next frame starts that frame with its bullet again, as it
|
||||
starts it with its first-line indent.
|
||||
`text.setFormat {list: "none"|"bullet"|"number", bullet: "none"|"dot"|"dash"|"circle"|"square", number:
|
||||
"none"|"arabic"|"upperLetters"|"lowerLetters"|"upperRoman"|"lowerRoman"|"twoDigits"|"threeDigits"}` (Paragraph panel ›
|
||||
Bullets and Numbered List) makes the paragraphs of the selected type (or `ids`, or the paragraphs a `start` / `end`
|
||||
range touches) a bulleted or numbered list or takes them out of one. `bullet` sets the bullet: a filled dot (•), a dash
|
||||
(–), a hollow circle (◦) or a filled square (▪). `list: "bullet"` keeps the bullet of a paragraph that is already in a
|
||||
list and gives the others the dot. `number` sets the number style: 1, 2, 3; A, B, C; a, b, c; I, II, III; i, ii, iii;
|
||||
01, 02, 03; or 001, 002, 003. `list: "number"` keeps the style of a paragraph that is already in a numbered list and
|
||||
gives the others 1, 2, 3. Without `list`, a `number` style makes a numbered list and a `bullet` makes a bulleted one; a
|
||||
numbered list keeps its bullet for when it is made bulleted again. `"none"` in any of the three keys turns the list off
|
||||
and leaves the indents as they are. An unknown value is an error and changes nothing. A paragraph that joins a list
|
||||
while its left and first-line indents are both 0 gets a left indent of 18 pt and a first-line indent of -18 pt, unless
|
||||
the same call sets `leftIndent` or `firstLineIndent`; other indents stay as they are, also when a list changes between
|
||||
bullets and numbers. In a horizontal right-to-left list paragraph the indents are mirrored: the left and first-line
|
||||
indents are measured from the right edge, and the right indent from the left edge. Its marker starts the first line at
|
||||
the right, and the lines it wraps to start at the right where the first line's text starts after the marker. A
|
||||
right-to-left paragraph without a list keeps its left and first-line indents at the left, and vertical type keeps its
|
||||
indents as they are. In threaded type, a list paragraph that continues into the next frame starts that frame where its
|
||||
wrapped lines start, without the bullet or number, and the paragraph after it takes the next number.
|
||||
|
||||
The layout draws the bullet before the first line of each list paragraph that has text, in the font, size, scales and
|
||||
color of the paragraph's first character, without that character's baseline shift, rotation, superscript or subscript,
|
||||
tracking, manual kerning and tate-chu-yoko. The bullet is not a character of the text: copied text leaves it out, the
|
||||
caret before the paragraph's first character stands at the text, a selection does not highlight the bullet, and Touch
|
||||
Type moves that character without the bullet. The text after it starts at the next tab stop, the left indent counting
|
||||
as a stop when no explicit stop comes before it, measured from the right in a right-to-left list paragraph. The lines
|
||||
the paragraph wraps to start at the left indent. Type on a path draws the bullet and the same gap before the
|
||||
paragraph's text; a right-to-left item on a path has its bullet at the right of its text. Justification and Optical
|
||||
Margin Alignment leave the bullet and the gap after it as they are, and an underline or strikethrough starts at the
|
||||
text. Return at the end of a list item starts another item in the same list. The list is saved with the paragraph
|
||||
(`list` in `para`, for example `{"bullet": "square"}`) only when it is set, and paragraph styles carry it: a style
|
||||
captured from a paragraph without a list turns the list off. Reset Panel turns it off. The change is one undo step.
|
||||
The layout draws the marker (the bullet, or the number and its period) before the first line of each list paragraph
|
||||
that has text, in the font, size, scales and color of the paragraph's first character, without that character's
|
||||
baseline shift, rotation, superscript or subscript, tracking, manual kerning and tate-chu-yoko. The marker is not a
|
||||
character of the text: copied text leaves it out, the caret before the paragraph's first character stands at the text,
|
||||
a selection does not highlight the marker, and Touch Type moves that character without the marker. The text after it
|
||||
starts at the next tab stop, the left indent counting as a stop when no explicit stop comes before it, measured from the
|
||||
right in a right-to-left list paragraph; a number that reaches past the left indent takes the text to the next tab stop
|
||||
after it. The lines the paragraph wraps to start at the left indent. Type on a path draws the marker and the same gap
|
||||
before the paragraph's text; a right-to-left item on a path has its marker at the right of its text. Justification and
|
||||
Optical Margin Alignment leave the marker and the gap after it as they are, and an underline or strikethrough starts at
|
||||
the text. Return at the end of a list item starts another item in the same list. The list is saved with the paragraph
|
||||
(`list` in `para`, for example `{"bullet": "square"}` or `{"bullet": "dot", "number": "upperRoman"}`) only when it is
|
||||
set, and paragraph styles carry it: a style captured from a paragraph without a list turns the list off. Reset Panel
|
||||
turns it off. The change is one undo step.
|
||||
|
||||
The canvas, EPS, PDF and Type › Create Outlines draw the bullet; in PDF it is a path, not text. Live SVG text writes the
|
||||
bullet's character before the paragraph's text, except type on a path with bullets, which it writes as outlines with a
|
||||
warning. DXF writes the bullet as a TEXT entity of its own. Legacy `.ai` writes the
|
||||
paragraph's text without the bullet and warns that bulleted lists are left out.
|
||||
A numbered paragraph's number counts the numbered paragraphs of its story up to and including it: the count runs through
|
||||
consecutive list paragraphs, through the frames of threaded type in thread order, and starts again at 1 after a
|
||||
paragraph in no list. An empty paragraph has no number and is not counted, and a bulleted paragraph keeps its bullet and
|
||||
is not counted. Letters run from A to Z, then AA to ZZ, then AAA; roman numerals use IV, IX, XL, XC, CD and CM; 01 and
|
||||
001 keep every digit past 99 and 999; letters and roman numerals past 3999 are written in Arabic digits. In a
|
||||
right-to-left paragraph the number reads left to right with its period at its left. Each threaded frame saves the count
|
||||
that its earlier frames carry into it (`listCount`, only when it is not 0). Type that is in no thread counts from 1:
|
||||
after Remove Threading or Release Selection, in a frame converted to point type, and in every copy of a frame (pasted,
|
||||
duplicated, copied in the Layers panel, or taken out of a symbol or a placed file). A symbol made of a thread keeps the
|
||||
numbers in the symbol's art.
|
||||
|
||||
The canvas, EPS, PDF and Type › Create Outlines draw the marker; in PDF it is a path, not text. Live SVG text writes the
|
||||
marker's characters before the paragraph's text, except type on a path with a list, which it writes as outlines with a
|
||||
warning. DXF writes the marker as a TEXT entity of its own, its characters in the order they are drawn. Legacy `.ai`
|
||||
writes the paragraph's text without the marker and warns that bulleted and numbered lists are left out.
|
||||
|
||||
Type › Bullets and Numbering › Apply Bullets and the canvas context menu's Bullets and Numbering › Apply Bullets run
|
||||
`type.applyBullets {bullet?}` on the selected type's paragraphs, or on those the Type tool's selection touches: without
|
||||
`bullet` as `text.setFormat {list: "bullet"}`, with it as `text.setFormat {bullet}`.
|
||||
`type.applyBullets {bullet?}` on the selected type's paragraphs, or on those the Type tool's selection touches. Without
|
||||
`bullet` it runs `text.setFormat {list: "bullet"}`, and with it it runs `text.setFormat {bullet}`. Apply Numbers in the
|
||||
same menus runs `type.applyNumbers {number?}`. Without `number` it runs `text.setFormat {list: "number"}`, and with it
|
||||
it runs `text.setFormat {number}`.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"bullet":"square"}}}
|
||||
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"number":"upperRoman"}}}
|
||||
```
|
||||
|
||||
## Small Caps, Superscript and Subscript
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# PDF backend
|
||||
|
||||
PDF writing uses `craft-pdf` from `storytold/craft-libs`, pinned to
|
||||
`ca50d63b773c99f96e75c77ce5a1d21e7c9cda0d`. Its `write` feature supplies Krilla configuration,
|
||||
`2eca9b3a53a98ac52efed2a27353c6a6927ead10`. Its `write` feature supplies Krilla configuration,
|
||||
metadata construction, fallible font preparation and serialization, and native unwind guards.
|
||||
The dependency disables default features and enables `write` and `interpret`.
|
||||
The production writer retains Krilla’s raster-image support without its optional text shaper.
|
||||
|
||||
+3
-6
File diff suppressed because one or more lines are too long
+37
-12
File diff suppressed because one or more lines are too long
+3
-2
File diff suppressed because one or more lines are too long
+11
-5
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user