- name
- ah-excalidraw-layout
- description
- Excalidraw 流程图排版引擎。输入结构化内容,输出零重叠的 Excalidraw 文件。
内嵌错题本规则,自动规避文字重叠/圆角挤压/容器撑破/箭头交叉等问题。
触发方式:/excalidraw、/画流程图、「帮我画Excalidraw」「用Excalidraw排版」「把这个做成图」
Not for: 自由绘图、手绘风格草图
# ah-excalidraw-layout:Excalidraw 流程图排版引擎
你不是画图工具,你是排版引擎。每次排版前先读完本文件的错题本规则。所有规则来自真实翻车案例。
## 错题本(MUST READ FIRST)
### 错误 1:文字与图形重叠
- 错误:多个文字元素放在同一个 `(x, y)`,或文字和矩形使用相同坐标但未绑定
- 正确:使用 `box()` 工厂函数自动绑定文字到矩形;独立文字必须确保距容器边框 >= 8px
### 错误 2:Group Frame 标签徽章与内部文字重叠
- 错误:在 group frame 边框上放置标签徽章(`y - 12`),与内部标题重叠
- 正确:取消标签徽章。Group 标题作为框内首行文字,放在 `y + PAD` 位置
### 错误 3:容器使用固定高度
- 错误:`rect(x, y, w, 350)` 硬编码高度
- 正确:`groupH = PAD + HEADER_H + 6 + items * (ITEM_H + ITEM_GAP) + PAD`
### 错误 4:内容向右无限扩展
- 错误:传记卡片放在右侧边栏,总宽度超出屏幕
- 正确:所有内容纵向排列,最大宽度 900px。需要并排时最大两列,单列 <= 400px
### 错误 5:箭头交叉混乱
- 错误:左右两组各有独立箭头直接连向下一节点
- 正确:左右两组先汇聚到"汇总节点",再从汇总节点连向下一段
### 错误 6:大圆角挤压文字
- 错误:`roundness: { type: 3 }`(大圆角)+ 文字贴近边框
- 正确:使用 `{ type: 2 }`(中小圆角),文字区相对边框内缩 >= 24px
### 错误 7:中文弯引号导致 JS 解析错误
- 错误:JS 双引号字符串内包含中文弯引号 `""`
- 正确:包含中文引号的字符串用单引号包裹,或替换为直角引号 `「」`
### 错误 8:同一卡片内不同内容区块无分隔
- 错误:传记卡片把基本信息和创作金句堆在一起
- 正确:不同区块之间加水平分隔线或 16px 以上空行
---
---
### 错误 9:不同体系的分组之间强行连线
- 错误:在两个独立体系的分组之间添加多条交叉虚线(如借势 pillars 和主角 pillars 之间 4 条 dot 连线)
- 正确:两个独立体系不需要在 pillar 级别建立关联。如果确实有关联,在更上层(汇总节点)统一交汇,不在细粒度节点之间拉线
### 错误 10:字体不统一 — 手写体与印刷体混用
- 错误:「AI 风口」「能力一」「能力二」等节点使用标准印刷字体(fontFamily: 2),与标题和正文的 Virgil 手写体(fontFamily: 1)风格割裂,像两套视觉系统拼在一起
- 发现轮次:第 7 轮(字体/边框/箭头/连线四维统一化调整)
- 正确:全图所有文字元素 `fontFamily: 1`(Virgil 手写体),生成后逐元素扫描确认无遗漏
- 自检:`grep -c '"fontFamily":2'` 返回 0
### 错误 11:边框风格不统一 — 直角细边与圆角粗边混用
- 错误:顶部节点(如三风口框)使用 `strokeWidth: 1` + 直角边框,下方色块使用 `strokeWidth: 2` + `roundness: {type: 2}`,视觉上像两套系统拼接
- 发现轮次:第 7 轮
- 正确:全图所有矩形统一 `strokeWidth: 2`、`roundness: {type: 2}`、`strokeStyle: "solid"`、`roughness: 2`
### 错误 12:箭头样式不统一 — 双线/单线/粗线/细线混用
- 错误:部分箭头是双线夹角样式,部分是普通细线箭头,部分是粗线,视觉噪音大
- 发现轮次:第 7 轮
- 正确:主流程箭头 `strokeWidth: 1.5`、辅助/汇聚箭头 `strokeWidth: 1`,全部使用 `strokeStyle: "solid"`、`endArrowhead: "arrow"`、`startArrowhead: null`,禁止双线或虚线样式
- 自检:所有 arrow 元素的 `strokeStyle === "solid"` 且 `endArrowhead === "arrow"`
### 错误 13:Mermaid 转换残留锚点 — 无意义的灰色控制点
- 错误:mermaid-to-excalidraw 转换器在多对多连接时自动插入 waypoint/anchor point(灰色圆点),这些不是有含义的内容节点,但视觉上看起来像"有东西"
- 发现轮次:第 5 轮(灰点/锚点/交叉线清理)
- 正确:直接生成 Excalidraw JSON 时(本 skill 的模式),永远不创建中间锚点。箭头路径为直接起点→终点,最多一次折弯
- 自检:不存在 type 非 arrow/rectangle/text 的中间节点
### 错误 14:文字溢出画布 — 节点宽度小于文字所需宽度
- 错误:节点设置了固定宽度,但文字行长度超出容器宽度,导致文字被截断或溢出到画布外
- 发现轮次:第 3 轮(文字溢出/分组重叠/连线交叉/宽度失控四合一)
- 正确:为每个节点计算所需宽度,公式:`nodeW = max(各文字行估算宽度) + 2 × PAD`。中文字符估算 `fontSize × 1.0 px`,英文/数字估算 `fontSize × 0.6 px`
- 自检:对每个 rect+text 组合,检查文字最长行的估算宽度 <= rect.width - 2 × PAD
### 错误 15:连接线被分组边框遮盖 — z-index 顺序导致线条不可见
- 错误:细的连接线(strokeWidth 0.8-1.2)被粗的分组边框(strokeWidth 2+)压在下面,导致关键连线在视觉上消失
- 发现轮次:第 5 轮
- 正确:连接线元素在 JSON elements 数组中的位置必须排在所有分组框矩形之后(后渲染 = 在上层)。分组框 strokeWidth 不超过连接线 strokeWidth + 1.0
### 错误 16:连接线斜向交叉 — 左节点连右目标、右节点连左目标
- 错误:「真实案例」到「能力一/能力二」的连线斜向穿插,以及「汇合」到「怎么练/核心洞察」的橙色线和紫色线交叉
- 发现轮次:第 7 轮(四维统一化调整中的连线部分)
- 正确:**左右一一对应路由** — 左侧来源 → 左侧目标,右侧来源 → 右侧目标。从左侧节点出发的箭头终点绝不出现在右侧节点上,反之亦然
- 自检:对每组并排节点,验证左侧组的箭头终点 x 坐标 < 页面中线,右侧组的箭头终点 x 坐标 > 页面中线
### 错误 17:container 绑定 ID 错位 — rect 和 text 互相引用不存在的 ID
- 错误:`box()` 工厂函数使用 `tid = rid()` 生成一个 ID,然后 `text(..., containerId=tid)` 和 `rect(..., boundElements=[tid])`——但 `text()` 和 `rect()` 内部各自通过 `rid()` 生成自己的真实 id,`tid` 既不是 text 的 id 也不是 rect 的 id。结果:rect 的 `boundElements` 指向不存在的元素,text 的 `containerId` 指向不存在的容器。渲染出来是空壳矩形,文字消失或错位。
- 发现轮次:第 8 轮(2026-07-22 三棱镜定位法 Excalidraw 生成,用户发现橙色案例框内每行前面的圆角框是空的)
- 正确:**禁止使用 containerId / boundElements 绑定**。所有 text 和 rect 用绝对坐标分别放置,text 坐标 = rect 坐标 + 内边距偏移。如必须用工厂函数,函数必须返回并正确关联两个元素的真实 id。
- 自检:`grep -c '"containerId":null'` 等于 text 元素总数;`grep -c '"boundElements"'` 返回 0
---
## 已验证模式(正例)
以下模式经 6 轮调试验证有效,生成时优先使用:
### 模式 1:游标推进布局
所有 y 坐标通过 `curY` 累进,禁止硬编码。每个 section 结束后 `curY += sectionH + GAP`。
### 模式 2:箭头汇聚路由
多来源 → 单汇总节点 → 下一段。禁止 N 条独立箭头从不同来源直接连向同一目标。
### 模式 3:左右一一对应路由(防交叉)
双列并排时,左侧列的箭头只连接左侧目标,右侧列只连接右侧目标。
### 模式 4:语义颜色体系
| 角色 | 主色 | 背景色 | 用于 |
|------|------|--------|------|
| 核心主张 | #f08c00 | #ffec99 | 标题/论点/判断力 |
| AI/技术 | #1971c2 | #d0ebff / #e7f5ff | AI 能力/工具 |
| 案例/验证 | #2f9e44 | #d3f9d8 | 真实案例 |
| 终点/结论 | #e03131 | #ffc9c9 | 最终结论 |
| 洞察/升华 | #9c36b5 | #e599f7 | 核心洞察 |
| 训练/行动 | #e8590c | #ffe8cc | 怎么练/行动 |
| 历史/辅助 | #868e96 | #f8f9fa | 历史案例 |
| 汇聚标记 | #adb5bd | #e9ecef | 箭头汇聚节点 |
### 模式 5:节点宽度自适应公式
```
估算宽度 = 中文字符数 × fontSize × 1.0 + 英文/数字字符数 × fontSize × 0.6
nodeW = max(各行估算宽度) + 2 × PAD
```
### 模式 6:双列 + 单列混合布局
- 标题/核心主张/案例/终点 → 单列全宽,居中
- 对比项/并列能力/两个分支 → 双列并排
- 汇聚节点 → 窄条居中
---
## 质量检查清单(生成后逐项自检)
| # | 检查项 | 标准 | 对应错题 |
|---|--------|------|---------|
| 1 | 字体统一 | 所有 text 元素 fontFamily: 1 | #10 |
| 2 | 边框统一 | 所有 rect 元素 strokeWidth: 2, roundness.type: 2 | #11 |
| 3 | 箭头统一 | 所有 arrow 元素 strokeStyle: solid, endArrowhead: arrow | #12 |
| 4 | 无残留锚点 | 不存在 type 非 arrow/rectangle/text 的节点 | #13 |
| 5 | 文字不溢出 | 每行估算宽度 <= 容器宽度 - 2 × PAD | #14 |
| 6 | 连接线可见 | 连接线排在分组框之后 | #15 |
| 7 | 无交叉连线 | 左源→左目标,右源→右目标 | #16 |
| 8 | 容器自适应 | group frame 高度 = 计算值,无硬编码 | #3 |
| 9 | 内边距充足 | 文字距容器边框 >= 20px | #6 |
| 10 | 圆角安全 | roundness.type <= 2,文字区内缩 >= 24px | #6 |
| 11 | 引号安全 | JS 字符串无不匹配引号 | #7 |
| 12 | 区块分隔 | 不同内容区块间有分隔线或 16px+ 间距 | #8 |
| 13 | 宽度不超限 | max(x + width) <= 960 | #4 |
| 14 | 箭头先汇聚 | 多来源 → 单汇总 → 下一段 | #5 #9 |
| 15 | 无标签重叠 | Group 标题在框内首行,不在边框上 | #2 |
| 16 | 文字不重叠 | 非绑定文字元素各有独立坐标 | #1 |
| 17 | 无 container 绑定 | 所有 text.containerId = null,rect.boundElements 不存在 | #17 |
**任一检查项未通过 → 回退修复对应元素,禁止带着缺陷交付。**
---
---
## 排版规范
### 全局常量
```
PAD = 24 // 容器内边距
HEADER_H = 22 // 分组标题高度
GAP = 20 // 段间距
ITEM_GAP = 4 // 同组条目间距
MAX_W = 880 // 最大页面宽度
```
### 布局模式
- 单列全宽(标题、金句区、总结):PAGE_W = 880
- 双列并排(对比项、并列能力、两个分支):左列 LEFT_X = 60,右列 LEFT_X + 400,单列 COL_W = 380
### 游标推进
所有 y 坐标通过 curY 逐行累进,禁止硬编码:
```
curY = 30
section1: curY += h1 + GAP
section2: curY += h2 + GAP
```
### 颜色体系
| 用途 | 主色 | 背景色 |
|---|---|---|
| 借势 | #c92a2a | #fff5f5 |
| 主角 | #6741d9 | #f8f0ff |
| 金句/交汇 | #e8590c | #fff9db |
| 总结/启示 | #2b8a3e | #ebfbee |
| 生平/辅助 | #495057 | #f8f9fa |
完整语义颜色体系见上方「已验证模式 → 模式 4」。
### 箭头规范
- 主流程箭头:strokeWidth 1.5, strokeStyle solid, endArrowhead arrow
- 汇聚辅助箭头:strokeWidth 1.0, strokeStyle solid, endArrowhead arrow
- 禁止:双线夹角样式、虚线样式、startArrowhead(见错题 #12)
- 多来源先汇聚到单节点,再统一流向下段(见错题 #5 #9 #16)
---
## 迭代记录
### v1.3.0:container 绑定 bug 修复(2026-07-22)
- 新增错题 #17:container 绑定 ID 错位
- `box()` 工厂函数的 `tid = rid()` 模式被证明在 text() 和 rect() 各自生成内部 id 时无法正确关联
- 全局规则:禁止使用 containerId / boundElements,统一用绝对坐标定位
- 质量清单新增第 17 项检查
### v1.2.0:错题本从 9 条扩至 16 条 + 新增正例模式库(2026-07-22)
**来源**:6 轮 Excalidraw 排版调试验证对话记录
对话覆盖的问题领域:
- Round 2:Mermaid 语法 → Excalidraw 转换原理
- Round 3:布局溢出四合一(文字溢出/分组重叠/连线交叉/宽度失控)
- Round 4:容器撑爆(固定高度/大圆角挤压/区块无分隔/标题贴边)
- Round 5:锚点清理(灰色 waypoint/连接线被遮盖/跨体系交叉)
- Round 7:四维统一化(字体/边框/箭头/连线风格不一致)
**新增错题 7 条(#10-#16)**:
| # | 错误 | 根因 | 解决方案 |
|---|------|------|---------|
| 10 | 字体不统一 | 部分节点使用印刷体 fontFamily:2 | 全图 fontFamily:1(Virgil 手写体) |
| 11 | 边框不统一 | 顶部用直角细边 vs 下方圆角粗边 | 统一 strokeWidth:2 + roundness.type:2 |
| 12 | 箭头不统一 | 双线/单线/粗线/细线混用 | 主流程 1.5 / 辅助 1.0 |
| 13 | Mermaid 残留锚点 | 转换器自动插入 waypoint | 直接生成 JSON 时不创建中间节点 |
| 14 | 文字溢出画布 | 节点宽度 < 文字所需宽度 | 宽度自适应公式 |
| 15 | 连接线被遮盖 | 分组框 strokeWidth 大于连接线 + z-index 在前 | 连接线排在分组框之后 |
| 16 | 连线斜向交叉 | 左源→右目标 / 右源→左目标 | 左右一一对应路由 |
**新增正例(已验证模式 6 个)**:游标推进 / 箭头汇聚 / 左右对应 / 语义颜色 / 宽度自适应 / 混合布局
**质量检查清单**:从 9 项扩至 16 项,每项标注对应错题编号
### v1.1.0:初版错题本(2026-07-22)
- 9 条错题:#1-#9(文字重叠/标签重叠/固定高度/无限右扩/箭头交叉/圆角挤压/引号错误/无分隔/跨体系连线)
- 排版规范、颜色体系、箭头规范初始版
Voir sur GitHub