stack-diagram
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesestack-diagram
stack-diagram
Produces engineering-drawing-style architecture diagrams: real logos on white tiles, labeled group boxes (like cloud-vendor VPC diagrams), orthogonal arrows with monospace labels, on a cool graph-paper background. One self-contained HTML file is the source of truth; PNG / TSX / artifact are derived from it.
生成工程制图风格的架构图:白色图块上的真实 Logo、带标签的分组框(类似云厂商 VPC 图)、带等宽字体标签的正交箭头,背景采用冷色方格纸。一个自包含的 HTML 文件是单一事实来源;PNG / TSX / Artifact 均由其派生。
Requirements
要求
- Python 3.9+ (standard library only for and
fetch_icons.py)build.py - Network access to the icon sources (svgl.app, raw.githubusercontent.com, cdn.jsdelivr.net, plus the brand's own site or repo for ) when fetching icons
name=URL - Playwright + Chromium for :
render_png.pypip install playwright && python3 -m playwright install --with-deps chromium
- Python 3.9+(和
fetch_icons.py仅使用标准库)build.py - 获取图标时需要网络访问权限,可访问以下图标来源:svgl.app、raw.githubusercontent.com、cdn.jsdelivr.net;对于 形式,还需访问品牌自己的官网或仓库
name=URL - 需要 Playwright + Chromium:
render_png.pypip install playwright && python3 -m playwright install --with-deps chromium
Workflow
工作流
- Settle the content (nodes, groups, edges)
- Fetch icons —
scripts/fetch_icons.py - Plan coordinates — read
references/geometry.md - Build the HTML from —
assets/template.htmlscripts/build.py - Render a PNG and look at it before delivering
- Deliver in the requested format(s)
- 确定内容(节点、分组、连线)
- 获取图标——
scripts/fetch_icons.py - 规划坐标——阅读
references/geometry.md - 基于 构建 HTML——
assets/template.htmlscripts/build.py - 渲染 PNG 并查看后再交付
- 按请求的格式交付
Step 1 — Content
第 1 步——内容
From the user's list of technologies/services, decide:
- Nodes: each tech = one node (name + a short lowercase role like ,
http server)sqlite db - Groups: draw a box around things that share a runtime, vendor, or repo (e.g. a cloud provider box containing a runtime box; a monorepo box for tooling). Nesting one level deep is fine; deeper gets noisy. Write group labels in the brand's own casing (,
GitHub); the template shows them as written.npm - Edges, three kinds:
- accent-colored solid = the primary request/data flow (one continuous path reads best)
- neutral solid = secondary internal connections
- neutral dashed = dev-time / deploy-time relationships (CI, git hooks, IaC deploy)
If the user gave only a flat list of tools, infer sensible groups and flows from what the tools are — only ask when a tool is ambiguous enough that the diagram would be wrong either way.
根据用户提供的技术/服务列表,确定:
- 节点:每项技术 = 一个节点(名称 + 简短的小写角色说明,如 、
http server)sqlite db - 分组:为共享 runtime、厂商或 repo 的内容绘制边框(例如,云厂商框内包含 runtime 框;工具使用 monorepo 框)。嵌套一层即可;更深会显得杂乱。分组标签使用品牌自身的拼写大小写(、
GitHub);模板会按原样呈现。npm - 连线,分为三种:
- 强调色实线 = 主要请求/数据流(最好呈现为一条连续路径)
- 中性色实线 = 次要内部连接
- 中性色虚线 = 开发时/部署时关系(CI、git hooks、IaC 部署)
如果用户只给出了一份扁平的工具列表,应根据这些工具的用途推断合理的分组和流向——仅当某个工具的含义足够模糊、无论采用哪种理解都会导致图表错误时才询问用户。
Step 2 — Icons
第 2 步——图标
bash
python3 scripts/fetch_icons.py <icons-dir> tanstack hono drizzle "betterauth:better-auth" "orpc=https://orpc.unnoq.com/logo.webp"Spec forms: (name used as slug), (different slug), (explicit URL). The script tries, in order: svgl → devicon → gilbarbara/logos, writes files plus , and flags icons whose fill is white. svgl (svgl.app) is first because it carries current official marks for modern dev brands (TanStack, Better Auth, Hono...). When a logo has light/dark versions, the light one becomes (tiles are white in both themes) and the dark one is saved as for group labels. svgl is matched by exact title only, so a miss just falls through. Its API also exposes variants — occasionally useful for a wide group-label, never for tiles.
namename:slugname=URLmanifest.json<name><name>_darkwordmarkRules that matter more than the mechanics:
- Never recolor a logo. Users notice. If a logo is white or too light for a white tile, put it on a dark tile (class) instead of touching its fill. The manifest's
tile darkflag is a heuristic — a white fill can be an inner detail on a colored mark (tRPC, Ubuntu), so glance at the SVG before reaching for the dark tile.white_fill - Official marks only. simple-icons is deliberately not a source: its glyphs are monochrome redraws, and users call them wrong on sight. When every source misses (), find the official file and pass it as
FAIL— look in the project's repo (rootname=URL,logo*.svg,docs/,assets/) and on its site (public/,/logo.svg,/favicon.svg). Some marks live only in the repo (Lefthook's is/images/logo.svgat the root oflogo_sign.svg).evilmartians/lefthook - Abstract concepts get pictograms, not text. A node like "dev environment", "cron", "queue", "cache", or "webhook receiver" has no brand — reach for a Lucide line icon via (e.g.
name:lucide/<icon>,devenv:lucide/terminal; browse lucide.dev/icons). No text-in-a-tile, no emoji.cron:lucide/clock - Vendor sub-products (Cloudflare D1/R2/KV, AWS's endless services...) usually have no distinct logo: just reuse the vendor logo on the tile and let the node name carry the distinction ("Cloudflare D1" / "sqlite db").
- Non-brand nodes (user, browser, internet, devices, bare-metal server): copy a glyph from into the icons dir under the node's name, or use
assets/generic/— don't use emoji. These aren't brand logos, so tinting them to the diagram's ink color is fine (the fetch script does it for Lucide automatically).name:lucide/<icon> - Group-label icons render at 14px — they need a square glyph mark. Wordmarks and wide lockups turn to mud at that size; if the main icon is a wordmark, fetch the brand's square icon separately for the label (svgl usually has it; otherwise the project's own icon or favicon file).
- Group labels sit on the group's tint, not on a white tile, so in the dark theme a mark with black parts vanishes there. If saved
fetch_icons.py, use the template's<name>_dark/ltpair in the label; if there is no dark variant and the mark has black parts, add classdkto the label'splate(white backing, dark theme only).<img> - Raster logos (png/webp) are fine; the build script embeds everything as data URIs, so nothing is loaded at view time.
bash
python3 scripts/fetch_icons.py <icons-dir> tanstack hono drizzle "betterauth:better-auth" "orpc=https://orpc.unnoq.com/logo.webp"规格形式:(用作 slug 的名称)、(使用不同的 slug)、(显式 URL)。脚本依次尝试 svgl → devicon → gilbarbara/logos,写入文件并生成 ,同时标记填充色为白色的图标。之所以优先使用 svgl(svgl.app),是因为它收录了现代开发品牌的最新官方标识(TanStack、Better Auth、Hono...)。当 Logo 同时有浅色和深色版本时,浅色版本成为 (两种主题中图块都为白色),深色版本保存为 ,供分组标签使用。svgl 仅按精确标题匹配,因此未命中时会继续尝试下一个来源。它的 API 还提供 wordmark 变体——偶尔适合较宽的分组标签,但绝不用于图块。
namename:slugname=URLmanifest.json<name><name>_dark以下规则比具体机制更重要:
- **绝不要重新着色 Logo。**用户一眼就能发现。如果某个 Logo 是白色或颜色太浅,在白色图块上不可见,应将其放在深色图块(类)上,而不是修改填充色。manifest 中的
tile dark标志只是一个启发式判断——白色填充可能只是彩色标识内部的一部分(tRPC、Ubuntu),所以在决定使用深色图块前,应先查看 SVG。white_fill - **只使用官方标识。**simple-icons 被有意排除为来源:它的字形是单色重绘版本,用户一眼就会认为不对。当所有来源都未命中()时,找到官方文件并通过
FAIL传入——查看项目仓库(根目录name=URL、logo*.svg、docs/、assets/)及其官网(public/、/logo.svg、favicon.svg)。有些标识仅存在于仓库中(Lefthook 的标识是images/logo.svg根目录中的evilmartians/lefthook)。logo_sign.svg - **抽象概念使用象形图标,而不是文字。**像“开发环境”“cron”“队列”“缓存”或“Webhook 接收器”这样的节点没有品牌标识——应使用 Lucide 线性图标,通过 指定(例如
name:lucide/<icon>、devenv:lucide/terminal;可浏览 lucide.dev/icons)。不要在图块中放文字,也不要使用 emoji。cron:lucide/clock - 云厂商子产品(Cloudflare D1/R2/KV,以及 AWS 众多服务等)通常没有独立 Logo:直接在图块中复用厂商 Logo,并让节点名称体现区别(“Cloudflare D1” / )。
sqlite db - 非品牌节点(用户、浏览器、互联网、设备、裸金属服务器):从 复制图标字形到图标目录并按节点名称命名,或使用
assets/generic/——不要使用 emoji。这些不是品牌 Logo,因此可以调整为图表的墨色(fetch 脚本会自动处理 Lucide)。name:lucide/<icon> - 分组标签图标以 14px 渲染——需要方形字形标识。wordmark 和宽幅组合标识在该尺寸下会变得模糊;如果主图标是 wordmark,应单独获取品牌的方形图标用于标签(svgl 通常提供;否则使用项目自身的图标或 favicon 文件)。
- 分组标签位于分组的底色上,而不是白色图块上,因此在深色主题中,带黑色部分的标识会消失。如果 保存了
fetch_icons.py,就在标签中使用模板的<name>_dark/lt配对;如果没有深色变体且标识含黑色部分,则给标签的dk添加<img>类(仅深色主题使用白色衬底)。plate - 栅格 Logo(png/webp)完全可用;构建脚本会将所有内容嵌入为 data URI,因此查看时不会发起任何网络请求。
Step 3 — Layout
第 3 步——布局
Read for the coordinate system, spacing constants, and — most importantly — the edge anchoring rules that prevent arrows from running through label text. The one-line version: a node is its tile plus the text under it; vertical arrows connect node-box boundaries, never tile-to-tile through text.
references/geometry.md阅读 ,了解坐标系、间距常量,以及最重要的边缘锚定规则,用于防止箭头穿过标签文字。一句话总结:节点由图块及其下方文字组成;垂直箭头连接节点框边界,绝不能穿过文字从一个图块连到另一个图块。
references/geometry.mdStep 4 — Build
第 4 步——构建
Copy , replace the sample GROUPS / EDGES / NODES / header sections with yours (the template's markup doubles as format documentation), then inline the assets. Set on to the language of the diagram's labels ( by default; use for Japanese so browsers pick Japanese glyph variants). Two options the template already carries: a dark variant (set on ; tiles stay white so logos survive, group-label icons follow the Step 2 rule, and arrowheads and legend swatches follow the tokens automatically), and a "deploy to platform X" edge where X is a group, not a node — point the arrow at the most representative node inside the group and label the edge (), which reads correctly.
assets/template.htmllang<html>enjadata-theme="dark"<html>deploybash
python3 scripts/build.py my-diagram.html <icons-dir> out.html --fonts <skill>/assets/fontsIcons are referenced in markup as ; fonts as (Geist) / (JetBrains Mono). The build fails loudly on unresolved tokens. Keep every color and font behind the CSS custom properties already in the template — tweak the tokens, not scattered values.
%%ICON_<name>%%%%FONT_DISPLAY%%%%FONT_JBM%%复制 ,将示例 GROUPS / EDGES / NODES / header 部分替换为你的内容(模板标记本身也可用作格式文档),然后内联资源。将 的 设置为图表标签所用语言(默认 ;日语使用 ,让浏览器选择日文字形变体)。模板已经提供两种方案:一是深色变体(在 上设置 ;图块保持白色以确保 Logo 可见,分组标签图标遵循第 2 步的规则,箭头和图例色块自动跟随主题变量);二是“deploy to platform X”边,其中 X 是分组而非节点——让箭头指向该分组内最具代表性的节点,并标注边(),这样表达更准确。
assets/template.html<html>langenja<html>data-theme="dark"deploybash
python3 scripts/build.py my-diagram.html <icons-dir> out.html --fonts <skill>/assets/fonts图标在标记中以 引用;字体以 (Geist)/ (JetBrains Mono)引用。构建脚本遇到未解析 token 时会明确失败。所有颜色和字体都应继续通过模板中现有的 CSS 自定义属性控制——应调整 token,而不是散落各处的值。
%%ICON_<name>%%%%FONT_DISPLAY%%%%FONT_JBM%%Step 5 — Verify by looking
第 5 步——通过查看进行验证
Render and actually read the image:
bash
python3 scripts/render_png.py out.html check.png --selector .canvas --scale 2If the diagram contains Japanese text, the render host needs a Japanese font, or CJK characters silently come out in Chinese glyph variants (users notice immediately). The script warns and prints a fix; the template's font stack already names the Noto JP faces.
Checklist while looking at it:
- No arrow crosses label text; no arrowhead lands inside text
- Edge labels readable (they have a paper-colored halo — confirm it's working)
- Every icon visible (white-on-white is the classic failure → dark tile), including group-label icons in the dark render
- Group labels sit in their group's top-left corner, clear of nodes and edges; each group's left and right padding match
- Nothing sticks out of its group box; nothing clipped at canvas edge
Fix and re-render until clean. This loop is cheap; shipping a diagram with overlapping text is not.
渲染并真正阅读图像:
bash
python3 scripts/render_png.py out.html check.png --selector .canvas --scale 2如果图表包含日文文本,渲染主机需要安装日文字体,否则 CJK 字符可能会悄悄显示为中文字形变体(用户会立即注意到)。脚本会发出警告并打印修复方法;模板的字体栈已包含 Noto JP 字体名称。
查看时逐项检查:
- 没有箭头穿过标签文字;没有箭头尖端落在文字内部
- 连线标签清晰可读(带有纸张色光晕——确认其正常生效)
- 每个图标都清晰可见(白底白图是经典失败 → 使用深色图块),包括深色渲染中的分组标签图标
- 分组标签位于各自分组左上角,避开节点和连线;每个分组的左右内边距一致
- 没有元素伸出分组框;画布边缘没有内容被裁切
修复并重新渲染,直到画面干净。这个循环成本很低;交付带有重叠文字的图表并不是好结果。
Step 6 — Deliver
第 6 步——交付
- HTML (default): the built file itself. Self-contained, works offline.
- PNG: always produced by screenshotting the built HTML with headless Chromium (,
render_png.pyfor crisp text) — never by drawing a separate image with matplotlib/PIL/standalone SVG rasterizers; the HTML is the single source of truth and the browser's text/SVG rendering is what makes it look right. By default the script emits BOTH themes (--scale 2light +out.png) with square corners (radius is stripped for the screenshot only) — deliver both unless the user asked for one;out-dark.pngcaptures the document as-authored. Target--singlefor just the diagram,.canvasfor the whole page.--full-page - TSX: convert per (self-contained React component, no deps).
references/tsx.md - Artifact (where the environment offers an artifact or page-publishing tool): publish the built HTML with the environment's artifact tool. Everything is already inlined as data URIs, so the artifact's blocked-network sandbox is a non-issue. Prefer this when the user will iterate on the diagram in conversation; pair it with a PNG when they need something to paste elsewhere.
If several formats are requested, build the HTML once and derive the rest from it.
In the footer, credit the icon sources actually used — the field of each entry in the icons dir's (e.g. ). Brand logos remain the trademarks of their owners. Keep the template's "made with stack-diagram" link after the credits unless the user asks to remove it; a default PNG captures only the canvas, so the link stays out of it.
sourcemanifest.jsonicons: svgl / devicon / Lucide- HTML(默认):构建后的文件本身。自包含,可离线使用。
- PNG:始终通过无头 Chromium 截取构建后的 HTML 生成(,使用
render_png.py让文字清晰)——绝不要使用 matplotlib/PIL/独立 SVG 栅格化工具另画一张图;HTML 是单一事实来源,而浏览器对文本/SVG 的渲染效果才能让图表呈现正确。默认脚本会输出两种主题(--scale 2浅色 +out.png深色),并使用直角(仅截图时移除圆角)——除非用户只要求一种,否则应同时交付;out-dark.png按文档原始外观截取。截取--single可仅得到图表,使用.canvas则截取整页。--full-page - TSX:按照 转换(自包含的 React 组件,无依赖)。
references/tsx.md - Artifact(当环境提供 artifact 或页面发布工具时):使用环境的 artifact 工具发布构建后的 HTML。所有内容均已作为 data URI 内联,因此 artifact 的网络隔离沙箱不会造成问题。当用户希望在对话中迭代修改图表时,优先采用此方式;当用户需要粘贴到其他地方时,可搭配 PNG 一同交付。
如果请求了多种格式,只需构建一次 HTML,其余格式均由它派生。
在页脚中注明实际使用的图标来源——即图标目录 中每个条目的 字段(例如 )。品牌 Logo 仍归其各自所有者所有。除非用户要求移除,否则应在来源说明之后保留模板中的“made with stack-diagram”链接;默认 PNG 只截取画布,因此该链接不会出现在图中。
manifest.jsonsourceicons: svgl / devicon / Lucide