[分享创造] 开发日志:把窗口「一键还原」之前,我先给它加上了「一键撤销」
先说痛点:我每天带着笔记本在家和公司之间跑,插拔一次外接屏,窗口全挤回内建屏,散落在不同的 Mission Control 桌面上,手动归位要好几分钟。为了治这个,我写了个小工具自动恢复窗口布局。
但这篇日志不讲"怎么恢复",讲"恢复错了怎么办"——因为一个替你搬窗口的工具,最危险的时刻不是它不工作,而是它自信满满地把窗口搬到了错误的位置,然后你连它们原来在哪都忘了。自动恢复天然有个问题:**它替你做了决定**。
所以我在功能优先级上做了一个反直觉的决定:**先做撤销,再做恢复**。
## 1. 每次恢复都是一次"交易",必须能回滚
最早的版本里,恢复是个整体动作:要么全成功,要么全失败。但很快踩了一个真坑:某个窗口因为应用还没启动完,恢复时拿不到窗口句柄,直接跳过,结果整个桌面只恢复了一半。更气人的是,恢复到一半的那几秒,你根本分不清哪些是它搬的、哪些是你自己开的。
现在的逻辑:恢复前先拍一张"现状快照"——每个窗口的位置、大小、所在桌面序号。恢复动作按窗口逐个执行,**每个窗口搬完立刻回读一次几何信息**,没到位就触发回滚:用快照把已经搬过的窗口全部放回去。恢复成功之后,快照也不丢——菜单栏一个按钮、一个全局快捷键 ⌃⌥⌘Z ,整个恢复一键撤销。
这让"自动恢复"从一个需要勇气点的按钮,变成了一个可以随便点的按钮。
## 2. 版式:同一块屏幕,工作和开会是两种生活
第二个决定是"版式"。同一块外接屏,我存了两套命名布局:一套叫"写代码",一套叫"开会"。写代码时左边编辑器、右边文档、通讯工具扔在第二个桌面;开会时会议窗口放大居中、编辑器缩到角落。菜单栏点一下切换。
实现上很简单——布局数据按"屏幕 EDID + 版式名"存成 JSON ,放在 `~/Library/Application Support/LayoutKeeper/`,你甚至可以直接打开看。但它解决了一个真实的问题:**恢复的目标不总是"上次的样子"**,有时候你想恢复的是"另一种样子"。
## 3. 历史版本:自动留档,因为人总会手贱
每次保存都会把旧版本往历史里推,自动保留最近 5 个。为什么?因为我真遇到过:保存了一次布局之后才发现,当时有个窗口是临时拖过去的——把"错误"的状态存成了"正确"的状态。有历史版本就能翻回去,而不是重新手动摆一遍。
5 个是拍脑袋定的,但刚好覆盖"我上周的布局是什么样"这种需求,又不会让数据目录无限膨胀。布局只存本机,不收集任何数据。
## 4. 恢复前先预览,同名窗口绝不猜
恢复前可以逐窗口预览:它会告诉你这个窗口要从桌面 2 搬到桌面 4 、从 (100,100) 搬到 (1920,300),确认后再动手。
还有个小细节:同名窗口(比如两个都叫"新建标签页"的浏览器窗口)分不清是哪个时,它标"需要确认"让你手动选,**绝不猜着移**。宁可少搬一个,不搬错一个。
## 5. 一点经验:自动化工具的"怂",是给用户省的"气"
回头看,做自动恢复这类工具,功能做"对"只是及格线,功能做"错"时的行为才是口碑线。预览、回读确认、快照回滚、一键撤销、历史版本——这些都不直接产生价值,但它们让用户敢用。用户敢用,工具才有机会产生价值。
---
上面这些,就是我写的 LayoutKeeper —— 开源免费( GPL-3.0 )的 macOS 菜单栏应用,记住并恢复外接屏窗口布局,连窗口原来在第几个 Mission Control 桌面都放回去。
仓库: https://github.com/tumingke0/LayoutKeeper
诚实写局限:
- 目前只支持**一块**外接屏;
- 跨桌面恢复要 macOS 26+(同一桌面内的位置/大小恢复 macOS 14+ 就行),依赖私有 API ,系统大更新后可能失效——应用启动时会自动检测,失效就自动关掉这个功能;
- Finder 窗口没法跨桌面移动;
- 跨桌面移动用 macOS 26 的 SkyLight 私有接口,**不用关 SIP 、不用注入 Dock**,只需要"辅助功能"一个权限;
- 未公证,第一次右键 → 打开;从 0.6.1 起支持应用内检查更新,更新后辅助功能权限不用重新授。
多屏办公的同学欢迎试用,提 Issue 时附上"设置 → 诊断 → 一键自检"的报告,我修得会快很多。
Post #238490
2