docs: chat canvas tools, annotations, agent tools, cross-page components

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Marcos Paulo
2026-10-24 01:54:58 -03:00
parent 872b693373
commit 97717beb6d
11 changed files with 171 additions and 18 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ Projects live in `~/Documents/papercraft/<name>/` (`project.json`, one JSON file
## Agents
PaperCraft exposes **67 tools** (`src/mcp/tools.rs`) three ways. They run the same code inside the app.
PaperCraft exposes **70 tools** (`src/mcp/tools.rs`) three ways. They run the same code inside the app.
| Way | Notes |
|---|---|
+13 -3
View File
@@ -1,12 +1,12 @@
# Driving PaperCraft with agents
PaperCraft exposes one set of 67 tools three ways. They all run the same code inside the app, so an agent sees what you see and every call is one undo step.
PaperCraft exposes one set of 70 tools three ways. They all run the same code inside the app, so an agent sees what you see and every call is one undo step.
| Way | Use it when | Needs the app running |
|---|---|---|
| MCP over HTTP, `http://127.0.0.1:29980/mcp` | the agent client speaks MCP (Claude Code, Codex, pi) | yes |
| MCP over stdio, `papercraft-mcp` | the client only starts stdio servers | for calls; `tools/list` works offline |
| Shell, `papercraft-cli` | the agent has a terminal and you want no tool schemas in its context | for `call`; `list`, `schema` and `icons` work offline |
| Shell, `papercraft-cli` | the agent has a terminal and you want no tool schemas in its context | for `call`; `list`, `schema` and `icons` work offline (with the app running, `list` and `schema` show the app's tools) |
If you would rather talk to the agent inside the app, Chat mode (⌘J, or `Design | Chat` in the top bar) is the in-app alternative. It runs Claude Code or Codex through their CLIs against the same MCP server, ACP agents such as OpenCode or Gemini CLI, or the Anthropic API with your own key, and keeps the conversation next to the canvas, with the same runs, review and revert. The setup and the thread are in [chat.md](chat.md). The rest of this guide covers driving the app from your own terminal or agent client.
@@ -91,6 +91,8 @@ papercraft-cli call get_screenshot '{"nodeId":"abc123:1"}' --project Demo -o car
### Shell and scripts
When `call` or `shell` reaches the app and the app is another version, the CLI prints once on stderr, for example `papercraft-cli 0.6.1 ≠ app 0.6.2; reinstall the CLI`. `list` and `schema` read the running app's tool list, so tools newer than the CLI still show; with the app closed they use the CLI's built-in list.
`papercraft-cli shell [--project X] [--offline]` keeps one session open (offline, one in-process project; otherwise the app). A line is `<tool> <json>` (JSON may span lines until its braces close) or a command: `:help :tools :schema <tool> :open <project> :pages :page <name|id> :shot [nodeId] [file] :undo :redo :save :watch on|off :quit`. `:watch on` renders the artboard last edited to `preview.png` after each editing call, shown inline on iTerm2, WezTerm and kitty. Input is plain stdin (no line editing or history; use `rlwrap` for that). Piped input prints no prompt and exits 1 if any line failed, listing the line numbers on stderr.
```sh
@@ -112,11 +114,19 @@ EOF
* Components: `get_components` lists mains of every page and enabled library with a `componentKey`; `create_instance` and `swap_instance` accept a main from any page or library (`componentId` or `componentKey`).
* Libraries: `publish_library`, `list_libraries`, `enable_library`, `update_library`, `disable_library`. They work offline too, on the project store.
## Seeing what the user sees
* `get_basic_info` and `get_selection` include `viewport: {pageId, rect: [x, y, w, h], zoom}`: the page the user is viewing and the world rect on screen.
* `set_viewport {pageId?, nodeIds? | rect?, zoom?, padding?, force?}` switches the user's page (as the page list does) and fits the nodes or rect, or centres them at an explicit `zoom` (0.01 to 256; `select_nodes {zoom: true}` stops at 100%). While the user is panning or zooming (10 s after their last camera move) it is refused with "user is moving the canvas", unless `force: true`.
* `find_nodes {textValue}` finds text layers containing the phrase on every page (or `pageId`), ignoring case and line breaks; `*` wildcards match the whole value instead. `get_tree_summary {nodeId, fullText: true}` prints every text layer's complete text, for copy review across screens.
* `get_screenshot {nodeId, annotations: true}` draws the notes on the captured layers (cards, number badges, leader lines), so an agent can check note placement.
## Annotation tools
* `annotate {pageId?, notes: [{node, text, type?}]}` adds many notes in one undo step. `node` is a node id or an exact layer name; the first line of `text` is the label and the rest the body; `type` is a category key (default `rule`) that the page's template must enable. Placement is automatic.
* `annotate {pageId?, notes: [{node, text, type?}]}` adds many notes in one undo step. `node` is a node id or an exact layer name; the first line of `text` is the label and the rest the body; `type` is a category key (default `rule`) that the page's template must enable. Placement is automatic. `annotate {tidy: true, frame?}` with no notes re-lays-out the existing notes and returns `{"tidied": n}`, the number of notes it moved.
* `get_annotation_template {pageId?}` returns the allowed types with their guidance and fields. Call it once before `annotate`.
* `set_annotation_template {template, scope?: "page" | "project", pageId?}` switches the template by id or name.
* `upsert_annotation_template {template: {id?, name, display?, categories: [{key, enabled?, guidance?, fields?}]}, categories?: [{key, name, color, icon}], setAs?: "project" | "page", pageId?}` creates or replaces a template and its category styles in one undo step. `delete_annotation_template {template}` removes one (the last stays).
* `get_annotations {nodeId?, pageId?, full?}` reads notes as `[number, node, type, text]` rows.
* `update_annotation {id | ref, text?, type?}` and `delete_annotations {ids}` take ids or references such as `Sign in #3`.
+21 -1
View File
@@ -45,7 +45,7 @@ The Annotate popover offers only the enabled categories of the active template,
Three levels, all view state. They are not saved in the design and not undoable.
* **Full**: every note shows its label and the first lines of its body.
* **Full**: every note shows its label and the first lines of its body. Below 60% zoom notes show as badges, so cards never spill over the design; zoom in to read them.
* **Badges**: only the number badges are shown.
* **Hidden**: nothing is drawn.
@@ -98,11 +98,27 @@ Agents work with notes through six MCP tools, all in `src/mcp/tools.rs`. They ar
|---|---|
| `get_annotation_template` | Reads the template: `[type key, guidance, field names]` for each enabled type. Call it once before `annotate`. |
| `set_annotation_template` | Switches the template by id or name, for one page (default) or the project (`scope: "project"`). |
| `upsert_annotation_template` | Creates or replaces a template, and creates or restyles categories in the same call (`categories: [{key, name, color, icon}]`). `setAs: "project"` or `"page"` makes it the default or the page override. Returns `{id, name, types}`. |
| `delete_annotation_template` | Deletes a template by id or name. The last template stays. |
| `annotate` | Adds many notes at once. Each note names a node id or an exact layer name on the page, a `text` and a `type` (default `rule`). |
| `get_annotations` | Reads notes as `[number, nodeName, type, text]` rows (text capped at 120 characters). `full: true` returns complete objects; `nodeId` limits to a subtree; `pageId` picks another page. |
| `update_annotation` | Changes the text or type of one note, by `id` or by reference such as `Sign in #3`. |
| `delete_annotations` | Deletes notes by ids or references such as `Sign in #3`. |
Custom template with a new category, used on the current page:
```json
{
"template": {"name": "Legal review", "categories": [
{"key": "legal", "guidance": "Legal risk this part carries.", "fields": ["Owner", "Law"]},
{"key": "rule"}]},
"categories": [{"key": "legal", "name": "Legal", "color": "#aa3355", "icon": "scale"}],
"setAs": "page"
}
```
`icon` must be one of the built-in icon names (an unknown name lists them). Every key in the template must exist, either already or in `categories`. The template and category edit is one undo step; `setAs: "page"` adds a second.
For `annotate` and `update_annotation`, the first line of `text` is the label and the rest is the body. There are no positions, icons or colours: PaperCraft places each note beside its target, outside the frame, stacked so none overlap. `annotate` is all or nothing: if one note is refused (an unknown layer, or a type the template disables), none is added and the error lists the allowed keys.
Batched call, on a page whose template is Product spec:
@@ -119,6 +135,10 @@ Batched call, on a page whose template is Product spec:
The reply is one row per note, `[number, node, type, label]`. Then `get_annotations {}` reads them back, and `delete_annotations {"ids":["Sign in #3"]}` removes one by its frame reference.
`annotate {"tidy": true}` with no notes re-runs placement for every note on the page (or on `frame`) in one undo step and returns `{"tidied": n}`, the number of notes it moved; `0` means the layout was already tidy.
To see the notes, `get_screenshot {"nodeId": "<frame>", "annotations": true}` draws the cards, number badges and leader lines of the notes whose target is inside the captured layers, placed and routed as on the canvas (full cards, scale 1). The image grows to include the cards. Without `annotations`, or with `false`, the screenshot is the same as before.
**Handoff.** `get_design_spec` and the code exports (`get_jsx`) include the notes of the frame as comments. Each note is one line, `// 1 Business rule: Lock after 5 failed tries`, with its body lines as more `//` lines, so code generated from a frame carries the rules next to the markup.
## Storage
+44 -3
View File
@@ -96,9 +96,48 @@ Replies are markdown: paragraphs, lists, code blocks with a copy button, inline
* **Plan**: when the agent keeps a todo list (Claude Code's `TodoWrite`, or an ACP plan), the latest list is pinned above the composer with a "done of total" count.
* **Approval**: on the API backend and for ACP agents, the destructive tools (`delete_pages`, `disable_library`, `import_design_system`, and `delete_nodes` on more than 20 layers) wait for Approve or Deny before they run. Deny returns an error to the model, which can then carry on. For ACP agents, any non-PaperCraft tool the agent asks for shows the same card. The Claude Code and Codex CLIs do not show this card. They use the CLI's own permission rules, and in non-interactive mode a tool that needs permission is denied. The papercraft tools are pre-allowed on both CLIs.
* **Checkpoint**: when an agent run starts, the thread shows "Checkpoint · before run N". "Restore to here" reverts that run and every run after it, through the same revert as the Agent view.
* **Run card**: when a run ends, "Agent finished, N changes" with Review, Revert and Accept. These are the same actions as the run review on the canvas.
* **Run card**: when a run ends, "Agent finished, N changes" with Review, Revert and Accept. These are the same actions as the run review on the canvas. The card names the client that made the run ("Chat", "Claude Code · 2") and refreshes after an undo.
* **Progress**: while a run is open, the step group and the Working pill count calls and touched layers ("Styling · 212 calls · 1,340 changes"). The pill clears when the turn ends.
* **Errors**: a failed turn shows a red row with Retry. A sign-in problem shows the sign-in card (see Setup).
* **Canvas follow**: when a tool call targets a node, the canvas pans to it, unless you moved the camera in the last 10 seconds.
* **Agent presence**: after each agent call, the layers it pointed at or changed get an accent outline that fades over 3 seconds, and the newest carries a label with the verb and the client ("Styling · Claude Code"). This works for chat turns and for any MCP client. When the agent works on another page, a chip "Agent on page X →" switches to it.
## Design-system context
Every turn carries a summary of the project's design system, so the agent reuses what exists instead of drawing look-alikes:
* variables by collection and group with their modes (colors as `name=#light/#dark`), tokens as scales;
* main components on every page and in enabled libraries, as `componentKey · name · w×h · uses`, most used first;
* enabled libraries and their update status, and the page's annotation template;
* a reuse rule: place components with `create_instance` / `swap_instance`, colors as `var(--…)`, spacing on tokens.
The summary is capped at about 1,500 tokens. A large system keeps its most used components and collapses variable groups to counts, with a pointer to `get_components` / `get_variables` for the rest. It is rebuilt only when the design system changes. The API backends get it as a separate system block (cached on `api.anthropic.com`); CLI and ACP agents get it in their first prompt, and again as `[Design system changed] …` on the next message after a change. The context meter includes it. Turn it off in Settings → Chat → **Send design-system summary**.
`lint_design` has a matching rule, `not-an-instance`: a frame built by hand that matches a component (same structure, size within 4 px, same fill, radius and stroke) is flagged with the component to use.
## Pointing at the canvas
* **Pick**: the Pick chip next to `@ Selection` turns the canvas into a picker without changing your selection. Click a layer to add it, or Shift+drag an area; Esc cancels. Each pick types `@Name` at the composer cursor and adds a chip.
* **⌥-click**: in Chat mode, ⌥-click a layer on the canvas to add it the same way.
* **Area chips** are sent as `[Area] page P, rect x,y,w,h, contains: <ids…>` (the top layers wholly inside the area, up to 40).
* **Crops**: layer, area and `@ Selection` chips carry an image of that part of the page with its notes drawn (1024 px max). The API backend gets it as an image block; CLI and ACP agents get a file under `.papercraft-chat-tmp/`.
* **Ask (⌘K)**: with a selection, ⌘K opens a prompt box anchored above it. Enter sends to the chat with the selection chip and its crop, and the reply streams in the chat column without leaving Design mode. With nothing selected, ⌘K opens the command palette (also ⌘⇧/).
## Actions on a selection
The **Actions** menu opens from the canvas right-click menu, from the Actions chip in the composer and from the command palette ("Actions: …"). It works in Design and Chat modes.
Instant actions run in the app with no model. Each one is a single undo step and its own run (client "Actions"), and a toast reports what it did with Review and Revert:
* **Fix lint here**: `lint_design {fix: true}` on the selection.
* **Bind to tokens**: binds hard-coded colors to the nearest variable (within 12 RGB) and gaps, padding and radii to the nearest spacing or radius token (within 2 px). Values with no close match are listed.
* **Use components**: replaces each `not-an-instance` hit with an instance of its component in the same place, keeping the texts.
* **Rename layers**: names default layers (`Frame 12`, `Text`) from their text or role.
* **Annotate**: opens the annotation popover.
Agent actions send a prompt with a selection chip to the chat and switch to Chat mode: **Optimize** (tighten layout and spacing to the token scale, fix contrast, keep the content, prefer components), **Make consistent with the design system**, **Write states** (empty, loading, error), **Variations ×3** and **Annotate with AI**.
**Send open notes to chat**, in the inspector's Annotations section for a top-level frame, sends the frame's open feedback and its `todo` / `question` notes as one prompt with a chip.
## Composer and attachments
@@ -110,7 +149,7 @@ Type in the composer, ⏎ sends and ⇧⏎ adds a line. The empty state offers s
For the API backend, images are sent as image blocks, so the model sees them. For the CLIs and ACP agents, each image is saved under `.papercraft-chat-tmp/` in the project and its path is added to the prompt for the agent to read.
While a turn runs, the send button becomes **Stop**. Stop asks the backend to interrupt: the CLIs get an interrupt, or SIGINT, then a kill after 3 seconds; ACP agents get `session/cancel`, then a kill if they do not exit. Every backend ends within 3 seconds. Tools that already ran stay applied, and the partial run can be reviewed or reverted like any other.
While a turn runs, the send button becomes **Stop**. Stop asks the backend to interrupt: the CLIs get an interrupt, or SIGINT, then a kill after 3 seconds; ACP agents get `session/cancel`, then a kill if they do not exit. Every backend ends within 3 seconds. The thread shows a muted "Stopped" line, not an error. Tools that already ran stay applied, and the partial run can be reviewed or reverted like any other.
## Storage
@@ -128,7 +167,9 @@ Prompts, project content and tool results go to whichever backend you chose: the
* One turn per project at a time. Sending while a turn runs is refused with "A turn is already running"; use Stop first. Switching to another project stops a running turn.
* The web build has no Chat mode.
* The canvas shows the run's outlines after the turn ends, when the run is reviewed. They are not drawn live while the agent works.
* While the agent works, the canvas shows fading outlines of the layers it touches; the full change review (added, changed, removed) comes after the turn, when the run is reviewed.
* Variations ×3 is a prompt for now: the agent makes the variants; there is no side-by-side Keep tag yet.
* Over the ACP stdio bridge the chat id does not reach the server, so those runs show under the agent's client name and close after 30 seconds idle instead of at turn end.
* The API backend stops after 40 tool rounds in one message and says so.
* Codex reports no per-call usage, so the context meter is hidden for it.
* Agents need to be installed and signed in with their own tools. Chat does not install them or sign them in for you.
+1 -1
View File
@@ -60,7 +60,7 @@ papercraft-cli call set_text_content '{"updates":[{"nodeId":"<label layer id fro
* `create_instance` returns the instance id and its layers, so you can set text and styles on them. Changes made through `set_text_content` or `update_styles` on an instance are recorded as overrides; everything else follows the main component.
* In `write_html`, `<x-paper-instance component-id="<id>" layer-name="Button / Save"/>` places an instance inline.
* `get_components` lists the main components of every page (and library components) with `componentKey`, page and how many instances each has.
* **Across pages.** Instances can be placed from a main on any page: `create_instance` and `swap_instance` take a `componentId` (`pageId:num`) or a `componentKey` (stable, from `get_components`). Editing a main on page A updates its instances on every other page, one undo step per page. If the main is deleted its instances stay as they are, marked "Main missing" in Assets, where you can detach them. `import_components` still copies a main onto a page, but reuse no longer needs it.
* **Across pages.** Instances can be placed from a main on any page: `create_instance` and `swap_instance` take a `componentId` (`pageId:num`) or a `componentKey` (stable, from `get_components`). Editing a main on page A updates its instances on every other page, one undo step per page. If the main is deleted its instances stay as they are, marked "Main missing" in Assets, where you can detach them. `<x-paper-instance component="Name"/>` in `write_html` links the same way when the main is on another page (it used to copy the main onto the page). `import_components` still copies mains onto a page, detached from the source; it is for moving a design system, not for using components.
* **Limits.** No variants or properties: model variants as separate components (`Button / Primary`, `Button / Secondary`). `detach_instance` and `swap_instance` exist as tools; in the editor detach with ⌘⌥B.
## Icons
Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 KiB

After

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 KiB

After

Width:  |  Height:  |  Size: 882 KiB

@@ -13,7 +13,7 @@ How to keep designs small, keep design and code in step, and spend few tokens do
* Build every repeated thing once: `create_component`, then `create_instance` or `<x-paper-instance>`. Never paste markup twice. A screen is mostly instances plus layout frames.
* No raw colours, radii, spacing or shadows in `write_html`: `var(--token)` only. Changing a token then updates every page, and `get_design_spec` reports token names, which is what code needs.
* Reuse across pages with `import_components` (instances stay linked) or `<x-paper-clone node-id="page:id">`. Do not rebuild a component per page.
* Reuse across pages with `create_instance {componentKey}` or `<x-paper-instance component="Name"/>` in `write_html`: a main on any page (or an enabled library, for `create_instance`) works directly and the instance stays linked to it. Do not `import_components` for that (it makes detached copies; it is for moving a design system). Do not rebuild a component per page.
* One design system per brand: `export_design_system` writes an OpenDesign package (variables, modes, tokens); `import_design_system` loads it into another project. That is the sync path between files and apps.
* Write one visual group per `write_html` (a header, a row), with `layer-name` on every box. Fewer, larger writes cost fewer round trips; groups that are too big make one bad style hard to find.
+90 -8
View File
@@ -60,14 +60,14 @@ Delete one or more pages. The last page of a project cannot be deleted. A page t
### `get_basic_info` (read-only)
Essential context about the open project: project name, the page, node count, artboards (top-level frames) with ids, sizes and world positions, other top-level layers, fonts, and `pages` with the active one marked isActive.
Essential context about the open project: project name, the page, node count, artboards (top-level frames) with ids, sizes and world positions, other top-level layers, fonts, `pages` with the active one marked isActive, and `viewport` ({pageId, rect: [x, y, w, h] world, zoom}: what the user sees).
- worldX/worldY are world positions; x/y are relative to the parent.
- Pass pageId to inspect another page without switching the user's view.
### `get_selection` (read-only)
The selected layers on the page the user is viewing: ids, names, types, sizes, positions and the artboard each belongs to.
The selected layers on the page the user is viewing: ids, names, types, sizes, positions and the artboard each belongs to, plus `viewport` ({pageId, rect: [x, y, w, h] world, zoom}).
### `get_node_info` (read-only)
@@ -94,9 +94,20 @@ Compact indented text tree of a subtree: type, name, id and size per node. Much
| Argument | Type | Required | Notes |
|---|---|---|---|
| `depth` | number | | Maximum depth (default 3, max 10). |
| `depth` | number | | Levels shown, counting nodeId (or each top-level layer for "root") as level 1 (default 3, max 10). Deeper levels collapse to "… N children". |
| `fullText` | boolean | | Show each text layer's complete text instead of its first 40 characters (for copy review). |
| `nodeId` | string | yes | Root of the summary, or "root". |
Depth counts from the node you pass, not from the page. For an artboard `Home` with a `Screens` frame holding rows, `{"nodeId": "<Home id>", "depth": 2}` stops at `Screens`:
```
frame "Home" [3fa9c1:1] 1440×900
frame "Screens" [3fa9c1:2] fill(1440)×hug(820)
… 3 children
```
The rows inside `Screens` are level 3, so they need `depth: 3` from `Home` (the default), or `depth: 2` from `Screens` itself. With `nodeId: "root"` every artboard is level 1, so the same rows also need `depth: 3`.
### `get_jsx` (read-only)
@@ -151,7 +162,7 @@ CSS styles (camelCase) of one or more nodes, as {nodeId: styles}.
### `find_nodes` (read-only)
Find nodes by text content and/or computed style. Searches the whole project by default; pass pageId for one page or nodeId for a subtree. `textValue` matches text case-insensitively with * wildcards anchored to the whole value. `filters` are {styleName, styleValue} pairs (AND), values may use * wildcards; colors match by value.
Find nodes by text content and/or computed style. Searches the whole project by default; pass pageId for one page or nodeId for a subtree. `textValue` finds text containing it, ignoring case and line breaks; with * wildcards it must match the whole value ("Get *"). `filters` are {styleName, styleValue} pairs (AND), values may use * wildcards; colors match by value.
| Argument | Type | Required | Notes |
|---|---|---|---|
@@ -195,6 +206,7 @@ Render a node (or the whole page when nodeId is omitted or "root") to a PNG and
| Argument | Type | Required | Notes |
|---|---|---|---|
| `annotations` | boolean | | Also draw the note cards, badges and leader lines of notes on the captured layers; the image grows to include the cards. |
| `nodeId` | string | | The node to capture; omit for the whole page. |
| `out` | string | | Optional file path to save the PNG to (also used by papercraft-cli). |
| `scale` | number | | Render scale: 1 (default) or 2. |
@@ -426,7 +438,7 @@ Main components of every page (or one with `pageId`): id, name, `componentKey` (
### `import_components`
Copy the main components anywhere on `fromPageId` (including inside artboards, typically on a `Design System` page) onto this one, so you can place instances here with create_instance or <x-paper-instance component="Name"/> / component-id="ID". Components with the same name already here are skipped. Variables and tokens are already shared by every page; components are copies, not live links.
Copies mains onto this page (detached from the source); only for moving a design system, not for using components: create_instance and swap_instance take a main on any page or an enabled library directly, with no import. Copies the main components anywhere on `fromPageId` (including inside artboards); components with the same name already here are skipped. Variables and tokens are already shared by every page.
| Argument | Type | Required | Notes |
|---|---|---|---|
@@ -446,7 +458,7 @@ Turn a frame or artboard into a main component. Build reusable parts (button, ro
### `create_instance`
Place an instance of a component. Edit text and styles on the instance's descendants with set_text_content / update_styles (they become overrides); edits to the main component flow to every instance. Returns the instance id and its text layers.
Place an instance of a component. componentKey may be a component on any page or an enabled library; no import needed (the instance stays linked to that main). Edit text and styles on the instance's descendants with set_text_content / update_styles (they become overrides); edits to the main component flow to every instance. Returns the instance id and its text layers.
| Argument | Type | Required | Notes |
|---|---|---|---|
@@ -461,7 +473,7 @@ Place an instance of a component. Edit text and styles on the instance's descend
### `swap_instance`
Swap an instance root to point at a different main component (any page), preserving overrides on descendants matched by name and layer type.
Swap an instance root to point at a different main component, preserving overrides on descendants matched by name and layer type. componentKey may be a component on any page or an enabled library; no import needed.
| Argument | Type | Required | Notes |
|---|---|---|---|
@@ -544,6 +556,62 @@ Stop using a library: bindings to its variables are detached (values kept), its
| `libraryId` | string | yes | |
## Annotations
### `annotate`
Add notes to node ids or exact layer names on a page in one undo step. First text line is label; remaining lines are body. Type is an allowed category key (default rule); put fields as "Name: value" lines in text. Placement is automatic (tidy re-lays-out). Returns [number, node, type, label] rows; tidy alone returns {tidied: moved}.
| Argument | Type | Required | Notes |
|---|---|---|---|
| `frame` | string | | With tidy: only the notes on this frame. |
| `notes` | array of object | | `{node, text, type?}`. |
| `pageId` | string | | |
| `tidy` | boolean | | Re-layout all notes (of frame, else the page) as part of this undo step; notes may then be omitted. |
Check the placement with `get_screenshot {"nodeId": "<frame>", "annotations": true}`.
### `get_annotation_template` (read-only)
Read the page's annotation template: [type key, guidance, field names] for each allowed type. Call once before annotate.
| Argument | Type | Required | Notes |
|---|---|---|---|
| `pageId` | string | | |
### `set_annotation_template`
Switch the annotation template by id or name, for one page or the whole project. Annotate only accepts the template's types.
| Argument | Type | Required | Notes |
|---|---|---|---|
| `pageId` | string | | |
| `scope` | string | | `page` or `project`. |
| `template` | string | yes | |
### `upsert_annotation_template`
Create or replace an annotation template, optionally defining new categories (key, name, #hex color, icon name) first. Reuse existing keys freely. One undo step for the edit; setAs page adds a second. Returns {id, name, types}.
| Argument | Type | Required | Notes |
|---|---|---|---|
| `categories` | array of object | | Category styles `{key, name, color, icon}`; an existing key is updated, a new key needs all four. |
| `pageId` | string | | |
| `setAs` | string | | `project` or `page`. |
| `template` | object | yes | `{id?, name, display?: full or badges, categories: [{key, enabled?, guidance?, fields?}]}`. |
### `delete_annotation_template`
Delete an annotation template by id or name. The last template cannot be deleted; a project default pointing at it reverts to Product spec.
| Argument | Type | Required | Notes |
|---|---|---|---|
| `template` | string | yes | |
## Session
### `get_feedback` (read-only)
@@ -581,7 +649,7 @@ Mark notes on a layer resolved, with a short reply saying what you changed. With
### `select_nodes`
Select nodes on the page the user is viewing so they can see what you mean; zoom: true also frames them in the viewport. An empty list clears the selection.
Select nodes on the page the user is viewing so they can see what you mean; zoom: true also frames them in the viewport (at most 100%). An empty list clears the selection. For another page or a closer zoom use set_viewport.
| Argument | Type | Required | Notes |
|---|---|---|---|
@@ -589,6 +657,20 @@ Select nodes on the page the user is viewing so they can see what you mean; zoom
| `zoom` | boolean | | |
### `set_viewport`
Move the user's view: switch to pageId (as clicking it in the page list does) and fit nodeIds or a world rect, optionally at an explicit zoom (0.01 to 256). With only zoom it zooms about the view's centre. Refused while the user is moving the canvas (within 10 s of their last pan or zoom) unless force: true. Returns the new viewport ({pageId, rect: [x, y, w, h] in world units, zoom}); get_basic_info and get_selection report the current one.
| Argument | Type | Required | Notes |
|---|---|---|---|
| `force` | boolean | | Move the view even if the user is panning or zooming. |
| `nodeIds` | array of string | | Nodes to fit in the view. |
| `padding` | number | | Screen margin when fitting, in px (default 64). |
| `pageId` | string | | Page to show; defaults to the page of nodeIds, else the current one. |
| `rect` | array of number | | World rect [x, y, width, height] to fit, instead of nodeIds. |
| `zoom` | number | | Explicit zoom level (1 = 100%); centres the nodes or rect at this zoom instead of fitting. |
### `undo`
Undo the last change on a page (the user's or an agent's).