目录

折腾 | 给博客做一个随身写作台

本文解决的是个人博客「已经可以在浏览器里更新,但还不够像一个真正的写作工具」的问题:在不替换 PagesCMS、不改变 Markdown 和 Hugo 发布方式的前提下,重新做一套在线写作 PWA,把本地保存、图片处理、冲突保护和发布都收进同一个工作台里。

https://img.philohao.com/blog/2026/07/8ae947c6-5d69-4fc2-bec1-2d6800b6086b-9beff060.webp

适用场景

适合已经用 Hugo、GitHub 这类静态博客,既喜欢 Markdown 和 Git 带来的可靠、可迁移,又希望日常写作能像在线笔记一样顺手的人。

如果每次更新博客都要先确认手边有没有电脑、仓库是不是最新、图片有没有压缩、网络中断会不会丢稿,那么真正拦住写作的往往已经不是「不会发布」,而是发布之前那些细碎又重复的动作。

这次折腾想做的,就是把这些动作藏到工具后面:我只管写,系统负责记住、检查和送达。

背景

前段时间,我在《折腾 | 全程浏览器创作博客》里整理过一套浏览器创作流程:PagesCMS 管文章和数据,图片上传器负责裁剪、压缩和上传 R2,最后仍然由 GitHub、Hugo 和 Vercel 完成发布。

这套方案已经把我从本地仓库里解放出来了,但真正用上一段时间后,还是能感觉到一些摩擦:

  • 写正文和处理图片要在两个页面之间来回切换;
  • PagesCMS 更像通用内容后台,不太像一个可以长时间待着的写作空间;
  • 手机上标题、工具栏、预览和发布操作不够舒服;
  • 网络短暂断开或应用被系统切走时,心里总会惦记稿子有没有保存;
  • PagesCMS、本地 Git 和新工具都可能改同一篇文章,需要有人在最后一步拦住误覆盖。

所以这次并不是再换一个 CMS,而是往前多走一步:给自己的博客做一个真正围绕「写作」设计的入口,同时保留原来的所有退路。

先把边界想清楚

动手之前,我先给这套工具定了几个不变的原则:

  1. GitHub 里的 Markdown 仍然是文章唯一的正式版本;
  2. PagesCMS 继续保留,数据表单和临时维护仍然可以用它;
  3. Hugo 和 Vercel 的构建发布方式不动;
  4. 图片继续放在 Cloudflare R2,不把仓库变成大文件仓库;
  5. 浏览器里的草稿要先保住,再考虑同步和发布;
  6. 任何不确定的转换和冲突,都不能悄悄覆盖原文。

最后形成的链路大致是这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
手机 / 平板 / 电脑
Hugo Writer PWA ── 本地自动保存与离线恢复
Cloudflare Worker ── 登录、签名与冲突检查
GitHub 文章仓库 ── Hugo / Vercel ── 个人网站
     PagesCMS

图片:浏览器压缩为 WebP ── 直传 R2 ── 写入公开链接

看起来多了一个新应用,但底层其实没有变复杂。新 PWA 只是站在浏览器和原有发布链路之间,把以前需要我手动完成的步骤接了起来。

核心实现

1. 先保存到眼前的设备

写作时最重要的不是马上把每个字传到云端,而是先保证它不会丢。

现在输入停止片刻后,文章就会自动保存到设备本地;每篇文章还会保留一组历史快照。哪怕网络断开、手机锁屏、浏览器被系统回收,重新打开后仍然可以回到刚才的位置继续写。

这也是我很喜欢「本地优先」这个思路的原因:网络负责同步,本机负责让人安心。在线时它当然是一套在线写作工具,但短暂离线并不会突然让编辑器失去意义。

2. 新文章舒服写,旧文章安全写

我原本很想让所有文章都进入富文本编辑器,但检查完站内 47 篇旧文后,很快就放弃了这种一刀切的想法。

旧文章里不只有普通 Markdown,还有 Hugo Shortcode、原始 HTML、脚注、表格、数学公式和一些历史字段。它们在网页上看起来没问题,却不一定能被富文本编辑器完整地来回转换。如果只追求界面好看,很可能打开一篇旧文再点一次保存,某些看不见的结构就被改坏了。

所以最终做成了两种模式:新文章和结构简单的正文使用富文本;遇到高风险语法时自动进入源码保护模式。源码模式仍然有工具栏、预览、图片插入和自动保存,只是不再擅自「整理」原文。

简单说就是:能舒服编辑的就舒服编辑,需要保持原样的就老老实实保持原样。

3. 图片不再是另一条工作流

以前的图片上传器已经能很好地完成裁剪、压缩和上传,但写文章时仍然要在两个工具之间复制链接。这次把图片流程直接接进了写作台:

  1. 从相册、相机或文件中选图;
  2. 在浏览器里预览尺寸和体积;
  3. 调整最长边与质量,并转成 WebP;
  4. 通过短时有效的上传地址直传 R2;
  5. 上传成功后,把正式图片链接插入正文。

图片文件不会先绕到 Worker 再转发,Worker 只负责临时签发一张「这张图可以上传到这里」的通行证。这样既减少中转,也不用把 R2 密钥交给浏览器。

如果上传中途失败,图片和任务会留在本地队列里等待重试;只要正文里还有未完成的临时图片,发布按钮就不会把一篇带着失效占位符的文章送上去。

4. 把敏感权限留在服务端

在线编辑器最不能偷懒的地方,是它手里握着文章仓库的写入权限。

现在登录、GitHub 仓库操作和 R2 上传签名都经过 Cloudflare Worker。浏览器里只保留短期登录状态,不保存 GitHub Token、R2 密钥或其他长期凭据。GitHub App 也只安装到博客仓库,只允许指定账号进入。

这样做的目的不是把登录流程搞复杂,而是把权限边界收紧:写作界面可以很轻,真正敏感的钥匙始终留在门后。

5. 发布前先确认没有人动过原文

保留 PagesCMS 以后,同一篇文章就可能从多个入口被编辑:Hugo Writer、PagesCMS、本地 Git,甚至以后别的自动化工具。

因此每次打开文章时,Writer 都会记下它在 GitHub 上的版本;保存或发布前再检查一次。如果远端文章已经变化,就停止覆盖并打开冲突处理,让我对照本机和远端内容,选择接受远端、保留本机,或者把两个版本都留下。

这一步平时几乎没有存在感,但它决定了 PagesCMS 能不能真正「保留」下来。多个入口可以共存,前提是最后写入的人不会假装自己是唯一入口。

6. 最后仍然回到 Markdown 和 Hugo

点击发布以后,Worker 写入的仍然是一份普通 Markdown 文件。GitHub 保存版本,Vercel 检测到仓库变化后运行 Hugo,最终生成静态页面。

也就是说,Hugo Writer 没有创造一套新的内容数据库,也没有把文章锁进某种专用格式。哪天不想用它了,仓库里的文章仍然可以被 PagesCMS、本地编辑器或任何文本工具继续打开。

对个人博客来说,这种「用的时候现代,离开时朴素」的感觉很重要。

一篇文章现在怎么发布

现在从一个念头到网站上的文章,流程变成了:

  1. 在手机或电脑打开 Hugo Writer,新建文章;
  2. 写标题和正文,内容自动保存到当前设备;
  3. 需要配图时直接选择相册或相机,完成压缩和上传;
  4. 中途切换应用、锁屏或断网,回来后继续写;
  5. 检查预览,选择保存草稿或正式发布;
  6. Worker 确认身份、文章版本和图片状态后写入 GitHub;
  7. Hugo 自动构建,文章出现在个人网站。

整个过程里不需要打开本地仓库,不需要手动处理图片地址,也不需要为了发布一段文字去理解背后的每一个服务。它们都还在,只是各自退回了应该待的位置。

最终效果

目前这套写作台已经在桌面浏览器和两台实体手机上走完了完整流程:安装 PWA、相册和相机选图、锁屏恢复、图片上传、保存草稿与正式发布都能正常工作。

界面也按真实使用过程反复调整过:手机上的长标题可以自然换行,常用操作贴近拇指区域;危险操作不再弹出生硬的浏览器确认框;PWA 有新版本时会明确提示更新;文章、图片和同步状态也尽量用普通语言说明,而不是丢下一串错误代码。

PagesCMS 没有被删除,Hugo 配置和原来的发布链路也没有被改写。新工具只是成为了日常写作的主入口,旧工具继续承担备用入口和结构化数据维护。

小结

回头看,这次折腾真正完成的并不是「又做了一个 Markdown 编辑器」,而是重新整理了从想写到发布之间的关系:

  • PWA 负责提供舒服、跨设备的写作界面;
  • 本地数据库负责自动保存、离线恢复和历史快照;
  • 浏览器负责在上传前处理图片;
  • Cloudflare Worker 负责守住登录、签名和权限边界;
  • R2 负责图片存储;
  • GitHub 负责文章版本;
  • Hugo 和 Vercel 负责把 Markdown 变成最终网站;
  • PagesCMS 作为并行入口继续保留。

它们没有被揉成一个庞大的系统,而是通过几条简单、明确的边界接在一起。平时写作时,我面对的只是一张安静的编辑页面;真的需要排查、迁移或回滚时,底下依然是熟悉的 Markdown、Git 和静态站点。

对我来说,这大概就是个人博客工具最理想的状态:既不会因为追求方便而失去掌控,也不会因为坚持掌控而让每次表达都变成一次部署工作。

参考资料

(2026-07-28@深圳)