Files
yixiong-claude-marketplace/plugins/obsidian/skills/obsidian-canvas/SKILL.md
T
SkyJourney ff48dcf072 feat(obsidian): 新增 obsidian-canvas skill(JSON Canvas 1.0 视觉层)
对标 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),团队拍板前生效
2026-06-12 19:50:55 +08:00

229 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 笔记 CRUDobsidian 核心)、不用于 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`: stringMarkdown 内容,**换行用 `\n`,不要字面量 `\\n`** | 在画布上写笔记片段 |
| `file` | `file`: stringvault 内相对路径,如 `"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
# BashGit 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 个 groupBacklog/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 且能及时发现失败 |
<!-- TODO(user): 由你最终拍板。建议复用 memcore 的并发冲突保护哲学保持一致性。
请用 5-10 行写入下方"决策"段落,包含:
1. 选哪个(A/B/C/D 或组合)
2. 为什么这个最适合本团队的 vault 使用场景
3. 失败时具体怎么回退(命令级)
4. 不适用边界(什么情况下不要这么做)
-->
### 决策(团队规范)
> **当前**[ 留待填入 ]
>
> 在确认前,AI agent **默认采用选项 C(原子写)+ 选项 DFile 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<br/>(视觉层)"]
core["obsidian<br/>(Markdown CRUD)"]
search["obsidian-search<br/>(找节点要的笔记)"]
meta["obsidian-meta<br/>(节点的 frontmatter)"]
workflow["obsidian-workflow-pkm<br/>(MOC 编排时调用)"]
workflow -- "MOC Builder 可输出 canvas" --> canvas
canvas -- "file 节点引用" --> core
canvas -- "新建 file 节点前查重" --> search
canvas -- "节点字段约定" --> meta
```