我不是什么有名的开发者,就是一个写了十年 Markdown 的人。这些年我用过各种编辑器:有的把源码和预览分成左右两栏,改一句话要在两栏之间来回找;有的把 Markdown 藏得很深,写着写着就变成了跟排版较劲。
于是今年,我决定自己写一个。它叫 Lumin :Electron 39 + Vue 3,打开 .md 就是排版好的正文,点哪句改哪句,保存下来还是纯 Markdown。今天它到了 1.0,我把这一路踩过的坑、做成的东西、以及还差得远的地方,都写在下面。 
一、为什么自己写:六个真实的小坑
不是"想做一个更好的编辑器",大多数时候是被一个具体的麻烦逼的。
1. 粘贴卡死。 从网页上复制一大段带表格的内容,粘进编辑器,窗口白住几秒,鼠标转圈,运气不好就直接未响应。原因不复杂:整段内容要重新解析、整张表要逐格重建。现在这件事有几道闸:内联图片超过上限就直接取消粘贴(文档一个字节都不动),结构太复杂就降级成纯文本,处理耗时超出预算就整次回滚到粘贴前,并如实告诉你发生了什么。
2. 图片点开裂。 想改一下图片路径或者尺寸,点一下图,它变成一行灰色占位或者一串源码,得再点一次才回来。现在点击图片只是把它的 Markdown 源码露出来,图还在原地。
3. 切视图变成"未保存"。 实时渲染看一眼、分屏对照一眼、源码再确认一眼 ------ 只是切了个视图,文档就被标成"已修改",关窗口时还要问一句"是否保存"。现在切模式不动脏标记,有单测专门守着这条。
4. 草稿误报。 明明按了保存,第二天打开还是弹"检测到未保存的草稿,是否恢复"。这种提示弹一次是贴心,弹三次就是狼来了。现在只有真的存在未落盘内容时才提示。
5. 代码块横滚。 一段长行把整个页面撑出横向滚动条,或者代码块里横滚到看不见行号。现在代码块自己软换行,行号固定在最左边,长行不会带着整个页面跑。
6. 中文没有拼写检查。 英文单词打错了有红波浪线,中文打错字只能靠眼睛。Chromium 自带的词典里根本没有中文(availableSpellCheckerLanguages 里没有 zh),所以这件事只能自己做。
这六条加起来,就是"写的时候总觉得哪里别扭"。Lumin 的第一版,基本就是在一条一条拆这些别扭。
二、它现在能做什么
下面十节,每节一张真机截图(Windows 11,简体中文界面)。
1. 真正的原位渲染
打开 .md 就是排版好的正文:标题、加粗、斜体、行内代码、引用、列表、任务清单、表格、图片、链接、分割线全部就地显示。把光标放进某一句,它周围的 Markdown 标记才会浮现;光标移走,标记又隐回去。文档模型仍然是 Markdown,所以保存下来的就是文件里真实的内容。

2. 表格是表格,不是一堆竖线
表格在原地就能编辑:加行、删列、改列对齐,改完还是普通的 Markdown 表格。不用切到源码去数 | 的个数。

3. 公式:行内与块级都用 KaTeX
行内 $E = mc^2$、块级 $$...$$ 都用 KaTeX 渲染,KaTeX 和字体随应用一起打包,断网也能显示。点一下公式,就能在源码和实时预览之间改它。

4. Mermaid 流程图
围栏标成 mermaid 的代码块会直接在正文里画成图。图运行时是懒加载的,只有文档里真的有图才付出这份体积;源码写错时保留源码并报错,不会把整块内容吞掉。

5. 代码块:CodeMirror 6
代码块里嵌的是 CodeMirror 6,有语法高亮、行号、括号补全、自动补全和软换行。语言选择器浮在块外 (截图右上角那个 js),可以输入任意语言名;围栏没写语言时自动识别。代码文本不会被 Markdown 往返改写。

6. 中文拼写:用规则,不用词典
英文走系统词典;中文走一套启发式规则:虚词连续重复(的的、了了)、常见词重复、中文句子里的半角标点、成对符号不配对、一小张高置信易错词表(比如"做为")。右键就有建议,比如把"的的"改回"的"。代码块、公式源码、Mermaid 源码都不参与检查,所以不会满屏红线。

7. 大纲与右侧停靠栏
大纲由文档的标题结构生成,不是另存一份目录。它默认住在左栏,也可以在设置里搬到编辑区右侧那条 40px 的停靠栏里 ------ 这样文件树和大纲可以同时看着。

8. 三套主题,三种界面语言
浅色、深色、羊皮纸三套主题,中文 / English / Русский 三种界面语言,切换立即生效。

9. 导出:PDF / HTML / Word / EPUB
PDF 走 Chromium 的打印管线,带页码和页眉;HTML 是单文件、图一起打包;Word 导出的是 HTML 兼容格式(Word 能打开、能继续编辑,但不是 OOXML 的 .docx);EPUB 按一级标题自动拆章,本地图片打进书里。

10. 文件树与本地工作区
选一个文件夹当工作区,左栏就是文件树:新建、重命名、移动都在原地完成。写东西终究要落到磁盘上看得见的文件,这一点我没打算用数据库替你安排。

三、技术上几个有意思的点
1. 保真:两万行,逐字节一致
"所见即所得"最容易翻车的地方不是显示,是往返 :打开、编辑、保存之后,文件里多出来一堆你没写过的空格,或者列表标记从 - 变成 *。
我做了一份两万行的 Markdown 做真机验证:打开 → 插入一个字再删掉(保证真的走了一遍序列化和落盘)→ 保存。结果是文件大小 449,999 字节、SHA256 与原始文件完全相同。仓库里还有一组判级单测,把"往返差异"分成三级:exact(逐字节一致)、normalized(只是写法同义,比如 - 和 *)、lossy(真的丢东西),每条规范化规则都必须证明它吃不掉真差异。
2. MutationObserver 自激:两个链接就能卡死
这是最像"玄学"的一个 bug。我给链接补 draggable="false" 想禁掉拖拽,结果 ProseMirror 的 DOMObserver 把这次属性变更 当成"外部程序改了我的 DOM",重渲染这段,draggable 被重建掉,我再写一次......死循环。实测 2.5 秒里 observer 回调了 9451 次 ,主线程再没回来,CDP 全超时,Debugger.pause 都进不去。
更气人的是触发门槛:一篇只有两个链接 的文档就够了(一个链接时重渲染恰好不重建属性,两个以上才进环),所以它表现成"随机卡死"。最后的修法不是在循环里加计数,而是根本不去碰 ProseMirror 管理的 DOM :拖拽在 window 捕获阶段 preventDefault 掉,再加一条 CSS,属性纠偏只对编辑器之外的 DOM 生效。
3. 启动:六段打点,和一份 352MB 的 asar
启动慢这件事,靠感觉是调不动的。主进程里加了六个点:app-ready / ipc-registered / first-window-created / first-window-loaded / remaining-windows-created / first-window-painted,每段都带 t+ms 与 process.uptime;渲染层用 performance.mark 记自己的轴。两组数字分开看,才能知道这 200ms 是 Electron 的,那 150ms 是我的入口 chunk 的。
同一轮还发现打包产物里塞进了不该进 asar 的东西,asar 一度是 352MB ,收窄之后是 11.5MB 。首屏前也不再加载 electron-store 和 updater ------ 第一版 FCP 是 668ms,现在是 280ms。
4. 中文拼写的边界:宁可少报
没有词典的中文检查,最大的风险不是漏报,是满屏误报 。所以规则只有六类:虚词重复、闭集词重复、中文句里的半角标点、成对符号不配对、高置信易错词、可选中英空格提示。的的确确、了了分明 这类正常说法按区间豁免;"台风登陆"不算错,"登陆账号"才提示;简体中文 这种语言名整个词豁免,但紧挨着它的 我们的的 照报。单次检查最多返回 500 条,文档一变先防抖 500ms ------ 宁可慢半拍,也不让装饰集无上界增长。
四、现在的状态
说清楚边界,比说清楚功能重要。
- 只支持 Windows 10 / 11(64 位)。macOS 和 Linux 的构建脚本会直接拒绝执行,因为我没有在那些平台上验过,不想发一个"应该能跑"的包。没有移动端,也没有云同步 ------ 文档就在你的磁盘上。
- 安装包没有代码签名。第一次运行,Windows SmartScreen 会提示"Windows 已保护你的电脑",点"更多信息 → 仍要运行"即可。签名证书要钱,这一版先欠着。
- AI 不在主分支 。仓库里有一个
feature/ai分支,是实验性质的,1.0 的主分支里没有任何 AI 功能,也没有联网的写作服务。 - 1336 个单元测试 (
node --test,全绿),加一套 CDP 真机验收套件;npm run ci会跑类型检查、ESLint、单测、声明与文档校验、生产构建。 - MIT 许可。免费、开源、没有广告,也没有订阅。
五、仓库与镜像
主仓库在 GitHub ,另外两个是国内镜像(内容与 GitHub 的 main 同步,方便访问):
| 平台 | 地址 | 说明 |
|---|---|---|
| GitHub(主) | https://github.com/yangbaibai-ops/Lumin |
主仓库;Issue / PR 优先提这里,自动更新也从这里走 |
| Gitee(镜像) | https://gitee.com/yangbai-bai/Lumin |
国内访问快;发行版里也放了安装包 |
| GitCode(镜像) | https://gitcode.com/yangbaibai-ops/Lumin |
国内访问快 |
镜像只做"读":代码与版本标签会和 GitHub 同步,但 Issue 请提到 GitHub,这样我不会漏看。
六、怎么用
方式一:直接下载安装包
- GitHub Releases (主):
https://github.com/yangbaibai-ops/Lumin/releases - Gitee 发行版 (镜像):
https://gitee.com/yangbai-bai/Lumin/releases
下载 Lumin-1.0.0-setup.exe,双击安装。装完第一次启动可能会慢几秒,是 Defender 在扫新文件,之后就快了。
安装包未签名,首次运行如提示"Windows 已保护你的电脑",点"更多信息 → 仍要运行"。
方式二:从源码构建(三个平台都可以)
需要 Node.js 22 (node -v 确认),以及一台 Windows 10/11。
powershell
# ① 从 GitHub(主仓库)
git clone https://github.com/yangbaibai-ops/Lumin.git
# ② 或者从 Gitee(国内更快)
git clone https://gitee.com/yangbai-bai/Lumin.git
# ③ 或者从 GitCode
git clone https://gitcode.com/yangbaibai-ops/Lumin.git
cd Lumin
npm install # 装依赖
npm run dev # 带热更新的开发模式
npm run build:win # 出安装包,产物在 dist/
构建完成后,安装包在 dist/Lumin-1.0.0-setup.exe,latest.yml 与 .blockmap 也在同一目录(发布更新时才需要后两个)。
想先看测试怎么跑:
npm run test:unit是纯 Node 的单测,不需要桌面会话;npm run test:suites会起隔离实例走 CDP,需要真实的 Windows 桌面。
七、请你帮三件事
- 给个 Star。 不是为了数字好看,是因为这个项目只有一个人在维护,Star 是我判断"还有人在用"的最直接信号。
- 试用一下,然后把不顺手的地方提成 Issue。 尤其是中文写作场景:你的输入法、你的长文档、你的导出需求。提 Issue 时如果能带上系统版本和复现步骤,我基本都能修。
- 告诉我你想要什么。 下一版做什么,我更多听用的人说,而不是自己拍脑袋。
八、写在最后
Lumin 是一个人的业余作品,没有广告,也没有订阅。如果它恰好帮到了你,我会很高兴;愿意的话,可以给项目点个 Star,或者请我喝杯咖啡 ------ 不勉强,也不用有负担。
祝写作愉快。