Architecture

轻量的桌面壳,纯文本的内核

HyMd 是一个基于 Tauri 2 的独立 Windows 桌面应用:系统 WebView2 渲染界面,Rust 负责文件与系统能力,编辑器内核建立在 ProseMirror 与 unified/remark 之上。所有能力都围绕同一份纯文本 .hy.md 展开,数据留在你自己的电脑上。

Overview

熟悉的工作台,专注的“文章表面”

外壳学的是 VS Code Workbench 的分区布局——标题栏、活动栏、侧栏、编辑区、面板、状态栏,打开文件夹、分栏、命令面板的手感都和你习惯的一样。

但编辑区里不是迷你 IDE,而是即写即得的“文章表面”:基于 Milkdown Crepe 的所见即所得 Markdown 编辑器,表格、流程图、幻灯、图版块以卡片形式嵌在正文里。

  • 为什么是 Tauri早期路线是 VS Code 扩展和 Code-OSS 分支,体积达 GB 级、扩展与安装包容易不同步。改为独立的 Tauri 桌面应用后,界面复用系统 WebView2,分发轻得多。
  • 为什么是纯文本编辑器里看到的一切最终都写回同一份 Markdown 字符串,任何 Markdown 工具都能降级读。
Title Bar · 标题栏 / 菜单
活动栏
主侧栏文件 · 搜索 · Git
说明.hy.md排版幻灯
sheet · 电子表格卡片
mermaid · 流程图卡片
编辑区(HyMd 业务)
辅侧栏主题画廊(可选)
Panel · 设置 / 终端 / 问题(可选)
Status Bar · 状态栏
Editing surface

编辑面的五层结构

一份 Markdown 字符串,同时被三种编辑器和几条语法树流水线共享。每层各管一件事。

1
系统网页壳

Tauri 2 + WebView2 渲染界面,通过自定义命令调用 Rust 宿主读写文件、跑 Git、做识字。

2
React 工作台壳

标题栏、侧栏、编辑器组网格、面板、状态栏;菜单、快捷键、命令面板共用一张命令注册表。

3
正文树

ProseMirror(经 Milkdown Crepe)——所见即所得正文

代码文本

CodeMirror 6——源码、纯文本、超长文档

表格 JSON

Univer——文内电子表格与表格文件

4
Markdown 语法树流水线

unified + remark(GFM、frontmatter)负责解析与序列化;另有排版投影、导出变换等流水线。

5
同一份 .hy.md 字符串

唯一的真相。编辑器改动经防抖回写到这里,保存时与文首 YAML 合并落盘。

Tech stack

技术栈

尽量选择成熟、许可证清晰的开源组件;AGPL / GPL / LGPL 组件不进安装包。版本号以当前代码为准,会随开发更新。

桌面与界面

桌面壳
Tauri 2,插件 fs / dialog / opener / window-state
前端
React 19、Vite 7
图标
@vscode/codicons
界面语言
自建轻量 i18n,中英键编译期对齐

编辑与渲染

正文编辑
Milkdown Crepe 7(基于 ProseMirror)
源码编辑
CodeMirror 6
Markdown 解析
unified + remark-parse + GFM + frontmatter
图表 / 公式
mermaid 11、KaTeX
Diff
diff、markdown-it

文内 Office 能力

电子表格
Univer(只用 sheets-core 预设)
表格文件
exceljs、jszip
幻灯
marp-core 渲染 HTML;modern-screenshot 拍图;pptxgenjs、jsPDF 拼文件
图片画布
Fabric.js 7
作图
JSXGraph(双许可,取 MIT)

文件与本机能力

Word 导出
remark-docx(MIT),内置生成
Office 预览
docx-preview、pptx-preview
CAD 字体
@mlightcad/shx-parser、opentype.js(SHX → TTF,本机生成)
本机识字
Rust ort(ONNX Runtime)+ PP-OCRv5 mobile
搜索
Rust regex
Layers

依赖只向下,规则写进测试

代码分五层,上层可以依赖下层,反过来不行。最底层的宿主无关包既能在浏览器跑、也能在 Node 跑,不碰 Tauri API。

  • 一功能一目录每个功能有自己的 Model、Pane 和入口文件,互不缠绕。
  • 模块预算新源码文件不超过 600 行;超标文件冻结,只拆不加。
  • 加功能七问落点、状态、入口(命令注册表 + 中英文案)、文件与进程守门、大依赖拆包、冻结文件、同轮更新文档——七问答完才能动手。
  • 命令单一来源菜单、快捷键、命令面板共用同一份注册表,不会出现“菜单能用、快捷键不行”。
L4壳 UIsrc/parts · overlays · layout · App.tsx
L3功能src/editor/<feature>
L2状态中心src/state
L1服务与纯逻辑services · commands · i18n · perf
L0宿主无关包packages/*
invoke 命令
RustRust 宿主src-tauri文件 · Git · 识字 · 授权 · CAD 管道
依赖方向:L4 → L3 → L2 → L1 → L0;L1 通过 invoke 调用 Rust 宿主。
Packages

六个宿主无关包

解析、排版、导出、视图投影这些核心逻辑都做成独立的包:不依赖界面、不依赖 Tauri,可以单独测试。

包职责单元测试(文档记录)
@hymd/parser解析与序列化、块注册表(unified + remark-parse + GFM + frontmatter)54
@hymd/webview-coreCrepe 装配、块卡片、表格与流程图叠层、幻灯栅格、选区工具条、i18n 小词典121
@hymd/blocks-sheetUniver 快照读写与挂载10
@hymd/layout纸面几何、分页投影、CAD 排版数据导出194
@hymd/export导出变换、表格转 GFM、幻灯、Word 生成(remark-docx)、打印 HTML26
@hymd/views块索引、锚点、胶片解析、投影(纯函数)47

规则:packages/* 禁止引用应用代码、禁止调用 Tauri API;Node 内置模块只能出现在独立入口。

Data flow

从打开到保存:一份文档的旅程

点开文件、编辑、按 Ctrl+S,数据在各模块间这样流动。

HyMd 关键数据流:文件树打开文件进入 DocStore,编辑器组挂载 HymdEditor,经 webview-core 驱动 Crepe、块卡片与右侧叠层;编辑经防抖回写 DocStore,Ctrl+S 合并文首后写盘。 openFile groups + activePath 每组挂活动文档 markdownUpdated防抖 220 ms onEdit saveDoc joinFrontmatter→ writeTextFile SideBar 文件树 DocStore EditorPart 多组 HymdEditor createHymdEditorwebview-core Ctrl+S tauri-plugin-fs写入磁盘 Crepe所见即所得正文 块卡片表格 HTML / 流程图 SVG 右侧叠层Univer / 源码

超长正文(超过 20 万字)自动改用源码窗编辑,避免卡死;图片、PDF、Word、PPT、表格文件按类型挂各自的预览或编辑面板。

本地图片总能找到

WebView 会把相对路径的图片解析到页面根目录而找不到。HyMd 依次在文档目录、assets、{stem}.assets、工作区和全局图库里查找,命中后只改显示,不改你的 Markdown。

表格快照不塞进正文

文内电子表格的完整状态存为 {stem}.assets/{blockId}.univer.json,正文里留的是可读的表格内容——在别的编辑器里看,依然是一张表。

一份 .hy.md 源文件:写作视图双向读写,排版、幻灯、论文视图单向投影。 .hy.md 唯一真相 写作视图直接改源 排版视图A4 纸面投影 幻灯视图marp-core 论文视图@hymd/layout
实线:双向读写;虚线:只读投影,视图调整写进各自的“胶片”。
One source, four views

投影是纯函数,原稿只有一份

每种视图都是 投影(源文本, 本视图胶片) → 视图用的 Markdown。幻灯交给 marp-core 渲染,排版和论文交给 @hymd/layout 分页。

存储隔离一个视图一个胶片文件:{stem}.assets/views/{kind}.hy.md
缓冲隔离视图修改只进视图缓冲,500 ms 防抖写胶片,源文件不标脏、不进 Git 差异
计算隔离投影函数的签名里拿不到别的视图的胶片,三个视图互不可见

原稿改了,标记怎么找回?

  1. 显式 id
  2. 块指纹
  3. 原文摘录
  4. 结构路径
  5. 失联清单

逐级匹配,全部对不上时进入“失联清单”等你处理,而不是悄悄删掉。每个视图有独立的 50 步撤销。第 5 级“模糊摘录”锚点 规划中

Export

三条导出管线

所有导出都先经过同一个导出变换(把表格块降为普通表格、处理围栏块等),再分流到不同出口。

Word (.docx)

  1. 导出变换
  2. remark-docx 直接生成
  3. .docx

当前代码已改为内置的 remark-docx(MIT)生成,无需另装 Pandoc;公式先成图,原生 Word 公式在规划中。完善中

幻灯 (.pptx / .pdf)

  1. marp-core 渲染 HTML
  2. 应用内拍成 PNG
  3. pptxgenjs / jsPDF 拼文件

无需 Marp CLI。当前导出的 pptx 每页是整页图片,可编辑的原生文本框 pptx 在规划中。界面待走查

整篇 PDF

  1. 生成打印 HTML
  2. 本机浏览器无界面打印
  3. .pdf

借助本机已有的 Chromium 系浏览器打印。PDF 页码与书签在规划中。

AutoCAD alignment

预览和图纸,用同一把尺

工程说明贴进 AutoCAD 后最怕“重新折行”:屏幕上一行,图纸上变两行。HyMd 的办法是在自己这边把每一行都量好、锁好,CAD 插件只负责照着画。

  1. 编辑器正文.hy.md
  2. 解析parseHymd
  3. 屏外分页锁定倍率,逐字量位置
  4. 排版数据每块带已折好的行与基线
  5. 命名管道只送给已绑定的窗口
  6. CAD 插件只画不折
1 mm ≈ 3.78 px

毫米与像素的换算

1 mm = 96 / 25.4 CSS 像素。屏外分页时锁定缩放倍率,对每个字取实际渲染位置合成行,再换算成毫米。

字号 = 字高 × 4/3

字体口径

微软雅黑按 CSS 字号 = 字高 × 4/3 换算;TSSD(天正 SHX)在本机转成网页字体,大写高做成 1 em;宋体口径单独处理。

行距 = 2.0 × 字高

对齐 AutoCAD 行距

按 AutoCAD 多行文字“固定行距 1.2 倍”换算。“CAD 说明”风格即字高 3.5 mm、行距 7.0 mm。

诚实说明:换算规则与字体口径已有单元测试;TSSD / 微软雅黑 / 宋体三套口径与 AutoCAD 实测的对照夹具仍在验收中(目标:中文字数差 ≤ 1,行高差 < 0.2 mm)。使用需本机已安装 AutoCAD;TSSD 字体文件需你本机已有,不随安装包分发。表格、幻灯、计算、流程图、公式等块在贴图时跳过,不会中断。
Local-first

本地优先:你的文件不离开你的电脑

HyMd 是本地桌面应用,没有云端存储。写作、排版、识字、导出都在本机完成。

离线识字

OCR 用 Rust 的 ONNX Runtime 跑 PP-OCRv5 模型,全部在本机执行,图片和扫描页不会上传。英语音标识别做了专门增强,模型换机无需重训。界面待走查

纯文本落盘

所有内容都是 Markdown 文本和同目录的资源文件。不绑定账号、不锁定格式,换电脑、换工具都带得走。

按需授权目录

应用默认只能访问自己的数据目录;你打开的文件夹才会在运行时放行。系统目录、凭据目录、用户目录根不会被放行。

保守的 Git 操作

Git 命令按形状校验:拒绝强推、强制切换、清空贮藏等危险操作;拉取只做快进。

不执行文档里的代码

条形图围栏只解析数据字面量、不执行代码;外部程序只认 Git 与浏览器等少数几类并按完整路径核对;“系统打开”拒绝可执行文件。

离线授权验证

激活码用 Ed25519 公钥在本机验签;文献校验等联网查询默认不自动发起。

Quality

测试很多,但我们只认“看得见”

全仓约 1600 个单元测试,TypeScript 严格模式零错误,Rust 有单测与 clippy 检查。但在 HyMd 的规则里,单测绿了不算通过。

  1. 包与应用单测证明纯函数与回归
  2. 构建与类型检查编译通过、中英文案齐全
  3. GUI 走查证明用户真的看得见
  4. 干净机冒烟安装包不依赖开发机

这也是为什么网站上很多功能标着“完善中”:代码与单测已就绪,但还没在窗口里逐项走查。我们宁可少说,也不提前说“已验证”。

看看这些技术会带向哪里

阅读路线图,了解验收门、视图与引用的下一步。