news 2026/9/7 14:45:04

Mermaid Local Editor 完全指南:构建零依赖、离线可用的本地图编辑器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Mermaid Local Editor 完全指南:构建零依赖、离线可用的本地图编辑器

Mermaid Local Editor 完全指南:构建零依赖、离线可用的本地图编辑器

【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid

本指南以 packages/mermaid-local-editor 为对象,系统讲解 Mermaid 官方仓库内嵌的“本地编辑器”子包——一种完全运行于浏览器、不依赖任何外部 CDN、可离线使用的 Mermaid 图编辑器。文章将带你走通从pnpm build:mermaid:full到最终产出dist/静态站的完整构建管线,并深入编辑器源码(index.html、app.js 及 js/ 目录下的 renderer、storage、ui、navigation 等模块),理解其图渲染、安全沙箱、持久化存储与键盘导航等设计实现。读完你既能一键构建并本地预览这套编辑器,也能照此模式为自己的项目打造类似的离线 Mermaid 工作台。

一、项目定位:一个跑在dist/里的独立本地编辑器

Mermaid Local Editor 是仓库中一个private: true的独立子包(见 packages/mermaid-local-editor/package.json),其 README 用一句话定义了它的本质:

Standalone local editor for Mermaid diagrams. Runs entirely fromdist/with no external dependencies.

这意味着它不同于线上版的 Mermaid Live Editor:它把 Mermaid 运行时、DOMPurify 净化库以及编辑器自身的 HTML/CSS/JS 全部打成本地静态文件,构建完成后无需任何后端、无需网络请求即可运行。

核心特性(README Notes 与源码可相互印证)

  • 不使用外部 CDNstatic/index.html中只通过本地相对路径引入./vendor/mermaid.min.js./vendor/purify.min.js,再以<script type="module" src="./app.js">挂载应用入口(见 static/index.html)。
  • DOMPurify 本地打包:净化库作为本地 vendor 文件随构建产物一并分发。
  • 完全离线可用:构建产物为纯静态文件,可双击打开或由任意静态服务器托管。
  • 专为从dist/直接运行而设计:构建脚本刻意将所有依赖拷贝进同一个输出目录。

从目录结构看,编辑器源码组织得非常清晰:

packages/mermaid-local-editor/ ├── README.md ├── package.json └── static/ ├── index.html # 页面骨架与工具栏 ├── styles.css # 界面样式 ├── app.js # 应用入口:初始化、装配各模块 └── js/ ├── config.js # Mermaid 初始化参数与全局视图状态 ├── renderer.js # 渲染、净化、缩放平移 ├── storage.js # localStorage 持久化 ├── ui.js # 工具栏交互与自动保存 └── navigation.js # 键盘在节点间导航

二、一键构建:理解pnpm build:mermaid:full

在仓库根目录执行 README 给出的唯一构建命令即可启动整套流程:

pnpm build:mermaid:full

该命令在根 package.json 中的真实定义为:

"build:mermaid:full": "pnpm clean && pnpm build:mermaid && pnpm copy:editor && pnpm copy:mermaid && pnpm copy:dompurify && pnpm serve:dist"

下面按 README 描述的五步,逐一对应到根 package.json 中的底层脚本:

1. Clean——清理旧产物

pnpm clean

对应脚本"clean": "rimraf packages/mermaid/dist"(package.json),移除上一次构建生成的packages/mermaid/dist,保证每次从干净状态出发。

2. Build Mermaid——编译 Mermaid 运行时

pnpm build:mermaid

对应"build:mermaid": "pnpm build:esbuild --mermaid"(package.json),调用仓库统一的 esbuild 构建管线,产出mermaid.min.js等运行时文件。这一步产生的是编辑器后续要引用的核心渲染引擎。

3. Copy Editor——拷贝编辑器静态源

pnpm copy:editor

对应"copy:editor": "cpy \"packages/mermaid-local-editor/static/**/*\" packages/mermaid/dist/mermaid-local-editor"(package.json),把本包static/下的index.htmlstyles.cssapp.jsjs/*.js原样复制到输出目录packages/mermaid/dist/mermaid-local-editor/。注意 index.html 中引用的就是这些同目录文件,因此复制后结构完全一致即可直接运行。

4. Bundle Dependencies——拷贝运行时依赖

pnpm copy:mermaid pnpm copy:dompurify

分别对应(package.json):

"copy:mermaid": "cpy packages/mermaid/dist/mermaid.min.js packages/mermaid/dist/mermaid-local-editor/vendor/ --flat", "copy:dompurify": "cpy node_modules/.pnpm/dompurify@*/node_modules/dompurify/dist/purify.min.js packages/mermaid/dist/mermaid-local-editor/vendor/ --flat"

这两步把 Mermaid 浏览器 bundle(mermaid.min.js)和从 pnpm 扁平化依赖目录(.pnpm/dompurify@*/...)中提取的purify.min.js统一放入输出目录下的vendor/子目录——正好对上 index.html 里的两个<script src="./vendor/...">

5. Serve——本地静态服务预览

pnpm serve:dist

对应"serve:dist": "sirv packages/mermaid/dist/mermaid-local-editor --port 8081 --dev --no-clear"(package.json),使用sirvdist产物托管在http://localhost:8081(默认端口,可改),浏览器打开即可使用编辑器。

构建产物

全部步骤执行完毕后,完整的可运行编辑器位于:

packages/mermaid/dist/mermaid-local-editor/ ├── index.html ├── styles.css ├── app.js ├── js/ │ ├── config.js │ ├── renderer.js │ ├── storage.js │ ├── ui.js │ └── navigation.js └── vendor/ ├── mermaid.min.js └── purify.min.js

该目录纯静态,可复制到任意环境离线使用。

三、编辑器是如何启动的:从index.htmlapp.js

index.html定义了编辑器的基本界面骨架(static/index.html):

  • 工具栏(#toolbar:包含折叠按钮、图表下拉框#diagrams、名称输入框#name,以及 Save / New / Delete / Reset View / Export SVG 五个操作按钮。
  • 主工作区(#main:左侧为源码文本域#srcPanel,右侧为渲染预览区#preview

app.js作为模块入口,按依赖顺序完成装配(static/app.js):

import { initMermaid, state, IS_E2E } from './js/config.js'; import { createStorage } from './js/storage.js'; import { renderDiagram } from './js/renderer.js'; import { setupUI, refreshList } from './js/ui.js'; import { createNavigation } from './js/navigation.js'; initMermaid(); // ① 按编辑器偏好初始化 Mermaid const storage = createStorage(); // ② 建立 localStorage 存储层 const navigation = createNavigation({...}); // ③ 键盘节点导航 // ... render() / load() / applyTransform() 等核心函数 setupUI({...}); // ④ 绑定工具栏交互 navigation.setupKeyboardNav(); load(storage.current); // ⑤ 载入当前图表并渲染

启动后默认加载storage.current指定的图表;若存储为空,storage 模块会自动以默认示例创建名为main的图表(见下文持久化一节)。

四、Mermaid 初始化配置与安全策略

initMermaid()在 static/js/config.js 中对 Mermaid 运行时做了针对本编辑器场景的初始化:

export function initMermaid() { mermaid.initialize({ startOnLoad: false, // 不自动渲染页面中的 <pre class="mermaid"> theme: 'dark', // 深色主题 securityLevel: 'strict', // 最高安全等级:禁用点击跳转/脚本执行 deterministicIds: true, // 确定性 id,利于结果稳定与测试 fontFamily: 'Arial', htmlLabels: false, // 使用非 HTML 标签渲染文本 flowchart: { useMaxWidth: false }, }); }

各关键配置的实际作用:

  • startOnLoad: false:本编辑器需要把用户在文本域中输入的 Mermaid 代码交给 JS 手动渲染(见 renderer),而非依赖页面加载时扫描class="mermaid"元素,因此必须关闭自动启动。
  • securityLevel: 'strict':这是核心安全防线。Mermaid 在 strict 模式下会剥离图表内的点击链接与脚本相关功能,从渲染源头规避了从用户输入的图源码中注入可执行内容的可能。
  • deterministicIds: true:让每次渲染生成的元素 id 稳定可预测,便于自动化测试与节点导航模块对g.node的稳定定位。
  • flowchart.useMaxWidth: false:关闭流程图按容器宽度自适应缩放,配合预览区的自由缩放/平移机制,保证用户缩放后的画布尺寸不被父容器约束回原形。

同一文件还导出了两个全局相关对象:

export const IS_E2E = navigator.webdriver || location.search.includes('graph='); export let state = { scale: 1, panX: 0, panY: 0, iframeRef: null };

IS_E2E用于识别“自动化测试或带graph=查询参数”的直出场景;state则集中管理预览区当前的缩放比、平移偏移量及 iframe 引用,是 renderer、navigation、ui 多个模块共享的“视图状态单例”。

五、渲染管线:mermaid.render+ DOMPurify 净化 + 沙箱 iframe

图渲染是编辑器的核心路径,实现在 static/js/renderer.js 的renderDiagram()中。整体流程分两条分支:

常规运行模式(iframe 沙箱)

  1. 渲染:调用await mermaid.render(id, srcValue)将 Mermaid 源码编译为 SVG 字符串。
  2. 净化DOMPurify.sanitize(svg, { ADD_TAGS: ['foreignObject'], ADD_ATTR: ['xmlns'] }),保留 Mermaid 渲染所需但 DOMPurify 默认可能剥离的foreignObject标签与xmlns属性,同时清除其余潜在危险内容。
  3. 二次加固:遍历 SVG 元素,删除所有以on开头的内联事件属性(如onclickonload),从 DOM 层面彻底封死事件注入:
    svgEl.querySelectorAll('*').forEach((el) => { [...el.attributes].forEach((attr) => { if (attr.name.startsWith('on')) { el.removeAttribute(attr.name); } }); });
  4. 沙箱隔离:新建<iframe sandbox="allow-same-origin">,把净化后的 SVG 通过iframe.contentDocument.importNode(svgEl, true)写入 iframe 文档。sandbox 属性(不放开allow-scripts)使渲染结果无法执行脚本,预览区域因此与主页面形成隔离边界。
  5. 注入交互样式:向 iframe 文档写入<style>,实现节点 hover 高亮(填充蓝色光晕)、.selected-node选中态(蓝色描边 + 发光)、文本浅色化(适配深色主题)以及body { overflow: hidden }等视觉效果。

E2E 模式(直出分支)

IS_E2E为真时,渲染结果不经过 iframe,而是把净化后的 SVG 直接作为 DOM 插入#preview。这保证了在自动化测试(如 Playwright、截图对比)中无需等待 iframe 布局,SVG 可直接被断言与截图。

缩放与平移

renderer 在 iframe 文档上注册了滚轮与鼠标事件实现漫游交互:

  • 缩放doc.onwheel中以state.scale += e.deltaY * -0.0015累加缩放系数,并将结果钳制在0.2~4倍之间,随后调用applyTransform()
  • 平移mousedown/mousemove/mouseup组合实现按住拖拽画布,同时将拖拽光标切换为grabbing

applyTransform()(定义于 static/app.js)最终把视图状态落到 SVG 上:

svg.style.transform = `translate(${state.panX}px, ${state.panY}px) scale(${state.scale})`;

平移量同样被钳制在 ±20000px 以内,并将view: { scale, panX, panY }同步写回存储——视图位置会随图表一起被记住

错误处理

任何渲染异常都会被try/catch捕获:清空预览区,并以红色<pre>展示e.message。用户在左侧输入无效 Mermaid 语法时,能在右侧立刻看到清晰的语法错误提示,而不是白屏或控制台报错。

六、多图管理与 localStorage 持久化

static/js/storage.js 用localStorage实现多图表管理与会话保持,涉及两个 key:

  • mermaid-diagrams:以 JSON 对象存储所有图表,结构为{ [名称]: { src, view } },其中src是 Mermaid 源码,view记录{ scale, panX, panY }
  • mermaid-current:记录当前选中图表名,默认值'main'

首次访问时,若main不存在,会自动创建一个预置了示例源码的默认图:

diagrams[current] = { src: `flowchart LR\n UI --> RuntimeBus --> Orchestrator --> Agents`, view: { scale: 1, panX: 0, panY: 0 }, };

storage 模块对外暴露的操作与 static/js/ui.js 中的按钮一一对应:

操作UI 触发行为
saveSave 按钮#name输入值作为当前图表名,保存src与视图状态
create(name)New 按钮(prompt输入名称)新建默认内容(flowchart LR\n A --> B)并切换到该图
deleteCurrent()Delete 按钮(confirm确认)删除当前图,回退到剩余第一张图或main
setCurrent/updateCurrent下拉框切换、自动保存切换当前图 / 合并更新当前图字段

同时编辑器提供了300ms 防抖自动保存:用户在#srcPanel输入时立即重渲染预览,同时clearTimeout + setTimeout(..., 300)延后把最新源码写回存储(static/js/ui.js)。因此即使忘记点 Save,源码也不会大量丢失。

此外 ui.js 还实现了:

  • Reset View(↺):将scale/panX/panY归位为1/0/0并重存视图。
  • Export SVG(⬇):从 iframe 中取出当前 SVG,用Blob([svg.outerHTML], { type: 'image/svg+xml' })生成URL.createObjectURL,以下载名为${当前图表名}.svg的链接触发保存,之后及时revokeObjectURL释放资源(static/js/ui.js)。
  • 工具栏折叠:折叠状态以toolbar-collapsed这个 localStorage key 记忆,刷新页面后保持用户的界面偏好。

七、键盘导航:在节点之间"行走"

static/js/navigation.js 提供了一个很实用的可访问性功能:无需鼠标,用键盘在流程图节点间逐跳导航。

  • 导航目标收集:渲染完成后(renderer 通过rebuildNavNodes回调触发),收集 iframe 内所有svg g.node作为导航节点列表。
  • 高亮当前节点:先清除已有节点的selected-node类,再把classList.add('selected-node')加到当前索引对应的节点上——样式层(renderer 注入的 CSS)随即呈现蓝色描边与发光效果。
  • 视图跟随centerCurrentNode()计算节点与预览容器的几何中心差并累加到panX/panY。注释中特别说明,偏移有意取1/6而非严格居中(/ 6而非/ 2),使焦点节点偏向画布左上,从而在 LR/TD 布局下让后续节点仍留在可视区域内,便于连续往下走。
  • 键盘控制ArrowDown前进到下一节点、ArrowUp回到上一节点;当焦点位于源码编辑框时按键不劫持,保证用户仍能正常编辑代码。

这套实现体现了编辑器对键盘可达性的考量:对视障或偏好键盘操作的用户,可逐个节点感知图表结构与空间位置。

八、安全模型小结:三层纵深防御

综合源码可见,编辑器对"渲染用户输入"这一高风险行为做了三层纵深防护,这也是它敢于完全离线、不对输入做任何服务端校验的底气所在:

  1. Mermaid 配置层securityLevel: 'strict'+deterministicIds,从图解析端抑制链接点击与脚本注入;
  2. 内容净化层:渲染出的 SVG 字符串统一经过 DOMPurifysanitize()(白名单放行foreignObjectxmlns),并额外剥离一切on*内联事件属性;
  3. 运行隔离层:预览 SVG 被写入sandbox="allow-same-origin"的 iframe(不授予allow-scripts),即便 SVG 中存在残余攻击载荷也无法执行脚本、无法逃逸出沙箱影响主页。

九、在 Mermaid 仓库中该包所处的位置

从构建与测试基础设施看,packages/mermaid-local-editor已被纳入仓库的开发闭环:

  • 根 package.json 将其完整构建链固化为build:mermaid:full一条命令,便于 CI 或本地一键复现;
  • 它出现在 ESLint 检查范围与 e2e 相关脚本的作用域内(scripts/e2e-diagram-scope.mjs中显式列出该包路径),说明其源码会与仓库其他代码一同经受静态检查与端到端测试的守护。

对于想要把 Mermaid 能力"离线化/私有化"的开发者,这个子包提供了一个非常干净的参考范式:构建期将mermaid.min.js+purify.min.js打进vendor/,运行时用"渲染 → 净化 → 沙箱 iframe → localStorage 持久化"的最小闭环完成所见即所得的图表编辑体验。整个编辑器的全部业务代码不足十个文件,非常适合作为二次开发或功能裁剪的起点。

【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 14:43:34

WinForm源码解构与企业级实战:被低估的桌面开发利器

WinForm这些年受到的“冷落”&#xff0c;我其实挺有感触的。社区里聊得热闹的是WPF的MVVM、MAUI的跨平台&#xff0c;招聘要求上也越来越少见“WinForm”字样。可真到了企业级现场——那些MES系统、ERP客户端、医疗设备上位机、工业控制软件——你会发现WinForm依然是绝对主力…

作者头像 李华
网站建设 2026/9/7 14:43:10

TVA具身架构详解(4):构建具身智能“原生大脑”的视觉底座

前沿技术探索&#xff1a;TVA智能体&#xff08;简称TVA&#xff09; TVA智能体&#xff08;亦称“AI智能体视觉”或“TVA视觉智能体”&#xff09;是依托Transformer架构与“因式智能体”理论构建的通用视觉技术体系。它有机融合深度强化学习&#xff08;DRL&#xff09;、卷…

作者头像 李华
网站建设 2026/9/7 14:42:52

从“躲得过初一”到技术债治理:软件工程中的预防思维

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 14:42:48

2026年AI面试被刷后的复盘与逆袭策略:从拒信到Offer的5步复原路径

文章目录一、AI面试被刷的5类常见原因1.1 五类被刷原因速查表1.2 怎么判断自己属于哪种类型二、每种类型的自我诊断与改进药方2.1 A型&#xff1a;内容空洞型2.2 B型&#xff1a;结构混乱型2.3 C型&#xff1a;技术不准型2.4 D型&#xff1a;表达不畅型2.5 E型&#xff1a;匹配…

作者头像 李华
网站建设 2026/9/7 14:42:12

Oracle Instant Client三版本共存与排错指南

简介&#xff1a;面向Windows x64平台的Oracle Instant Client三版本离线合集&#xff0c;一次集齐10.2、11.2、12.2三个常用客户端版本&#xff0c;方便开发人员与DBA在本地搭建多版本Oracle运行环境&#xff0c;解决不同业务系统对客户端版本兼容性的差异化需求。压缩包采用r…

作者头像 李华
网站建设 2026/9/7 14:42:09

开源项目学习指南:用HelloGitHub建立自己的技术雷达

GitHub账号注册了几年&#xff0c;星标仓库攒了上百个&#xff0c;但真正常点开看README的没几个。我猜不少人有同感&#xff1a;不是不想看&#xff0c;是开源项目太多了&#xff0c;每天的热榜都在变&#xff0c;今天刷到一个Star暴涨的AI框架&#xff0c;明天又冒出一个看起…

作者头像 李华