简体中文
将 ASCII 字符画转换为精美的手绘风格 Excalidraw JSON 文件。该技能专门用于 解析 ASCII 图 并生成结构化的 .excalidraw 文件,可在 excalidraw.com 打开。核心工作流:先分析结构 → 逐模块生成 JSON → 合并输出。
Input:
text
┌────────────┐ ┌──────────────┐ ┌──────────┐
│ Web App │─────▶│ API Server │─────▶│ Database │
└────────────┘ └──────────────┘ ──────────┘
│
┌─────▼─────┐
│ Cache │
└───────────┘
Output: run this skill → generates ~/.excalidraw/system_arch.excalidraw, open at excalidraw.com
Input:
text
────────────┐ ┌────────────┐ ┌────────────┐
│ Collect │───▶│ Compute │───▶│ Update │
│ Traject. │ │ Advantage │ │ Policy │
└────────────┘ ────────────┘ └─────┬──────┘
▲ │
│ ┌──────────── │
─────────│ Evaluate │◀────────
│ & Check │
└────────────┘
Output: run this skill → generates ~/.excalidraw/ppo_loop.excalidraw, open at excalidraw.com
Convert ASCII art diagrams into polished, hand-drawn style Excalidraw JSON files. This skill specializes in parsing ASCII diagrams and producing structured .excalidraw files that can be opened at excalidraw.com.
The user provides an ASCII diagram (boxes, arrows, text) and wants a visual Excalidraw file. Typical inputs:
text
┌────────────┐ ┌──────────────┐
│ Web App │─────▶│ API Server │
└────────────┘ └──────────────┘
│
┌─────▼─────┐
│ Database │
└───────────┘
Default output: ~/.excalidraw/. All .excalidraw files and intermediate module JSONs MUST be saved here. Never use /tmp/ or the user's home directory root for output.
Before starting, create the directories:
bash
mkdir -p ~/.excalidraw/modules
File naming: use a descriptive name derived from the diagram content (e.g., system_arch.excalidraw, ppo_loop.excalidraw, memory_consolidation.excalidraw).
CRITICAL: Do NOT generate the entire JSON in one shot. Follow this multi-step process to reduce errors.
Parse the full ASCII diagram and produce a structured analysis plus a quantitative layout plan before generating any JSON.
+------+, ┌──┐ corner patterns. Record positions, sizes, and text content.--->, -->, ▼, ▲, ◄, ►, │ patterns. Record source, target, direction, label text.Before computing any coordinates, estimate the rendered width of every text element:
Text TypeApprox char width (px)ExampleShape label (fontFamily 5, fontSize 16-18)~9-10px per char"API Server" (10 chars) → ~95pxBody text / code snippet (fontSize 14-15)~8-9px per char"returns[i] = ones_like(kl[i])" → ~250pxAnnotation badge (fontSize 12-14)~7-8px per char"标量奖励广播" → ~75pxTitle (fontSize 24-28)~14-16px per char"System Architecture" → ~280px
For monospace/code text, add a 10% buffer. For CJK text, use ~12-14px per char.
Using the text widths from 1b, compute:
max(text_width + 30px padding, min_width). Min widths: 140x70 (single-line), 180x90 (multi-line).max(shape_height, line1_text_height, line2_text_height) + 40px buffer.left_margin + shape_column_width + gap + description_column_width + right_margin.top_margin + num_rows * row_height + bottom_margin.Before proceeding to Step 2, verify:
shape_width + gap < column_width for all columns.If proportion issues are detected, recompute the layout (adjust canvas width, reduce font sizes, or increase spacing) before generating JSON.
Output a structured plan:
text
Diagram Analysis:
- Title: "compute_advantages_and_returns() dispatch"
- Layout: left-to-right (method column + description column), 7 rows
- Canvas: ~830px wide × ~750px tall (estimated)
- Proportion: width/height = 1.1 ✓
Module 1 (background): Outer zone [gray]
Module 2 (header): Title box [bold]
Module 3 (connector): Vertical dispatch line
Module 4 (branches): 7 horizontal arrows
Module 5 (methods): 7 method boxes [varied colors]
Module 6 (descriptions): 7 description lines + 7 annotation badges
Method list (name → color, estimated shape width):
GRPO → blue (#1971c2), ~120px
GSPO → teal (#087f93), ~120px
PPO → violet (#7048e8), ~110px
step_wise → green (#2f9e44), ~160px
reinforce_plus_plus → orange (#f08c00), ~220px
reinforce_plus_plus_baseline → pink (#e64980), ~260px
on_policy_distillation → teal (#087f5b), ~220px
Generate Excalidraw JSON for each identified module separately. Each module output is a JSON array of element objects.
Module ordering: Follow drawing order — background zones first, then layers top-to-bottom, then connections.
For each module:
opacity: 35 and fillStyle: "solid"Between modules, maintain consistent coordinate space. Track cursor positions so subsequent modules align properly.
Write each module to a JSON file under ~/.excalidraw/modules/:
text
~/.excalidraw/modules/module_1.json
~/.excalidraw/modules/module_2.json
...
IMPORTANT: All .excalidraw output files must be saved to ~/.excalidraw/ by default. Never use /tmp/ for final output.
Use the helper script to assemble all module arrays into the final .excalidraw file in ~/.excalidraw/:
bash
python ~/.claude/skills/ascii-excalidraw/scripts/merge_modules.py \
-o ~/.excalidraw/diagram.excalidraw \
~/.excalidraw/modules/module_1.json \
~/.excalidraw/modules/module_2.json \
...
The script handles ID deduplication, binding reference updates, and produces the standard .excalidraw format.
ASCII PatternExcalidraw TypeStyle+------+ or ┌──┐ cornersrectangle with roundness: { "type": 3 }Standard components( oval ) or wavy bordersellipseDatabases, cloud services< text > or ◇``diamondDecision points, gates═══ double linesrectangle with strokeWidth: 3Emphasized nodes- - - dashed bordersshape with strokeStyle: "dashed"Optional/proxy components---> or ──▶``arrow with endArrowhead: "arrow"Directional flow--- plain linearrow with endArrowhead: nullUndirected connection- -> dashed arrowarrow with strokeStyle: "dashed"Optional/async flow│ vertical linearrow with vertical pointsVertical flowLarge box enclosing othersrectangle with opacity: 35Background zone=== Title ===``text with fontSize: 24+Section headerText inside a boxBound text ( containerId + boundElements)Shape labelText above/below arrowBound text on arrow ( containerId)Arrow label
Automatically assign colors based on keyword detection in box label text:
KeywordsFillStrokeUseweb, app, frontend, ui, client, browser, vue, react#a5d8ff``#1971c2Frontend/UIapi, server, backend, service, proxy, gateway, nginx#d0bfff``#7048e8Backend/Servicesdb, database, postgres, mysql, redis, mongo, cache, data#b2f2bb``#2f9e44Data/Storageexternal, third-party, stripe, aws, s3, cdn, webhook#ffc9c9``#e03131External servicesqueue, rabbitmq, kafka, sqs, event, stream, pubsub#fff3bf``#f08c00Message/Queueai, ml, model, llm, embed, vector, rag, prompt#eebefa``#9c36b5AI/MLauth, login, oauth, token, jwt, permission#c3fae8``#087f5bAuthenticationlog, monitor, metric, alert, trace, observ#fcc2d7``#e64980Monitoring(no match)#f8f9fa``#495057General purpose
Rules:
opacity: 35, fillStyle: "solid"#1e1e1e for neutralApply these properties to ALL elements:
roughness: 1 (default sketch) or 2 (more sketchy)fillStyle: "hachure" for filled shapes; "solid" for background zonesfontFamily: 5 (Excalifont) for all text — this is the key to the sketch lookstrokeWidth: 2 (default), 3 for emphasized elementsstrokeStyle: "solid" (default), "dashed" for optional/async connectionsEstimate rendered widths before computing coordinates (see Step 1b):
Text TypeApprox char width (px)NotesShape label (fontFamily 5, fontSize 16-18)~9-10px"API Server" → ~95pxBody text / code (fontSize 14-15)~8-9pxAdd 10% for code/monospaceAnnotation badge (fontSize 12-14)~7-8pxTitle (fontSize 24-28)14-16pxCJK characters12-14pxPer character
max(text_width + 30px padding, min_width). Min widths: 140x70 (single-line), 180x90 (multi-line).When a single text element would be too wide:
For labeled shapes, use bidirectional binding:
json
{
"type": "rectangle",
"id": "box_api",
"x": 200, "y": 150, "width": 180, "height": 80,
"backgroundColor": "#d0bfff", "fillStyle": "hachure",
"strokeColor": "#7048e8", "roughness": 1,
"roundness": { "type": 3 },
"boundElements": [
{ "id": "t_box_api", "type": "text" },
{ "id": "arrow_1", "type": "arrow" }
]
},
{
"type": "text",
"id": "t_box_api",
"x": 210, "y": 170, "width": 160, "height": 30,
"text": "API Server",
"fontSize": 18, "fontFamily": 5,
"strokeColor": "#1e1e1e",
"textAlign": "center", "verticalAlign": "middle",
"containerId": "box_api",
"originalText": "API Server",
"autoResize": true
}
The shape's boundElements MUST list the text element ID, and the text MUST have containerId pointing back.
json
{
"type": "arrow",
"id": "arrow_1",
"x": 380, "y": 190, "width": 150, "height": 0,
"points": [[0, 0], [150, 0]],
"endArrowhead": "arrow",
"strokeColor": "#7048e8", "strokeWidth": 2, "roughness": 1,
"startBinding": { "elementId": "box_api", "fixedPoint": [1, 0.5] },
"endBinding": { "elementId": "box_db", "fixedPoint": [0, 0.5] }
}
fixedPoint: [horizontal_ratio, vertical_ratio] where 0=top/left, 1=bottom/right, 0.5=center.
"label" property on shapes — use container binding ( containerId + boundElements)boundElements AND text needs containerIdfontFamily: 5 on text for Excalifont sketch styleoriginalText and autoResize: true on text elementsid across all modules#757575 on white backgrounds; use dark variants on colored fills