AIGC标识 开源 MacPilot:一个用 Swift 原生实现的 macOS 菜单栏效率工具箱

最近把自己做的一款 macOS 菜单栏应用 MacPilot 整理开源了。它用 Swift 6 + SwiftUI + SwiftPM 构建,只面向 macOS 14+,不携带任何跨平台运行时,经过 Apple 公证后分发。这篇文章介绍它能做什么、以及几个我觉得值得聊的实现细节。

它能做什么

MacPilot 的出发点很简单:邮件、聊天、社交、浏览器这些应用,在你不用它们的时候不应该继续占据你的注意力。围绕这个出发点,它慢慢长成了一个小工具箱,目前包含:

1. 退出/启动规则(核心功能)
为每个应用单独配置「闲置一段时间后隐藏 / 关闭窗口但保留后台进程 / 退出」,也可以设置「被隐藏一段时间后退出」;还能为每个应用配置登录后的延迟启动和启动行为。规则列表会显示下一次触发的倒计时。

2. BLE 蓝牙靠近解锁
iPhone、Apple Watch 或任何使用固定 MAC 地址的 BLE 设备靠近时自动解锁 Mac,离开时自动锁屏。支持双设备、双阈值(解锁 RSSI / 锁定 RSSI)、锁定延迟与信号超时,登录密码只存钥匙串。

3. 输入法自动化
按应用或浏览器网站自动切换输入法,支持屏幕提示、菜单栏循环切换、按应用强制英文标点、按应用切换功能键/媒体键模式。

4. 剪贴板历史
⌘⇧V 呼出可搜索面板,支持固定、去重、数量上限,重启后保留;文本、图片、URL 都能存。

5. 窗口切换器
⌥Tab 跨应用切换窗口,带窗口标题与缩略图,可合并同一应用的多个窗口,可包含最小化/隐藏的窗口。

6. 截图与贴图
智能元素截图、区域框选、窗口/全屏捕获、标注(箭头、模糊、聚光、序号、水印等)、OCR 文字识别、滚动长截图、贴图(钉在桌面上)、直传 GitHub/Gitee 图床。

7. 画中画
ScreenCaptureKit 逐窗口捕获,生成可以跨全屏 Space 的悬浮面板,支持区域放大、滚轮缩放、真实 Now Playing 媒体控制;对 Chromium/Electron 与 Firefox 等应用提供离屏渲染修复方案。

8. 平滑滚动
把鼠标滚轮事件改写成插值后的连续滚动帧,让普通滚轮鼠标的手感接近触控板,支持按应用排除和方向反转。

9. Finder 右键菜单
复制路径、用指定应用打开、新建文件、常用目录、直接删除、AirDrop 等快捷操作,全部可配置排序。

10. 存储压缩
利用 APFS/HFS+ 的文件系统透明压缩,对日志、JSON、Markdown 等稳定文本文件节省磁盘空间,原子替换前做 SHA-256 校验,保留全部时间戳与元数据,可一键恢复。

11. 录屏
框选/全屏/应用窗口/纯音频/iOS 设备录制,支持 H.264/HEVC/HDR、系统声音与麦克风混音、鼠标高亮与放大镜、倒计时、演示者叠加(Presenter Overlay)、8 个可自定义快捷键。

几个实现细节

以下面向对 macOS 开发感兴趣的读者。

1. BLE 靠近解锁:RSSI 双阈值 + 滑动均值

BLE 信号强度(RSSI)抖动很大,直接用单阈值会导致锁了又解、解了又锁。MacPilot 的做法是取最近 5 次 RSSI 的滑动均值,配合解锁 RSSI锁定 RSSI 两个独立阈值(类似施密特触发器),再加上「锁定延迟」和「无信号超时」两个计时器兜底。默认主动连接设备读取 RSSI(更稳定),也提供被动扫描模式,避免和其他蓝牙外设互相干扰。针对办公室里几十台设备同时广播的场景,设备列表做了刷新合并(最多 200ms 一次)、候选上限和稳定排序,避免 UI 卡顿和列表跳动。

2. 录屏引擎:ScreenCaptureKit 全家桶

录屏模块由 15 个文件组成,规划(码率预算、滤镜构建、窗口选择)与引擎本体分离,纯函数部分全部有 Swift Testing 覆盖。码率按分辨率、帧率、编码器系数动态预算;支持 HDR(BT.2020 PQ、HEVC Main10)和 HEVC With Alpha;录制完成后用 remux 把麦克风轨混入系统音轨。鼠标高亮、放大镜、倒计时、悬浮控制条这些辅助件都以独立窗口实现,并会在录制流里排除自身窗口。

3. 平滑滚动:CGEventTap 事件改写

通过 CGEventTap 捕获真实滚轮事件,跳过触控板和已平滑的事件,按原始目标进程用 CGEventPostToPid 直投插值帧,包含最小步长归一化、持续时间曲线、滚动/动量相位状态机(可模拟触控板相位)。相位模拟先发送 TrackingBegin 再进入惯性阶段——不少应用会忽略缺少开始阶段的滚动序列。整个输入路径只做集合查询,PID 与 Bundle ID 的关联在应用生命周期里缓存。

4. FinderSync 扩展的 App Group 坑

Finder 右键菜单是沙盒扩展,主应用是非沙盒的,两者通过 App Group UserDefaultsDistributedNotificationCenter 通信。这里踩过一个启动即崩溃的坑:非沙盒应用即使声明了 App Group entitlement,也无法向 App Group 容器写文件(TCC 直接拒绝),最初把 SwiftData 的 SQLite 放在 App Group 容器里导致 fatalError。修复方式是先检查目录真实可写性,不可写就回退到 Application Support 下的自有路径。教训:判断权限必须用真实签名进程验证,从终端直接跑二进制继承的是终端的 TCC 身份。

5. 分发与更新的安全链路

这类依赖辅助功能、蓝牙、屏幕录制的工具不适合上 App Store(强制沙盒),所以采用 Developer ID 公证分发。CI 上完成签名 → notarytool 公证 → stapler 装订 → GitHub Release 全流程;应用内更新除了 SHA-256、codesign、Gatekeeper 校验外,还额外校验更新包的 designated requirement 与运行中应用一致,避免更新后辅助功能/屏幕录制授权全部失效。

6. 零第三方依赖

整个项目不引入任何第三方 Swift 包:配置统一写进一个带版本号的 config.json 并自动迁移,键位映射、偏好存储等功能全部基于系统 API 独立实现。好处是构建可复现、体积小,也避免了供应链风险;代价自然是部分轮子要自己造。

性能与隐私

  • 剪贴板大内容(>64KB、图片)落盘存储,内存里只留索引;
  • 窗口缩略图缩放到 256×160、最多预取 30 个、LRU 缓存;
  • 关闭某项功能后,对应的全部事件监听、定时器、后台任务都会被释放;
  • 配置全部保存在本机,登录密码只在钥匙串。

下载与源码

项目还在快速迭代中,欢迎试用、提 issue 和 PR。如果你也在写 macOS 原生应用,上面的实现细节文章里对应的坑应该能帮你省一点时间。

posted @ 2026-09-05 09:38  misswell  阅读(4)  评论(0)    收藏  举报