
26 小时四个版本:语言设置的管道工程与一次"桥接发布"
GeekBye v1.7.6 到 v1.8.1:一个从未离开过笔记本的语言设置、一个显示你真实键位的 Cmd+/ 悬浮层、一个零代码变更的版本——以及迁移自动更新源时的先有鸡还是先有蛋。
2026 年 2 月的一个周四上午到周五下午之间,GeekBye 发布了四个版本:v1.7.6、v1.7.7、v1.8.0 和 v1.8.1——16 commits,第一个 tag 到最后一个 tag 之间大约 26 小时。单看哪一个都上不了头条。但合在一起,它们是"小版本周"真实内容的一幅肖像:一个功能终于走完了它的旅程、一个提升体验的悬浮层、一个不含任何代码的版本号,以及一段真正有教学意义的发布工程。
那个从未离开过笔记本的语言
GeekBye 从 2025 年 9 月起就有转录语言设置。它是能用的——在本地。选西班牙语,端上的语音转文字管线就尽职尽责地转写西班牙语。但在 v1.7.5 的代码库里 grep 这个设置,你只会在三个地方找到它:音频处理器、Settings UI 和设置 hook。API 客户端里没有。会话追踪器里也没有。整整四个月,每一个离开这台机器的请求都对语言一无所知:后端的会话记录不知道自己记录的是什么语言,AI 聊天收到的是西班牙语转录稿,却没有任何提示告诉它这是西班牙语。
v1.7.6 是这个设置走完旅程的版本——两个 commits,一天。音频会话启动的 payload 现在带上了 transcriptLanguage,AI 流式聊天请求也加上了同一个字段,两者都穿过一个小 helper:它读取设置,并在值为 auto 或未设置时刻意返回 undefined,这样旧后端会优雅降级,而不是被一个它不认识的字段噎住。diff 里还藏着一个不声不响的附赠修复:为了传递语言,会话启动必须挪到语言解析之后——照我们的解读,这也顺带关掉了一条错误路径:会话可能已经启动(并计入配额),下一刻语言解析失败,它就成了孤儿。
这个教训可以推广:一个设置有了 UI、有了效果,并不等于发布完成。只有当它抵达每一个应该知道它的消费者时,才算发布完成。我们的设置在网络边界卡了四个月,没有人注意到,因为本地行为——看得见的那部分行为——始终是正确的。(郑重说明,这只是语言处理粗糙的第一步;教会 AI 真正用对话的语言回答,是这个故事很久之后的章节。)
Cmd+/ ——一个不对你的键位撒谎的悬浮层
v1.8.0 面向用户的功能是一个绑在 Cmd+/ 上的键盘快捷键悬浮层——193 行新组件,外加一个值得偷师的设计决策:它不渲染一张硬编码的速查表。它获取实时的快捷键配置并渲染 customKeys || defaultKeys,所以改过键位的用户看到的是自己的键位。一个对改过键位的人展示默认值的快捷键悬浮层不是文档;它是排版精美的错误信息。
其余都是些小的正确性打磨:悬浮层根据屏幕哪一侧有空间,把自己放在应用卡片的左边或右边;按 Escape、再按一次 Cmd+/、或点击外部都能关掉它。Cmd+/ 本身也加入了和其他所有快捷键相同的可重映射预设系统——这很重要,因为 Cmd+/ 在大多数编辑器里是"切换注释",一个给开发者用的工具,不应该霸占一个你还挪不走的编辑器按键。
桥接发布
有意思的工程发生在没人会多看一眼的版本号里。v1.8.0 还把项目的 releases 迁到了一个新的 GitHub 组织——表面上是 org 层面的收拾屋子,但桌面自动更新有一个特性:更新源的 URL 是烤进二进制里的。每一个 v1.8.0 安装都会轮询新组织的 releases。而世上已装机的每一份——全是 v1.7.7 或更老——会永远轮询旧的那个,除非那里出现点什么。
这就是先有鸡还是先有蛋:下一个版本只发到新仓库,整个存量客户端群就永远看不到它。v1.8.0 tag 之后 16 分钟,发布脚本里落地了一个新标志:--publish-owner,它在构建之后改写 electron-builder 上传产物的位置——用代码注释的原话说,明确做到"不影响编译后的应用代码"。全部技巧就这一个,而且这正是对的接缝:二进制里烤死的更新源,和产物的上传目的地,是两个不同的决定,而迁移需要它们解耦一个版本的时间。把 v1.8.1 构建成指向新源,再把它上传到旧仓库;旧客户端在那里看到更新、装上它,醒来之后轮询的就是新组织了。commit message 把这叫作"桥接发布"(bridge release),这名字起得对。v1.8.1 在 v1.8.0 之后 2 小时 20 分钟打上了标签。(至于这个项目为什么对自动更新怀着近乎恐惧的敬意,起源故事见那次让我们的应用杀不死的自动更新。)
那 v1.7.7 呢?一个 commit。版本号提升本身。没有修复、没有回滚、历史里没有任何解释——我们最合理的解读是一次需要重跑的 publish,我们宁可承认这一点,也不想编个故事。有时一个版本号之所以存在,是因为发布软件是一个真实世界的过程,会有真实世界的磕绊,而不是源码树的纯函数。至于 1.7 为什么变成了 1.8:没有哪个 commit 说明过,但更新源迁移才是有意义的兼容性边界——每一个 1.8.x 二进制看的组织,和每一个 1.7.x 二进制都不一样——这比任何功能都更配得上一次 minor 号提升。
这一周教会我们的三件事
- 把一个设置追踪到它的每一个消费者。 语言偏好"完成"了四个月,后端却一直蒙在鼓里。恰恰是本地可见行为的正确,让这个缺口活了下来——审计的问题是"还有谁应该知道这个值?",并且要在每一道边界上问一遍。
- 渲染实时配置,而不是文档。 快捷键悬浮层读取的是快捷键系统所用的同一个事实来源。任何别的做法,都会在用户第一次自定义时开始跑偏。
- 更新源迁移需要一个同时活在两个世界里的版本。 二进制里是新源,上传去旧仓库。如果你的发布工具没法把这两个目标分开,就在需要之前把标志加上——我们的标志在被需要之后 16 分钟才到,勉强也算及时。
v1 故事的上一章,见登录页本身就是 Demo(v1.7.5);整段弧线则见把软件打磨到完美地发布出去的解剖学。