我把 Touch Bar 变成了一个 AI 工作搭子状态灯。根据文末的 GitHub 链接也来试试吧。
它不是那种冷冰冰的 RUNNING / IDLE / DONE,而是这样:
思考时,它会显示:

需要授权时,它会显示:


跑命令时,它会显示:

改文件时,它会显示:

空闲时,它会进入一种非常合理的工作状态:

旁边还有一个 Codex 小宠物,在 Touch Bar 上慢慢走路。
还有一些其他状态显示:


为什么要做这个
Touch Bar 是一块很有意思、但越来越没人用的小屏幕。
而我用 Codex 跑代码的时候,恰好有一堆状态需要"用余光感知":
它在思考、在跑命令、在改文件,在等我点权限,还是刚刚收工?
这些都在 Codex 窗口里。但只要我切去浏览器或文档,就立刻断联。
那为什么不把这条闲着的小屏,变成 Codex 的"工作状态仪表带"?
整体方案
实现并不复杂,核心只有三件事:
- Codex hook 捕获运行事件;
- 一个 Node 脚本把事件写成状态 JSON;
- BetterTouchTool 每 1--2 秒读取 JSON,渲染到 Touch Bar。
架构大概是这样:
yaml
Codex Hook Event
|
v
codex-touchbar-hook.mjs
|
v
.state/codex-touchbar-status.json
|
v
codex-touchbar-read.mjs
|
v
BetterTouchTool Touch Bar Widget
状态文件里不会保存 prompt 或 assistant 正文,只保存一些用于展示的元信息:
json
{
"event": "PreToolUse",
"status": "TOOL",
"phase": "file_change",
"cwd": "/Users/me/project",
"toolName": "apply_patch",
"startedAt": 1760000000000,
"updatedAt": 1760000018000,
"lastMessage": "read.mjs +12 -3"
}
好处是:展示层和 Codex 运行层完全解耦。Touch Bar 想怎么渲染,只看同一个 JSON。
先聊聊 BetterTouchTool
在拆解实现之前,先简单介绍下 BetterTouchTool(下面简称 BTT),因为这个状态灯的"显示侧"完全建立在它之上。
BTT 是 Mac 上一个老牌的"输入设备改造工具",主要能改这些东西:
- 触控板手势;
- 鼠标手势;
- 键盘快捷键;
- 菜单栏小组件;
- 以及------这次的重点------Touch Bar。
它有一个非常关键的能力:Touch Bar 上可以放"会跑脚本的按钮"。
具体来说,你可以在 Touch Bar 上加一个 widget,让 BTT 每隔 N 秒重跑一段 AppleScript 或 Shell。这段脚本只要返回一段 JSON------里面带着文本、颜色、图标路径------BTT 就会照着 JSON 重新渲染这个按钮。
也就是说:
只要我能在 1 秒内吐出一段 JSON,Touch Bar 上就会出现一个对应状态的小胶囊。
这就是整个状态灯能跑起来的核心机制。Codex hook 负责"产生状态",BTT 负责"显示状态",中间靠一份 JSON 接力。
BTT 是付费软件,作者是德国独立开发者 Andreas Hegenberg,有 2 年许可和永久许可两种版本,价格几十到一百多人民币不等。在 Mac 玩家圈里属于"用过就回不去"的那类工具------它不重,但能改的东西特别多。
如果你只是想跟着这篇文章试一试,它也有 45 天免费试用,足够把 Touch Bar 折腾明白。
状态从哪来:Codex hooks
Codex 自带 hook,可以在这些事件里触发脚本:
SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / PermissionRequest / PreCompact / PostCompact / Stop
但我不想直接把这些"开发者术语"丢到 Touch Bar 上,于是给它们做了一层"人格化翻译":
rust
UserPromptSubmit -> 我想想...
PreToolUse + Bash -> 跑个命令
PreToolUse + apply_patch -> 改两笔
PermissionRequest -> 等你点头
Stop -> 收工啦
Idle -> 摸鱼中...
Error -> 有点卡住
比起 RUNNING,我更喜欢"我想想..."。
改了几行?Touch Bar 也能告诉你
这是我最喜欢的一块。
当 Codex 通过 apply_patch 改文件时,hook 能拿到 patch 原文。脚本就可以从里面解析出:改了哪个文件、加了几行、删了几行、是单文件还是多文件。
单文件时显示:
arduino
read.mjs +12 -3
多文件时显示:
3文件 +24 -6
你不用切回 Codex,也能大概感受到它刚刚动了多大的刀。
多槽位:把 Touch Bar 真正用起来
最初我只做了一个状态胶囊。
后来发现 Touch Bar 这么长一条屏,只显示一个小东西实在浪费,于是改成了多槽位:
css
main 主状态
timer 当前回合耗时
tool 当前工具
diff 增删行数
file 当前文件
pet 单独宠物
walk 横向走动宠物槽
每个槽位都是一个 BTT 小组件,它们读同一个状态文件,只是传不同的参数:
java
return do shell script ((quoted form of "/Applications/Codex.app/Contents/Resources/node") & " " & (quoted form of "/Users/me/agent-status/codex-touchbar-read.mjs") & " --slot main")
把 --slot main 换成 --slot diff、--slot file、--slot pet,就是另一个槽。
这种设计还有一个隐藏好处:非活跃槽位返回透明空白。
例如没有文件修改时,diff 槽位不会显示 +0 -0,而是直接消失。Touch Bar 不会越用越乱。
小宠物是怎么来的
Codex App 里本来就带桌面宠物的资源。
我从 App 包里翻出官方 spritesheet,裁成适合 Touch Bar 的 36×36 小帧,再按状态映射动作:
- 空闲:走路帧;
- 思考:电脑 / 思考帧;
- 工具执行:走动帧;
- 等权限:等待表情;
- 完成:开心表情;
- 错误:异常表情。
BTT 每 1--2 秒重跑一次脚本,只要每次返回不同的 icon_path,Touch Bar 上就能跑出一段很轻量的动画。
返回给 BTT 的 JSON 大概长这样:
json
{
"text": "摸鱼中.",
"icon_path": "/Users/me/agent-status/assets/pet/frames/codex-pet-tool-1.png",
"background_color": "6,182,212,255",
"font_color": "255,255,255,255",
"font_size": 14
}
视觉规则
为了不把 Touch Bar 做成一块迷你监控大屏,我给立了几条规矩:
- 思考中:蓝色;
- 工具执行:橙色;
- 等审批:紫色;
- 成功结束:绿色;
- 摸鱼:青绿色;
- 错误:红色;
- 文案尽量 ≤ 12 个中文字;
- 副槽位没内容就透明;
- 只有主槽位保留宠物图标。
实际用下来
这个东西不会让 Codex 写代码更快。但它会让整个过程更"可感知"。
以前 Codex 一忙起来,状态全藏在窗口里。现在我扫一眼 Touch Bar 就知道------它在想、在跑、在改、在等我点头,还是刚刚收工。
更妙的是,"摸鱼中"的小宠物会让空闲状态也变得不那么生硬。
后续还能玩什么
这个小项目后面还可以继续扩展:
- 点击状态灯打开 Codex App;
- 长按弹出最近状态 JSON;
- 不同项目显示不同颜色;
- 根据当前模型显示不同宠物;
- 出错时点一下直接跳到日志;
- walk 槽位让宠物在 Touch Bar 上来回散步;
- 把任务耗时、文件数、审批状态做成更完整的仪表带。
Touch Bar 已经是一个时代的尾巴。
但拿来做 AI 状态栏,意外地合适。
最后
这个功能本质上很简单:hook 写状态,BTT 读状态。
但真正有意思的,是它把一个后台运行的 AI 编程助手,变成了一个可以被余光感知的小伙伴。
它会思考,会跑命令,会改文件,会等你点头。
没事的时候,它会在 Touch Bar 上摸鱼。
我觉得这就够好了。 你可以根据下面的项目地址来尝试。
项目地址:
公众号:DailyHappy 一位后端写码师,一位黑暗料理制造者。