Sync OneOS workspace with new prototypes, annotations, and Gitea remote fix.
Add vehicle-h2-fee-ledger, customer-management, lease and self-operated ledgers, annotation sources, agent skills, and vite annotation runtime support. Update vehicle management, contract templates, and lease contract flows. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,41 +1,54 @@
|
||||
---
|
||||
name: canvas-workspace
|
||||
description: 当任务涉及 Axhub 画布、Excalidraw 文件、画布节点、批注、截图、画布图片、原型/文档/主题嵌入节点或 AI 生成节点时使用。
|
||||
description: 仅当任务明确涉及 Axhub 画布、原型草稿、Excalidraw 画布文件、画布节点/批注/截图/图片,或需要把文档、原型页面、图片、流程图等产物落到画布上时使用。
|
||||
---
|
||||
|
||||
# Canvas Workspace — 画布工作区
|
||||
|
||||
当任务涉及 Axhub 画布时使用本技能。每个原型拥有自己的 Excalidraw 画布文件:
|
||||
仅当任务明确涉及 Axhub 画布、原型草稿,或需要把产物落到画布/Excalidraw 上时使用本技能。每个原型拥有自己的 Excalidraw 画布文件:
|
||||
|
||||
```text
|
||||
src/prototypes/<prototype-name>/canvas.excalidraw
|
||||
src/prototypes/<prototype-name>/canvas-assets/
|
||||
```
|
||||
|
||||
本技能用于按 Axhub Make 约定读取和写入画布,重点关注 `customData`、嵌入资源节点、批注、图片文件和 AI 生成节点。
|
||||
本技能按四类产物分流:文档、原型页面、图片、流程图。先判断产物类型;产物类型不清时先问一个问题。如果用户已在画布/草稿中工作,不再询问放在哪里,默认更新当前 `canvas.excalidraw`。
|
||||
|
||||
## 工具优先级
|
||||
|
||||
- 实时画布已连接 MCP 时,优先调用 `axhub-canvas` 的工具更新当前画布。
|
||||
- 生成 Mermaid 流程、关系、序列、状态、类、ER 或简单盒线架构图时,优先调用 `canvas_insert_mermaid`,传入 `mermaidCode` 和可选 `position`,由浏览器画布转换成可编辑 Excalidraw 元素并保存。
|
||||
- MCP 不可用、没有实时画布、或用户明确要求离线编辑文件时,直接更新对应 `.excalidraw` 文件;需要插入 Mermaid 时,先得到已转换的 Excalidraw elements/files,再写入 `elements` 和 `files`。
|
||||
- 只有需要读取状态、插入普通元素、刷新、截图、更新、删除或聚焦画布时,才改用 `canvas_get_state`、`canvas_insert_elements`、`canvas_refresh`、`canvas_capture`、`canvas_update_elements`、`canvas_delete_elements`、`canvas_focus`。
|
||||
|
||||
## 读取顺序
|
||||
|
||||
1. 用户指定画布名或画布链接时,先从名称或链接定位对应的 `canvas.excalidraw`。
|
||||
2. 查看 `elements`、`files` 和元素的 `customData`。
|
||||
3. 只有元素引用了持久化截图或图片文件时,才读取 `canvas-assets/`。
|
||||
4. CLI 用于获取当前浏览器会话信息或截图。
|
||||
4. 不使用 `axhub-make canvas` CLI;画布内容读取和修改仍以 `.excalidraw` 文件为准。
|
||||
|
||||
## 参考文档
|
||||
## 参考文档分流
|
||||
|
||||
- 文件路径、读写规则、CLI 命令和关系检查:`references/canvas-read-write.md`
|
||||
- Axhub 专属节点和 `customData` 字段:`references/axhub-nodes.md`
|
||||
- Excalidraw 图形结构与布局基础:`references/excalidraw-basics.md`
|
||||
- JSON 元素结构模板:`references/element-templates.md`
|
||||
- 读写画布文件本身仍不清楚时,才读 `references/canvas-read-write.md`。
|
||||
- 遇到 Axhub 专属节点或不确定 `customData` 字段含义时,才读 `references/axhub-nodes.md`。
|
||||
- 需要普通 Excalidraw 元素绘制时,才读 `references/excalidraw-basics.md`。
|
||||
- 确定要创建或编辑 Drawio 节点时,才读 `references/drawio/SKILL.md`。
|
||||
|
||||
## 产物分流
|
||||
|
||||
- 文档:用户要求生成文档、说明、PRD、清单、列表、报告或其他文本内容时,默认先生成 Markdown 文档到 `src/resources/`,再把该文档作为文档节点创建或更新到当前 `canvas.excalidraw`;不要把正文直接拆成大量画布文本框。
|
||||
- 原型页面:创建或更新 `src/prototypes/<prototype-name>/` 中的页面,再把原型页面作为预览节点放到画布;节点尺寸与网页内部视口分开处理,用 `customData.embedContentScale` 缩放显示。
|
||||
- 图片:先确认它是画布参考、画布节点,还是项目实现素材;需要持久化时放入当前原型的 `canvas-assets/`,再插入图片节点。
|
||||
- 流程图:先判断图表类型和可编辑载体。流程、关系、序列、状态、类、ER 和简单盒线架构优先用 Mermaid 作为中间结构并转普通 Excalidraw 元素;简单手绘式图也可直接画普通 Excalidraw。复杂泳道、排期/甘特、复杂云架构、网络拓扑或厂商图标等需要 Draw.io 语义或素材库的图,才按 `references/drawio/SKILL.md` 生成或编辑 Drawio 资产,并按 `references/axhub-nodes.md` 的 Drawio 节点结构更新画布;只有类型或载体重叠不确定时才询问用户。
|
||||
|
||||
## 默认规则
|
||||
|
||||
- 优先直接编辑 `.excalidraw` JSON。
|
||||
- 优先使用可用的 `axhub-canvas` MCP 工具更新当前画布;离线或 MCP 不可用时直接编辑 `.excalidraw` JSON。
|
||||
- 元素 `id` 必须唯一,并尽量沿用现有文件的 ID 风格。
|
||||
- 修改元素时同步更新 `version`、`versionNonce` 和 `updated`。
|
||||
- 结构性改动后检查绑定、容器、分组和 Frame 引用。
|
||||
- 除非用户需求要求修改,否则保留已有 Axhub `customData`。
|
||||
- 创建或替换 prototype 预览节点时,画布上的节点尺寸与网页内部视口要分开处理:节点可以用较小可视尺寸避免占满画布,但网页仍按真实浏览器尺寸设计,通过 `customData.embedContentScale` 缩放显示。
|
||||
|
||||
## 回复要求
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
interface:
|
||||
display_name: "画布工作区"
|
||||
short_description: "在 Axhub 画布上绘图、构思方案、整理节点,读取批注、截图或图片"
|
||||
default_prompt: "使用 $canvas-workspace 读取或整理 Axhub 画布内容。"
|
||||
short_description: "把文档、原型页面、图片、流程图等产物创建或更新到 Axhub 画布"
|
||||
default_prompt: "使用 $canvas-workspace 处理这个画布需求,先判断产物类型再更新当前画布。"
|
||||
|
||||
@@ -9,7 +9,7 @@ Axhub 画布节点本质上是标准 Excalidraw 元素,Axhub 扩展信息存
|
||||
| `customData.title` | 面向用户的节点标题 |
|
||||
| `customData.previewUrl` | 预览模式中渲染的 URL |
|
||||
| `customData.openUrl` | 节点操作中打开的 URL |
|
||||
| `customData.previewKind` | 渲染类型,例如 `web`、`doc`、`image`、`none`、`ai-image-generator`、`prototype-generator` |
|
||||
| `customData.previewKind` | 渲染类型,例如 `web`、`doc`、`image`、`none` |
|
||||
| `customData.resourceType` | 资源类型:`prototype`、`doc` 或 `theme` |
|
||||
| `customData.resourceId` | 项目 metadata 中的资源 id 或名称 |
|
||||
| `customData.embedViewMode` | `link` 表示紧凑链接卡片,`preview` 表示渲染嵌入预览 |
|
||||
@@ -44,19 +44,10 @@ Axhub 画布节点本质上是标准 Excalidraw 元素,Axhub 扩展信息存
|
||||
}
|
||||
```
|
||||
|
||||
由 AI 原型生成能力产出的原型节点还可能包含:
|
||||
|
||||
```json
|
||||
{
|
||||
"generatedBy": "axhub-prototype-generator",
|
||||
"sourceTaskId": "<task-id>",
|
||||
"prompt": "<prompt>"
|
||||
}
|
||||
```
|
||||
|
||||
### 文档节点
|
||||
|
||||
通过 `customData.type: "axhub-doc"` 或 `customData.resourceType: "doc"` 识别。
|
||||
当画布任务需要生成文档、说明、PRD、清单、列表、报告或其他文本内容时,优先把正文写成 `src/resources/` 下的 Markdown,再用文档节点引用该资源;画布只放摘要或入口。
|
||||
|
||||
常见字段:
|
||||
|
||||
@@ -82,65 +73,31 @@ Axhub 画布节点本质上是标准 Excalidraw 元素,Axhub 扩展信息存
|
||||
|
||||
主题节点与原型/文档节点使用相同的 `embeddable` 结构,`resourceType` 为 `theme`,`previewKind` 通常为 `web` 或 `none`。
|
||||
|
||||
## AI 生成节点
|
||||
## Drawio 节点
|
||||
|
||||
AI 生成节点是图片元素。占位图或生成图片数据保存在 `files[fileId]`。
|
||||
Drawio 节点是图片元素。`files[fileId].dataURL` 保存带 Drawio XML 的 SVG 预览,`customData.type` 固定为 `axhub-drawio`。
|
||||
|
||||
### AI 图片生成节点
|
||||
只有用户明确要求 Draw.io、`.drawio`、diagrams.net、可编辑 Draw.io 资产,或 `canvas-workspace` 已选择 Drawio 节点时,才在当前原型的 `canvas.excalidraw` 中创建或更新这种节点。
|
||||
识别 Drawio 节点以 `customData.type: "axhub-drawio"` 为准;`previewKind` 只是预览展示元信息。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "image",
|
||||
"fileId": "axhub-ai-image-placeholder-v2",
|
||||
"fileId": "drawio-file-<id>",
|
||||
"customData": {
|
||||
"type": "axhub-ai-image-generator",
|
||||
"title": "AI 生成图片",
|
||||
"previewKind": "ai-image-generator"
|
||||
"type": "axhub-drawio",
|
||||
"title": "Drawio 图表",
|
||||
"previewKind": "drawio"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AI 图片结果节点
|
||||
创建或更新 Drawio 节点时:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "image",
|
||||
"fileId": "<image-id>",
|
||||
"customData": {
|
||||
"type": "axhub-ai-image",
|
||||
"generatedBy": "axhub-ai-image",
|
||||
"sourceTaskId": "<task-id>",
|
||||
"prompt": "<prompt>",
|
||||
"previewKind": "image"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
多张生成图片可能共享同一个 `groupIds` 值。
|
||||
|
||||
### AI 原型生成节点
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "image",
|
||||
"fileId": "axhub-prototype-generator-placeholder-v1",
|
||||
"customData": {
|
||||
"type": "axhub-prototype-generator",
|
||||
"title": "AI 生成原型",
|
||||
"previewKind": "prototype-generator"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
生成完成后,占位节点会被替换为原型嵌入节点,并带有 `generatedBy: "axhub-prototype-generator"`。
|
||||
|
||||
AI 生成原型替换节点的推荐尺寸:
|
||||
|
||||
- 不要把网页内部布局做小;页面代码仍按正常浏览器视口设计。
|
||||
- `previewUrl`、`openUrl`、`link` 使用客户端原型运行时地址,例如 `/prototypes/<prototypeId>` 或带 hash/page 的同源 runtime URL;不要使用 Make 管理端首页 deep link,例如 `/?p=...` 或 `/?resourceType=prototype...`。
|
||||
- 为了避免画布被完整桌面尺寸占满,推荐生成节点可视尺寸为 `720 x 450`。
|
||||
- 同时设置 `customData.embedSizePreset: "desktop"`、`customData.embedContentScale: 0.5`、`customData.storedPreviewSize: { "width": 720, "height": 450 }`。这样画布显示为 720x450,iframe 与截图按 1440x900 视口渲染。
|
||||
- 新生成的 prototype embeddable 可设置 `customData.captureScreenshotOnMount: true`,让宿主首次渲染后自动捕获预览截图。截图成功后宿主会清除此字段并写入 `screenshotUrl`,不要手写 `screenshotUrl`。
|
||||
- 推荐持久化资源文件后缀为 `.drawio.svg`,例如 `src/prototypes/<prototype-name>/canvas-assets/diagrams/<diagram-id>.drawio.svg`。
|
||||
- `files[fileId].dataURL` 应是 `data:image/svg+xml;base64,...`。
|
||||
- SVG 根节点应使用 `data-drawio="<base64-encoded mxfile>"` 保存 Drawio XML,便于后续在 diagrams.net 编辑器里继续编辑。
|
||||
- 如果只是初始化一个空 Drawio 节点,可以使用默认空图 XML;如果已经确定使用 Draw.io 承载流程图或关系图,应把图结构写入 Drawio XML,而不是只写普通 Excalidraw 文本框。
|
||||
|
||||
## 图片文件
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
# 画布读写能力参考
|
||||
|
||||
面向使用 Skill 的 Agent:优先读写本地 `.excalidraw` 文件。用户指定画布名或画布链接时,直接定位本地画布文件;CLI 用于获取当前浏览器会话信息或截图。
|
||||
面向使用 Skill 的 Agent:优先读写本地 `.excalidraw` 文件。用户指定画布名或画布链接时,直接定位本地画布文件;不要使用 `axhub-make canvas` CLI。
|
||||
|
||||
## 快速判断
|
||||
|
||||
| 目标 | 优先方式 |
|
||||
| 目标 | 做法 |
|
||||
|------|----------|
|
||||
| 读取画布元素、批注、节点信息 | 直接读 `.excalidraw` |
|
||||
| 修改画布内容 | 直接改 `.excalidraw` |
|
||||
| 从用户给的画布链接定位元素 | 从链接提取画布名和元素 ID,再读文件 |
|
||||
| 获取当前浏览器里画布的截图 | `axhub-make canvas screenshot` |
|
||||
| 查看当前浏览器连接了哪些画布 | `axhub-make canvas info` |
|
||||
| 获取画布截图 | 优先使用已有 `canvas-assets` 截图;需要当前浏览器画布时用全局截图 API |
|
||||
|
||||
## 文件位置
|
||||
|
||||
@@ -50,31 +49,53 @@ src/prototypes/<prototype-name>/canvas-assets/embed-<elementId>.png
|
||||
| 原型节点 | `type == "embeddable"` 且 `customData.resourceType == "prototype"`,或 `link`/`previewUrl` 指向原型 |
|
||||
| 文档节点 | `type == "embeddable"` 且 `customData.type == "axhub-doc"` 或 `customData.resourceType == "doc"` |
|
||||
| 主题节点 | `type == "embeddable"` 且 `customData.resourceType == "theme"` 或 `customData.type == "axhub-theme"` |
|
||||
| AI 图片生成节点 | `type == "image"` 且 `customData.type == "axhub-ai-image-generator"` |
|
||||
| AI 图片结果节点 | `type == "image"` 且 `customData.type == "axhub-ai-image"` |
|
||||
| AI 原型生成节点 | `type == "image"` 且 `customData.type == "axhub-prototype-generator"` |
|
||||
| Drawio 节点 | `type == "image"` 且 `customData.type == "axhub-drawio"` |
|
||||
| 图片元素 | `type == "image"` |
|
||||
| 批注元素 | `customData.annotation` 有值 |
|
||||
|
||||
Axhub 节点字段见 `axhub-nodes.md`。
|
||||
|
||||
## CLI 读取
|
||||
## CLI
|
||||
|
||||
CLI 面向当前浏览器会话。读取元素、节点和批注时仍以 `.excalidraw` 文件为准。
|
||||
没有画布专用 CLI。读取元素、节点和批注时仍以 `.excalidraw` 文件为准;需要截图时,优先使用已有 `canvas-assets` 截图或浏览器页面能力。
|
||||
|
||||
查看当前浏览器连接的画布:
|
||||
## 浏览器截图 API
|
||||
|
||||
```bash
|
||||
axhub-make canvas info
|
||||
Excalidraw 官方暴露的是导出工具方法,例如 `exportToBlob`、`exportToCanvas`、`exportToSvg`,不是当前画布实例的一键截图命令。Axhub 在浏览器里的当前画布实例上封装了全局截图 API:
|
||||
|
||||
```js
|
||||
await window.__AXHUB_EXCALIDRAW_CAPTURE__.captureCanvas()
|
||||
await window.__AXHUB_EXCALIDRAW_CAPTURE__.captureElement('<elementId>')
|
||||
```
|
||||
|
||||
获取当前画布截图:
|
||||
两个方法都返回:
|
||||
|
||||
```bash
|
||||
axhub-make canvas screenshot -o ./canvas.png
|
||||
axhub-make canvas screenshot -c prototypes/my-proto/canvas -o ./canvas.png
|
||||
```ts
|
||||
{
|
||||
blob: Blob
|
||||
dataUrl: string
|
||||
width?: number
|
||||
height?: number
|
||||
elementIds: string[]
|
||||
}
|
||||
```
|
||||
|
||||
可选参数:
|
||||
|
||||
```ts
|
||||
{
|
||||
exportBackground?: boolean
|
||||
exportPadding?: number
|
||||
maxWidthOrHeight?: number
|
||||
mimeType?: string
|
||||
quality?: number
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
```
|
||||
|
||||
默认导出 PNG、带背景、16px padding。`captureCanvas()` 导出当前画布所有未删除元素;`captureElement(elementId)` 只导出指定未删除元素。该能力只在画布页面打开并完成初始化后可用。
|
||||
|
||||
## 从链接定位
|
||||
|
||||
用户可能给一个带节点 ID 的画布链接。处理步骤:
|
||||
|
||||
194
.claude/skills/canvas-workspace/references/drawio/SKILL.md
Normal file
194
.claude/skills/canvas-workspace/references/drawio/SKILL.md
Normal file
@@ -0,0 +1,194 @@
|
||||
---
|
||||
name: drawio
|
||||
version: "2.2.0"
|
||||
description: "Create, edit, replicate, import, and export draw.io diagrams with an offline YAML-first workflow. Use for general engineering and product diagrams: architecture, network topologies, flowcharts, UML/ER, org charts, Mermaid/CSV conversion, existing .drawio bundles, style presets, themes, and non-publication formula diagrams. For paper, thesis, journal, conference, IEEE/ACM, manuscript, camera-ready, or publication figures, prefer drawio-academic-skills; this base provides shared CLI, references, themes, schemas, styles, and optional Desktop export."
|
||||
license: MIT
|
||||
homepage: https://github.com/bahayonghang/drawio-skills
|
||||
compatibility: "Node 20+ for the YAML/CLI workflow. draw.io Desktop is optional and only needed for PNG/PDF/JPG or embedded .drawio.svg exports. No MCP server is required for offline authoring; the optional live-refinement backend needs a browser/MCP provider."
|
||||
platforms: [macos, linux, windows]
|
||||
metadata:
|
||||
category: visual-design
|
||||
tags:
|
||||
- diagram
|
||||
- drawio
|
||||
- architecture
|
||||
- flowchart
|
||||
- network-topology
|
||||
- uml
|
||||
- mermaid
|
||||
- csv
|
||||
- design-system
|
||||
- math
|
||||
argument-hint: [diagram-description-or-instruction]
|
||||
allowed-tools: Read, Write, Bash, AskUserQuestion
|
||||
---
|
||||
|
||||
# Draw.io Base Skill
|
||||
|
||||
Create, edit, validate, replicate, import, and export draw.io diagrams through the shared YAML-first Draw.io Base Skill.
|
||||
|
||||
This package is the single maintained base capability surface for sibling overlays. It owns the local CLI, schemas, shared references, themes, reusable examples, style presets, Desktop export helpers, diagrams.net URL fallback, and optional live-refinement backend.
|
||||
|
||||
## Scope
|
||||
|
||||
Use this base skill for general draw.io work:
|
||||
|
||||
- software and system architecture diagrams
|
||||
- network topologies and infrastructure maps
|
||||
- flowcharts, swimlanes, process maps, and org charts
|
||||
- UML class, sequence, state, and ER diagrams
|
||||
- Mermaid and CSV conversion into draw.io
|
||||
- structured redraw and non-academic replication
|
||||
- formula-bearing technical diagrams
|
||||
- `.drawio` import, sidecar export, and local validation
|
||||
|
||||
For paper, thesis, IEEE, journal, manuscript, or publication-ready figure requests, use `drawio-academic-skills` as the policy overlay. The overlay depends on this sibling base for execution; the base does not automatically apply academic publication gates.
|
||||
|
||||
## Runtime Stack
|
||||
|
||||
Use the lightest path that satisfies the request.
|
||||
|
||||
| Runtime | Role | Source of truth | Notes |
|
||||
| ----------------------- | ------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| Offline Authoring Path | Default create/edit/replicate/import/export | YAML spec in project work dir | Generates final `.drawio` and `.drawio.svg` locally; keeps `.spec.yaml` and `.arch.json` in a separate work dir unless explicitly requested beside the output. |
|
||||
| Desktop-Enhanced Export | Optional final export | Existing offline bundle | Adds PNG/PDF/JPG or embedded `.drawio.svg` when draw.io Desktop is available. |
|
||||
| Live Refinement Backend | Optional browser refinement provider | Offline bundle remains canonical | Use only when the user explicitly wants browser/inline iteration and required live capabilities exist. |
|
||||
| Direct XML Exception | Tiny one-off or raw mxGraph handoff | `.drawio` XML | Use only when YAML/CLI is unavailable or exact XML control is the real requirement. |
|
||||
|
||||
The optional MCP/live backend is a refinement provider only. Do not treat it as required for normal authoring, editing, import, replication, or export.
|
||||
|
||||
## Task Routing
|
||||
|
||||
Choose the route first, then load only the references needed for that route.
|
||||
|
||||
| Route | When to use | Required references |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `create` | New diagram from text, YAML, Mermaid, CSV, or a concise spec | `references/workflows/create.md`, `references/docs/design-system/README.md`, `references/docs/design-system/specification.md` |
|
||||
| `edit` | Modify an existing sidecar bundle or imported `.drawio` | `references/workflows/edit.md`, `references/docs/migration-readiness.md` |
|
||||
| `replicate` | Redraw an uploaded image, screenshot, SVG, or reference diagram | `references/workflows/replicate.md`, `references/docs/design-system/README.md`, `references/docs/design-system/specification.md`, `references/docs/design-system/color-guide.md` |
|
||||
| `math-formula` | Labels contain formulas, equations, LaTeX, AsciiMath, MathJax, or Chinese formula keywords | `references/docs/math-typesetting.md`, `references/docs/design-system/formulas.md` |
|
||||
| `stencil-heavy` | Cloud, provider icon, network gear, or exact draw.io shape work | `references/docs/stencil-library-guide.md`, `references/official/xml-reference.md`, `references/official/style-reference.md` |
|
||||
| `network-topology` | Network topology, VLAN / subnet / gateway, campus / data-center / cloud network maps(拓扑、子网、网关、VLAN) | `references/docs/ieee-network-diagrams.md`, `references/docs/stencil-library-guide.md`, `references/official/xml-reference.md` |
|
||||
| `edge-audit` | Dense diagrams or routing-sensitive diagrams | `references/docs/edge-quality-rules.md`, `references/official/xml-reference.md` |
|
||||
| `live-refinement` | Explicit browser/inline visual refinement | `references/docs/mcp-tools.md`, `references/docs/migration-readiness.md` |
|
||||
| `direct-xml` | Tiny XML-only handoff or raw mxGraph edits | `references/official/xml-reference.md`, `references/official/style-reference.md`, `references/docs/xml-format.md`, `references/upstream/pure-drawio-skill.md` |
|
||||
|
||||
Use `network-topology` when the diagram **is** a network/infrastructure map; use `stencil-heavy` when the focus is provider icons or exact draw.io shapes in any diagram type.
|
||||
|
||||
Academic triggers such as `paper`, `thesis`, `IEEE`, `journal`, `manuscript`, or `publication-ready figure` should route to the sibling `drawio-academic-skills` overlay when that skill is available. If the overlay is not available, this base can still render a local YAML bundle, but report that academic overlay policy was not applied.
|
||||
|
||||
## Default Operating Rules
|
||||
|
||||
1. Keep YAML spec as the canonical representation. Mermaid, CSV, natural language, and imported `.drawio` files are input surfaces that normalize into YAML before rendering.
|
||||
2. Keep final delivery directories clean by default: deliver `<name>.drawio` and `<name>.drawio.svg`; keep canonical sidecars such as `<name>.spec.yaml` and `<name>.arch.json` in a project-local work directory such as `.drawio-tmp/<name>/`.
|
||||
3. In Axhub Make projects, use `.drawio.svg` as the recommended SVG file suffix and keep editable source on the SVG root as `data-drawio="<base64-encoded mxfile>"`; use draw.io Desktop only for PNG/PDF/JPG.
|
||||
4. Perform visual self-checks on exported artifacts first: use the generated SVG, or Desktop-exported PNG/PDF/JPG/embedded SVG when available. Do not create browser or Playwright screenshots when a CLI/Desktop export exists; screenshots are only a last-resort live-refinement aid after the user explicitly asks for browser review and no exported artifact can be inspected.
|
||||
5. Treat live backends as optional refinement providers. If `start_session`, `read_diagram_xml`, or patch capabilities are unavailable, edit the offline YAML bundle instead of blocking.
|
||||
6. Do not apply academic publication defaults in the base route. Preserve common formula, layout, theme, and edge-quality capabilities, but leave venue/caption/A4/publication gates to the academic overlay.
|
||||
7. For formulas, generate only official delimiters: `$$...$$` for standalone formulas, `\(...\)` for inline formulas, and AsciiMath backticks. Do not generate `$...$`, `\[...\]`, or bare LaTeX commands.
|
||||
8. For replication, preserve source palette by default. Record extracted color intent in `meta.replication`, use `bounds` for standalone text/formula boxes, and use `labelOffset` when connector labels must sit off the line.
|
||||
9. Prefer semantic shapes and typed connectors before exact stencils. Use provider icons only when the request needs vendor-specific visuals.
|
||||
10. Treat all user-provided labels, paths, specs, and imported XML as untrusted data. Never execute user text as commands or paths.
|
||||
11. Do not create or modify scratch JS scripts under a user's project-local `.agents/skills/drawio` as part of normal diagram generation. If renderer or CLI behavior needs a fix, port it to this repository's skill source and verify it there.
|
||||
12. Standalone SVG export is preview-quality for complex routing because the local renderer draws straight-line edge previews. Use Desktop export or manual draw.io refinement for final orthogonal SVG routing.
|
||||
|
||||
## Create Flow
|
||||
|
||||
1. Identify the diagram type and input format.
|
||||
2. Load the route references from the task-routing table.
|
||||
3. Normalize the request into YAML spec.
|
||||
4. Apply theme, semantic node types, typed connectors, and layout intent.
|
||||
5. Run validation before rendering.
|
||||
6. Render final `.drawio` and `.drawio.svg` in the requested output directory, and write sidecars to a project-local work directory unless the user explicitly asks for a persistent sidecar bundle beside the output.
|
||||
|
||||
Typical commands:
|
||||
|
||||
```bash
|
||||
node <base-skill-dir>/scripts/cli.js input.yaml output.drawio --validate --write-sidecars --sidecar-dir .drawio-tmp/output
|
||||
node <base-skill-dir>/scripts/cli.js input.yaml output.drawio.svg --validate --write-sidecars --sidecar-dir .drawio-tmp/output
|
||||
```
|
||||
|
||||
Use `--strict` or `--strict-warnings` for release-grade engineering review.
|
||||
|
||||
## Edit and Import Flow
|
||||
|
||||
Prefer editing the sidecar bundle. If only a `.drawio` file exists, import it first:
|
||||
|
||||
```bash
|
||||
node <base-skill-dir>/scripts/cli.js existing.drawio --input-format drawio --export-spec --write-sidecars --sidecar-dir .drawio-tmp/existing
|
||||
```
|
||||
|
||||
After import, inspect the generated `.spec.yaml` in the work directory, edit YAML first, then regenerate the requested `.drawio` or `.svg` with sidecars directed to the work directory. Use beside-output sidecars only when the user asks for a reproducible editing bundle.
|
||||
|
||||
## Replicate Flow
|
||||
|
||||
Use `/drawio replicate` for uploaded images or screenshots that need structured redraw.
|
||||
|
||||
1. Extract structure, palette, and text-placement intent.
|
||||
2. Decide whether to preserve source colors or normalize to a theme.
|
||||
3. Represent position-sensitive titles, captions, formulas, callouts, and edge labels explicitly.
|
||||
4. Generate YAML spec with `meta.source: replicated`.
|
||||
5. Render and perform a text-position self-check against the exported SVG or Desktop-exported image before claiming completion.
|
||||
|
||||
## Desktop and Diagrams.net Export
|
||||
|
||||
Desktop-enhanced exports require draw.io Desktop:
|
||||
|
||||
```bash
|
||||
node <base-skill-dir>/scripts/cli.js input.yaml output.pdf --validate --use-desktop
|
||||
node <base-skill-dir>/scripts/cli.js input.yaml output.png --validate --use-desktop
|
||||
node <base-skill-dir>/scripts/cli.js input.yaml output.drawio.svg --validate --write-sidecars --sidecar-dir .drawio-tmp/output --use-desktop
|
||||
```
|
||||
|
||||
If Desktop is unavailable, still deliver the final `.drawio` and `.drawio.svg`, with sidecars in the work directory. For browser handoff, generate a diagrams.net URL from the `.drawio` file:
|
||||
|
||||
```bash
|
||||
node <base-skill-dir>/scripts/runtime/diagrams-net-url.js output.drawio
|
||||
```
|
||||
|
||||
The diagram content is encoded in the URL fragment after `#R` and is not sent as a server query parameter.
|
||||
|
||||
## Style Presets
|
||||
|
||||
The base owns shared bundled style presets under `styles/built-in/`. User presets should live outside the repository, for example `~/.drawio-skill/styles/` or an overlay-specific user directory.
|
||||
|
||||
To learn a reusable preset from an existing diagram ("learn my style from `<path>` as `<name>`") and render an approval sample, follow `references/docs/style-extraction.md`.
|
||||
|
||||
Never mutate bundled presets. Copy a bundled preset to the user preset directory before making it the default or editing it.
|
||||
|
||||
## Validation Policy
|
||||
|
||||
Validate before claiming completion.
|
||||
|
||||
- Structure validation: schema, IDs, theme/layout/profile correctness.
|
||||
- Layout validation: complexity, manual position consistency, overlap risk.
|
||||
- Quality validation: edge-quality rules, label clearance, connection-point policy, and text-placement checks for replication.
|
||||
- Visual verification: inspect exported SVG first, or Desktop-exported PNG/PDF/JPG/embedded SVG when that is the requested final artifact. Use browser/live screenshots only when the user explicitly requested live review and no exported artifact can be inspected.
|
||||
|
||||
If validation fails, fix the YAML or imported XML first and rerun validation. If an optional export cannot run because Desktop or a live backend is unavailable, report the missing provider and provide the offline bundle fallback.
|
||||
|
||||
## Completion Report
|
||||
|
||||
End with a concise report containing:
|
||||
|
||||
- deliverables written, with paths
|
||||
- intermediate work directory, when sidecars or diagnostics were generated
|
||||
- validation and export commands run
|
||||
- exported artifact used for visual verification, or why no visual check could be performed
|
||||
- unavailable optional exports or live-refinement providers
|
||||
- any remaining manual visual checks
|
||||
|
||||
## Reference Highlights
|
||||
|
||||
- `references/workflows/create.md`, `edit.md`, `replicate.md`: route playbooks
|
||||
- `references/docs/design-system/specification.md`: YAML schema and authoring contract
|
||||
- `references/docs/math-typesetting.md`: formula delimiters and export guidance
|
||||
- `references/docs/edge-quality-rules.md`: routing and label-clearance checks
|
||||
- `references/docs/stencil-library-guide.md`: provider-icon and stencil fallback rules
|
||||
- `references/docs/ieee-network-diagrams.md`: IEEE-style network topology and infrastructure reference
|
||||
- `references/docs/mcp-tools.md`: optional live-refinement capability vocabulary
|
||||
- `references/official/xml-reference.md`: upstream XML-generation mirror
|
||||
- `references/official/style-reference.md`: upstream style-property mirror
|
||||
- `references/upstream/pure-drawio-skill.md`: vendored upstream pure-XML skill, for the direct-XML exception path only
|
||||
- `references/docs/style-extraction.md`: learn a reusable style preset from an existing diagram
|
||||
- `references/examples/`: reusable YAML examples
|
||||
@@ -0,0 +1,578 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Draw.io YAML Specification",
|
||||
"description": "Schema for the draw.io skill YAML specification format. Validates diagram structure including nodes, edges, modules, and meta configuration.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"description": "Diagram-level configuration",
|
||||
"properties": {
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Theme name (e.g. tech-blue, academic, nature, dark, high-contrast)"
|
||||
},
|
||||
"layout": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"horizontal",
|
||||
"vertical",
|
||||
"hierarchical",
|
||||
"star",
|
||||
"mesh"
|
||||
],
|
||||
"description": "Layout direction or topology intent for automatic positioning"
|
||||
},
|
||||
"routing": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"orthogonal",
|
||||
"rounded"
|
||||
],
|
||||
"description": "Connector routing style"
|
||||
},
|
||||
"profile": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"academic-paper",
|
||||
"engineering-review"
|
||||
],
|
||||
"description": "Workflow profile that enables domain-specific validation and defaults"
|
||||
},
|
||||
"figureType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"architecture",
|
||||
"roadmap",
|
||||
"workflow"
|
||||
],
|
||||
"description": "Academic figure intent used for paper-mode guidance and validation"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"generated",
|
||||
"replicated",
|
||||
"edited"
|
||||
],
|
||||
"description": "How this spec was produced"
|
||||
},
|
||||
"canvas": {
|
||||
"type": "string",
|
||||
"description": "Canvas size (e.g. auto, 800x600, 1200x800)"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Diagram title"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Diagram description"
|
||||
},
|
||||
"legend": {
|
||||
"type": "string",
|
||||
"description": "Optional legend summary used by academic-paper validation"
|
||||
},
|
||||
"grid": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"snap": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"replication": {
|
||||
"type": "object",
|
||||
"description": "Optional metadata for image-driven redraws and source-palette preservation",
|
||||
"properties": {
|
||||
"colorMode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"preserve-original",
|
||||
"theme-first"
|
||||
],
|
||||
"description": "Whether to preserve extracted source colors or normalize them to the selected theme"
|
||||
},
|
||||
"background": {
|
||||
"type": "string",
|
||||
"description": "Detected source background color"
|
||||
},
|
||||
"palette": {
|
||||
"type": "array",
|
||||
"description": "Detected flat colors from the source image",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hex": {
|
||||
"type": "string"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"appliesTo": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"canvas",
|
||||
"nodes",
|
||||
"edges",
|
||||
"modules",
|
||||
"mixed"
|
||||
]
|
||||
},
|
||||
"confidence": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"low",
|
||||
"medium",
|
||||
"high"
|
||||
]
|
||||
},
|
||||
"notes": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"confidenceNotes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Freeform notes about low-confidence color extraction or normalization decisions"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"type": "array",
|
||||
"description": "Diagram nodes/elements",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"label"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
||||
"description": "Unique node identifier"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"maxLength": 200,
|
||||
"description": "Display label"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"service",
|
||||
"database",
|
||||
"decision",
|
||||
"terminal",
|
||||
"queue",
|
||||
"user",
|
||||
"document",
|
||||
"formula",
|
||||
"text",
|
||||
"cloud",
|
||||
"process",
|
||||
"input",
|
||||
"output",
|
||||
"loss",
|
||||
"feature",
|
||||
"conv",
|
||||
"pool",
|
||||
"embed",
|
||||
"temporal",
|
||||
"attention",
|
||||
"gate",
|
||||
"norm",
|
||||
"graph",
|
||||
"matrix",
|
||||
"operator",
|
||||
"tensor3d",
|
||||
"router",
|
||||
"switch",
|
||||
"firewall",
|
||||
"server",
|
||||
"load_balancer",
|
||||
"subnet",
|
||||
"internet",
|
||||
"ap"
|
||||
],
|
||||
"description": "Semantic type for automatic shape selection"
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
||||
"description": "Parent module ID"
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xl"
|
||||
],
|
||||
"description": "Size preset"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z][a-zA-Z0-9._-]*$",
|
||||
"description": "Icon identifier (e.g. aws.lambda, gcp.compute)"
|
||||
},
|
||||
"network": {
|
||||
"type": "object",
|
||||
"description": "Optional network-specific node metadata",
|
||||
"properties": {
|
||||
"device": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z][a-zA-Z0-9._-]*$"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"vendor": {
|
||||
"type": "string"
|
||||
},
|
||||
"zone": {
|
||||
"type": "string"
|
||||
},
|
||||
"ip": {
|
||||
"type": "string"
|
||||
},
|
||||
"cidr": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"position": {
|
||||
"type": "object",
|
||||
"description": "Manual position override",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"style": {
|
||||
"type": "object",
|
||||
"description": "Style overrides (fillColor, strokeColor, etc.)",
|
||||
"properties": {
|
||||
"fillColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"strokeColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"strokeWidth": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"fontColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"fontSize": {
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"fontWeight": {
|
||||
"type": "number"
|
||||
},
|
||||
"fontFamily": {
|
||||
"type": "string"
|
||||
},
|
||||
"fontStyle": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
"italic": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"bold": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"align": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"left",
|
||||
"center",
|
||||
"right"
|
||||
]
|
||||
},
|
||||
"verticalAlign": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"top",
|
||||
"middle",
|
||||
"bottom"
|
||||
]
|
||||
},
|
||||
"spacingLeft": {
|
||||
"type": "number"
|
||||
},
|
||||
"spacingRight": {
|
||||
"type": "number"
|
||||
},
|
||||
"spacingTop": {
|
||||
"type": "number"
|
||||
},
|
||||
"spacingBottom": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bounds": {
|
||||
"type": "object",
|
||||
"description": "Explicit top-left bounds for high-fidelity replication of text boxes and annotations",
|
||||
"required": [
|
||||
"x",
|
||||
"y",
|
||||
"width",
|
||||
"height"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"width": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
},
|
||||
"height": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"edges": {
|
||||
"type": "array",
|
||||
"description": "Connections between nodes",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"from",
|
||||
"to"
|
||||
],
|
||||
"properties": {
|
||||
"from": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
||||
"description": "Source node ID"
|
||||
},
|
||||
"to": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
||||
"description": "Target node ID"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"primary",
|
||||
"data",
|
||||
"optional",
|
||||
"dependency",
|
||||
"bidirectional"
|
||||
],
|
||||
"description": "Connector semantic type"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "Edge label text"
|
||||
},
|
||||
"labelPosition": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"start",
|
||||
"center",
|
||||
"end"
|
||||
],
|
||||
"description": "Label position along edge"
|
||||
},
|
||||
"bidirectional": {
|
||||
"type": "boolean",
|
||||
"description": "Two-way connection flag"
|
||||
},
|
||||
"srcInterface": {
|
||||
"type": "string",
|
||||
"description": "Source interface label for network links"
|
||||
},
|
||||
"dstInterface": {
|
||||
"type": "string",
|
||||
"description": "Target interface label for network links"
|
||||
},
|
||||
"ip": {
|
||||
"type": "string",
|
||||
"description": "IP address or subnet label for the link"
|
||||
},
|
||||
"vlan": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"description": "VLAN identifier for the link"
|
||||
},
|
||||
"bandwidth": {
|
||||
"type": "string",
|
||||
"description": "Bandwidth/capacity label for the link"
|
||||
},
|
||||
"linkType": {
|
||||
"type": "string",
|
||||
"description": "Link media or semantic category (e.g. trunk, access, fiber)"
|
||||
},
|
||||
"style": {
|
||||
"type": "object",
|
||||
"description": "Style overrides for this edge",
|
||||
"properties": {
|
||||
"strokeColor": {
|
||||
"type": "string"
|
||||
},
|
||||
"strokeWidth": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"dashed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"dashPattern": {
|
||||
"type": "string"
|
||||
},
|
||||
"endArrow": {
|
||||
"type": "string"
|
||||
},
|
||||
"exitX": {
|
||||
"type": "number"
|
||||
},
|
||||
"exitY": {
|
||||
"type": "number"
|
||||
},
|
||||
"exitDx": {
|
||||
"type": "number"
|
||||
},
|
||||
"exitDy": {
|
||||
"type": "number"
|
||||
},
|
||||
"entryX": {
|
||||
"type": "number"
|
||||
},
|
||||
"entryY": {
|
||||
"type": "number"
|
||||
},
|
||||
"entryDx": {
|
||||
"type": "number"
|
||||
},
|
||||
"entryDy": {
|
||||
"type": "number"
|
||||
},
|
||||
"fontSize": {
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"fontColor": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"waypoints": {
|
||||
"type": "array",
|
||||
"description": "Optional explicit routing waypoints for orthogonal edges",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"labelOffset": {
|
||||
"type": "object",
|
||||
"description": "Explicit draw.io edge-label offset from the label anchor, in pixels",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"type": "array",
|
||||
"description": "Container groups for organizing nodes",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"label"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
||||
"description": "Unique module identifier"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "Module display name"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "Fill color or theme token (e.g. $primary)"
|
||||
},
|
||||
"style": {
|
||||
"type": "object",
|
||||
"description": "Style overrides for this module"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
# Workflow: /drawio create
|
||||
|
||||
Create diagrams from text, Mermaid, CSV, or explicit YAML spec using the Draw.io design system.
|
||||
|
||||
## Trigger
|
||||
|
||||
- **Command**: `/drawio create ...`
|
||||
- **Keywords**: `create`, `generate`, `make`, `draw`, `生成`, `创建`
|
||||
|
||||
## Route Selection
|
||||
|
||||
Determine the route before asking questions:
|
||||
|
||||
1. **Fast Path**
|
||||
- Use when the request already specifies the diagram type and at least 3 of: audience/profile, theme, layout, complexity.
|
||||
- Use when the estimated graph is small (`<= 12` nodes) and not stencil-heavy.
|
||||
2. **Full Path**
|
||||
- Use for ambiguous, large, academic, replication-like, or routing-sensitive diagrams.
|
||||
3. **Academic Branch**
|
||||
- Force-enable when prompt contains `paper`, `academic`, `IEEE`, `journal`, `thesis`, `figure`, `manuscript`, `research`.
|
||||
- Default `meta.profile = academic-paper`.
|
||||
- Classify the figure as `architecture`, `roadmap`, or `workflow` before final layout and set `meta.figureType`.
|
||||
4. **Math / Formula Branch**
|
||||
- Enable when the prompt mentions `formula`, `equation`, `LaTeX`, `AsciiMath`, `MathJax`, `loss function`, `derivation`, `symbol legend`, `公式`, `行内公式`, or `行间公式`.
|
||||
- Load `references/docs/math-typesetting.md` as the syntax source of truth.
|
||||
- Load `references/docs/design-system/formulas.md` for formula-node placement and sizing.
|
||||
5. **Stencil Branch**
|
||||
- Enable when the prompt mentions AWS, Azure, GCP, Cisco, Kubernetes, or vendor icons.
|
||||
- Use `references/docs/stencil-library-guide.md` to decide whether `search_shape_catalog` would help or whether semantic/icon fallbacks are sufficient.
|
||||
|
||||
## Procedure
|
||||
|
||||
```text
|
||||
Step 1: Identify Input Mode
|
||||
├── Natural language
|
||||
├── YAML spec
|
||||
├── Mermaid (flowchart/sequence/class/state/ER/gantt)
|
||||
└── CSV hierarchy/org chart
|
||||
|
||||
Step 2: Determine profile and theme defaults
|
||||
├── academic-paper -> theme academic by default
|
||||
├── academic-paper + explicit color request -> academic-color
|
||||
├── engineering-review -> theme tech-blue by default
|
||||
└── otherwise -> theme from request or tech-blue
|
||||
|
||||
Step 3: Classify academic figure intent when profile=academic-paper
|
||||
├── structure / modules / runtime interaction -> meta.figureType=architecture
|
||||
├── stage progression / milestones / study phases -> meta.figureType=roadmap
|
||||
└── ordered execution / branching / fallback / loop -> meta.figureType=workflow
|
||||
|
||||
Step 4: Decide Fast Path vs Full Path
|
||||
├── Fast Path -> skip AskUserQuestion and skip ASCII confirmation
|
||||
└── Full Path -> continue to Step 5
|
||||
|
||||
Step 5: Design Consultation (Full Path only)
|
||||
├── Ask only unresolved questions:
|
||||
│ • audience/profile
|
||||
│ • theme
|
||||
│ • layout
|
||||
│ • figureType when academic intent is still ambiguous
|
||||
│ • expected complexity
|
||||
└── Store decisions in designIntent and pre-fill YAML meta
|
||||
|
||||
Step 6: Academic / Math / Stencil references
|
||||
├── math/formula request -> load math typesetting + formula integration guide
|
||||
├── academic-paper -> load academic figure playbook + export checklist + IEEE + math typesetting
|
||||
└── stencil-heavy -> decide whether shape search is needed
|
||||
├── if `search_shape_catalog` exists, use it for exact vendor/device lookup
|
||||
└── otherwise use design-system icons or semantic fallbacks
|
||||
|
||||
Step 7: Build the YAML spec
|
||||
├── Normalize Mermaid/CSV inputs to YAML spec
|
||||
├── Ensure meta.theme, meta.layout, meta.profile are present
|
||||
├── Ensure meta.figureType is present when profile=academic-paper
|
||||
├── Use semantic node types and typed connectors
|
||||
└── Add manual positions when branching or dense routing requires it
|
||||
|
||||
Step 8: ASCII Draft (Full Path only)
|
||||
├── Render semantic ASCII draft
|
||||
├── Include Design Summary:
|
||||
│ • theme
|
||||
│ • profile
|
||||
│ • figureType
|
||||
│ • layout
|
||||
│ • node/edge/module counts
|
||||
│ • validation status
|
||||
└── Pause for confirmation only when logic or structure is still ambiguous
|
||||
|
||||
Step 9: Validation
|
||||
├── validateColorScheme()
|
||||
├── validateLayoutConsistency()
|
||||
├── validateConnectionPointPolicy()
|
||||
├── validateEdgeQuality()
|
||||
├── validateAcademicProfile() when profile=academic-paper
|
||||
└── checkComplexity()
|
||||
|
||||
Step 10: Edge Audit
|
||||
├── No corner connection points
|
||||
├── No shared face slots on the same corridor
|
||||
├── Last segment >= 30px
|
||||
├── Labels offset from edge lines
|
||||
├── No waypoint + explicit connection-point mixing
|
||||
└── Prefer straight arrows when alignment allows it
|
||||
|
||||
Step 11: Render
|
||||
├── node <skill-dir>/scripts/cli.js input --input-format <yaml|mermaid|csv> output.drawio --validate --write-sidecars --sidecar-dir .drawio-tmp/output
|
||||
├── For paper-quality diagrams prefer output.svg --validate --write-sidecars --sidecar-dir .drawio-tmp/output
|
||||
├── For thesis / A4 / Word / PNG requests, add a matching PNG only when draw.io Desktop export is available
|
||||
├── Note: standalone SVG (without --use-desktop) is preview-quality (straight-line edges).
|
||||
│ For publication-grade vector output, add --use-desktop or export to .drawio and refine in draw.io.
|
||||
└── When embedded export matters and draw.io Desktop exists, add --use-desktop for SVG or export to PNG/PDF/JPG
|
||||
|
||||
Step 12: Exported-Artifact Verification / Optional Live Handoff
|
||||
├── Inspect the exported SVG first when it is available and readable by the current environment
|
||||
├── If a raster/final-fidelity check is needed and draw.io Desktop is available -> export PNG/PDF/JPG or embedded SVG through the CLI
|
||||
├── Do not create browser or Playwright screenshots when an exported SVG/PNG/PDF/JPG exists
|
||||
├── live backend has `replace_diagram_xml` + user wants browser or inline refinement
|
||||
│ └── use the provider-specific tool mapping from `references/docs/mcp-tools.md`
|
||||
├── browser/live screenshots are a last-resort review aid only when the user explicitly requested live review and no exported artifact can be inspected
|
||||
└── otherwise present .drawio + standalone SVG and report any remaining manual visual check
|
||||
```
|
||||
|
||||
## Academic Branch Rules
|
||||
|
||||
When `meta.profile = academic-paper`:
|
||||
|
||||
- `meta.figureType` is required and must be exactly `architecture`, `roadmap`, or `workflow`.
|
||||
- `meta.title` is required for figure captioning.
|
||||
- `meta.description` is recommended for figure context.
|
||||
- `meta.legend` is required when icons are used or connector types are mixed.
|
||||
- Prefer `academic` theme unless the request explicitly asks for a color paper figure.
|
||||
- Default final deliverables are `.drawio` and `.svg`; keep `.spec.yaml` and `.arch.json` in a project-local work directory unless a sidecar bundle is explicitly requested.
|
||||
- Add `.png` only for thesis, A4, Word, raster-first, screenshot rebuild, or explicit PNG requests.
|
||||
- Do not rely on color alone to distinguish semantics.
|
||||
- Treat A4 readability and grayscale print safety as final review gates, not optional polish.
|
||||
|
||||
## Math / Formula Branch Rules
|
||||
|
||||
When the request includes formulas, equations, or math-heavy labels:
|
||||
|
||||
- Use `$$...$$` only for standalone equations or labels that are entirely formula content.
|
||||
- Use `\(...\)` for sentence-level inline math inside a longer label.
|
||||
- Use `` `...` `` only when the user explicitly prefers AsciiMath or when the notation is simple.
|
||||
- Do not generate bare LaTeX, `$...$`, or `\[...\]` in final YAML/XML output.
|
||||
- Tell the user to enable `Extras > Mathematical Typesetting` when raw formulas may be edited in draw.io.
|
||||
- For PDF exports where selectable math matters, recommend `math-output=html`.
|
||||
|
||||
## Notes
|
||||
|
||||
- YAML remains the canonical intermediate representation.
|
||||
- `.drawio` is the editable final artifact; `.spec.yaml` and `.arch.json` remain the canonical offline sidecars in the work directory unless the user explicitly requests a beside-output bundle.
|
||||
- Mermaid and CSV inputs are convenience adapters, not separate rendering pipelines.
|
||||
- For formula-bearing labels, use only the three supported syntaxes: `$$...$$`, `\(...\)`, and `` `...` ``.
|
||||
- Stencil-heavy requests may use shape search when available, but the create flow must still succeed without it.
|
||||
- Academic figures should not blend structure, progression, and control flow into one ambiguous visual grammar.
|
||||
386
.claude/skills/canvas-workspace/references/drawio/scripts/cli.js
Normal file
386
.claude/skills/canvas-workspace/references/drawio/scripts/cli.js
Normal file
@@ -0,0 +1,386 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* CLI tool for converting YAML specifications to draw.io XML or SVG
|
||||
* Usage: node cli.js input.yaml [output.drawio|output.svg] [--theme name] [--strict] [--validate]
|
||||
*/
|
||||
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { basename, extname, join, resolve } from 'node:path'
|
||||
import { parseSpecYaml, specToDrawioXml, validateSpec, validateXml } from './dsl/spec-to-drawio.js'
|
||||
import { parseMermaidToSpec, parseCsvToSpec } from './adapters/index.js'
|
||||
import { drawioToSpec } from './dsl/drawio-to-spec.js'
|
||||
import {
|
||||
buildArchMetadata,
|
||||
createDrawioFileContent,
|
||||
deriveArtifactPaths,
|
||||
serializeSpecYaml
|
||||
} from './runtime/artifacts.js'
|
||||
import { exportWithDrawioDesktop, isDesktopExportFormat } from './runtime/desktop.js'
|
||||
|
||||
/** draw.io format compatibility version */
|
||||
const DRAWIO_COMPAT_VERSION = '21.0.0'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Argument parsing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
||||
console.log(
|
||||
`
|
||||
draw.io YAML → XML/SVG Converter
|
||||
|
||||
Usage:
|
||||
node cli.js <input> [output.drawio|output.svg] [options]
|
||||
|
||||
Arguments:
|
||||
input Path to input file, or - for stdin
|
||||
output file Optional output file. Extension determines format:
|
||||
.drawio → draw.io XML file format
|
||||
.svg → Standalone SVG (or desktop SVG with --use-desktop)
|
||||
.png → PNG via draw.io Desktop CLI
|
||||
.pdf → PDF via draw.io Desktop CLI
|
||||
.jpg → JPG via draw.io Desktop CLI
|
||||
If omitted, XML is printed to stdout.
|
||||
|
||||
Options:
|
||||
--input-format <f> Input format: yaml (default), mermaid, csv, drawio
|
||||
--theme <name> Override theme (e.g. tech-blue, academic, nature, dark)
|
||||
--page <selector> drawio only: page index (0-based) or diagram name
|
||||
--export-spec Export the canonical YAML spec instead of generating XML/SVG
|
||||
--strict Fail on complexity and spec validation warnings
|
||||
--strict-warnings Alias of --strict (recommended for paper-grade validation)
|
||||
--validate Run XML validation and print results (also summarizes spec warnings)
|
||||
--write-sidecars Emit canonical .spec.yaml and .arch.json next to the output
|
||||
--sidecar-dir <dir> Emit sidecars in this directory when --write-sidecars is set
|
||||
--use-desktop Prefer draw.io Desktop CLI for SVG export; required for PNG/PDF/JPG
|
||||
--help, -h Show this help message
|
||||
`.trim()
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Extract positional arguments (non-flag args, excluding values of --flags)
|
||||
const flagsWithValues = new Set(['--theme', '--input-format', '--page', '--sidecar-dir'])
|
||||
const positional = []
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (flagsWithValues.has(args[i])) {
|
||||
i++ // skip the flag value
|
||||
} else if (!args[i].startsWith('--')) {
|
||||
positional.push(args[i])
|
||||
}
|
||||
}
|
||||
const inputFile = positional[0]
|
||||
const outputFile = positional[1] || null
|
||||
|
||||
// Extract flags
|
||||
const themeIndex = args.indexOf('--theme')
|
||||
const themeName = themeIndex !== -1 ? args[themeIndex + 1] : null
|
||||
const inputFormatIndex = args.indexOf('--input-format')
|
||||
const inputFormat = inputFormatIndex !== -1 ? args[inputFormatIndex + 1] : 'yaml'
|
||||
const strict = args.includes('--strict') || args.includes('--strict-warnings')
|
||||
const doValidate = args.includes('--validate')
|
||||
const writeSidecars = args.includes('--write-sidecars')
|
||||
const useDesktop = args.includes('--use-desktop')
|
||||
const exportSpec = args.includes('--export-spec')
|
||||
const pageIndex = args.indexOf('--page')
|
||||
const pageSelector = pageIndex !== -1 ? args[pageIndex + 1] : null
|
||||
const sidecarDirIndex = args.indexOf('--sidecar-dir')
|
||||
const sidecarDir = sidecarDirIndex !== -1 ? args[sidecarDirIndex + 1] : null
|
||||
const resolvedSidecarDir = sidecarDir ? resolve(sidecarDir) : null
|
||||
|
||||
if (sidecarDirIndex !== -1 && (!sidecarDir || sidecarDir.startsWith('--'))) {
|
||||
console.error('Error: --sidecar-dir requires a directory path.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (sidecarDir && !writeSidecars) {
|
||||
console.error('Error: --sidecar-dir requires --write-sidecars.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (resolvedSidecarDir) {
|
||||
try {
|
||||
mkdirSync(resolvedSidecarDir, { recursive: true })
|
||||
} catch (err) {
|
||||
console.error(`Error: Could not create sidecar directory "${sidecarDir}": ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SVG module (optional)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
let drawioToSvg = null
|
||||
try {
|
||||
const svgModule = await import('./svg/drawio-to-svg.js')
|
||||
drawioToSvg = svgModule.drawioToSvg
|
||||
} catch {
|
||||
// SVG export not available
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Read and convert
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
let inputText
|
||||
if (inputFile === '-' || (!inputFile && !process.stdin.isTTY)) {
|
||||
const chunks = []
|
||||
for await (const chunk of process.stdin) chunks.push(chunk)
|
||||
inputText = Buffer.concat(chunks).toString('utf-8')
|
||||
} else if (!inputFile) {
|
||||
console.error('Error: input file is required. Use - for stdin.')
|
||||
process.exit(1)
|
||||
} else {
|
||||
try {
|
||||
inputText = readFileSync(resolve(inputFile), 'utf-8')
|
||||
} catch (err) {
|
||||
console.error(`Error: Could not read input file "${inputFile}": ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
let spec
|
||||
try {
|
||||
if (inputFormat === 'yaml') {
|
||||
spec = parseSpecYaml(inputText)
|
||||
} else if (inputFormat === 'mermaid') {
|
||||
spec = parseMermaidToSpec(inputText, { profile: themeName?.startsWith('academic') ? 'academic-paper' : 'default' })
|
||||
} else if (inputFormat === 'csv') {
|
||||
spec = parseCsvToSpec(inputText, { profile: themeName?.startsWith('academic') ? 'academic-paper' : 'default' })
|
||||
} else if (inputFormat === 'drawio') {
|
||||
spec = drawioToSpec(inputText, { theme: themeName || undefined, page: pageSelector })
|
||||
} else {
|
||||
throw new Error(`Unsupported input format "${inputFormat}"`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error: Failed to parse ${inputFormat}: ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
try {
|
||||
validateSpec(spec)
|
||||
} catch (err) {
|
||||
console.error(`Error: Spec validation failed: ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Apply CLI theme override
|
||||
if (themeName) {
|
||||
spec.meta = spec.meta || {}
|
||||
spec.meta.theme = themeName
|
||||
}
|
||||
|
||||
let xml
|
||||
try {
|
||||
if (exportSpec) {
|
||||
xml = null
|
||||
} else if (doValidate) {
|
||||
const result = specToDrawioXml(spec, { strict, returnWarnings: true, silent: true })
|
||||
xml = result.xml
|
||||
const problems = (result.warnings || []).filter((w) => w.level && w.level !== 'fatal')
|
||||
if (problems.length === 0) {
|
||||
console.error('Spec validation: PASSED (no warnings)')
|
||||
} else {
|
||||
console.error(`Spec validation: WARNINGS (${problems.length})`)
|
||||
problems.forEach((w) => console.error(` • [${w.level}] ${w.message}`))
|
||||
}
|
||||
} else {
|
||||
xml = specToDrawioXml(spec, { strict })
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error: Conversion failed: ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Validation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
if (doValidate && !exportSpec) {
|
||||
const result = validateXml(xml)
|
||||
if (result.valid) {
|
||||
console.error('XML validation: PASSED (no errors)')
|
||||
} else {
|
||||
console.error('XML validation: FAILED')
|
||||
for (const e of result.errors) {
|
||||
console.error(` - ${e}`)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!exportSpec &&
|
||||
spec.meta?.profile === 'academic-paper' &&
|
||||
outputFile &&
|
||||
extname(outputFile).toLowerCase() !== '.svg'
|
||||
) {
|
||||
console.error('Validation: academic-paper profile recommends SVG export for paper-ready vector output.')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Output
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
if (exportSpec) {
|
||||
const yamlOut = serializeSpecYaml(spec)
|
||||
let specPath = outputFile
|
||||
if (!specPath && inputFormat === 'drawio' && inputFile && inputFile !== '-') {
|
||||
specPath = deriveArtifactPaths(inputFile).specPath
|
||||
}
|
||||
|
||||
if (specPath && resolvedSidecarDir) {
|
||||
specPath = resolve(resolvedSidecarDir, basename(specPath))
|
||||
}
|
||||
|
||||
if (!specPath) {
|
||||
process.stdout.write(yamlOut)
|
||||
if (!yamlOut.endsWith('\n')) process.stdout.write('\n')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
try {
|
||||
writeFileSync(resolve(specPath), yamlOut, 'utf-8')
|
||||
console.error(`Saved spec: ${specPath}`)
|
||||
} catch (err) {
|
||||
console.error(`Error: Could not write spec file "${specPath}": ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (writeSidecars) {
|
||||
const normalized = specPath.replace(/\\/g, '/')
|
||||
let archPath = null
|
||||
if (/\.spec\.ya?ml$/i.test(normalized)) {
|
||||
archPath = normalized.replace(/\.spec\.ya?ml$/i, '.arch.json')
|
||||
} else if (/\.ya?ml$/i.test(normalized)) {
|
||||
archPath = normalized.replace(/\.ya?ml$/i, '.arch.json')
|
||||
}
|
||||
|
||||
if (archPath) {
|
||||
if (resolvedSidecarDir) {
|
||||
archPath = resolve(resolvedSidecarDir, basename(archPath))
|
||||
}
|
||||
const drawioPath = /\.arch\.json$/i.test(archPath) ? archPath.replace(/\.arch\.json$/i, '.drawio') : null
|
||||
try {
|
||||
writeFileSync(
|
||||
resolve(archPath),
|
||||
JSON.stringify(buildArchMetadata(spec, { outputFile: drawioPath || specPath }), null, 2) + '\n',
|
||||
'utf-8'
|
||||
)
|
||||
console.error(`Saved arch: ${archPath}`)
|
||||
} catch (err) {
|
||||
console.error(`Error: Could not write arch file "${archPath}": ${err.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (!outputFile) {
|
||||
process.stdout.write(xml)
|
||||
process.stdout.write('\n')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const ext = extname(outputFile).toLowerCase()
|
||||
const drawioContent = createDrawioFileContent(xml, { version: DRAWIO_COMPAT_VERSION })
|
||||
const artifactPaths = deriveArtifactPaths(outputFile)
|
||||
const sidecarArtifactPaths = resolvedSidecarDir
|
||||
? deriveArtifactPaths(resolve(resolvedSidecarDir, basename(artifactPaths.drawioPath)))
|
||||
: artifactPaths
|
||||
const needsDesktopExport = isDesktopExportFormat(ext.slice(1)) && (ext !== '.svg' || useDesktop)
|
||||
let tempDir = null
|
||||
let desktopInputPath = null
|
||||
|
||||
function writeCanonicalSidecars() {
|
||||
if (!writeSidecars) return
|
||||
|
||||
writeFileSync(resolve(sidecarArtifactPaths.specPath), serializeSpecYaml(spec), 'utf-8')
|
||||
writeFileSync(
|
||||
resolve(sidecarArtifactPaths.archPath),
|
||||
JSON.stringify(buildArchMetadata(spec, { outputFile }), null, 2) + '\n',
|
||||
'utf-8'
|
||||
)
|
||||
}
|
||||
|
||||
function ensureDesktopInput() {
|
||||
if (desktopInputPath) return desktopInputPath
|
||||
|
||||
if (writeSidecars) {
|
||||
desktopInputPath = resolve(artifactPaths.drawioPath)
|
||||
writeFileSync(desktopInputPath, drawioContent, 'utf-8')
|
||||
return desktopInputPath
|
||||
}
|
||||
|
||||
tempDir = mkdtempSync(join(tmpdir(), 'drawio-skill-'))
|
||||
desktopInputPath = resolve(tempDir, 'export-input.drawio')
|
||||
writeFileSync(desktopInputPath, drawioContent, 'utf-8')
|
||||
return desktopInputPath
|
||||
}
|
||||
|
||||
let exitCode = 0
|
||||
|
||||
try {
|
||||
if (ext === '.drawio') {
|
||||
writeFileSync(resolve(outputFile), drawioContent, 'utf-8')
|
||||
writeCanonicalSidecars()
|
||||
console.error(`Saved: ${outputFile}`)
|
||||
} else if (needsDesktopExport) {
|
||||
try {
|
||||
exportWithDrawioDesktop({
|
||||
inputFile: ensureDesktopInput(),
|
||||
outputFile: resolve(outputFile),
|
||||
format: ext.slice(1)
|
||||
})
|
||||
writeCanonicalSidecars()
|
||||
console.error(`Saved: ${outputFile}`)
|
||||
} catch (err) {
|
||||
console.error(`Error: ${err.message}`)
|
||||
exitCode = 1
|
||||
}
|
||||
} else if (ext === '.svg') {
|
||||
if (!drawioToSvg) {
|
||||
console.error('Error: SVG export is not available (drawio-to-svg module not found).')
|
||||
exitCode = 1
|
||||
} else {
|
||||
let svg
|
||||
try {
|
||||
svg = drawioToSvg(xml)
|
||||
} catch (err) {
|
||||
console.error(`Error: SVG conversion failed: ${err.message}`)
|
||||
exitCode = 1
|
||||
}
|
||||
|
||||
if (exitCode === 0) {
|
||||
try {
|
||||
writeFileSync(resolve(outputFile), svg, 'utf-8')
|
||||
if (writeSidecars) {
|
||||
writeFileSync(resolve(artifactPaths.drawioPath), drawioContent, 'utf-8')
|
||||
}
|
||||
writeCanonicalSidecars()
|
||||
console.error(`Saved SVG: ${outputFile}`)
|
||||
} catch (err) {
|
||||
console.error(`Error: Could not write output file "${outputFile}": ${err.message}`)
|
||||
exitCode = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
`Error: Unsupported output extension "${ext || '(none)'}". ` + 'Use .drawio, .svg, .png, .pdf, or .jpg/.jpeg.'
|
||||
)
|
||||
exitCode = 1
|
||||
}
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
rmSync(tempDir, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(exitCode)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
# Excalidraw 基础指导
|
||||
|
||||
这份只说明如何把内容组织成 Excalidraw 图,不规定 Axhub 画布流程。基础思路参考 `excalidraw-diagram-generator`:先判断图类型,再抽取元素、关系和复杂度,最后生成清晰布局。
|
||||
|
||||
参考来源:https://www.skills.sh/github/awesome-copilot/excalidraw-diagram-generator
|
||||
这份只说明如何把内容组织成普通 Excalidraw 图,不规定 Axhub 画布流程。流程图、关系图、架构图等请求先按 `canvas-workspace` 主文档分流;已确定要画成 Excalidraw 元素后,再读本文件。
|
||||
|
||||
## 先判断图类型
|
||||
|
||||
|
||||
Reference in New Issue
Block a user