draw.io Desktop 官方 Electron 桌面构建实战:安装选型、离线安全模型与开发发布流程
本文以 README 为主体,系统讲解 drawio-desktop 这个官方 Electron 桌面应用的定位、Windows 三种官方安装包的选择依据、其“与互联网完全隔离”的安全模型在源码中的落地方式,以及从克隆子模块、本地调试到个人构建与发布的完整开发流程。读完本文,你能够独立完成 draw.io 桌面版的选型安装、更新行为管控、命令行参数解析理解与源码级本地构建。
一、项目定位:Electron 外壳如何包裹 draw.io 核心编辑器
drawio-desktop 是基于 Electron 的图表绘制桌面应用,其本身并不实现编辑器,而是以 git 子模块的形式包裹 核心 draw.io 编辑器——仓库根目录下的 drawio/ 目录就是子模块挂载点。当前 package.json 声明版本为 31.4.2,依赖 electron ^44.1.1,并要求 node >= 22.12.0。
项目采用 Apache 2.0 协议(见 LICENSE):只要不修改代码、接受“按原样提供”,即可将其用于任何目的,完全免费。
从主进程 src/main/electron.js 可以验证“离线自包含”这一设计前提。应用把编辑器目录直接拼成本地 file:// 地址加载:
// src/main/electron.js L204-L205
const codeDir = path.join(__dirname, '/../../drawio/src/main/webapp');
const codeUrl = url.pathToFileURL(codeDir).href.replace(/\/.\:\//, str => str.toUpperCase());
随后创建窗口时,向 index.html 传入一组 URL 查询参数(src/main/electron.js),其中 gapi、db、od、gh、gl、tr、browser、picker 等云端集成开关全部为 0,mode 固定为 device——从源码结构看,这意味着桌面版默认关闭 Google Drive / OneDrive / GitHub 等在线功能,编辑器完全运行在本地文件上。此外还支持在当前工作目录放置 urlParams.json 覆盖这些参数(src/main/electron.js),为本地定制提供了官方认可的扩展点。
二、Windows 安装:三种官方安装包的选型依据
README 明确 Windows 发布三种安装包,各自权限模型不同,选型依据如下表:
| 文件名 | 类型 | 安装范围 | 权限要求 | 适用场景 |
|---|---|---|---|---|
draw.io-<version>-windows-installer.exe |
NSIS 安装器 | per-machine,装入 Program Files |
需要管理员权限 | 有管理权、面向全机的标准安装 |
draw.io-<version>.msi |
MSI 安装器 | per-user,装入用户配置目录 | 不需要管理员权限 | 无管理员权限的受管终端 |
draw.io-<version>-windows-no-installer.exe |
便携版 | 免安装直接运行 | 不需要管理员权限 | 绿色运行;不注册文件类型关联 |
此外,Microsoft Store(APPX)构建同样可以 per-user 安装、无需管理员权限。
这些说法与构建配置一一对应。electron-builder-win.json 中:
nsis.perMachine: true印证 NSIS 包是机器级安装;artifactName模板${productName}-${version}-windows-installer.${ext}与 README 中的 exe 命名一致;msi.artifactName为${productName}-${version}.${ext},即draw.io-<version>.msi;fileAssociations声明了.drawio、.vsdx、.mmd/.mermaid三类文件关联——这也解释了为何 README 特别注明便携版“不注册文件类型关联”:关联依赖安装器写入注册表,免安装形态做不到。
Store 版本对应 electron-builder-appx.json,Linux/macOS 与 snap 分别由 electron-builder-linux-mac.json 和 electron-builder-snap.json 描述,package.json 中的 release-win、release-appx、release-linux、release-snap 脚本即为对应的 electron-builder 调用入口。
三、安全模型:离线隔离在源码中如何落地
README 将“安全与隔离”列为桌面版的首要设计目标,并声明:除更新流程外,应用与互联网完全隔离。以下逐条给出源码级证据。
3.1 唯一的网络行为:启动时版本检查
更新检查基于 electron-updater。启动时触发检查(src/main/electron.js):
safeUpdaterCall('checkForUpdates (boot)', () => autoUpdater.checkForUpdates());
下载与安装策略被刻意收紧(src/main/electron.js):autoDownload = false——永远由代码显式调用 downloadUpdate(),从而在“静默更新”与“交互式更新”之间分支;autoInstallOnAppQuit = silentUpdate——静默模式下退出应用时自动安装。
完全关闭更新检查(例如集中管控的企业部署)的两种官方方式:
- 设置环境变量
DRAWIO_DISABLE_UPDATE=true - 启动时传
--disable-update
对应实现(src/main/electron.js):
const disableUpdate = disUpPkg() ||
process.env.DRAWIO_DISABLE_UPDATE === 'true' ||
process.argv.indexOf('--disable-update') !== -1 ||
fs.existsSync('/.flatpak-info'); // flatpak 沙箱内自动禁用
const silentUpdate = !disableUpdate && (process.env.DRAWIO_NO_SILENT_UPDATE !== 'true' &&
process.argv.indexOf('--no-silent-update') === -1);
其中 disUpPkg() 来自 src/main/disableUpdate.js,是一个由构建脚本生成的开关(默认返回 false,可由 sync 流程改写为 true);/.flatpak-info 的存在则说明运行在 Flatpak 沙箱中,更新机制同样被禁用。另外两个相关开关是 DRAWIO_NO_SILENT_UPDATE=true 或 --no-silent-update:不禁用更新,但改为下载前提示而非静默更新。更新失败时,notifyUpdateFailure() 会以去重方式弹出手动下载指引,避免重复弹窗(src/main/electron.js)。
3.2 自包含的 JavaScript 与强制 CSP
README 称“所有 JavaScript 文件自包含,CSP 禁止运行远程加载的脚本”。主进程在 app.whenReady() 后通过 webRequest.onHeadersReceived 对全部响应强制注入 CSP(src/main/electron.js):
'Content-Security-Policy': ['default-src \'self\'; script-src \'self\' \'wasm-unsafe-eval\'; connect-src \'self\'' +
(isGoogleFontsEnabled? ' https://fonts.googleapis.com https://fonts.gstatic.com' : '') +
'; img-src * data:; media-src *; font-src * data:; frame-src \'self\'; style-src \'self\' \'unsafe-inline\'' +
(isGoogleFontsEnabled? ' https://fonts.googleapis.com' : '') +
'; base-uri \'none\';child-src \'self\';object-src \'none\';']
要点:
script-src 'self':只允许本应用目录内的脚本;'wasm-unsafe-eval'是为内联的 libavoid WASM 边路由器编译所需;connect-src 'self':把应用自身的网络连接限制到自身;- 仅在用户显式开启 Google Fonts(
isGoogleFontsEnabled,默认false,持久化于 electron-store)时才向fonts.googleapis.com/fonts.gstatic.com放开。
与此同时,onBeforeRequest 拦截所有 file://* 请求,阻止加载应用目录之外的本地文件(src/main/electron.js);IPC 侧再由 validateSender() 校验消息发送方 frame 的 URL 必须位于应用代码目录之下(src/main/electron.js),配合 contextIsolation: true、webviewTag: false、webSecurity: true 的窗口配置(src/main/electron.js),构成多层防御。
3.3 数据外发边界与一个已知例外
README 明确:图表数据永远不会外发,也没有任何应用使用分析被发送到外部;CSP 把网络能力收敛到“自身”。但有一个需要了解的例外:图表可以引用外部媒体(如嵌入 URL 的图片、背景或字体),打开图表时这些资源会被拉取以便正确渲染——因此打开不受信任来源的图表可能向被引用 URL 发起请求,从而暴露 IP 等元数据,但不会传输图表内容。
README 最后给出项目态度:任何要求默认开启外部连接的功能,答案都是“否”。此外,从源码结构看应用是单实例模型:主进程监听 second-instance 事件(src/main/electron.js),重复启动只会唤起已有实例——这在本地同时跑官方版与自编译版时尤其需要注意。
四、开发流程:克隆、运行与调试
README 给出了最小开发回路,核心前提是必须递归克隆,因为编辑器本体是子模块:
git clone --recursive https://github.com/jgraph/drawio-desktop.git
(若已克隆但未初始化子模块,执行 git submodule update --init。sync.cjs 在找不到 drawio/VERSION 时会直接报错退出,提示的正是“是否使用了 --recursive 克隆”。)
运行步骤:
npm install(仓库根目录);export DRAWIO_ENV=dev(README 标注为内部用途,用于开发/调试模式);npm start(即 package.json 中的electron .);调试时使用npm start --enable-logging。
DRAWIO_ENV=dev 的作用在 src/main/electron.js 中可见:__DEV__ 为真时,queryObj 的 dev/test 置 1,并且 createWindow() 会为每个窗口自动打开 DevTools(src/main/electron.js)。
README 还有一条容易踩坑的说明:如果不用子模块而是用符号链接指向 drawio 仓库,必须把 drawio/src/main/webapp 内的 node_modules 目录一并符号链接。
仓库自带单元测试,覆盖 CLI 参数解析、MSI 工程构建钩子与窗口边界恢复三个模块(package.json 的 test 脚本):
npm test
# 等价于 node --test src/test/cli-args.test.js src/test/msi-project-created.test.js src/test/window-bounds.test.js
例如 src/test/cli-args.test.js 验证了 format 默认 pdf、embed-svg-fonts 默认 true、页码区间由 1 起始换算为 0 起始内部索引等行为。
4.1 命令行导出:桌面版附带的批量导出能力
桌面版同时是 CLI 工具,drawio [options] [input file/folder]... 支持批量导出。全部选项定义在 src/main/args.js 的 OPTION_DEFS 表中,常用项摘录:
| 参数 | 说明 |
|---|---|
-x, --export |
按给定选项导出输入文件/目录,除 draw.io 文件外还支持 vsdx、csv、Mermaid(.mmd/.mermaid) |
-o <file/folder> |
输出文件/目录;省略时按输入名加格式扩展名输出 |
-f <format> |
输出格式 pdf/svg/png/jpeg/jpg/xml/html(默认 pdf;若输出名带扩展名则以此为准) |
-t / -e / -b <n> |
透明背景(PNG/SVG)/ 内嵌图表副本 / 边框宽度(默认 0) |
-s <scale>、--width、--height |
缩放与按宽/高适配(保持纵横比) |
-a, --all-pages |
导出所有页(PDF/HTML) |
-p <pageIndex>、-g <from>..<to> |
指定页/页码区间(1 起始,PDF 格式支持区间) |
--theme <dark|light|auto> |
导出主题(默认 auto),覆盖已废弃的 --svg-theme |
-r, --recursive |
目录输入时递归处理子目录 |
-k, --check |
不覆盖已存在的文件 |
--layout <name|json> |
打开后先执行布局(verticalFlow 等预设或自定义 JSON 序列) |
解析器还兼容 -fpng 这类短参数粘连写法,以及 --flag=value 内联语法(src/main/args.js),以兼容旧脚本(如 Makefile 中的 drawio -xa --crop)。目录批量导出时只收集可识别扩展名(.drawio/.dio/.xml/.csv/.vsdx/.mmd/.mermaid/.png/.svg/.pdf),扫描到的 PNG/PDF/SVG 若无内嵌图表数据会跳过而不中断批次(src/main/electron.js)。
4.2 个人构建:fork 后构建未签名应用
README 指向 doc/BUILDING_FOR_PERSONAL_USE.md,其要点是:官方二进制由 GitHub Actions 发布工作流产出,依赖私有基础设施完成签名与公证(Apple Developer 证书、Azure Trusted Signing 等),fork 不具备这些条件,因此个人构建是未签名的,需要显式设置 DRAWIO_UNSIGNED=true 跳过签名与公证——这本身即是对“系统会警告未签名应用”的确认。
打包前先同步版本并禁用自动更新:
npm install
npm run sync -- disableUpdate
sync(sync.cjs)做两件事:把子模块 drawio/VERSION 中的版本号写入 package.json,并按参数重写 src/main/disableUpdate.js 的返回值——禁用自动更新是为了防止个人构建被“静默更新”回官方原版、悄悄抹掉你的修改。
各平台构建命令(输出到 dist/):
- macOS:
DRAWIO_UNSIGNED=true CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --config electron-builder-linux-mac.json --mac dmg --arm64 --publish never(Intel 机器改--x64;拷贝到其他 Mac 需xattr -cr /Applications/draw.io.app清除隔离标记) - Windows:
$env:DRAWIO_UNSIGNED="true"; npx electron-builder --config electron-builder-win.json --publish never(注意 NSIS 安装器是机器级的,会替换官方安装) - Linux:
npx electron-builder --config electron-builder-linux-mac.json --linux AppImage deb --x64 --publish never(Linux 不签名,无需该变量)
该文档还强调:npm run release-* 脚本是给 CI 用的(除 release-snap 外会向 releases 发布产物),本地构建应直接调用 electron-builder;若没有对应平台的机器,也可以利用 fork 上的 personal-build.yml 工作流在 Actions 中构建并下载产物。
五、发布流程:版本号同步与签名链路
README 记录了官方发布的 7 步流程:
- 更新 draw.io 子模块并推送变更,推送前打版本 tag;
- 等待构建完成(历史上由 Travis CI 负责 macOS/Linux、AppVeyor 负责 Windows,见 DEVELOPMENT.md 中对 CI 工作流、
GH_TOKEN/CSC_LINK/CSC_KEY_PASSWORD环境变量与证书导出的完整说明); - 到仓库 releases 页编辑预览版发布;
- 下载 Windows exe 与 portable,用 signtool 签名:
signtool sign /a /tr http://rfc3161timestamp.globalsign.com/advanced /td SHA256 c:/path/to/your/file.exe - 以
draw.io-windows-installer-x.y.z.exe和draw.io-windows-no-installer-x.y.z.exe重新上传签名后的文件; - 补充发布说明;
- 正式发布。
当前仓库中这条链路的自动化程度可以进一步验证:electron-builder-win.json 的 win.signtoolOptions 指定了 build/sign-trusted.mjs 作为签名脚本、sha256 哈希算法,并用 afterPack/msiProjectCreated 钩子处理 fuses 与 MSI 工程调整(后者正是 src/test/msi-project-created.test.js 的测试对象)。而 doc/BUILDING_FOR_PERSONAL_USE.md 明确:官方二进制现由 GitHub Actions 发布工作流产出,tag 触发的发布工作流依赖私有 drawio-dev 仓库与签名密钥,在 fork 上无法运行——这解释了为什么个人构建必须走 DRAWIO_UNSIGNED 旁路。完整发布细节可另见 doc/RELEASE_PROCESS.md。
六、本地存储位置与运行时数据
README 指明 Local Storage / Session Storage 落在各平台的 AppData 目录:
- macOS:
~/Library/Application Support/draw.io - Windows:
C:\Users\<USER-NAME>\AppData\Roaming\draw.io\
从源码结构看,同一 userData 目录下还存放 electron-store 的配置数据,主进程会持久化:窗口上次尺寸与位置(lastWinSize,默认 1200,800,0,0,false,false)、拼写检查开关(macOS 默认开启)、Google Fonts 开关、以及经可信 UI 授权过的文件路径集合 blessedPaths(上限 500 条,用于限制渲染进程可写入的路径,见 src/main/electron.js)。窗口位置恢复逻辑(防止显示器变化后窗口落在屏外)由 src/main/window-bounds.js 实现,并有 src/test/window-bounds.test.js 覆盖。
七、支持政策与贡献边界
README 对支持政策表述克制:支持基于合理商业约束提供、但无合同约束;所有支持只通过本仓库进行,非付费用户没有私有工单渠道;购买 draw.io for Confluence/Jira 不包含桌面版的商业支持。
关于贡献,项目明确不开放外部贡献(“Not open-contribution”):项目复杂度决定了即使简单改动也可能破坏大量联动部件,所需测试量远超表象;即便收到 PR 也基本会推倒重写。维护者长期保持这一决定以保障项目可持续运营,同时欢迎社区提交 bug 报告与功能请求。这也与 Apache 2.0 协议并存——闭源贡献不等于限制使用:fork、修改、自用于个人构建始终被 doc/BUILDING_FOR_PERSONAL_USE.md 视为合法路径。
小结
drawio-desktop 的价值不在功能堆叠,而在工程克制:一个仅数千行主进程的 Electron 外壳,把“离线、自包含、可审计”做成了可验证的事实——CSP 头、file:// 拦截、IPC 发送方校验、更新开关链都能在 src/main/electron.js 中逐行对照。对企业部署而言,DRAWIO_DISABLE_UPDATE 与三种 Windows 安装包的权限差异是落地决策的关键;对二次开发者而言,--recursive 克隆 + DRAWIO_UNSIGNED=true + npm run sync -- disableUpdate 就是一条可复现的个人构建链路。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
