LifeOS Tldraw 技能深度解析:确定性读写 .tldr 画布文件
【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS
导读
本文讲解 LifeOS 中 Tldraw 技能(LifeOS/install/skills/Tldraw/SKILL.md)的核心能力:以确定性(deterministic)方式读写 tldraw 的.tldr画布文件,让 AI Agent 既能“画”——按手绘风格把流程图、笔记、框架图直接写入用户可在任意 tldraw 界面打开的画布文件,也能“读”——把人类随手画的杂乱画布解析为结构化数据并整理成有聚类、有框架、有连线的版本。读完本文,你将掌握.tldr的 JSON 文件格式、Tldr.ts的七个子命令、两个标准工作流(SketchDiagram / StructureCanvas)以及全部已知坑位,可直接在 LifeOS 环境中复现同样的读写流程。
技能定位:模型 ↔ 画布的双向通道
Tldraw 技能是 LifeOS 中面向白板/画布场景的专项技能。根据 SKILL.md 的 frontmatter 描述,它适用的典型触发词包括:tldraw、.tldr file、whiteboard、canvas、sketch a diagram、hand-drawn diagram、draw this on a canvas、structure my canvas、organize my whiteboard、read my canvas、cluster my sticky notes等;同时明确声明不适用于精美静态图、信息图或 mermaid 图(应使用 Art 技能)、Web UI 设计(Webdesign 技能)、程序化视频(Remotion 技能)。
技能的核心主张是:.tldr格式本质上是纯 JSON({tldrawFileFormatVersion: 1, schema, records}),而 Tools/Tldr.ts 写出的记录能通过 tldraw 自身的校验器(validator),因此生成的文件可以在 tldraw 网页编辑器、VS Code tldraw 扩展、桌面应用中干净打开。它提供两个方向的能力:
- 模型 → 画布(SketchDiagram):把结构化描述翻译为手绘风格图形;
- 画布 → 模型(StructureCanvas):读取人类粗糙的思维草稿,整理成结构化画布。
理解 .tldr 文件格式
要安全地读写.tldr,必须先理解其格式。详细规范见 References/TldrFormat.md,其中所有结论都声明是对照 tldraw@5.2.5 验证过的:按此方式构造的记录可以通过parseTldrawJsonFile(tldraw 自身的加载路径)。
文件容器
{ "tldrawFileFormatVersion": 1, "schema": { "schemaVersion": 2, "sequences": { "...": 0 } }, "records": [ ... ] }schema驱动加载时的迁移逻辑。本技能在 References/SchemaSnapshot.json 中内置了一份取自 tldraw 5.2.5 的序列化快照(schemaVersion: 2,含com.tldraw.store、com.tldraw.shape.geo、com.tldraw.shape.arrow、com.tldraw.binding.arrow等全部类型序列号)。更旧的 tldraw 界面会拒绝更新 schema 的文件,而更新的界面会自动迁移旧文件。- 最小可行记录:一条
document:document+ 一条page:page。instance/camera记录由编辑器在加载时自行合成,不要写入文件。
每条形状的 record 信封
{ "id": "shape:<name>", "typeName": "shape", "type": "geo|text|note|frame|arrow", "parentId": "page:page", "x": 0, "y": 0, "rotation": 0, "index": "a1", "isLocked": false, "opacity": 1, "meta": {}, "props": { ... } }index是分数索引字符串(base62,字符集0-9A-Za-z,按字典序决定 z-order),绝不能以0结尾;- 直接写入文件的原始记录不会经过编辑器默认值填充,因此下面列出的每个 prop 都是必填的。
各类型必填 props(tldraw 5.2.5getDefaultProps()值)
| 类型 | Props |
|---|---|
geo | geo, w, h, color, labelColor, fill, dash, size, font, align, verticalAlign, growY, url, scale, richText |
text | color, size, w, font, textAlign, autoSize, scale, richText |
note | color, richText, size, font, align, verticalAlign, labelColor, growY, fontSizeAdjustment, url, scale, textLastEditedBy |
frame | w, h, name, color |
arrow | kind ("arc"), elbowMidPoint, dash, size, fill, color, labelColor, bend, start {x,y}, end {x,y}, arrowheadStart, arrowheadEnd, richText, labelPosition, font, scale |
箭头绑定记录(每个被绑定的端点一条):
{ "id": "binding:<name>", "typeName": "binding", "type": "arrow", "fromId": "shape:<arrow>", "toId": "shape:<target>", "props": { "isPrecise": false, "isExact": false, "terminal": "start", "normalizedAnchor": { "x": 0.5, "y": 0.5 }, "snap": "none" }, "meta": {} }注意:terminal("start"或"end")是校验器强制要求的,尽管ArrowBindingUtil.getDefaultProps()会省略它——这是最容易手写出错的字段之一。
richText:文本不是字符串
tldraw 中所有标签文本都是 ProseMirror 文档 JSON,而不是普通字符串。每个段落一个paragraph节点,空段落省略content:
{ "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "line 1" } ] } ] }裸字符串的textprop 会被 tldraw 校验器拒绝。Tldr.ts会替你构造 richText,永远不要手写textprop。
枚举值速查
- color / labelColor:black, grey, light-violet, violet, blue, light-blue, yellow, orange, green, light-green, light-red, red, white
- fill:none, semi, solid, pattern, fill
- dash:draw, solid, dashed, dotted
- size:s, m, l, xl
- font:draw(手绘风), sans, serif, mono
- geo:rectangle, ellipse, triangle, diamond, pentagon, hexagon, octagon, star, rhombus, oval, trapezoid, arrow-right, arrow-left, arrow-up, arrow-down, x-box, check-box, heart, cloud
- arrowheadStart / arrowheadEnd:none, arrow, triangle, square, dot, pipe, diamond, inverted, bar
以上枚举同时被 Tldr.ts 中的COLORS、GEOS集合硬编码约束,非法值会被工具直接拒绝(invalid color/invalid geo错误)。
坐标系
页面空间(page space),y 轴向下,原点任意;x,y表示形状的左上角。绑定了起止形状的箭头会由编辑器根据绑定形状重算路径,所以它们start/end点只在首次渲染前有意义。
Tldr.ts:确定性读写的 CLI 工具
Tldr.ts是纯 Bun 脚本、零外部依赖,核心注释声明其写出的记录“对照 tldraw@5.2.5 的parseTldrawJsonFile验证”,内嵌的 schema 快照保证生成的文件可被该版本及以上的任意 tldraw 界面加载。命令入口在 Tools/Tldr.ts,安装后位于~/.claude/skills/Tldraw/Tools/Tldr.ts(仓库路径为LifeOS/install/skills/Tldraw/Tools/Tldr.ts)。
完整命令签名(见文件头部 Usage 注释与 SKILL.md 的 Quick Reference):
bun Tldr.ts create <file> [--title "Heading text"] bun Tldr.ts inspect <file> [--json] bun Tldr.ts add <file> --spec <spec.json | -> bun Tldr.ts remove <file> --ids id1,id2 bun Tldr.ts move <file> --id <shapeId> --x N --y N bun Tldr.ts settext <file> --id <shapeId> --text "New label" bun Tldr.ts validate <file>create:建文件
写入两条最小记录(document:document与page:page,其中 page 的index为"a1")。若提供--title,还会追加一个shape:title的 text 形状(坐标为(0, -80),字号xl,宽度 700),用作画布标题。输出形如created <file> (3 records)。
add:按 spec 数组批量添加
--spec接受一个 JSON数组,每个条目定义一种形状,支持六种kind:
| kind | Required | Optional |
|---|---|---|
box | text 或 name; x, y | w, h, color, fill, geo, dash, size, font, url, name |
ellipse | 同 box(geo 强制为 ellipse) | — |
text | text; x, y | size, font, color, w(设置 w 会禁用 autoSize), textAlign, name |
note | text; x, y | color(默认 yellow), size, font, name |
frame | title; x, y, w, h | color, name |
arrow | from, to(指向已存在形状的 name 或 id) | text, color, bend, dash, size, arrowheadStart, arrowheadEnd, name |
name会成为形状 id(shape:<name>);省略时从文本自动派生(slug函数:小写化、非字母数字替换为-、截断 40 字符、空则回退"shape")。箭头必须排在它连接的形状之后(同一 spec 数组里靠后即可)。从 Tldr.ts 的expandSpec可看到各 kind 的默认值细节:
box:默认220×120、fill: "none"、dash: "draw"、size: "m"、font: "draw"、对齐middle、growY: 0、url: ""、scale: 1;ellipse:同一套默认值,但geo被强制为ellipse;text:默认宽 400、textAlign: "start",且只有未指定w时autoSize才为true;note:默认颜色yellow(与 tldraw 便签的视觉惯例一致)、fontSizeAdjustment: 1、textLastEditedBy: null;frame:默认640×360,name取title;arrow:kind: "arc"、elbowMidPoint: 0.5、bend: 0、arrowheadStart: "none"、arrowheadEnd: "arrow"、labelPosition: 0.5,并自动生成两条 binding 记录(terminal: "start"/"end"),normalizedAnchor为{x: 0.5, y: 0.5}、snap: "none";箭头的起止点取自两个形状的中心(centerOf)。
分数索引由nextIndex生成:若当前最大索引末位不是最后一个 base62 字符则末位 +1,否则追加"1",保证字典序单调且不以0结尾。uniqueId在 id 冲突时自动追加-2、-3后缀。
inspect:读回画布
inspect --json输出结构化数据:页面列表、每个形状的id/type/x/y/w/h/color/文本(frame 额外给出标题)、以及由 binding 推导的边列表(每条箭头记录from → to与标签文本)。非 JSON 模式输出人类可读的摘要(2 page(s), 5 shape(s)及各形状的定位、尺寸、文本)。文本通过plainText从 richText 递归提取(text 节点拼字符串、paragraph 追加换行、trimEnd去尾)。
remove / move / settext / validate
- remove:
--ids以逗号分隔。实现了一个级联删除(注释标明移植自 tldraw 公共 PR #1739,@elhoim):反复迭代到不动点,凡是 binding 引用了被删形状(toId或fromId),则删除该 binding 以及它所属的箭头——因为删掉箭头会孤立其另一条 binding,单趟遍历会漏掉。输出removed N record(s)。 - move:
--id指定形状,--x/--y缺省时保持原值,用于重排画布。 - settext:对 frame 修改
props.name,对带richText的形状重写 richText;无文本的形状会报错。 - validate:内置一致性门禁——检查
document/page记录存在、每条记录有id/typeName、形状的parentId有效、binding 必须有terminal且两端不悬空(fromId/toId都在文件内)、颜色必须在枚举内;任一失败即输出INVALID:错误列表并以退出码 1 结束,通过则输出valid: <file> (N records)。
实战一:SketchDiagram 工作流(画出手绘风格图表)
工作流定义在 Workflows/SketchDiagram.md,用于“把用户口头描述的流程/结构画成手绘风画布”。其目标是:生成的文件通过Tldr.ts validate,用户点名的每个概念都是一个形状、每条关系都是一条箭头,无多余元素;布局按流程顺序从左到右或从上到下,形状不重叠,相关内容用邻近或 frame 分组。
Step 0 — 充分性检查
动笔前先确认三件事:图表要传达什么、大约多少个元素、文件落在哪里(默认按用户偏好进入Canvases/目录,否则放当前项目)。如果存在解释分叉会改变图表结构,用一行标注(⚠️ Picking X over Y because R; redirect if wrong.)并继续采用最佳默认值。
工具契约
T=~/.claude/skills/Tldraw/Tools/Tldr.ts bun $T create <file.tldr> [--title "Heading"] bun $T add <file.tldr> --spec <spec.json> # spec: JSON array, kinds: box, ellipse, text, note, frame, arrow bun $T validate <file.tldr> bun $T inspect <file.tldr> # confirm what actually landed布局约束(经验约定)
- 盒子默认 220×120,水平间隙 ≥140 px、垂直间隙 ≥100 px,给绑定箭头留足空间;
- 箭头按
name引用形状并自动绑定——先加盒子(或同一 spec 数组内盒子在前); - 颜色即语义:从枚举中最多挑 2–4 种颜色,
fill: "solid"只用于想突出的形状; - 一个 frame 分组并命名一个区域,一个聚类一个 frame,优于散落的浮动标签。
验证门(gate)
validate通过 +inspect输出与预期的形状/边列表一致,才能闭环。技能明确要求诚实表达:你无法看到渲染后的像素,所以应报告“结构上已验证;请打开画布目测布局”,而不能说“看起来不错”。
一个完整的示例(SKILL.md Examples):
User: "Sketch the three-stage pipeline as a hand-drawn diagram" → Invokes SketchDiagram workflow → Writes spec JSON, runs Tldr.ts create + add, validates → Returns the .tldr path and how to open it; user nudges shapes and exports实战二:StructureCanvas 工作流(读并整理人类画布)
工作流定义在 Workflows/StructureCanvas.md,处理反向场景:读取用户散落的笔记、盒子、碎片,写回一个整理后的版本——聚类、加框架、连线,不丢失任何内容。
Step 0 与安全门
先确认文件路径,以及用户要的是原地修改同一文件(默认)还是旁边生成结构化副本。若画布正在编辑器中打开,请用户关闭或预期需要重开(见下文的 Gotchas)。在第一次改动前,先把文件复制为<file>.bak.tldr并明确告知——这是用户亲手创作的内容,备份就是撤销按钮。
工具契约与理想状态
T=~/.claude/skills/Tldraw/Tools/Tldr.ts bun $T inspect <file.tldr> --json # full read: shapes, text, positions, edges bun $T move <file.tldr> --id <id> --x N --y N # regroup existing shapes bun $T add <file.tldr> --spec <spec.json> # frames, arrows, summary labels bun $T validate <file.tldr>理想结果:用户的每一条文本都原样保留——整理意味着移动、分组、加框、连线,而不是改写用户的话(新增摘要/标签形状是允许且鼓励的);相关内容进入带标题的命名聚类并与其它聚类空间分离;跨聚类关系用带标签的箭头画出;文件仍通过validate,inspect前后用户文本完全一致;最后把“读懂了什么”作为交付物——回复中总结聚类结构与画布的一行式故事,而不只是返回文件改动。
隐私
创意画布属于个人内容,所有读写都应保持在本地,绝不建议把私人画布放到网页界面。
对应的示例:
User: "I dumped ideas on my canvas — structure them" → Invokes StructureCanvas workflow → Tldr.ts inspect --json reads every shape's text and position → Clusters related items, adds frames + arrows, moves shapes into groups → User reopens the same file and sees the organized version六个已知坑位(Gotchas)
SKILL.md 把踩过的坑全部列了出来,逐条解读:
- zsh 的
echo会破坏 spec JSON——它会把字符串里的\n展开成真实换行,导致 JSON 损坏。解法:把 spec 写进文件后以--spec <file>传入;或使用printf '%s'。工具也支持--spec -从 stdin 读取,但只能交给不重新解释转义的来源。 - 文本永远是
richText,绝不是普通字符串——geo/text/note/arrow 的标签是 ProseMirror doc JSON。裸字符串 prop 会被校验器拒绝。Tldr.ts会替你构建,永远不要手写textprop。 - 箭头绑定必须有
terminal: "start"|"end"——tldraw 自身的ArrowBindingUtil.getDefaultProps()会漏掉它,但 schema 校验器拒绝缺它的绑定(对照 5.2.5 验证)。工具会设置它;若手工编辑绑定,务必保留。 - 原始记录需要每个 prop——直接写入文件的记录绕过了编辑器默认值填充,缺 prop(如 geo 的
growY)会导致加载校验失败。始终走Tldr.ts add,不要手工拼接记录。 - 分数索引字符串决定形状顺序——
index(a1、a2…)是 base62 字典序且不能以0结尾;工具自动生成,重复会导致编辑器 z-order 错乱。 - 桌面应用的
.tldraw格式是另一种东西——tldraw 桌面应用的原生保存是 zip(内含 sqlite + assets + scripts),不是这个 JSON。本技能针对可移植的.tldrJSON,网页编辑器、VS Code 扩展、桌面应用都能打开/导入。 - 编辑器会持有文件内存副本——用户开着画布时你改磁盘文件,界面可能不重载(或在保存时覆盖你的改动)。改之前先关闭画布,或写完后告知用户重开。
打开与导出画布
根据 SKILL.md 的 “Opening a canvas”:
- VS Code / Cursor:官方 tldraw 扩展可在编辑器内打开
.tldr文件——完全本地,适合私密内容; - tldraw.com:File → Open。内容会进入第三方 Web 应用,只用于本来就打算公开的内容;
- 导出图片:在任意 tldraw 界面全选 → Export as SVG/PNG(本技能不随附无头导出路径)。
与 LifeOS 的集成机制
Tldraw 技能不是孤立工具,它嵌入了 LifeOS 的几项平台机制:
- 个性化定制(Customization):执行前检查
~/.claude/LIFEOS/USER/CUSTOMIZATIONS/SKILLS/Tldraw/(仓库中对应的模板目录为 LifeOS/install/USER/CUSTOMIZATIONS/SKILLS/)。若存在,加载其中的PREFERENCES.md(默认画布目录、偏好颜色/寄存器、默认打开界面);否则使用默认值。 - 语音通知(Voice Notification):每次执行工作流时双通道通知——向 LifeOS 本地通知服务(
http://localhost:31337/notify)POST 一条 JSON 消息,同时在文本输出中给出Running **WorkflowName** in **Tldraw**...。 - 执行日志(Execution Log):工作流完成后向
~/.claude/LIFEOS/MEMORY/SKILLS/execution.jsonl追加一条 JSONL:
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","skill":"Tldraw","workflow":"WORKFLOW_USED","input":"8_WORD_SUMMARY","status":"ok|error","duration_s":SECONDS}' >> ~/.claude/LIFEOS/MEMORY/SKILLS/execution.jsonl- Schema 维护(Maintenance):若 tldraw 大版本升级导致生成文件打不开,需要重新快照 schema:在临时目录
bun add tldraw,然后执行createTLStore({shapeUtils: defaultShapeUtils, bindingUtils: defaultBindingUtils}).schema.serialize(),把结果 JSON 覆盖写入 References/SchemaSnapshot.json,并再次用parseTldrawJsonFile验证一个生成文件。
小结
Tldraw 技能的价值在于把“白板”变成了 Agent 可读写的结构化数据源:Tldr.ts以 7 个确定性子命令覆盖画布文件全生命周期,SketchDiagram与StructureCanvas两个工作流分别覆盖“画”与“读/整理”两个方向,而格式规范(TldrFormat.md)、schema 快照(SchemaSnapshot.json)与源码(Tldr.ts)共同保证了生成文件在 tldraw 5.2.5 及更新版本界面中的兼容性。对于需要在 Agent 工作流里落地“手绘式图表产出”或“思维草稿整理”的场景,这套技能提供了一条不依赖渲染、纯结构化、可验证的实现路径。
【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考