
bigsongandClaude Opus 5
ec5ea4b07a
fix(vocab-card): 收起添加词卡片后,胶囊要回到屏幕中间 (#969)
* fix(vocab-card): 收起卡片时把胶囊的位置也还回去
卡片和录音胶囊是同一个窗口。弹卡片要把窗口挪到右下角,收卡片时
`hide_vocab_suggestion_card` 还了尺寸、还了鼠标穿透,唯独没还位置 ——
于是下一次录音,胶囊出现在右下角。
漏这一步之所以致命,是因为 `maybe_position_capsule_bottom_center` 有个
去重缓存,而那个缓存只记「显示器 + 翻译态」。卡片这一挪它一无所知,
下次录音时拿到相同的显示器快照就判定「没变化」,直接跳过重新定位。
换句话说:窗口被挪走了,而唯一会把它挪回来的那段代码以为自己不用动。
所以复位和清缓存两件事都做,各堵一个方向:清缓存保证「就算这次复位
失败,下一次 emit_capsule 也一定会重算」;复位保证「就算有哪条路径
绕过 emit_capsule 直接 show,窗口也已经在对的地方」。
顺带把同一形状的另一处堵上:卡片直接调 set_ignore_cursor_events,却没
同步 `capsule_cursor_passthrough` 那个缓存。目前触发不了,但一旦缓存和
窗口真实状态分家,emit_capsule 就会跳过它该调的那一次 —— 表现是经典
胶囊上的 ✓/✕ 点不动。
复位顺序改成先 hide 再改几何:尺寸和位置要一起动,窗口还亮着时改就有
概率被合成出一帧「卡片被拉宽、还横着飞过半个屏幕」。
真机量过(临时脚手架 + CGWindowList 取窗口 frame,外接屏 1920x1080):
修复前 录音 → x=453(居中)→ 卡片 x=1299(右下)→ 收卡片后窗口
仍在 x=1299 → 下一次录音胶囊 x=1299 ← bug
修复后 录音 → x=526(居中)→ 卡片 x=1168(右下)→ 收卡片当场回到
x=453(居中)→ 下一次录音胶囊 x=453 ← 对
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit d3fedcd13da0a3ba002a6f29dda330eeabee62f1)
* test(capsule): 把「卡片借走胶囊窗口必须完整归还」钉成契约
上一条修复(收起卡片时把胶囊位置也还回去)在 8 月丢过一次 —— 它只活在
一个没合并的本地分支上,主线从 PR #917 重新长出了漏还位置的版本,于是
真机 bug 原样复发:用过一次带添加词的卡片,下一次录音的胶囊就出现在
右下角,再也回不到底部居中。
这类不变量单测抓不到:整段是 Tauri 窗口调用,跑在 run_on_main_thread 的
闭包里,没有窗口就没有断言对象。所以按 repo 既有的做法收进
macos-capsule-spaces-contract —— 那个文件已经是「胶囊窗口几何」这类
源码级不变量的归属地(多屏定位缓存那条就在里面)。
守两侧四个函数(词条卡片 + 落字回退卡片):
弹卡片:挪走了共享窗口,就必须让 capsule_layout 去重缓存当场作废,
并同步 capsule_cursor_passthrough
收卡片:穿透、尺寸、位置、两个缓存,一样都不能少;且必须先 hide
再改几何(否则复原会被合成出一帧横飞过半个屏幕的动画)
「还位置」和「清缓存」分开断言是有意的:这两件事各堵一个方向,漏任何
一个 bug 都会回来,所以要能各自报出自己的名字。
验证(三态):
未修复的 coordinator.rs(HEAD~1) → 红,报 show_vocab_suggestion_card
缺 capsule_layout 失效
变异:只删掉 hide_vocab 里那一行
position_capsule_bottom_center → 红,且精确报出「restoring size
alone leaves it in the card's
bottom-right corner」
修复后 → 绿;npm test 全量退出码 0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 23:30:48 +08:00
..
2026-08-07 21:50:09 +08:00
2026-08-07 21:50:09 +08:00
2026-08-07 21:50:09 +08:00
2026-07-18 18:13:34 +08:00
2026-08-07 21:50:09 +08:00
2026-07-15 13:20:57 +08:00
2026-06-13 23:34:54 +08:00
2026-07-15 15:26:48 +08:00
2026-06-19 13:55:53 +08:00
2026-07-15 15:55:37 +08:00
2026-05-04 20:00:36 +08:00
2026-07-15 15:26:48 +08:00
2026-05-04 20:27:05 +08:00
2026-06-13 19:48:07 +08:00
2026-08-10 21:59:10 +08:00
2026-08-07 21:50:09 +08:00
2026-07-15 15:55:37 +08:00
2026-08-03 16:03:41 +08:00
2026-08-19 01:08:15 +08:00
2026-08-03 16:03:41 +08:00
2026-08-19 23:30:48 +08:00
2026-07-15 15:55:37 +08:00
2026-08-02 20:22:22 +08:00
2026-06-12 18:13:12 +08:00
2026-08-07 21:50:09 +08:00
2026-08-07 21:50:09 +08:00
2026-06-13 19:48:07 +08:00
2026-06-12 18:13:12 +08:00
2026-08-10 23:06:12 +08:00
2026-08-07 21:50:09 +08:00
2026-08-07 21:50:09 +08:00
2026-07-15 14:46:06 +08:00
2026-07-19 00:11:34 +08:00
2026-07-14 19:34:11 +08:00
2026-06-13 19:48:07 +08:00
2026-08-10 23:06:12 +08:00
2026-05-06 11:20:24 +08:00
2026-08-14 22:04:47 +08:00
2026-05-02 09:46:39 +08:00
2026-05-02 03:59:01 +08:00
2026-05-02 03:29:49 +08:00
2026-04-30 14:19:29 +08:00
2026-08-14 22:04:47 +08:00
2026-05-03 22:59:38 +08:00
2026-05-05 19:31:49 +08:00
2026-05-04 10:17:56 +08:00
2026-05-04 10:17:56 +08:00
2026-08-14 22:04:47 +08:00
2026-04-30 14:21:35 +08:00
2026-08-10 23:06:12 +08:00
2026-05-03 19:03:43 +08:00
2026-05-11 16:50:20 +08:00
2026-05-11 16:50:20 +08:00
2026-05-01 21:44:26 +08:00
2026-08-14 22:04:47 +08:00
2026-05-06 15:49:28 +08:00
2026-05-06 15:21:29 +08:00
2026-05-06 15:21:29 +08:00
2026-06-17 11:13:27 +08:00
2026-05-02 03:59:01 +08:00
2026-07-15 13:53:09 +08:00
2026-08-10 23:06:12 +08:00
2026-07-19 00:11:34 +08:00