{"id":43526,"plugin_id":"plugin_asdk_app_6ab242dbeb5c8191ba5277534ef0c570","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-24T13:11:17.937Z","digest":"2744dee01e041f7314c0fbe5dad16ed350b64ed2b451ad5601a42167b9cfb4e0","against":null,"payload":{"description":"Draw and diagram on a tldraw canvas with the tldraw plugin. Use when the user asks for a diagram, flowchart, sequence diagram, state machine, mindmap, sketch, or board, gives you mermaid to draw, or wants to edit shapes on a canvas.","included_files":[],"name":"tldraw","skill_md_contents":"---\nname: tldraw\ndescription: Draw and diagram on a tldraw canvas with the tldraw plugin. Use when the user asks for a diagram, flowchart, sequence diagram, state machine, mindmap, sketch, or board, gives you mermaid to draw, or wants to edit shapes on a canvas.\n---\n\n# tldraw canvas\n\nThe plugin gives you a real tldraw editor. You draw by writing JavaScript that runs against it. For a flowchart, sequence diagram, state machine, or mindmap, that JavaScript can be one call: write the diagram as mermaid and draw it with the `helpers.createMermaidDiagram` editor helper (see Diagrams from mermaid).\n\nEvery board is a tldraw.com board. Find one with `search_boards`, then open it with `open_board` (`action: \"open\"`), passing its `boardId` and name, before writing drawing code. The app opens in the thread side panel; then call `exec` to draw on it. `exec` waits for the view to connect, so call it right after opening. `open_board` with `action: \"home\"` lets the user browse; the user can also open the board browser from the global sidebar or thread side panel.\n\nThe board is the real one, live: `exec` edits reach tldraw.com at once and anyone else on it sees them. Change it only as the user asked. A view-only board opens read-only.\n\nTo start a new board, call `create_board` with a name, and the workspace if the user named one (it defaults to their personal workspace). Then open it with `open_board` (`action: \"open\"`), passing the returned `boardId` and name. Only create a board when the user asks for a new one; otherwise find the existing board with `search_boards`.\n\nTo rename a board, call `rename_board` with its `boardId` and the new name. Only rename when the user asks. Only members of the workspace that owns a board can rename it; a board shared by link cannot be renamed, so tell the user why.\n\nIf the user @-mentions a board, the message contains a `tldraw://board/<id>` link. Pass that URI (or the id) as `boardId` to `open_board`, then edit with `exec`.\n\nUse the tldraw tools yourself, in the main thread; do not hand the drawing to a sub-agent. A board a sub-agent opens cannot load, because Codex does not let that view call this plugin.\n\nPass the board's `boardId` to every `exec` and `screenshot` call; `open_board` returns it, and the model context lists each open board. Do not reopen the board for each edit. If `exec` reports that the board is not open, use `list_open_boards`, or open it with `open_board`; do not claim the drawing was made.\n\n## Tools\n\n- `search` — query the installed SDK's API documentation with JavaScript. Use `spec.members` (names, signatures, descriptions, categories, examples), `spec.types.shapes` (shape types and props), and `spec.helpers`. Return only the relevant entries, e.g. `return spec.members.filter(m => /createShape|alignShapes/.test(m.name))`.\n- `search_boards` — find the user's tldraw.com boards by name; returns IDs for `open_board`.\n- `create_board` — create a new, empty tldraw.com board in the user's personal workspace or a named workspace; returns its `boardId` for `open_board`.\n- `rename_board` — rename a tldraw.com board, by `boardId`; returns the new and previous names.\n- `exec` — run JavaScript against an open board, named by `boardId`, with the full `editor` API and `helpers`: `createShapeId`, `toRichText`, `createArrowBetweenShapes(fromId, toId, { text?, bend? })`, which binds an arrow to two shapes so it follows them, `addImage(url, { x?, y?, w?, h?, id? })`, which puts an image from the web on the board (see Images), `createMermaidDiagram(source, options?)`, which draws a mermaid diagram as editable shapes (see Diagrams from mermaid), and the comment helpers `getComments` and `addComment` (see Comments). Return a small JSON value to inspect the board. The result includes the board ID, return value, and record-change counts. Document edits commit together as one undo step. Errors discard the proposed edit, as does a change made meanwhile to anything the edit touches; reread the board before retrying a conflict.\n- `screenshot` — explicitly inspect a board's page, viewport, or selection. No screenshots are taken automatically. Use this when visual inspection is needed.\n\nAwait all asynchronous work in `exec`. Do not retain editor references, schedule work after returning, access browser globals, or perform external side effects. The temporary editor protects document edits, not network or browser side effects. Use `signal` to cooperate with cancellation. Prefer short, bounded operations. User-facing dialogs, undo history, and DOM-dependent operations belong in the app UI, not in scripts.\n\n## Diagrams from mermaid\n\nFor a whole diagram drawn from its structure (a flowchart, sequence diagram, state machine, or mindmap), write mermaid and let `helpers.createMermaidDiagram(source, options?)` draw it rather than placing nodes and arrows by hand. It makes real, editable shapes: geo shapes and arrows bound to them, usually all in one group. A subgraph (or a compound state) is a rectangle geo shape with its nodes as children and its title in `richText`. Await it.\n\n```js\nconst diagram = await helpers.createMermaidDiagram(`flowchart TD\n  A[Write the change] --> B{Tests pass?}\n  B -->|Yes| C[Open a PR]\n  B -->|No| A`)\nreturn diagram.bounds\n```\n\n- Supported kinds: `flowchart` (or `graph`), `sequenceDiagram`, `stateDiagram-v2`, and `mindmap`. Anything else (class, ER, gantt, pie, …) throws; build those from shapes.\n- In the portrait side panel, a flowchart reads best top-down: `flowchart TD`, not `LR`.\n- The diagram lands centred on the viewport. `options.blueprintRender.position` centres it on a page point instead, or puts its top-left corner there with `centerOnPosition: false`. It returns `{ rootShapeId, shapeIds, bounds }`: `rootShapeId` is the diagram's top-level shape: usually a group, or the one shape a diagram has at the top (a lone node, or a subgraph holding the rest). A second diagram placed from the first one's `bounds` does not overlap it; or put both in one source as `subgraph`s. A diagram placed wholly inside a frame goes into it; keep diagrams clear of frame edges, where one can be clipped by the frame or left beside it.\n- Invalid source throws mermaid's parse error and draws nothing, which discards the whole `exec`; fix the source and run it again.\n- When the user gives you mermaid (pasted into the chat, or from a file or doc), draw it with `createMermaidDiagram` as it is rather than rebuilding it by hand.\n- It draws a new diagram. To change a diagram already on the board, edit its shapes.\n- Users can paste mermaid onto the board themselves; it becomes shapes the same way.\n\n## Images\n\nTo put an image from the web on the board, use `helpers.addImage(url, options?)`. Await it. It copies the image onto the board's own storage, so it shows in this view and on tldraw.com. Do not create an image asset whose `src` is another site's URL: this view cannot load it, and it shows as broken.\n\n```js\nconst urls = ['https://example.com/a.jpg', 'https://example.com/b.jpg']\nlet y = 0\nfor (const url of urls) {\n\tconst { bounds } = await helpers.addImage(url, { x: 0, y, w: 300 })\n\ty += bounds.h + 40\n}\n```\n\n- `x`, `y` is the image's top-left corner; without them it is centred on the viewport. `w` or `h` sets its size, keeping the aspect ratio; with neither, the longer side is at most 800.\n- It returns `{ shapeId, assetId, bounds }`. Pass `id` to name the shape.\n- It throws when the URL is not a public http(s) image, is over 5 MB, or its host refuses, which discards the rest of that `exec`. When adding many images, add them in their own `exec` calls, or catch each failure and report which ones did not load.\n- Use a direct image URL (one that serves the image itself), not the page it appears on.\n\n## Comments\n\nPeople leave comments on a board as pinned threads: each thread has an anchor saying what it is about (a shape, a point, or a region) and a conversation of comments. They are the board's own, the same ones tldraw.com shows.\n\n- `helpers.getComments()` returns the current page's threads, oldest first, resolved ones included (pass `{ pageId }` for another page). Read the anchor before the words: `anchor.shapeId` is the shape under discussion, and `position` is the pin's page point (a shape anchor's own `x`, `y` are 0–1 within the shape).\n- `helpers.addComment(text, target)` posts as the signed-in user, and tldraw.com shows it under their name. `{ threadId }` replies in an open thread (a resolved one takes no replies: start a new thread); `{ shapeId }` starts a thread on a shape, and adding page coordinates `x`, `y` pins that spot on it; `{ x, y }` starts one at a page point. It returns `{ threadId, commentId }`.\n\n```js\nconst threads = helpers.getComments()\nreturn threads\n\t.filter((t) => !t.resolved)\n\t.map((t) => ({ id: t.id, on: t.anchor.shapeId, said: t.comments.map((c) => c.text) }))\n```\n\n```js\nhelpers.addComment('I widened both boxes to fit their labels.', { threadId: 'comment-thread:…' })\n```\n\nComment only when the user asks you to, or to answer a thread you acted on. A comment is visible to everyone on the board and is not undone with the edit. Post it in its own `exec` call once the edit has landed: when `addComment` throws, the whole script's edit is discarded with it. It throws when this connection cannot post comments (its link to tldraw.com is not signed in, or the account has not loaded); tell the user rather than working around it. `exec` can only add comments this way: it refuses to change or remove comment records, so resolving, editing and deleting comments are for the user to do. Like every `exec`, `getComments` does not run on a view-only board.\n\n## Shape format\n\nRaw tldraw SDK records. No agent dialect.\n\n```js\nconst { createShapeId, toRichText } = helpers\neditor.createShape({\n\tid: createShapeId('box1'),\n\ttype: 'geo',\n\tx: 100,\n\ty: 100,\n\tprops: { geo: 'rectangle', w: 300, h: 200, richText: toRichText('Label') },\n})\n```\n\n- The board usually opens in the thread side panel, which is tall and narrow: stack content vertically (flows top-to-bottom, columns over rows) so the whole drawing fits the panel without horizontal panning.\n- A card and its words are one shape: put the text in the geo's `richText`.\n- Choose your own ids with `createShapeId('name')` so later calls can refer to what you drew.\n- Connect shapes with `helpers.createArrowBetweenShapes('box1', 'box2', { text: 'next' })` rather than positioning arrow endpoints by hand; the arrow stays attached when either shape moves.\n\n## Valid style values\n\n- `color`: black, grey, light-violet, violet, blue, light-blue, yellow, orange, green, light-green, light-red, red, white\n- `fill`: none, semi, solid, pattern, fill, lined-fill\n- `size`: s, m, l, xl · `font`: draw, sans, serif, mono\n- `geo`: rectangle, ellipse, triangle, diamond, pentagon, hexagon, octagon, star, rhombus, oval, trapezoid, cloud, heart, x-box, check-box\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}