Files
skills/tldraw-file/SKILL.md
2026-05-08 16:07:26 +01:00

8.2 KiB

name, description, argument-hint, user-invocable
name description argument-hint user-invocable
tldraw-file Create, repair, validate, or generate valid `.tldr` files that tldraw can open. Use when the user asks for a tldraw diagram, `.tldr` file, corrupted tldraw repair, or a diagram they can open in tldraw. [diagram request, file path, or validation target] true

tldraw File

Use this skill when the user asks to create, repair, validate, or generate a tldraw document/file.

Examples:

  • "Make me a tldraw diagram"
  • "Create a .tldr file"
  • "tldraw says this file is corrupted"
  • "Generate a diagram I can open in tldraw"

Core Rule

Do not hand-wave validity. A .tldr file must be validated with tldraw's own schema/importer, not just JSON.parse.

A file can be valid JSON and still be corrupted to tldraw.

Temporary Setup

If the current project does not already depend on tldraw, install it in /tmp/opencode, not in the user's repo.

# Work outside the repo
bun init -y
bun add tldraw

Use /tmp/opencode as the working directory for validation scripts.

.tldr File Shape

A current .tldr JSON file has this top-level shape:

{
  "tldrawFileFormatVersion": 1,
  "schema": {
    "schemaVersion": 2,
    "sequences": {
      "com.tldraw.store": 5,
      "com.tldraw.asset": 1,
      "com.tldraw.camera": 1,
      "com.tldraw.document": 2,
      "com.tldraw.instance": 26,
      "com.tldraw.instance_page_state": 5,
      "com.tldraw.page": 1,
      "com.tldraw.instance_presence": 6,
      "com.tldraw.pointer": 1,
      "com.tldraw.shape": 4,
      "com.tldraw.user": 1,
      "com.tldraw.asset.image": 6,
      "com.tldraw.asset.video": 5,
      "com.tldraw.asset.bookmark": 2,
      "com.tldraw.shape.arrow": 8,
      "com.tldraw.shape.bookmark": 2,
      "com.tldraw.shape.draw": 4,
      "com.tldraw.shape.embed": 4,
      "com.tldraw.shape.frame": 1,
      "com.tldraw.shape.geo": 11,
      "com.tldraw.shape.group": 0,
      "com.tldraw.shape.highlight": 3,
      "com.tldraw.shape.image": 5,
      "com.tldraw.shape.line": 5,
      "com.tldraw.shape.note": 12,
      "com.tldraw.shape.text": 4,
      "com.tldraw.shape.video": 4,
      "com.tldraw.binding.arrow": 1
    }
  },
  "records": []
}

Records must be an array of valid tldraw records. Each record needs at least:

{
  "id": "shape:example",
  "typeName": "shape"
}

Actual records must also satisfy their shape-specific schema.

Required Base Records

Include at least:

{
  "gridSize": 10,
  "name": "",
  "meta": {},
  "id": "document:document",
  "typeName": "document"
}
{
  "id": "page:page",
  "name": "Page",
  "index": "a1",
  "meta": {},
  "typeName": "page"
}

Every shape should have:

{
  "parentId": "page:page",
  "index": "a2",
  "typeName": "shape"
}

Rich Text

Use tldraw rich text for labels.

Helper shape:

{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Label"
        }
      ]
    }
  ]
}

For compatibility, also include the legacy text prop with the same plain text.

Geo Shape Template

Use geo shapes for most boxes. They are easier than notes and less fragile.

{
  "x": 100,
  "y": 100,
  "rotation": 0,
  "isLocked": false,
  "opacity": 1,
  "meta": {},
  "id": "shape:box",
  "type": "geo",
  "props": {
    "geo": "rectangle",
    "w": 260,
    "h": 90,
    "color": "blue",
    "labelColor": "black",
    "fill": "solid",
    "dash": "solid",
    "size": "m",
    "font": "sans",
    "align": "middle",
    "verticalAlign": "middle",
    "growY": 0,
    "url": "",
    "scale": 1,
    "richText": {
      "type": "doc",
      "content": [
        {
          "type": "paragraph",
          "content": [
            {
              "type": "text",
              "text": "My box"
            }
          ]
        }
      ]
    },
    "text": "My box"
  },
  "parentId": "page:page",
  "index": "a2",
  "typeName": "shape"
}

Text Shape Template

{
  "x": 100,
  "y": 40,
  "rotation": 0,
  "isLocked": false,
  "opacity": 1,
  "meta": {},
  "id": "shape:title",
  "type": "text",
  "props": {
    "color": "black",
    "size": "xl",
    "font": "sans",
    "textAlign": "start",
    "autoSize": true,
    "w": 900,
    "scale": 1,
    "richText": {
      "type": "doc",
      "content": [
        {
          "type": "paragraph",
          "content": [
            {
              "type": "text",
              "text": "Diagram Title"
            }
          ]
        }
      ]
    },
    "text": "Diagram Title"
  },
  "parentId": "page:page",
  "index": "a1",
  "typeName": "shape"
}

Arrow Shape Template

Arrows require labelColor. Missing it can make tldraw report the file as corrupted.

{
  "x": 400,
  "y": 200,
  "rotation": 0,
  "isLocked": false,
  "opacity": 1,
  "meta": {},
  "id": "shape:arrow",
  "type": "arrow",
  "props": {
    "kind": "arc",
    "color": "black",
    "labelColor": "black",
    "fill": "none",
    "dash": "solid",
    "size": "m",
    "arrowheadStart": "none",
    "arrowheadEnd": "arrow",
    "bend": 0,
    "start": {
      "x": 0,
      "y": 0
    },
    "end": {
      "x": 120,
      "y": 0
    },
    "labelPosition": 0.5,
    "font": "sans",
    "scale": 1,
    "richText": {
      "type": "doc",
      "content": [
        {
          "type": "paragraph",
          "content": [
            {
              "type": "text",
              "text": "label"
            }
          ]
        }
      ]
    },
    "text": "label"
  },
  "parentId": "page:page",
  "index": "a3",
  "typeName": "shape"
}

Valid Colors

For color and labelColor, use only known tldraw colors:

  • black
  • grey
  • light-violet
  • violet
  • blue
  • light-blue
  • yellow
  • orange
  • green
  • light-green
  • light-red
  • red
  • white

Invalid or missing labelColor can corrupt the file.

Notes

Avoid note shapes unless you know the current schema. They require additional props and are more version-sensitive. Prefer geo rectangles with fill: solid or fill: semi.

Validation

First validate JSON syntax:

bun -e "const f='/path/to/file.tldr'; const data=await Bun.file(f).text(); JSON.parse(data); console.log('valid json')"

Then validate with tldraw's store/schema:

bun -e "import { createTLStore } from 'tldraw'; const file=JSON.parse(await Bun.file('/path/to/file.tldr').text()); const snapshot={store:Object.fromEntries(file.records.map(r=>[r.id,r])), schema:file.schema}; try { const store=createTLStore({snapshot}); console.log('ok records', store.allRecords().length); } catch(e){ console.error(e); process.exit(1)} process.exit(0)"

Finally validate with the same importer path tldraw uses for .tldr files:

bun -e "import { createTLStore } from 'tldraw'; import { parseTldrawJsonFile } from './node_modules/tldraw/dist-esm/lib/utils/tldr/file.mjs'; const json=await Bun.file('/path/to/file.tldr').text(); const schema=createTLStore().schema; const result=parseTldrawJsonFile({json, schema}); console.log(result.ok ? 'parse ok' : result.error); process.exit(result.ok ? 0 : 1)"

Run importer validation from the temp directory where tldraw is installed, for example /tmp/opencode.

Common Corruption Causes

  • File is valid JSON but does not match tldraw schema.
  • Missing labelColor on arrow shapes.
  • Missing required shape props for note shapes.
  • Old schema sequence numbers copied from another tldraw version.
  • Invalid color names.
  • Shape parentId points to a missing page.
  • Duplicate record IDs.
  • Records object used instead of records array in .tldr file format.
  • richText missing where current schemas expect it.
  1. Create or update .tldr with apply_patch.
  2. Run JSON validation.
  3. Run createTLStore({ snapshot }) validation.
  4. Run parseTldrawJsonFile validation.
  5. If tldraw says corrupted, trust tldraw and inspect the validation error path.
  6. Fix the specific schema path, not just the JSON.

Practical Diagram Advice

  • Use geo rectangles for sections and entities.
  • Use text for headings.
  • Use simple arrow shapes for flow.
  • Keep IDs stable and readable: shape:admin-review, shape:stream-category.
  • Use indexed records like a1, a2, a3.
  • Keep diagrams editable rather than pixel-perfect.