From ff48dcf07243567b5f0c8ba5cf6d0908a9033fa8 Mon Sep 17 00:00:00 2001 From: SkyJourney Date: Fri, 12 Jun 2026 19:50:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(obsidian):=20=E6=96=B0=E5=A2=9E=20obsidian?= =?UTF-8?q?-canvas=20skill=EF=BC=88JSON=20Canvas=201.0=20=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E5=B1=82=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对标 kepano/obsidian-skills 与 AgriciDaniel/claude-obsidian 的 canvas 模块: Canvas 是 Obsidian 开放格式,独立于 Markdown 笔记体系,社区共识作为独立技能维护。 内容覆盖: - JSON Canvas 1.0 schema 速查(4 种 node + edges) - 16 字符 hex ID 生成规范(Bash/Python 双实现) - 直接 Read/Write JSON 文件路线(obsidian-cli 不原生支持 .canvas 写入) - canvas 与 Markdown 笔记的协作模式(项目板/主题鸟瞰/研究地图) - 安全 SOP 4 选项对照表(A git stash / B .bak / C 原子写 / D File Recovery) TODO: 团队决策由用户填入 - 默认兜底实现 = C+D(原子写 + File Recovery),团队拍板前生效 --- .../obsidian/skills/obsidian-canvas/SKILL.md | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 plugins/obsidian/skills/obsidian-canvas/SKILL.md diff --git a/plugins/obsidian/skills/obsidian-canvas/SKILL.md b/plugins/obsidian/skills/obsidian-canvas/SKILL.md new file mode 100644 index 0000000..1be4c61 --- /dev/null +++ b/plugins/obsidian/skills/obsidian-canvas/SKILL.md @@ -0,0 +1,228 @@ +--- +name: obsidian-canvas +description: Obsidian 视觉层操作——读写 .canvas 文件(JSON Canvas 1.0 开放格式):思维导图、项目看板、视觉知识图、AI 生成节点布局。支持 4 种 node 类型(text/file/link/group)+ edges 连线(fromNode/toNode/fromSide/toSide)。触发词:canvas、画布、思维导图、视觉知识图、项目看板、白板、可视化笔记、mind map、白板视图、节点图。不用于 Markdown 笔记 CRUD(obsidian 核心)、不用于 Bases 数据库视图(obsidian-bases,那是表格不是画布)。社区共识:obsidian-cli 不原生支持 .canvas 写入,本技能走直接 JSON 文件 Read/Write 路线。 +--- + +# Obsidian Canvas · JSON Canvas 视觉层 + +> **为什么独立成技能**:Canvas 是 Obsidian 的开放格式(`.canvas` = JSON),社区标杆(kepano/obsidian-skills、AgriciDaniel/claude-obsidian)都把它作为独立 skill 维护,因为它的 schema 完全独立于 Markdown 笔记体系——AI 不能用一句 wikilink 替代一张画布。 + +--- + +## 何时使用 + +- 用户说"把这些笔记画成一张图/思维导图/项目看板" +- 用户给一个 `.canvas` 文件让 AI 读取/修改 +- 大主题展开时,AI 主动建议"做成 canvas 更清晰" +- 项目启动时,用 canvas 做"模块 - 任务 - 负责人"鸟瞰图 +- 把研究网络(多篇笔记 + 几张图片 + 几个外链)摆到一张画布上 + +## 不用于 + +- Markdown 笔记本身的 CRUD → `obsidian` 核心技能 +- 结构化数据库视图(表格、列表、卡片) → `obsidian-bases` +- 笔记间的双向链接图(Obsidian 自带 Graph View) → `obsidian-search` 的 graph 子集 +- 单纯画流程图/时序图(Mermaid 块) → 写在 Markdown 笔记里即可 + +--- + +## 1. JSON Canvas 1.0 Schema 速查 + +`.canvas` 文件结构: + +```json +{ + "nodes": [ /* 数组顺序 = z-index(先画的在底层) */ ], + "edges": [ /* 连线,order 不影响 z-index */ ] +} +``` + +### 1.1 Node 公共字段 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `id` | string | ✅ | **16 字符小写 hex**(Obsidian 约定,64-bit 随机) | +| `type` | enum | ✅ | `text` / `file` / `link` / `group` | +| `x`, `y` | number | ✅ | 画布坐标(左上原点,向右/下为正,无单位) | +| `width`, `height` | number | ✅ | 节点尺寸(像素) | +| `color` | string | ❌ | `"1"`-`"6"`(预设色)或 `"#RRGGBB"`(自定义) | + +预设色:`1`=红, `2`=橙, `3`=黄, `4`=绿, `5`=青, `6`=紫。 + +### 1.2 四种 Node 专属字段 + +| `type` | 专属字段 | 用途 | +|--------|---------|------| +| `text` | `text`: string(Markdown 内容,**换行用 `\n`,不要字面量 `\\n`**) | 在画布上写笔记片段 | +| `file` | `file`: string(vault 内相对路径,如 `"50-Zettel/abc.md"`);可选 `subpath`: 跳转锚点(`#标题` 或 `#^blockid`) | 嵌入一篇笔记/图片/PDF | +| `link` | `url`: string | 嵌入外部网页 | +| `group` | `label`: string(分组标题);通常配 `color` 区分 | 视觉容器(不是真包含,只是覆盖矩形) | + +### 1.3 Edge 字段 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `id` | string | ✅ | 同 16 字符 hex | +| `fromNode` / `toNode` | string | ✅ | 引用 node 的 `id` | +| `fromSide` / `toSide` | enum | ❌ | `top` / `right` / `bottom` / `left`(默认自动选最近边) | +| `fromEnd` / `toEnd` | enum | ❌ | `none` / `arrow`(默认 `toEnd=arrow`,`fromEnd=none`) | +| `color` | string | ❌ | 同 node color | +| `label` | string | ❌ | 连线上的文字 | + +--- + +## 2. 16 字符 hex ID 生成 + +```bash +# Bash(Git Bash 可用) +openssl rand -hex 8 # 推荐 +# 或 +head -c 8 /dev/urandom | xxd -p # POSIX 兼容 + +# Python +python -c "import secrets; print(secrets.token_hex(8))" + +# 节点和边的 ID 不要复用;同一 canvas 内全局唯一 +``` + +--- + +## 3. 标准操作模式 + +> **社区共识**:obsidian-cli **不**原生支持 `.canvas` 写入。本技能走 `Read` + `Write` 工具直接编辑 JSON。 + +### 3.1 读取一张 canvas + +```bash +# 列 vault 中所有 canvas +obsidian files ext=canvas format=json + +# 直接读(用 Read 工具或 cat) +cat "MyVault/项目鸟瞰.canvas" | jq . +``` + +### 3.2 新建一张空 canvas + +```bash +echo '{"nodes":[],"edges":[]}' > "MyVault/项目鸟瞰.canvas" +``` + +### 3.3 添加节点(Python helper 比 bash 安全) + +```python +# add_node.py +import json, secrets, sys + +path = sys.argv[1] +with open(path, 'r', encoding='utf-8') as f: + data = json.load(f) + +data['nodes'].append({ + "id": secrets.token_hex(8), + "type": "text", + "x": 0, "y": 0, "width": 250, "height": 60, + "text": "新节点" +}) + +with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) +``` + +### 3.4 布局建议 + +- **网格起步**:节点 250×60,间隔 80px,AI 先排成网格再让用户手动调 +- **文本节点行高**:每行约 22px,预估 `height = 行数 × 22 + 16`(padding) +- **group 覆盖范围**:先算所有子节点的 bounding box,再 `x -= 20, y -= 40, width += 40, height += 60` +- **文件节点**:默认 400×400(带预览渲染),单行文本节点 250×60 + +--- + +## 4. 与 Markdown 笔记的协作模式 + +最强用法:**canvas 当成"鸟瞰图 + 入口",节点 = 笔记 file 引用**。 +不要在 canvas 里写大段正文——那是 Markdown 的活;canvas 只承担**空间关系**。 + +| 模式 | 怎么做 | +|------|--------| +| 项目板 | 4 个 group(Backlog/Doing/Done/Archive),每个任务一个 file 节点 | +| 主题鸟瞰 | 中心 group = 主题名,周围环绕 file 节点(每篇相关 zettel) | +| 研究地图 | text 节点写问题,file 节点放参考资料,edge 标关系("支持/反驳/引用") | +| AI 生成 | AI 读多篇笔记后生成 canvas:自动布局 + 用 edge.label 标"先验/同源/对立" | + +--- + +## 5. 安全 SOP(AI 大批量写入的回滚策略) + +> ⚠ **决策待定**:以下 4 选项需要团队定调。在写入前防止半写状态导致 canvas 损坏。 + +| 选项 | 操作 | 依赖 | 失败回退 | 适用边界 | +|------|------|------|---------|---------| +| **A · git stash** | 写前 `git stash`;失败 `git stash pop` | vault 是 git repo | stash 仍在 stash 栈 | vault 用 git 托管 | +| **B · .bak 备份** | 写前 `cp x.canvas x.canvas.bak`;失败手动 `mv` 回滚 | 无 | 半人工 | 任何 vault | +| **C · 原子写** | 写 `x.canvas.tmp` → `mv .tmp .canvas`(rename 原子操作) | 无 | tmp 残留可清理 | 任何 vault | +| **D · 依赖 File Recovery** | 不做额外保护,靠 Obsidian 内置 File Recovery(默认 7 天)回滚 | Obsidian 内置 | 需手动进入 obsidian-history 找版本 | vault 用户启用 File Recovery 且能及时发现失败 | + + + +### 决策(团队规范) + +> **当前**:[ 留待填入 ] +> +> 在确认前,AI agent **默认采用选项 C(原子写)+ 选项 D(File Recovery 兜底)**——成本最低、不依赖 git、对 vault 无侵入。但这不是最终决策,请团队确认。 + +```bash +# 选项 C 默认实现(在团队决策前生效的兜底) +canvas_atomic_write() { + local path="$1" + local new_content="$2" + local tmp="${path}.tmp.$$" + echo "$new_content" > "$tmp" && \ + python -c "import json,sys; json.load(open('$tmp'))" && \ + mv "$tmp" "$path" + # JSON 校验失败时不 mv,原文件不受影响,tmp 残留待清理 +} +``` + +--- + +## 6. 常见陷阱 + +| 现象 | 原因 | 对策 | +|------|------|------| +| Canvas 打开后空白 | JSON 不合法(缺逗号、注释) | JSON Canvas 不支持注释;用 `jq .` 先校验 | +| 节点位置错乱 | x/y 用了字符串 | 必须是 number,不要 `"x": "100"` | +| 中文字符乱码 | 编码不是 UTF-8 | Python 打开/保存必须 `encoding='utf-8'`,`ensure_ascii=False` | +| `\n` 显示为字面量 | 用了 `\\n`(双反斜杠) | JSON 字符串里换行就是单 `\n` | +| Edge 不显示 | `fromNode`/`toNode` 引用的 ID 不存在 | 写 edge 前先确认两端 node 已写入 | +| 文件节点没预览 | `file` 路径错误(不是 vault 内相对路径) | 用 `obsidian files` 获取的标准路径 | + +--- + +## 7. 与社区 schema 兼容性 + +本技能严格遵循 [JSON Canvas Spec 1.0](https://jsoncanvas.org/)(Obsidian、Logseq、Anytype 等多家共用)。 +即便用户离开 Obsidian,`.canvas` 文件依然可被其他兼容工具读取——这是 Obsidian 开放格式承诺的核心价值。 + +--- + +## 8. 与其他 obsidian-* 技能的关系 + +```mermaid +graph LR + canvas["obsidian-canvas
(视觉层)"] + core["obsidian
(Markdown CRUD)"] + search["obsidian-search
(找节点要的笔记)"] + meta["obsidian-meta
(节点的 frontmatter)"] + workflow["obsidian-workflow-pkm
(MOC 编排时调用)"] + + workflow -- "MOC Builder 可输出 canvas" --> canvas + canvas -- "file 节点引用" --> core + canvas -- "新建 file 节点前查重" --> search + canvas -- "节点字段约定" --> meta +```