2026-10-21 · Codex 教程 ⑩ · 技巧
把温度搬到菜单栏
让它一直看得见
最初,温度条浮在 Codex 窗口右上角。窗口一遮住,数字就失去意义; Codex 一更新,原来的临时浮层还可能失效。这次把它搬进 macOS 菜单栏, 留下一个始终可见、只读、离线的小工具。
现在菜单栏上的读数很克制:CPU 48° S 40°。
CPU 是 Apple Silicon 的综合温度,S 是外接存储盘;
左边的小圆点在正常、偏热、高温时分别变绿、橙、红。点开后才有完整小数、
更新时间和手动刷新,不把菜单栏挤成一块仪表盘。
窗口内是好原型,不是最终位置
第一版把温度条挂在 Codex 的界面内,开发快、样式自由,也能直接拖动。 但它把显示层绑在 Codex 窗口里:Codex 关闭或界面结构变化, 温度条都会跟着受影响。监控工具不该依赖被监控的窗口。
| 层级 | 实现 | 职责 |
|---|---|---|
| 数据源 | macmon、smartctl | 只读本机传感器 |
| 采样 | 后台进程 + 定时器 | CPU 每 3 秒、外接盘每 15 秒更新 |
| 显示 | AppKit NSStatusItem | 菜单栏读数、详情和阈值颜色 |
| 生命周期 | LaunchAgent | 登录后启动,异常退出后恢复 |
数据仍然来自本机,但菜单栏程序不再依赖 Codex 窗口,也不修改应用文件。 之后 Codex 怎样升级,都与这枚温度读数没有直接依赖。
先定义两个真正有用的数
可用的数据不少,菜单栏却只适合放两项。这里选择 CPU/SoC 综合温度和外接盘温度: 前者能看出处理器是否持续高负载,后者能发现外置 SSD 长时间读写后的积热。 两项数据先在本机确认:
/opt/homebrew/bin/macmon pipe -s 1 -i 100
/usr/sbin/diskutil info /Volumes/1TB
/opt/homebrew/bin/smartctl -a /dev/disk6
macmon 最后一行 JSON 里的 temp.cpu_temp_avg 是 CPU 综合值;
smartctl 的 Temperature 行给出外接盘温度。磁盘编号不要永远写死:
先从 diskutil 输出里找 APFS Physical Store 或
Part of Whole,再拼成 /dev/diskN。盘换接口、重新编号时,
热插拔才不会继续读旧设备。
状态阈值沿用这次实机的判断:低于 68°C 为正常,68–81°C 为偏热,82°C 起标红。 颜色只是辅助,菜单里同时写着“正常”“偏热”或“较高”,不让色觉差异成为唯一信息来源。
菜单栏只做三件事
原生状态栏项目由 NSStatusItem 创建。标题使用等宽数字,温度变化时不会左右抖动;
小圆点负责快速扫一眼状态;点击后的菜单才显示两位小数、更新时间、刷新和退出。
程序启动时把激活策略设为 .accessory,所以只在菜单栏出现,不占 Dock。
let item = NSStatusBar.system.statusItem(
withLength: NSStatusItem.variableLength
)
item.button?.attributedTitle = NSAttributedString(
string: "CPU 48° S 40°",
attributes: [
.font: NSFont.monospacedDigitSystemFont(
ofSize: 11.5, weight: .medium
)
]
)
item.button?.toolTip = "CPU / SoC 47.6 °C,S · ZHITAI 40.0 °C"
读数更新时只改标题、圆点和提示文字,不做动画,也不弹出通知。 温度是持续变化的环境信息,安静地待在那里比每一次变化都抢注意力更有用。
采样要短,失败也要能收尾
macmon 和 smartctl 都是外部进程,不能直接卡在主线程上跑。
采样放到后台队列,分别设置 6 秒和 8 秒超时;同一类采样尚未结束时直接跳过下一轮,
防止慢命令越积越多。任一项失败时只把该数字显示成 --°,
另一项仍可继续工作。
显示器休眠、外接盘拔掉都属于正常运行状态,不需要报警弹窗。温度工具的价值是提供上下文, 不是替用户做故障判断。真要延长排查,菜单里的更新时间和“不可用”状态已经够用。
用 LaunchAgent 管生命周期
小工具编译成一个本机可执行文件后,由 LaunchAgent 在登录时启动。
RunAtLoad 保证登录后出现,SuccessfulExit=false
让异常退出可以被重新拉起;用户在菜单里主动选择退出则算正常结束,不会立刻复活。
/usr/bin/xcrun swiftc -O TemperatureMenu.swift \
-o CodexTemperatureMenu
launchctl bootstrap "gui/$(id -u)" \
"$HOME/Library/LaunchAgents/com.dola.codex-temperature.plist"
验收不看“编译成功”,而看四件事:状态栏项目真的出现、读数来自真实传感器、 点击菜单能刷新、登录启动项处于运行状态。这次实机读到 CPU 48°、外接盘 40°,菜单详情为 47.6°C 和 40.0°C,进程由 LaunchAgent 保持运行。
迁移时真正踩到的坑
一、不要靠重载主界面清理旧浮层。 旧温度条属于窗口内的临时层,直接重载 Codex 主界面可能得到一个空白窗口, 还会连带丢失当前界面状态。先停用旧浮层,再用应用自身的重启流程清理更稳。
二、硬盘编号不会永远不变。
第一版默认读取 /dev/disk6,短期能用,换接口后就会失效。
现在先从已挂载卷定位物理盘,再调用 smartctl。
三、短标签比完整名称重要。
菜单栏空间有限,外接盘没有再写“外接盘”四个字,而是用大写 S;
点开菜单后才写清 S · ZHITAI。信息没有丢,只是分层了。
结论
这次最重要的变化不是“多了一个温度条”,而是把数据源、采样、显示和生命周期拆开。 菜单栏负责一直可见,后台采样负责稳定更新,LaunchAgent 负责启动和恢复; 它们都不再依赖 Codex 窗口是否开着。
一个监控组件最好的状态,是平时安静、需要时立刻能读到, 而且被监控的应用更新后,它仍然活着。
实测环境:Mac mini M2、macOS 27、macmon 与
smartmontools 均在本机运行;无网络请求、无模型调用、无 Codex 应用文件修改。
上一篇 ⑨ 什么该自己跑,什么必须停一下,
继续读 AI 能生成界面,但质感藏在交互里,
或回到 全部笔记。
回到首页