
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 typemodule 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/distpackage.json移除上一次构建生成的packages/mermaid/dist保证每次从干净状态出发。2. Build Mermaid——编译 Mermaid 运行时pnpm build:mermaid对应build:mermaid: pnpm build:esbuild --mermaidpackage.json调用仓库统一的 esbuild 构建管线产出mermaid.min.js等运行时文件。这一步产生的是编辑器后续要引用的核心渲染引擎。3. Copy Editor——拷贝编辑器静态源pnpm copy:editor对应copy:editor: cpy \packages/mermaid-local-editor/static/**/*\ packages/mermaid/dist/mermaid-local-editorpackage.json把本包static/下的index.html、styles.css、app.js与js/*.js原样复制到输出目录packages/mermaid/dist/mermaid-local-editor/。注意 index.html 中引用的就是这些同目录文件因此复制后结构完全一致即可直接运行。4. Bundle Dependencies——拷贝运行时依赖pnpm copy:mermaid pnpm copy:dompurify分别对应package.jsoncopy: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 浏览器 bundlemermaid.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-clearpackage.json使用sirv将dist产物托管在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.html到app.jsindex.html定义了编辑器的基本界面骨架static/index.html工具栏#toolbar包含折叠按钮、图表下拉框#diagrams、名称输入框#name以及 Save / New / Delete / Reset View / Export SVG 五个操作按钮。主工作区#main左侧为源码文本域#srcPanel右侧为渲染预览区#preview。app.js作为模块入口按依赖顺序完成装配static/app.jsimport { 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 classmermaid theme: dark, // 深色主题 securityLevel: strict, // 最高安全等级禁用点击跳转/脚本执行 deterministicIds: true, // 确定性 id利于结果稳定与测试 fontFamily: Arial, htmlLabels: false, // 使用非 HTML 标签渲染文本 flowchart: { useMaxWidth: false }, }); }各关键配置的实际作用startOnLoad: false本编辑器需要把用户在文本域中输入的 Mermaid 代码交给 JS 手动渲染见 renderer而非依赖页面加载时扫描classmermaid元素因此必须关闭自动启动。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 沙箱渲染调用await mermaid.render(id, srcValue)将 Mermaid 源码编译为 SVG 字符串。净化DOMPurify.sanitize(svg, { ADD_TAGS: [foreignObject], ADD_ATTR: [xmlns] })保留 Mermaid 渲染所需但 DOMPurify 默认可能剥离的foreignObject标签与xmlns属性同时清除其余潜在危险内容。二次加固遍历 SVG 元素删除所有以on开头的内联事件属性如onclick、onload从 DOM 层面彻底封死事件注入svgEl.querySelectorAll(*).forEach((el) { [...el.attributes].forEach((attr) { if (attr.name.startsWith(on)) { el.removeAttribute(attr.name); } }); });沙箱隔离新建iframe sandboxallow-same-origin把净化后的 SVG 通过iframe.contentDocument.importNode(svgEl, true)写入 iframe 文档。sandbox 属性不放开allow-scripts使渲染结果无法执行脚本预览区域因此与主页面形成隔离边界。注入交互样式向 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.24倍之间随后调用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实现多图表管理与会话保持涉及两个 keymermaid-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确认删除当前图回退到剩余第一张图或mainsetCurrent/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/svgxml })生成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回到上一节点当焦点位于源码编辑框时按键不劫持保证用户仍能正常编辑代码。这套实现体现了编辑器对键盘可达性的考量对视障或偏好键盘操作的用户可逐个节点感知图表结构与空间位置。八、安全模型小结三层纵深防御综合源码可见编辑器对渲染用户输入这一高风险行为做了三层纵深防护这也是它敢于完全离线、不对输入做任何服务端校验的底气所在Mermaid 配置层securityLevel: strictdeterministicIds从图解析端抑制链接点击与脚本注入内容净化层渲染出的 SVG 字符串统一经过 DOMPurifysanitize()白名单放行foreignObject与xmlns并额外剥离一切on*内联事件属性运行隔离层预览 SVG 被写入sandboxallow-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.jspurify.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),仅供参考