作品

Focus Keeper

macOS 菜单栏专注计时工具:专注不能暂停、没有提前完成入口,历史里只显示完成的专注。

为什么做

用过不少番茄钟,它们都对使用者太客气了:随时可以暂停,随时可以「提前完成」,一次专注被切碎成几段也照样记进统计。这些逃生口让计时器变成了摆设——反正随时能停,「开始专注」就不构成任何承诺。

我想要一个不客气的版本:一旦开始,这段时间就交出去了,唯一的路是等它自然结束,或者承认这次专注失败、明确取消。Focus Keeper 的名字里写了它的定位:专注力的守护者。

它是什么

Focus Keeper 是一个原生 macOS 菜单栏应用。选定时长开始专注,计时期间没有暂停按钮,也没有提前完成入口;只能等计时走完,或明确取消这次会话。历史记录只显示完成的专注——提前取消的不会出现,统计里没有「打了折扣的专注」这个类别。

陪伴计时的是一个可替换的守护形象,目前有猫咪与黑洞两种。界面语言支持中英文,跟随系统,没有语言选择项——系统已经知道你用什么语言,应用不必再问一遍。

技术要点与设计决策

产品规则是这个项目的核心,代码反而是次要的。「不能暂停、不能提前完成、历史只记完成」这三条是刻意反主流的强约束:工具的价值恰恰在于它不提供余地。设定 25 分钟就是 25 分钟,中途放弃的成本(这次专注不被记录)清晰可见。约束在产品层面写死,而不是做成一个可开关的「严格模式」——可以关闭的约束等于没有约束。

守护形象的人设有明确边界:它是「守护者」不是「侵入者」,安静陪伴,不打断、不弹窗、不催促。形象做成了可替换的抽象,文案不与任何具体形象绑定。黑洞形象是从一个开源 GLSL shader 移植到 Metal 的实现,动效在菜单栏应用的功耗预算内运行。

这个项目由早年的一个 Electron 番茄钟演进而来,迁移到原生的过程留下一个值得记录的坑:迁移后菜单栏图标的位置每次启动都会跳回默认位置。根因是 NSStatusItemautosaveName 断裂——macOS 用这个名字持久化图标位置,Electron 时代与原生版本的标识不一致,系统就当成了新图标。修复是在创建 NSStatusItem 后立刻设回旧标识,并且退出时不调用 removeStatusItem(调用会把已保存的位置一并清掉)。对所有菜单栏应用这都是通用要求:autosaveName 要跨版本、跨迁移稳定。

工程上还有一条本地分发的经验:构建脚本固定使用稳定的开发者证书签名,不做 ad-hoc 回退。ad-hoc 签名的应用每次重装都会被 macOS 当作新应用,辅助功能等 TCC 授权全部失效,对常驻工具是无法接受的体验。签不上就直接构建失败,不留降级路径。

当前状态

个人日常使用中,未开源。由 Electron 原型演进为原生应用的路径,和我另外几个 macOS 工具一致:脚本或跨端方案验证想法,确认长期日用后用原生重写。