1.8 之后的三个小版本,起因都是用户的一句话:这个按钮只能居中,是不是有点浪费;系统里的预览、访达长按之后怎么没反应。这一篇把它们放在一起写,重点是每次动手前要先看清楚现在是什么情况。
开发细节补充:这篇记录放在 DockTouchBar 的产品日记里,不是为了把一个功能包装成故事,而是把“13 · 1.9 到 1.10:居中变最大化,长按“关不掉”也要有反馈”放回真实项目推进中看。它要同时回答三件事:用户为什么需要这个点,开发时哪些边界必须先定住,以及这个选择会怎样影响上架、推广和后续课程复盘。平台口径是 macOS / Touch Bar / Swift / AppKit,当前公开状态是 已公证 DMG / 源码可见 / 本地优先,所以文案不能脱离真实发布进度。
对应的 docs 线索主要来自 项目开发记录、私有接口可行性验证、跨桌面切换与手势实验、签名、公证与发布检查记录。公开页面不会照搬内部工作记录,而是把可公开、可学习、不会泄露私密路径和账号信息的事实整理出来。DockTouchBar 的开发记录从一次不满出发:现成的 Touch Bar Dock 工具功能太杂、用久了不稳定,于是把范围收窄到只做 Dock,并把稳定放在第一位。 动手写产品之前先做了私有接口的可行性验证,发现旧方法名已被系统移除,也发现 isVisible 会说谎,这两条直接决定了后面的技术选型。 跨桌面切换、双击隐藏(原计划是最小化,被台前调度挡住)、长按退出,每一个都有实测表格和被否掉的方案,而不是一次写对。 发布链路记录了权限绑定签名、公证凭据、二进制里的本机路径这几个真实踩过的坑,以及为此重新编译并重新公证的过程。 连点失效的排查记录了“先复现、分层找根因、从固定等待改成看结果、再加上持续纠正”的全过程,也记录了测试本身踩的坑:键鼠空闲判断、Touch Bar 触摸看不见、台前调度窗口干扰判据。 1.5 记录了一次新机制的副作用:双击隐藏被“盯着结果并纠正”当成异常拉回来,修法是让隐藏也成为一次新点击并在隐藏后核对结果;这次没有拿到自动化数据,只有用户手动确认。 1.6 记录了亮度调到 0 时的死循环和右端齿轮兜底:暂停与自动恢复的几种情况、被否掉的“调到 0 自动让位”(需要轮询亮度,会破坏空闲唤醒 0 次),以及哪些只能由用户在真机上验证。 1.7 记录了齿轮改成小眼睛、临时隐藏时长可选,以及菜单的重新命名和分组(功能不变,只减少误解)。 1.9 到 1.10 记录了居中按钮变成居中 / 最大化切换(先判断窗口现在的样子)、提示结尾改成转场式风暴、长按按窗口状态决定关窗口还是退出、被确认框拦住时切过去,以及新增的权限菜单。 1.24 记录了 Vibe 并入 DockTouchBar:先查出 vibecoding 是 main 的超集,再还原身份、合入三个外部 PR,以及数据迁移放在监听之前才不会删掉 socket 的坑。 1.8 记录了长按提示从一条红色进度条到像素画四季的几次推翻重来,右侧按钮换成咖啡杯,以及哪些效果只能在真实窗口里验证、哪些只能由用户在真机上确认。
从产品功能看,DockTouchBar 关联的能力包括:Touch Bar 上常驻显示 Dock:访达、固定的 App、其他正在运行的 App,顺序和系统 Dock 一致、单击切换(窗口在别的桌面时自动切过去),双击隐藏,长按退出;长按时 Touch Bar 右边缘会显示“正在关闭…”和倒计时,手指不会挡住,背景是像素画的春夏秋冬四季小场景,一个小角色沿着进度条往前跑,跑到头就关闭并扫过一阵花瓣或雪;长按会先看窗口:在最前面且有多个窗口只关当前窗口,访达这种退不了的会隐藏,App 在等你回答(要保存吗之类的确认框)或没关掉时会自动切过去并提示,每次长按都有反馈、睡眠唤醒、锁屏解锁、控制条进程重启之后自动恢复、图标位置可选靠左、居中、靠右(图标放不下时仍从左边滑动);按 Fn、截图录屏时 Dock 自动让位,万一没收到“让位结束”的信号,会每隔几秒自动核对并恢复(1.25 起,可在设置里调整或关闭)、右侧有两个白色 8 位像素风的小按钮:咖啡杯(杯口有蒸汽)点一下暂时隐藏 Dock、把 Touch Bar 还给系统(亮度、音量),10–60 秒后自动恢复,屏幕被调到全黑也不会把你锁在外面;最右边是窗口居中 / 最大化按钮:先居中,再点一下最大化,再点回到居中,你自己拖过或换了 App 就先居中,图标跟着窗口现在的样子变、连续快速点击以最后一下为准;切桌面被系统丢掉或焦点被抢走,会自动纠正回你最后点的那个。写这类日记时,不能只说“做了什么”,还要说明为什么先做这些、为什么暂时不做另一些。比如一个按钮、一个导入流程、一个本地模型开关或一段截图文案,放在代码里只是小改动,放在产品里却会影响用户理解、审核员复现和后续推广素材。
从工程推进看,这篇日记对应的检查点是:和 Claude Code 协作完成:先做私有接口的可行性验证,再写正式代码;Developer ID 签名并通过 Apple 公证,DMG 在 GitHub Releases 分发;发布前专门检查过二进制里有没有带出本机路径;空闲占用是实测数字:CPU 0.0%、空闲唤醒 0 次、内存约 32 MB。菜单里有“权限”一项,列出唯一需要的辅助功能权限、状态和用途。真实开发最容易失真的是中间过程,因为最后页面看起来只有一个结果,但实际会经历方案取舍、权限确认、素材准备、测试设备、审核备注和发布节奏。把这些过程写下来,后面做同类产品时才不会重新踩同一个坑。
从隐私和合规看,当前约束是:不联网,没有统计,没有账号。只把开关、语言、长按时长、提示风格和居中窗口大小这几项偏好设置保存在你的 Mac 上。这类信息必须前置到开发日记里,因为独立产品的可信度不是靠口号建立的,而是靠数据在哪里处理、用户能不能退出、功能是否离线可用、商店页怎么承诺、隐私政策是否与实现一致这些小事实积累出来的。
从课程和复用看,这篇内容可以沉淀到 Swift 原生 macOS、AppKit 与 NSTouchBar、私有 API 的风险控制、签名、公证与 DMG 发布。它的价值不只是给访问者看一个产品,而是展示一个独立开发者怎样把想法转成可验证的产品:先收窄场景,再选技术路径,再做体验最小闭环,最后把审核、推广、运营数据和失败教训都纳入下一次迭代。
小工具的价值来自克制和验证:先验证最大的不确定性,再写代码;发布前检查产物本身,而不只是源码。 这也是为什么每篇产品日记都要写到足够长:不是为了凑字数,而是为了把“证据、决策、实现、边界、复盘”都放在同一页,让读者看到一个判断是怎样被逐步验证出来的。最难的是让一个依赖私有接口的后台工具,在系统更新、睡眠唤醒和权限变化之后仍然稳定,并且在接口消失时安全地关闭功能。
所以这篇日记的结论不是“功能已经写完”,而是把一个阶段的判断公开化:哪些证据足够支撑继续推进,哪些资料还需要回到源码、商店材料、公开文案或运营观察里补齐。这样的记录会比单纯的发布公告更慢,但也更真实,能让产品页、发布记录和课程内容保持同一条事实线。
验收时我会把它拆成四个层次:第一层是用户路径能不能走通,第二层是异常状态有没有被诚实处理,第三层是页面上的按钮、状态、截图和文案是否对应真实发布渠道,第四层是公开证据能否支撑这个判断。只要其中一层对不上,产品看起来再完整,也不能算真正进入下一个阶段。
交接时也要保留边界:源码、构建、测试、商店元数据、公开文案、平台反馈和运营观察分别保存原始资料。产品日记只把这些事实翻译成读者能理解的过程,不替任何私有记录保存原始材料。
把这些内容公开出来,还有一个很现实的原因:AI 教程如果只展示成功结果,很容易让人误以为产品是一次生成出来的。真实情况恰好相反,真正可学习的是一次次收窄、验证、失败、补证据和重新提交。日记越具体,后续读者越能看到判断的脉络,而不是只看到一个漂亮的截图。
居中按钮的第二下:先判断,再动手
用户的想法是第二下点击就全屏,但他同时点出了难点:换了一个 App、或者自己拖成了别的大小,再点应该重新算。所以每次点之前先读窗口现在的位置和大小,只分三种:正好是居中的样子就最大化;正好是最大化的样子就回到居中;其他一律先居中。“最大化”是铺满屏幕可用区域,不是 macOS 的原生全屏,因为原生全屏会新开一个桌面,动画要将近一秒,还会和跨桌面切换互相干扰。
有些 App 有最小或最大尺寸,摆出来的大小和算出来的不一样,认不出自己摆过的窗口就永远最大化不了。所以摆完后把窗口实际的样子记下来。按钮的图标也分两种,居中是取景框,最大化是闭合的外框,并用系统的辅助功能观察者盯着窗口,你自己拖了,图标也对得上,空闲时不轮询。测试用的是真实的记事本窗口:居中、最大化、拖偏、自己拉成最大化,每种情况的下一下都读回数字核对过。
转场式的结尾
长按走完之后提示会“突然消失”,我在真实窗口里连续抓帧才看清:先盖上一整块浅色闪光,下一帧就黑了。用户的建议是让漂浮物变成前景,后面的文字和场景慢慢淡掉,最后只剩漂浮物自己越来越少。照着做了:小角色蹦一下,季节风暴涌到最上层,场景先淡、文字后淡,文字变成“已关闭 ✓”。手指抬起触发的普通淡出不能打断它,否则结尾会被切掉。
长按“关不掉”:三种情况
先实验,再分析。苹果的退出只是“请求”:`terminate()` 返回成功只说明请求发出去了,App 可以拒绝或推迟,有未保存内容会弹确认,这时 App 在后台等你回答,而你在 Touch Bar 上什么都看不到。强制退出(`forceTerminate`)是杀进程,会丢数据,所以不用。实测:预览和记事本本来就能退;终端里有进程在跑会被确认框拦住;访达则根本退不了。
所以规则改成先看这个 App:在最前面且有两个或更多窗口,只关当前窗口;否则退出整个 App;访达退不了,隐藏它,在最前面有窗口时关当前窗口,没有窗口就直接说明。动作发出后核对结果,被确认框拦住或没关掉,就自动切到那个 App(包括切桌面),并提示“X 在等你确认,已切换过去”。原则只有一条:每次长按都要有反馈,做不到的要说清楚。
权限单独列出来
这些功能都依赖辅助功能权限,用户提出应该在设置里单独列出,能看到状态、点一下去开启。所以菜单里有了“权限”一项:标题直接显示已开启或未开启,子菜单写明用来做什么,没有它其他功能照常,只是这几项不可用,以及除此之外不需要任何权限。我核对过,代码里确实只用到这一项。
验证:能验证的和不能验证的
能验证的:居中和最大化的整套判断用真实的记事本窗口测过;长按的决策和执行用记事本(多窗口、单窗口、不在前台)、访达(有窗口、没窗口、不在前台)、终端(有进程在跑被确认框拦住)测过;转场结尾在四个季节里抓了连续帧;菜单和权限子菜单读了真实运行的 App。重新实测了占用:空闲唤醒 0 次,CPU 0.0%,内存约 32 MB。
不能验证的:Touch Bar 上真实的触摸手感;画图软件、文档类 App 各自的确认框我没有逐个试;“权限未开启”那一支要撤销授权才能触发,没有测;窗口在别的桌面的 App,辅助功能看不到,会按“退出”处理;没有响应的 App 只提示,不做强制退出,是否做由用户决定。另外这一版“关于”里长按那句说明还是旧的“退出这个 App”,下个版本再改。