--- name: tldraw-file description: 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. argument-hint: "[diagram request, file path, or validation target]" user-invocable: 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. ```bash # 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: ```json { "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: ```json { "id": "shape:example", "typeName": "shape" } ``` Actual records must also satisfy their shape-specific schema. ## Required Base Records Include at least: ```json { "gridSize": 10, "name": "", "meta": {}, "id": "document:document", "typeName": "document" } ``` ```json { "id": "page:page", "name": "Page", "index": "a1", "meta": {}, "typeName": "page" } ``` Every shape should have: ```json { "parentId": "page:page", "index": "a2", "typeName": "shape" } ``` ## Rich Text Use tldraw rich text for labels. Helper shape: ```json { "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. ```json { "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 ```json { "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. ```json { "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: ```bash 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: ```bash 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: ```bash 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. ## Recommended Workflow 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.