Add tldraw file skill
This commit is contained in:
375
tldraw-file/SKILL.md
Normal file
375
tldraw-file/SKILL.md
Normal file
@@ -0,0 +1,375 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user