CATIMATION Canvas — generate on canvas + auto-edit from annotations
The canvas is an infinite tldraw surface embedded in the Codex page. You drive it
through MCP tools; the user draws on it. There is no manual submit button — when
the user finishes annotating, the canvas auto-enqueues an edit request and you pick
it up via the watch loop below.
Open the canvas
Call canvas_open first (idempotent). If the canvas is not visible the tool opens
it as the active center tab.
See what's on the canvas
You CAN inspect the canvas — do not tell the user you cannot see it. Call
canvas_snapshot: it returns a structured list of every shape (images, dashed
holders, arrows/circles/text annotations with their positions/bounds, plus each
image's assetId/assetPath/intrinsic size) AND an imagePath — a real on-disk
PNG render (screenshotScope tells you if it shows the whole canvas or just the
viewport). Open/view that imagePath to actually see the pixels (layout, what the
user drew, current image). Use canvas_snapshot whenever the user asks "what's on
the canvas / 看一下画布", or before editing so you know the exact target and where
the marks are. Pass screenshot: false when you only need the structured data.
On LARGE canvases (>40 shapes) the snapshot is TIERED: viewport shapes come as a
reduced overview, off-viewport shapes are grouped into peripheralClusters
(bounds + count + type histogram), and the PNG is cropped to the viewport. To
explore: call canvas_focus_region with a cluster's bounds (or shapeIds) —
by default this sets YOUR OWN virtual viewport (the user's camera does NOT
move), then re-call canvas_snapshot to see that region in detail. Pass
mode: 'camera' ONLY when the user asks to be shown/taken somewhere (it moves
the shared camera), and clear: true to follow the user's camera again.
focusShapeIds: […] gets full detail for specific shapes; full: true
forces the complete dump (large).
Picking and fetching one image (list → fetch)
When you need to act on a specific image (not the whole layout), prefer the
focused pair instead of eyeballing the full snapshot:
list_canvas_images (cheap, read-only) → a flat index of image shapes:
shapeId, assetId, on-canvas w/h, role, version, assetPath, and
hasFile. Use it to choose the right shapeId.
get_canvas_image { shapeId } → that one image's focused metadata plus an
imagePath — an on-disk PNG of just that image, annotations excluded. This
is the clean edit source: pass its imagePath to generate_image as a
referenceImages entry. Never claim you can't find the image's file — fetch it
here. (If hasFile was already true in the list, assetPath is also usable.)
Open-canvas hook
When the user opens the canvas themselves, the next turn arrives with a leading
[canvas] note telling you the canvas is now the active surface. Treat it as a
signal to stay in canvas mode: the canvas is already open (no need to call
canvas_open), and if you need to know what's on it, call canvas_snapshot
before acting. Do not echo the [canvas] note back to the user.
Generate an image onto the canvas
prepare_image_generation with the user's request + aspect ratio → returns a
holder shape id, bounds, and a suggested prompt.
generate_image with that prompt (and any referenceImages the user gave).
insert_image_into_holder with the returned holderShapeId + the generated
image path. The image now lives on the canvas.
Put a video on the canvas
After you generate a video (e.g. a Seedance/Sora clip), call insert_video with
videoPath (the local file path) to drop it onto the canvas as a real video shape
that plays inline. Optional x/y to position it (e.g. next to its source image)
and w/h to size it — omit them to use the clip's intrinsic size (capped to
640px on the longest edge). Use this for "把视频也放到画布上 / 出个视频放上去" requests.
(The user can also drag a file straight from the workspace file tree — or the OS —
onto the canvas; images and videos land as real shapes. insert_video /
insert_image_into_holder are the programmatic paths so YOU can place media
precisely.) For text/labels, use
canvas_exec to create a text/note shape (toRichText is injected).
To go the OTHER way — get a canvas video's file back so you can ffmpeg / contact-sheet
it — call get_canvas_video (no args; acts on the selected, or only, video). It
returns videoPath: an absolute on-disk mp4/webm/mov (the clip's recorded path, or a
materialized copy if it had none) plus shapeId/assetUrl/title. This is the video
analog of list_canvas_images→get_canvas_image: never hunt the disk by filename for
a canvas clip. (For semantic 理解/分析 of the clip instead, use understand_canvas_video.)
Auto-edit mode (Codex 直接监听) — the main loop
When the user says 开启自动修图 / 自动修图模式 (or asks you to keep applying canvas edits),
enter this loop and DO NOT stop until the user tells you to:
- Call
watch_edit_requests (it long-polls ~25s and claims the next request). If
it returns nothing, call it again — keep looping.
- When a request arrives, set it to processing if useful, then call
generate_image with the request's editPrompt and pass its targetImagePath
as a referenceImages entry (image-to-image edit). targetImagePath is ALWAYS a
real, on-disk PNG that the canvas exported for you — use it directly; never claim
the file is missing.
- Geometry-only marks (
needsClarification: true, e.g. the user drew an arrow,
circle, or box but no text label): the marks tell you WHERE to change; take WHAT
to change from the user's most recent chat instruction (e.g. "人物换成真人"). Combine
them into the edit and proceed — do NOT dead-end. Only stop and ask one short
question if neither the annotations nor the recent conversation give any intent.
- Call
create_image_version with sourceShapeId = the request's
targetShapeId and the new image path. This places the new version to the
right of the original and preserves the old image — never overwrite it.
- Call
update_edit_request with status: 'completed' (or needs_clarification
only when there is genuinely no intent anywhere).
- Go back to step 1.
If the watch loop has been idle for a long time and stops, the canvas shows the
user that you've paused; when they ask you to continue, just re-enter the loop.
Reading annotations directly (one-off, no loop)
To apply the current marks once without the loop: prepare_annotation_edit
(optionally with a targetShapeId) returns the parsed annotation plan + a ready
editPrompt. Then do generate_image → create_image_version exactly as above.
Tidy layout in one call (canvas_arrange)
To line up multiple shapes (storyboard grids, image rows, comparison columns),
call canvas_arrange { shapeIds, operation, gap? } instead of nudging x/y one
shape at a time. Operations: align-left/right/top/bottom,
align-center-horizontal/vertical, distribute-horizontal/vertical (≥3 shapes),
stack-horizontal/vertical (row/column with a gap), pack (grid),
bring-to-front/send-to-back (z-order, ≥1 shape — e.g. a note hidden behind
an image). One atomic transaction, and the camera frames the result (z-order
ops leave the camera alone).
Draw native shapes (canvas_create_shape)
To draw boxes, sticky notes, labels, lines and connector arrows, call
canvas_create_shape instead of writing canvas_exec code:
{ kind:'geo', geoType:'rectangle|ellipse|star|cloud|…', x, y, w?, h?, text?, color?, fill? } — labeled boxes/shapes.
{ kind:'note', x, y, text?, color? } — sticky note.
{ kind:'text', x, y, text, maxWidth?, color? } — free text (maxWidth wraps).
{ kind:'line', x1, y1, x2, y2, color? } — plain line.
{ kind:'arrow', fromId, toId, text?, bend?, color? } — connector BOUND to two
shapes: it follows them when they move. Prefer bindings over raw x1/y1/x2/y2
for storyboard shot-flow arrows and diagram edges.
Edit / delete single shapes (canvas_update_shape / canvas_delete_shapes)
canvas_update_shape { shapeId, x?, y?, w?, h?, rotation?, text?, color? }:
change ONE shape's position, size, rotation (degrees), text/label or color in
a single structured call — no canvas_exec code needed for simple edits.
canvas_delete_shapes { shapeIds }: batch-delete shapes (single undo step).
Destructive — only delete what the user clearly wants gone.
Relative placement — never do coordinate math yourself
Both canvas_update_shape and canvas_create_shape accept
{ referenceId, side, align?, sideOffset?, alignOffset? }: the shape lands on
side (top/bottom/left/right) of the reference shape, aligned start/center/end
(default center), with optional px offsets. The math runs in code against REAL
measured bounds. Use it for captions under images, shot numbers pinned to a
corner (side:'top', align:'start'), labels beside file cards. Example — a
caption 16px below an image, centered:
canvas_create_shape { kind:'text', text:'Shot 3 — dawn alley', referenceId: IMG_ID, side:'bottom', sideOffset:16, maxWidth:360 }.
Placement wins over x/y in the same call.
Noticing user changes between looks
canvas_snapshot returns changedSinceLastSnapshot ({created/updated/deleted}
shape ids) whenever the canvas changed since your previous snapshot in this
thread; its byAgent field lists the subset YOU wrote via the structured tools
— treat ids NOT in byAgent as the user's edits and respect them (don't "fix"
them back). Check it first — if the user moved/edited/deleted shapes while you
were working, re-read those shapes before acting on stale positions. Canvas
lints are surfaced ONCE per thread — react when you see one; absence later
doesn't mean it was fixed. The snapshot
also carries userViewportBounds — the region the USER is looking at right now
(it may differ from your virtual viewport): when the user says "the images on
my screen", match shapes against THAT box, not yours.
Free-form canvas control (canvas_exec + canvas_search)
For layout/edits the structured tools above don't cover — group, complex
reorder, multi-step transforms, exotic shape props — use the escape
hatch:
canvas_search { code } (read-only): write JS that gets spec and returns a
result, to discover the Editor API. e.g.
return spec.members.filter(m => m.category === 'layout').map(m => m.signature)
or return spec.types.shapes.find(s => s.shapeType === 'arrow').
canvas_exec { code }: write JS that runs on the live tldraw editor. In scope:
editor (real tldraw Editor API) + helpers createShapeId, createBindingId,
createArrowBetweenShapes(fromId,toId,{text?,bend?}), boxShapes(ids,{text?,color?}),
zoomToFit(ids), Box, Vec, Mat, clamp, getArrowBindings, toRichText.
Use return to read data back. Examples:
return editor.getCurrentPageShapes().map(s => ({ id: s.id, type: s.type }))
editor.createShape({ type:'geo', x:200, y:120, props:{ geo:'rectangle', w:320, h:180 } })
createArrowBetweenShapes('shape:a','shape:b',{ text:'next' })
editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
canvas_exec returns { success, result?, error? }. On success:false the code did
NOT apply — read error, fix the snippet, and retry. Prefer the dedicated image
tools (insert_image_into_holder / create_image_version) for the image-version flow;
use exec for everything else. Don't delete the user's images unless asked.
Saving / exposing the canvas as a file
save_snapshot persists the canvas and returns imagePath — an on-disk PNG of the
whole canvas you can open or share, like an uploaded attachment. Use it when the
user wants to "save / 导出 / 存一下画布".
Restorable checkpoints (save / load / list)
Beyond the flat PNG, you can save the full editable canvas state and restore it
later — useful as a "fork"/branch point before risky edits, or to keep named
versions:
save_checkpoint { name? } → saves the whole canvas (tldraw snapshot JSON) to
disk; returns { checkpointId, path, shapeCount }. Call this BEFORE a big/risky
change so you can return to it.
list_checkpoints → newest-first { checkpointId, name, createdAt, shapeCount, path }.
load_checkpoint { checkpointId } → replaces the current canvas with that
checkpoint (switches to that branch). Save the current state first if you might
want it back. Returns { ok:false, error } on an incompatible snapshot instead
of crashing the canvas.
Use checkpoints for "存个版本 / 回到之前那版 / 试一个分支" type requests. Use
save_snapshot (PNG) only when the user just wants a flat image to share.
Notes
- Arrows with a text label = "change the thing this arrow points at, per the note".
Circles / boxes scope a region. Bare short notes (改一下 / 不好看) are too vague —
ask one crisp clarifying question rather than guessing.
- Keep every prior version on the canvas; iterations go left → right.
- generate_image is the in-app path that actually displays + saves the result; use
it rather than any built-in image tool.
1---2name: catimation-canvas3description: Interactive AI image canvas (tldraw) in the CATIMATION desktop app. Trigger when the user wants to work on the 画布 / canvas, place a generated image there, or iterate on an image by drawing annotations (arrows+notes, circles, boxes). Especially trigger on 打开画布 / 开启自动修图 / 自动修图模式 / 按标注修图 / 在画布上改图 / canvas edit. The canvas AUTO-SUBMITS an edit request when the user finishes annotating (no button); keep watching for and applying those. Uses the canvas_* + generate_image tools.4---56# CATIMATION Canvas — generate on canvas + auto-edit from annotations78The canvas is an infinite tldraw surface embedded in the Codex page. You drive it9through MCP tools; the user draws on it. There is **no manual submit button** — when10the user finishes annotating, the canvas auto-enqueues an edit request and you pick11it up via the watch loop below.1213## Open the canvas1415Call `canvas_open` first (idempotent). If the canvas is not visible the tool opens16it as the active center tab.1718## See what's on the canvas1920You CAN inspect the canvas — do not tell the user you cannot see it. Call21`canvas_snapshot`: it returns a structured list of every shape (images, dashed22holders, arrows/circles/text annotations with their positions/bounds, plus each23image's `assetId`/`assetPath`/intrinsic size) AND an `imagePath` — a real on-disk24PNG render (`screenshotScope` tells you if it shows the whole canvas or just the25viewport). Open/view that `imagePath` to actually see the pixels (layout, what the26user drew, current image). Use `canvas_snapshot` whenever the user asks "what's on27the canvas / 看一下画布", or before editing so you know the exact target and where28the marks are. Pass `screenshot: false` when you only need the structured data.2930On LARGE canvases (>40 shapes) the snapshot is TIERED: viewport shapes come as a31reduced overview, off-viewport shapes are grouped into `peripheralClusters`32(bounds + count + type histogram), and the PNG is cropped to the viewport. To33explore: call `canvas_focus_region` with a cluster's `bounds` (or `shapeIds`) —34by default this sets YOUR OWN virtual viewport (the user's camera does NOT35move), then re-call `canvas_snapshot` to see that region in detail. Pass36`mode: 'camera'` ONLY when the user asks to be shown/taken somewhere (it moves37the shared camera), and `clear: true` to follow the user's camera again.38`focusShapeIds: […]` gets full detail for specific shapes; `full: true`39forces the complete dump (large).4041### Picking and fetching one image (list → fetch)4243When you need to act on a specific image (not the whole layout), prefer the44focused pair instead of eyeballing the full snapshot:45461. `list_canvas_images` (cheap, read-only) → a flat index of image shapes:47 `shapeId`, `assetId`, on-canvas `w`/`h`, `role`, `version`, `assetPath`, and48 `hasFile`. Use it to choose the right `shapeId`.492. `get_canvas_image { shapeId }` → that one image's focused metadata plus an50 `imagePath` — an on-disk PNG of just that image, **annotations excluded**. This51 is the clean edit source: pass its `imagePath` to `generate_image` as a52 `referenceImages` entry. Never claim you can't find the image's file — fetch it53 here. (If `hasFile` was already true in the list, `assetPath` is also usable.)5455## Open-canvas hook5657When the user opens the canvas themselves, the next turn arrives with a leading58`[canvas]` note telling you the canvas is now the active surface. Treat it as a59signal to stay in canvas mode: the canvas is already open (no need to call60`canvas_open`), and if you need to know what's on it, call `canvas_snapshot`61before acting. Do not echo the `[canvas]` note back to the user.6263## Generate an image onto the canvas64651. `prepare_image_generation` with the user's request + aspect ratio → returns a66 holder shape id, bounds, and a suggested prompt.672. `generate_image` with that prompt (and any `referenceImages` the user gave).683. `insert_image_into_holder` with the returned `holderShapeId` + the generated69 image path. The image now lives on the canvas.7071## Put a video on the canvas7273After you generate a video (e.g. a Seedance/Sora clip), call `insert_video` with74`videoPath` (the local file path) to drop it onto the canvas as a real video shape75that plays inline. Optional `x`/`y` to position it (e.g. next to its source image)76and `w`/`h` to size it — omit them to use the clip's intrinsic size (capped to77640px on the longest edge). Use this for "把视频也放到画布上 / 出个视频放上去" requests.78(The user can also drag a file straight from the workspace file tree — or the OS —79onto the canvas; images and videos land as real shapes. `insert_video` /80`insert_image_into_holder` are the programmatic paths so YOU can place media81precisely.) For text/labels, use82`canvas_exec` to create a `text`/`note` shape (`toRichText` is injected).8384To go the OTHER way — get a canvas video's file back so you can ffmpeg / contact-sheet85it — call `get_canvas_video` (no args; acts on the selected, or only, video). It86returns `videoPath`: an absolute on-disk mp4/webm/mov (the clip's recorded path, or a87materialized copy if it had none) plus `shapeId`/`assetUrl`/`title`. This is the video88analog of `list_canvas_images`→`get_canvas_image`: never hunt the disk by filename for89a canvas clip. (For semantic 理解/分析 of the clip instead, use `understand_canvas_video`.)9091## Auto-edit mode (Codex 直接监听) — the main loop9293When the user says 开启自动修图 / 自动修图模式 (or asks you to keep applying canvas edits),94enter this loop and DO NOT stop until the user tells you to:95961. Call `watch_edit_requests` (it long-polls ~25s and claims the next request). If97 it returns nothing, call it again — keep looping.982. When a request arrives, set it to processing if useful, then call99 `generate_image` with the request's `editPrompt` and pass its `targetImagePath`100 as a `referenceImages` entry (image-to-image edit). `targetImagePath` is ALWAYS a101 real, on-disk PNG that the canvas exported for you — use it directly; never claim102 the file is missing.1033. **Geometry-only marks** (`needsClarification: true`, e.g. the user drew an arrow,104 circle, or box but no text label): the marks tell you WHERE to change; take WHAT105 to change from the user's most recent chat instruction (e.g. "人物换成真人"). Combine106 them into the edit and proceed — do NOT dead-end. Only stop and ask one short107 question if neither the annotations nor the recent conversation give any intent.1084. Call `create_image_version` with `sourceShapeId` = the request's109 `targetShapeId` and the new image path. This places the new version **to the110 right of the original and preserves the old image** — never overwrite it.1115. Call `update_edit_request` with `status: 'completed'` (or `needs_clarification`112 only when there is genuinely no intent anywhere).1136. Go back to step 1.114115If the watch loop has been idle for a long time and stops, the canvas shows the116user that you've paused; when they ask you to continue, just re-enter the loop.117118## Reading annotations directly (one-off, no loop)119120To apply the current marks once without the loop: `prepare_annotation_edit`121(optionally with a `targetShapeId`) returns the parsed annotation plan + a ready122`editPrompt`. Then do generate_image → create_image_version exactly as above.123124## Tidy layout in one call (canvas_arrange)125126To line up multiple shapes (storyboard grids, image rows, comparison columns),127call `canvas_arrange { shapeIds, operation, gap? }` instead of nudging x/y one128shape at a time. Operations: `align-left/right/top/bottom`,129`align-center-horizontal/vertical`, `distribute-horizontal/vertical` (≥3 shapes),130`stack-horizontal/vertical` (row/column with a gap), `pack` (grid),131`bring-to-front`/`send-to-back` (z-order, ≥1 shape — e.g. a note hidden behind132an image). One atomic transaction, and the camera frames the result (z-order133ops leave the camera alone).134135## Draw native shapes (canvas_create_shape)136137To draw boxes, sticky notes, labels, lines and connector arrows, call138`canvas_create_shape` instead of writing canvas_exec code:139140- `{ kind:'geo', geoType:'rectangle|ellipse|star|cloud|…', x, y, w?, h?, text?, color?, fill? }` — labeled boxes/shapes.141- `{ kind:'note', x, y, text?, color? }` — sticky note.142- `{ kind:'text', x, y, text, maxWidth?, color? }` — free text (maxWidth wraps).143- `{ kind:'line', x1, y1, x2, y2, color? }` — plain line.144- `{ kind:'arrow', fromId, toId, text?, bend?, color? }` — connector BOUND to two145 shapes: it follows them when they move. Prefer bindings over raw x1/y1/x2/y2146 for storyboard shot-flow arrows and diagram edges.147148## Edit / delete single shapes (canvas_update_shape / canvas_delete_shapes)149150- `canvas_update_shape { shapeId, x?, y?, w?, h?, rotation?, text?, color? }`:151 change ONE shape's position, size, rotation (degrees), text/label or color in152 a single structured call — no canvas_exec code needed for simple edits.153- `canvas_delete_shapes { shapeIds }`: batch-delete shapes (single undo step).154 Destructive — only delete what the user clearly wants gone.155156## Relative placement — never do coordinate math yourself157158Both `canvas_update_shape` and `canvas_create_shape` accept159`{ referenceId, side, align?, sideOffset?, alignOffset? }`: the shape lands on160`side` (top/bottom/left/right) of the reference shape, aligned `start/center/end`161(default center), with optional px offsets. The math runs in code against REAL162measured bounds. Use it for captions under images, shot numbers pinned to a163corner (`side:'top', align:'start'`), labels beside file cards. Example — a164caption 16px below an image, centered:165`canvas_create_shape { kind:'text', text:'Shot 3 — dawn alley', referenceId: IMG_ID, side:'bottom', sideOffset:16, maxWidth:360 }`.166Placement wins over x/y in the same call.167168## Noticing user changes between looks169170`canvas_snapshot` returns `changedSinceLastSnapshot` ({created/updated/deleted}171shape ids) whenever the canvas changed since your previous snapshot in this172thread; its `byAgent` field lists the subset YOU wrote via the structured tools173— treat ids NOT in `byAgent` as the user's edits and respect them (don't "fix"174them back). Check it first — if the user moved/edited/deleted shapes while you175were working, re-read those shapes before acting on stale positions. Canvas176`lints` are surfaced ONCE per thread — react when you see one; absence later177doesn't mean it was fixed. The snapshot178also carries `userViewportBounds` — the region the USER is looking at right now179(it may differ from your virtual viewport): when the user says "the images on180my screen", match shapes against THAT box, not yours.181182## Free-form canvas control (canvas_exec + canvas_search)183184For layout/edits the structured tools above don't cover — group, complex185reorder, multi-step transforms, exotic shape props — use the escape186hatch:1871881. `canvas_search { code }` (read-only): write JS that gets `spec` and returns a189 result, to discover the Editor API. e.g.190 `return spec.members.filter(m => m.category === 'layout').map(m => m.signature)`191 or `return spec.types.shapes.find(s => s.shapeType === 'arrow')`.1922. `canvas_exec { code }`: write JS that runs on the live tldraw `editor`. In scope:193 `editor` (real tldraw Editor API) + helpers `createShapeId`, `createBindingId`,194 `createArrowBetweenShapes(fromId,toId,{text?,bend?})`, `boxShapes(ids,{text?,color?})`,195 `zoomToFit(ids)`, `Box`, `Vec`, `Mat`, `clamp`, `getArrowBindings`, `toRichText`.196 Use `return` to read data back. Examples:197 - `return editor.getCurrentPageShapes().map(s => ({ id: s.id, type: s.type }))`198 - `editor.createShape({ type:'geo', x:200, y:120, props:{ geo:'rectangle', w:320, h:180 } })`199 - `createArrowBetweenShapes('shape:a','shape:b',{ text:'next' })`200 - `editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')`201202`canvas_exec` returns `{ success, result?, error? }`. On `success:false` the code did203NOT apply — read `error`, fix the snippet, and retry. Prefer the dedicated image204tools (insert_image_into_holder / create_image_version) for the image-version flow;205use exec for everything else. Don't delete the user's images unless asked.206207## Saving / exposing the canvas as a file208209`save_snapshot` persists the canvas and returns `imagePath` — an on-disk PNG of the210whole canvas you can open or share, like an uploaded attachment. Use it when the211user wants to "save / 导出 / 存一下画布".212213## Restorable checkpoints (save / load / list)214215Beyond the flat PNG, you can save the **full editable canvas state** and restore it216later — useful as a "fork"/branch point before risky edits, or to keep named217versions:218219- `save_checkpoint { name? }` → saves the whole canvas (tldraw snapshot JSON) to220 disk; returns `{ checkpointId, path, shapeCount }`. Call this BEFORE a big/risky221 change so you can return to it.222- `list_checkpoints` → newest-first `{ checkpointId, name, createdAt, shapeCount, path }`.223- `load_checkpoint { checkpointId }` → **replaces** the current canvas with that224 checkpoint (switches to that branch). Save the current state first if you might225 want it back. Returns `{ ok:false, error }` on an incompatible snapshot instead226 of crashing the canvas.227228Use checkpoints for "存个版本 / 回到之前那版 / 试一个分支" type requests. Use229`save_snapshot` (PNG) only when the user just wants a flat image to share.230231## Notes232233- Arrows with a text label = "change the thing this arrow points at, per the note".234 Circles / boxes scope a region. Bare short notes (改一下 / 不好看) are too vague —235 ask one crisp clarifying question rather than guessing.236- Keep every prior version on the canvas; iterations go left → right.237- generate_image is the in-app path that actually displays + saves the result; use238 it rather than any built-in image tool.