因为喜欢开源,我用 Halo 写了一个插件并发布到了应用市场

我特别偏爱开源的东西。我的博客跑在开源的 Halo 上,我的笔记用开源的 Obsidian,我平时写的代码也总喜欢把能开源的放出去。

最近我做了一件让这份偏爱落地成真的事——因为晚上写博客觉得后台太刺眼,我干脆自己写了一个「深色模式」插件给 Halo,一路从自用打磨到上架应用市场。这篇文章不讲多高深的技术,只想聊聊这趟旅程本身。


一、先从那句「情有独钟」说起

我确实对开源文化情有独钟,它吸引我的地方主要有三点:

  1. 透明的自由:代码摆在那里,任何人能看、能改、能学习。遇到问题不用干等官方,自己能钻进去查。
  2. 协作的温暖:一个项目背后往往是世界各地的人一起维护,你用的每一个好东西,背后都有很多双志愿的手。
  3. 回流的善意:开源不是单向索取——你用了别人的,也把你自己好的东西放回去,形成一种良性循环。

对我来说,开源不只是一个技术概念,更像一种「我为人人、人人为我」的生活方式。

我每天都在享受这种生活方式:博客是用 Halo(开源 CMS)跑的,笔记是用 Obsidian(开源笔记软件)记的,连我写的个人代码也尽量开源。用得多了,我一直在想——我是不是也应该为这个生态做点什么?

这个机会,在一个普通的深夜到来了。


二、痛点:晚上写博客,后台太亮了

我写博客的习惯是晚上窝在电脑前。可每次打开 Halo 的后台管理面板,面对的都是大片刺眼的白色背景,在一两个小时的写作里眼睛越来越酸。

我很自然地想:要是有深色模式就好了。

然后我停住了——等等,Halo 是开源的。如果没有,我是不是可以……自己造一个?

带着这个念头,我上网仔仔细细搜了一圈,把应用市场和开源生态翻了个遍,确认当时确实没有现成的深色模式方案。于是那个「为开源做点什么」的念头,和「我自己也想要」的诉求撞在了一起。

我决定动手写一个 Halo 深色模式插件。


三、从零到一:把「想要」变成「实现」

做一个 Halo 插件,比我想象的要有意思,因为它天然是「前后端」分离的:

  • 后端:用 Java 写插件骨架(Gradle 构建),负责让 Halo 认识这个插件、露出设置入口;
  • 前端:用 Vue 3 + TypeScript 写设置界面,提供「浅色 / 深色 / 跟随系统」三种模式选择,并把用户偏好记住,刷新不丢失;
  • 核心引擎:内置了开源的 Dark Reader——它是一个通用的暗色引擎,会自动分析页面 CSS 和 DOM,把页面变暗,连第三方插件动态渲染的内容也能覆盖。

这里我想多说几句「核心引擎」是怎么来的,因为它最能体现开源的魅力,也最能说明我的开发方式。

做深色模式,最难的部分其实是「如何把任意页面都能干净地变暗」——这涉及对 CSS、DOM 的深层分析,靠一点点手写几乎不可能做到完善。我当然可以偷懒让 AI 从零给我编一套,但我没有。我选择自己上 GitHub,找到了 Dark Reader 这个成熟的开源项目,把它的构建产物集成进来、仔细对照它的源码理解它的思路。

可以说,这个插件的「灵魂」来自开源的 Dark Reader——我站在了巨人的肩膀上,而不是重新造轮子。也正因为如此,我在上架时把 Dark Reader 的 MIT 许可、源码链接都如实标注清楚——用别人的开源,也要尊重别人的开源。

至于开发过程,我也确实用了 Claude Code、Codex 这些 AI 工具来辅助,比如帮我更快地搭框架、查 API、改小 bug。但我不让 AI 全盘代劳——核心思路是我自己定的,Dark Reader 是我自己找到和借鉴的,每一步我都会自己读懂、自己确认。 AI 是我的得力助手,但方向盘一直在我自己手里。

第一版很快就跑起来了。当我在自己那个刺眼的后台里按下「深色」、看到眼前瞬间暗下来、眼睛舒服多了的时候,那种「我自己造了个想要的东西」的成就感,真的很上头。


四、从自用到开源:打磨的过程

自用版本能用,但离「给别人用」还有距离。我给自己定了个目标:把它打磨到可以公开、让任何 Halo 用户都能装上用。 于是后面是一段持续打磨的过程:

  • 三种模式 + 记忆:浅色、深色、跟随系统——跟随系统是很多夜猫子的刚需,而且选择要记住,不能刷新就丢;
  • 兼容第三方页面:用 Dark Reader 的持续监听,让其他插件动态渲染的内容也能自动变暗,而不是只处理自己那几个页面;
  • 设置入口规范:把设置项放进 Halo 官方的「外观」分组,跟主题、菜单、插件放在一起,符合用户习惯;
  • 界面风格对齐:模式选择从自己设计的大卡片,改成 Halo 官方的列表组件,跟官方后台风格完全统一。

除了功能,我还在「工程质量」上下了不少功夫:

  • 写了一份 CHANGELOG,每次改动都记下来,作为发布说明的唯一来源;
  • 配了 CI 自动发布,代码一推送就自动构建、自动出 Release;
  • 加了依赖安全审计,升级有漏洞的依赖,确保 pnpm audit 清零;
  • 后续还按应用市场的要求,规范了插件包名(详见下一节)。

这些看起来繁琐的「软件工程琐事」,其实恰恰是让一个「随手写的小工具」变成一个「可以放心交给陌生人用的产品」的分水岭。版本也从最初的 1.0 一路迭代到了 1.1.3。


五、上架:让全世界的 Halo 用户都能用上

当我把它打磨到稳定、文档写全、截图配好之后,我主动向 Halo 官方应用市场提交了上架申请

申请不是点一下「发布」就完事——Halo 的插件上架有一套审核流程,官方会审查插件的代码质量、命名空间、安全性和文档规范。我记得当时也按要求做了几处调整,比如把插件包名从通用的命名空间迁移成属于我自己的 top.liuhangyv.darkmode,才顺利通过审查。

审核通过的那一刻,我的插件出现在了 Halo 的应用商店里——任何人,不管认不认识我,都能一键安装,给自己的后台换上深色模式。

截图挂在 Halo 官方静态资源上,界面就是那些我从自己需求里一点点做出来的功能:

  • 插件信息页
  • 模式选择界面
  • 文章写作时的深色效果
  • 仪表盘、主题页的暗色展示

一个多月前它还只是我脑子里「要是能深色就好了」的一句抱怨,现在它成了可以为所有 Halo 用户服务的开源插件。这种感觉,很难用语言形容。


六、这趟旅程教会我的:开源的互惠

回头看我为什么「情有独钟」,这趟插件之旅给了我一个特别具象的答案——开源是互惠的

我用的 Halo 是开源的,所以我才能深入它、给它写插件;我用的 Dark Reader 是开源的,所以我才能站在它的肩膀上快速实现;而我把自己的插件也开源、上架,又为这个生态添了一块砖,让后面的人也能按需取用。

每个人既是「消费者」,也有可能成为「贡献者」。今天你享受的每一份开源便利,都曾有人把「我想要」变成了「我做了,送给你」。

写到最后,我想说:多去拥抱开源吧。 你不需要成为什么大牛——哪怕只是从「用了某个开源软件,觉得体验不错,去它的文档里发现一个错别字、提一个 issue」开始,就已经在回馈这个可爱的生态了。说不定哪天,你也会像我一样,从一句「要是……就好了」,做到「我做了,给大家用」。


插件已在 Halo 应用市场上架:Halo 深色模式top.liuhangyv.darkmode),GitHub 开源:github.com/LHY0125/halo-dark-mode-plugin

感谢开源,让我这个普通大学生,也能为全世界的 Halo 用户添一点光(或者说,暗一点光 😄)。