参赛赛道:全链路开发实战
选题方向:真实工作场景提效——Markdown 公众号排版工具的开发与迭代
技术文章在 Markdown 里写得很顺,搬到微信公众号却经常要再排一遍:标题层级要调,代码需要高亮,本地图片不一定能带过去,手机上的长表格还可能撑出正文。
我想把这件重复的事做成一个小工具:粘贴 Markdown,检查预览,点击复制,然后去公众号正文编辑区粘贴。它叫“墨贴”,目前是一个纯静态网页。
这次分享的是已有项目的一轮实际迭代。我使用新版 TRAE 调整样式、修改功能,围绕真实文章里遇到的问题继续完善工具。下面按需求、关键决定、问题修复和交付结果展开。
- 在线体验:墨贴
- 项目源码:dragonxjy/motie
- 使用说明:从输入、图片处理到公众号粘贴
先把目标缩小到一篇文章能用
最初的描述是“把 Markdown 转成微信公众号识别的格式”。拆开之后,需要交付的是一份带格式的 HTML 富文本。
我把范围定在一条使用流程上:输入一篇现成的技术文章,处理标题、代码、图片、列表和表格,检查手机宽度,再复制到公众号。账号登录、素材上传和自动发布留在平台上完成,网页本身打开就能试。
界面也跟着这个目标收敛:编辑区和预览区占主要空间,复制放在顶部,排版选项放到侧栏。现在默认主色是 #1c8feb,图注、大纲和段落间距都是围绕长文章编辑补上的。
在需求反复调整时,我发现“再好看一点”很难验收。换成“预览里不显示滚动条,但仍能阅读全文”“图片粘在光标处”,就能明确检查结果。使用 AI 修改项目时,约束写得具体,后面才知道该验证哪里。
预览与复制使用同一份内容
排版工具容易把力气花在预览样式上,但读者最终使用的是粘贴到公众号里的内容。
如果代码颜色只来自页面自己的 CSS,或者标题依赖某个 class,复制离开这个网页后就可能失效。因此,墨贴把转换结果整理成内联样式,同一份 HTML 同时用于预览、富文本复制和独立文件导出。
项目的处理过程是:
Markdown
→ Marked 解析
→ DOMPurify 清理不可信 HTML
→ 处理代码、图片、列表与参考链接
→ 写入内联样式
→ 预览 / 复制 / 导出
Marked、DOMPurify、highlight.js 和 Lucide 都随项目提供,打开网页不需要临时请求 CDN。标题、引用、表格以及代码标记的样式写到元素上,复制后的正文也不锁死在某个像素宽度。
这一步能减少结果对原网页环境的依赖,但目标编辑器仍可能调整样式,所以后续需要分别检查工具输出和公众号粘贴产物。
真正暴露问题的是一次粘贴
实际试用里,出现了几个比配色更影响使用的问题:代码中的参数拆行、缩进错乱,任务清单的勾选符和文字分成两段,部分参考网址重复拼接。
这些反馈发生在粘贴到公众号之后,工具预览本身不一定能复现。因此修复时要看输出结构。
代码不能只依赖 white-space
只设置 white-space: pre-wrap,目标编辑器处理 HTML 时未必保留相同效果。目前代码块会用显式 <br> 保存源代码换行,用不间断空格保留空格,Tab 按两列显示;高亮后的参数片段保持行内结构。
例如下面的原始代码,参数、缩进和空行都需要保留:
async function fetchUsers(page, { active = true, pageSize = 20 } = {}) {
const params = { page, active, pageSize };
return request('/api/users', { params });
}
手机宽度不足时,长代码自然折行是允许的;把参数拆成新的正文段落,则是需要避免的错误。纯文本剪贴板另外保留原始代码,包括源文件中的 Tab。
勾选符和事项必须处在同一段
任务清单也做了结构调整:☑ / ☐ 直接放进事项文字所在的段落。遇到已经被拆成“符号一段、文字一段”的旧内容,只合并符号和第一句,后续说明段落仍保留。
参考链接则集中处理了重复问题:裸网址不再显示成“URL:URL”;已经写好的 [1]、[2] 来源保留原编号。长地址可以随正文自然折行,但不会人为把编号与链接拆成多个段落。
项目把这些输入整理成了公众号粘贴回归样例。后续修改可以反复使用同一份文章,比较原文、预览和公众号结果。目前相关结构测试已通过,修复后的实际公众号效果仍需复测。
双滚动条:一个很直观的交互问题
预览区还出现过内外两条纵向滚动条。滚轮有时在文章里滚,有时在外面滚,读长文章时很别扭。
解决它时先处理滚动容器:文章改为直接显示在外层预览画布中,去掉嵌套的 iframe 视口,让正文自然撑开,由外层统一负责纵向滚动。
之后又做了一层外观调整:预览不显示滚动条,但滚轮、触控和键盘仍可阅读。长代码关闭自动换行时仍能横向查看,侧栏大纲和顶部返回按钮提供定位入口。
手机、电脑预览的宽度上限分别是 375px 和 680px,面板不够宽时自动收缩。这里模拟的是正文阅读宽度,最终还要看公众号自己的手机预览。
这次调整提醒我,界面问题的描述和实现层次要对上。双滚动条先检查布局与滚动容器;确定滚动区域后,再处理滚动条是否可见。
图片粘贴之后,事情还没结束
写技术文章经常需要补一张截图,所以现在可以在编辑区光标处直接粘贴图片,也能选择文件或拖入 PNG、JPG、WebP、GIF。插入后生成 Markdown 图片引用,预览使用当前页的资源映射。
原稿中的 ./images/a.png 只有路径,网页不会凭这段文本自动读取电脑文件。图片管理提供逐张替换:选择本地原图,或者填写完整的 HTTP / HTTPS 地址。
这里有两个需要提前知道的限制。
第一,本地图片目前只保留在当前页面内存中。保存草稿或下载 Markdown 不会打包图片;刷新前需要带图备份时,应当导出 HTML 或单独保存原图。
第二,图片在工具里显示成功,不代表公众号已经接收。外链可能有防盗链,内嵌 data: 图片也可能被拒绝。粘贴后仍需逐张检查,缺图就在公众号上传原图。
因此,这个工具的一键复制解决的是排版内容的准备,图片是否被接收要以公众号实际结果为准。
交付结果怎样验证
当前版本为 1.1.0,源码已推送到 GitHub,GitHub Pages 地址可以访问。2026 年 10 月 22 日核对时,线上入口和核心脚本、样式均返回 HTTP 200,并与本地交付文件一致。
本地测试记录是 44 项通过:26 项转换测试、18 项界面逻辑测试。其中包含代码换行与缩进、任务清单、参考链接、图片插入、危险内容清理,以及段落间距、图注开关和大纲定位。
测试还检查了一个容易漏掉的时机:编辑器有 200ms 防抖,用户输入完立刻复制,按钮应当先刷新转换结果,再读取当前正文。
这些验证有边界。界面测试使用 jsdom,剪贴板使用接口替身;线上资源能访问,也不等于真实浏览器布局和公众号编辑器已经验收。目前仍应在真实账号草稿中检查粘贴效果,再发送微信手机预览。
想自己运行,可以直接打开项目的 index.html;有 Node.js 环境时,在项目目录执行:
npm start
需要修改代码和复现测试时再执行:
npm ci
npm run check
npm test
这次迭代留下的开发流程
相比一次写出多少代码,我更愿意保留这条流程:拿一篇实际文章提出需求,用具体的异常继续修改,保留问题输入,把同一个结果用于预览、复制和导出,最后到目标平台检查。
TRAE 在这次实践中参与了样式调整和功能修改。对我来说,值得继续使用的部分,是把一个“看着差不多”的需求逐步变成能够复核的改动:哪里不对,改了什么,用什么输入再检查一次。具体对话和代码差异,应当和真实操作画面放在一起看。
Markdown 排版已有不少成熟工具。墨贴的价值在于围绕自己的文章反复处理那些小而具体的麻烦,并留下一个可以打开、可以使用、还能继续修改的结果。