[!NOTE] Environment dependencies: Drawing on the whiteboard requires
@larksuite/whiteboard-cli(whiteboard Node.js CLI tool), andlark-cli(LarkSuite CLI tool). If execution fails, install manually and retry:npm install -g @larksuite/whiteboard-cli@^0.2.0
[!IMPORTANT] Before executing
npm installto install new dependencies, you must obtain user consent!
Workflow
This is a whiteboard, not a webpage. A whiteboard is an infinite canvas where elements are freely placed; flex layout is an optional enhancement.
Step 1: 路由 & 读取知识
- 判断渲染路径(见路由表):Mermaid 还是 DSL?
- 读对应 scene 指南 — 了解结构特征和布局策略
- 确定布局策略(见下方快速判断)和构建方式
- 读 references/ 核心模块 — 语法、布局、配色、排版、连线
Step 2: Generate the complete DSL (including colors)
- Plan the amount of information and grouping according to content.md
- Choose layout mode and spacing according to layout.md
- It is recommended to use icons to make charts more intuitive. Run
npx -y @larksuite/whiteboard-cli@^0.2.0 --iconsto view available icons, and select appropriate icons, but do not overuse them or use icons in all charts; decide whether to use icons based on the chart type and content - Apply colors according to style.md (use the default classic palette when the user has not specified one)
- Output the complete JSON according to the schema.md syntax
- For connectors, refer to connectors.md; for typography, refer to typography.md
Note: Some graphics (fishbone/flywheel/bar/line, etc.) must be generated as JSON by writing a .js script according to the script templates in the scene guide: 1. Create an artifact directory ./diagrams/YYYY-MM-DDTHHMMSS/ 2. Save the script as diagram.gen.js, and run node diagram.gen.js to produce diagram.json 3. Use the produced diagram.json to proceed to Step 3
Step 3: Render & review → deliver
- Self-check before rendering (see the checklist below)
- Render PNG, and check: · Is the information complete? Is the layout reasonable? Is the color scheme coordinated? · Is there no text truncation? Are there no crossing connectors?
- If there are problems → fix them according to the symptom table → re-render (at most 2 rounds)
- If serious problems remain after 2 rounds → consider falling back to the Mermaid path
- If there are no problems → deliver: · If the user requests uploading to Feishu → see the instructions in the "Upload to Feishu whiteboard" section below · If the user does not specify → display the PNG image to the user
**布局策略快速判断**(详见 `references/layout.md`):
先定**主布局**,再定子布局:**结构化信息**优先用 Flex,**关系链路**优先用 Dagre,**灵活定位**用绝对布局。
涉及 Dagre / Flex 的具体边界、危险模式、混合布局原则,统一以 `references/layout.md` 为准;scene 文件只描述场景差异,不重复定义通用布局规则。
> **构建方式是强约束**:当 scene 指南要求"脚本生成"时,必须先写脚本(.js)并用 `node` 执行来产出 JSON 文件。绝对定位场景(鱼骨图、飞轮图、柱状图、折线图等)的坐标需要数学计算,直接手写 JSON 极易导致节点重叠或连线穿模。
---
## 渲染路径选择(DSL or Mermaid)
| 图表类型 | 路径 | 理由 |
| ------------ | ----------- | ------------------- |
| 思维导图 | **Mermaid** | 辐射结构自动布局 |
| 时序图 | **Mermaid** | 参与方+消息自动排列 |
| 类图 | **Mermaid** | 类关系自动布局 |
| 饼图 | **Mermaid** | Mermaid 原生支持 |
| 其他所有类型 | **DSL** | 精确控制样式和布局 |
**路由规则**:
1. **自动 Mermaid**:思维导图、时序图、类图、饼图 → 默认走 Mermaid
2. **显式 Mermaid**:用户输入包含 Mermaid 语法 → 走 Mermaid
3. **DSL 路径**:其他所有类型 → 先读核心模块,再读对应场景指南
**Mermaid 路径**:参考 `scenes/mermaid.md` 编写 `.mmd` 文件,跳过 DSL 模块。
**DSL 路径**:按 Workflow 3 步执行。
---
## 模块索引
### 核心参考(DSL 路径必读)
| 模块 | 文件 | 说明 |
| -------- | -------------------------- | ------------------------------- |
| DSL 语法 | `references/schema.md` | 节点类型、属性、尺寸值 |
| 内容规划 | `references/content.md` | 信息提取、密度决策、连线预判 |
| 布局系统 | `references/layout.md` | 网格方法论、Flex 映射、间距规则 |
| 排版规则 | `references/typography.md` | 字号层级、对齐、行距 |
| 连线系统 | `references/connectors.md` | 拓扑规划、锚点选择 |
| 配色系统 | `references/style.md` | 多色板、视觉层级 |
### 场景指南(按类型选读一个)
| 图表类型 | 文件 | 适用场景 |
| ----------- | ------------------------ | -------------------------------------- |
| 架构图 | `scenes/architecture.md` | 分层架构、微服务架构 |
| 组织架构图 | `scenes/organization.md` | 公司组织、树形层级 |
| 泳道图 | `scenes/swimlane.md` | 跨角色流程、跨系统交互流程、端到端链路 |
| 对比图 | `scenes/comparison.md` | 方案对比、功能矩阵 |
| 鱼骨图 | `scenes/fishbone.md` | 因果分析、根因分析 |
| 柱状图 | `scenes/bar-chart.md` | 柱状图、条形图 |
| 折线图 | `scenes/line-chart.md` | 折线图、趋势图 |
| 树状图 | `scenes/treemap.md` | 矩形树图、层级占比 |
| 漏斗图 | `scenes/funnel.md` | 转化漏斗、销售漏斗 |
| 金字塔图 | `scenes/pyramid.md` | 层级结构、需求层次 |
| 循环/飞轮图 | `scenes/flywheel.md` | 增长飞轮、闭环链路 |
| 里程碑 | `scenes/milestone.md` | 时间线、版本演进 |
| 流程图 | `scenes/flowchart.md` | 业务流、状态机、带条件判断的链路 |
| Mermaid | `scenes/mermaid.md` | 思维导图、时序图、类图、饼图 |
---
## 文件产物规范
每次绘图在 `./diagrams/` 下按当前时间创建子目录(格式 `YYYY-MM-DDTHHMMSS`),目录内文件名固定。用户指定了保存路径时以用户为准。
./diagrams/ 2026-03-27T143000/ ← automatically created by time, no need to name it diagram.json ← DSL (CLI input) diagram.gen.js ← coordinate calculation script (only for the script construction method) diagram.png ← final image diagram.mmd ← Mermaid source (only for the Mermaid path)
## CLI 命令
**查看可用图标**:
```bash
npx -y @larksuite/whiteboard-cli@^0.2.0 --icons
Render:
npx -y @larksuite/whiteboard-cli@^0.2.0 -i ./diagrams/2026-03-27T143000/diagram.json -o ./diagrams/2026-03-27T143000/diagram.png # DSL
npx -y @larksuite/whiteboard-cli@^0.2.0 -i ./diagrams/2026-03-27T143000/diagram.mmd -o ./diagrams/2026-03-27T143000/diagram.png # Mermaid
Upload to Feishu whiteboard:
Uploading requires Feishu authentication. When encountering authentication or permission errors, read
../lark-shared/SKILL.mdto learn about login and permission handling.
Step 1: Obtain the whiteboard Token
| What the user provided | How to obtain the Token |
|---|---|
Whiteboard Token (XXX) |
Use directly |
| Document URL or doc_id, and the document already has a whiteboard | lark-cli docs +fetch --doc <URL> --as user, extract the token from the returned <whiteboard token=”XXX”/> Extract token from |
| Document URL or doc_id, and a new whiteboard needs to be created | lark-cli docs +update --doc <doc_id> --mode append --markdown '<whiteboard type=”blank”></whiteboard>' --as user, obtain the token from the response's data.board_tokens[0] Get token |
For more operations such as creating and reading Feishu documents, please refer to the lark-doc skill ../lark-doc/SKILL.md。
Step 2: Upload
[!CAUTION] MANDATORY PRE-FLIGHT CHECK (mandatory interception check before upload) When you are about to send to aExisting board Token When writing content,absolutely forbiddento directly execute the upload command! You must strictly follow these two steps: Enforce Dry Run (status check) You must first add the
--overwrite --dry-runparameter to the command to check the board's current status. Example command:npx -y @larksuite/whiteboard-cli@^0.2.0 --to openapi -i <输入文件> --format json | lark-cli whiteboard +update --whiteboard-token <Token> --source - --overwrite --dry-run --as userParse the result and intercept
- Carefully read the Dry Run output log.
- If the log contains
XX whiteboard nodes will be deleted: this means the boardis not empty, and the current operation will overwrite and destroy the user's existing diagram!- You must stop the operation immediately, and via the
AskUserQuestiontool (or by replying directly) confirm with the user: "The target board is currently not empty; continuing the update will clear the original XX nodes. Are you sure you want to overwrite?"- Only after the user explicitly authorizes "agree to overwrite" may you remove
--dry-runto actually perform the upload.- The user may ask you not to overwrite the board content; in that case, remove
--overwriteand--dry-runparameters before uploading.
npx -y @larksuite/whiteboard-cli@^0.2.0 --to openapi -i <输入文件> --format json | lark-cli whiteboard +update --whiteboard-token <画板Token> --source - --yes --as user
Once a board is uploaded, it cannot be modified. To upload with app identity, replace
--as userwith--as bot. If the board is not empty, first add--overwrite --dry-runto check the number of nodes to be deleted, and after confirming with the user, remove--dry-runto execute.
You can also output the layout as native OpenAPI json format, then import it into the Feishu board via lark-cli. For more ways to operate the board with lark-cli, refer to ../lark-whiteboard/SKILL.md
Symptom→Fix table(refer to this when visual review finds problems):
| Problem seen | What to change |
|---|---|
| Text is truncated | change height to fit-content |
| Text overflows the right side of the container | increase width, or shorten the text |
| Nodes overlap and stick together | increase gap |
| Nodes are crammed together | increase padding and gap |
| Connectors pass through nodes | adjust fromAnchor/toAnchor or increase spacing |
| Large blank areas | reduce the outer frame width |
| Text and background color are too similar | adjust fillColor or textColor |
| The overall layout leans left/right | adjust the x coordinate of absolute positioning to center the content |
Self-check before rendering
After generating the DSL and before rendering, quickly check:
- Do different groups use different colors? Are nodes in the same group completely consistent in style?
- Outer light-colored background, inner white nodes? (heavy outside, light inside)
- Do all nodes have borders (borderWidth=2)? Is the text clearly readable on the background?
- Do connectors use gray (#BBBFC4), not color?
- Do all frames have the layout attribute written? Are gap and padding explicitly set?
- Do text-containing nodes use fit-content for height? Are connectors in the top-level nodes array?
Key constraints quick reference
The most frequently error-prone rules, which must be followed even if you do not read the submodule files.
- The height of text-containing nodes must use
'fit-content'— Hard-coded values will truncate the text fill-containerOnly takes effect in a flex parent container —layout: 'none'the width degenerates to 0layout: 'none'the container must have a fixed width and height — Do not write it asfit-content- connector must be placed in the top-level nodes array — It cannot be nested inside frame children
- Layer order — Array order = drawing order. Elements defined later have a higher layer and will cover those defined earlier. Overlapping/floating/annotation elements must be placed at the end of the array.
- x/y inside a flex container are completely ignored — When free positioning is needed, use
layout: 'none'or place it in the top-level nodes - Dagre child containers are opaque nodes by default — Outer connections cannot address their internal child nodes (the engine automatically redirects to the outer shell). To penetrate through, declare
layout: "dagre"+layoutOptions: { isCluster: true }
❌ Fatal error: setting x/y inside a flex container has no effect on coordinates, and nodes are arranged in order
{ "type": "frame", "layout": "vertical", "children": [
{ "type": "rect", "x": 100, "y": 0, "text": "成都" },
{ "type": "rect", "x": 540, "y": 0, "text": "康定" }
]}
✅ Correct: use layout: "none" or place it in the top-level nodes and position it with x/y.
❌ Fatal error:layout: "none" the container itself writes width: "fit-content", height: "fit-content", then place absolutely positioned nodes inside
✅ Correct: For an absolutely positioned container, first give it a fixed width and height, then place child nodes inside it using x/y.