Skip to content

fix(vocab-card): 收起添加词卡片后,胶囊要回到屏幕中间 - #969

Open
bigsongeth wants to merge 2 commits into
Open-Less:betafrom
bigsongeth:fix/vocab-card-capsule-position-restore
Open

fix(vocab-card): 收起添加词卡片后,胶囊要回到屏幕中间#969
bigsongeth wants to merge 2 commits into
Open-Less:betafrom
bigsongeth:fix/vocab-card-capsule-position-restore

Conversation

@bigsongeth

Copy link
Copy Markdown
Contributor

用过一次"要不要把这个词加进词典"的卡片之后,下一次录音的胶囊会跑到屏幕右下角,而且再也回不到底部居中——除非重启 OpenLess。

这个 bug 其实修过一次,但那次修复只活在一个没合并的本地分支上,主线从没拿到过。这个 PR 把它接回来,并且钉了道保险,让它不会再丢第三次。

为什么会这样

卡片和录音胶囊是同一个窗口。弹卡片要把这个窗口缩到卡片大小、挪到右下角;收卡片时要原样还回去。当时还了尺寸、还了鼠标穿透,唯独没还位置。

漏这一步之所以致命,是因为负责把胶囊摆回中间的那段代码有个去重缓存,而缓存只记「显示器 + 翻译态」——卡片这一挪它一无所知。下次录音拿到相同的显示器快照就判定"没变化",直接跳过重新定位。说白了:窗口被挪走了,而唯一会把它挪回来的那段代码以为自己不用动。

技术细节

根因链

环节 问题
show_vocab_suggestion_card 把共享的 capsule 窗口挪到右下角,不清 capsule_layout 缓存、不同步 capsule_cursor_passthrough
hide_vocab_suggestion_card 还了尺寸和穿透,没还位置
maybe_position_capsule_bottom_center 去重缓存 key 只有「显示器 + 翻译态」→ 判定无变化 → 跳过重定位

复位和清缓存两件事都做,各堵一个方向:清缓存保证「就算这次复位失败,下一次 emit_capsule 也一定会重算」;复位保证「就算有哪条路径绕过 emit_capsule 直接 show,窗口也已经在对的地方」。

顺带堵上同一形状的另一处:卡片直接调 set_ignore_cursor_events,却没同步 capsule_cursor_passthrough 那个缓存。目前触发不了,但一旦缓存和窗口真实状态分家,emit_capsule 就会跳过它该调的那一次——表现是经典的"胶囊上的 ✓/✕ 点不动"。

复位顺序是先 hide() 再改几何:尺寸和位置要一起动,窗口还亮着时改就有概率被合成出一帧「卡片被拉宽、还横着飞过半个屏幕」。

这个修复是怎么丢的

原提交 d3fedcd(8/5)写在 #917feat/cursor-context-vocab squash 合入之后,就留在了本地分支上,主线重新长出了漏还位置的版本。

一条旁证:#943 加的落字回退卡片照抄了这套修复,注释里写着「理由见 hide_vocab_suggestion_card」——但主线上那个函数里已经没有这段理由了。原件丢了,只剩副本。

防回归

这类不变量单测抓不到:整段是 Tauri 窗口调用,跑在 run_on_main_thread 的闭包里,没有窗口就没有断言对象。所以按 repo 既有做法收进 macos-capsule-spaces-contract.test.mjs——那个文件已经是「胶囊窗口几何」这类源码级不变量的归属地(多屏定位缓存那条就在里面)。

守两侧四个函数(词条卡片 + 落字回退卡片):

  • 弹卡片:挪走了共享窗口,就必须让 capsule_layout 去重缓存当场作废,并同步 capsule_cursor_passthrough
  • 收卡片:穿透、尺寸、位置、两个缓存,一样都不能少;且必须先 hide() 再改几何

「还位置」和「清缓存」分开断言是有意的:这两件事各堵一个方向,漏任何一个 bug 都会回来,所以要能各自报出自己的名字。

验证

原修复当时在真机上量过(临时脚手架 + CGWindowList 取窗口 frame,外接屏 1920x1080):

修复前  录音 → x=453(居中)→ 卡片 x=1299(右下)→ 收卡片后窗口仍在
        x=1299 → 下一次录音胶囊 x=1299  ← bug
修复后  录音 → x=526(居中)→ 卡片 x=1168(右下)→ 收卡片当场回到
        x=453(居中)→ 下一次录音胶囊 x=453  ← 对

本 PR 的契约测试按三态验证,不是"写完就绿":

状态 结果
未修复的 coordinator.rs 🔴 报 show_vocab_suggestion_cardcapsule_layout 失效
变异:只删掉 hide_vocab 里那一行 position_capsule_bottom_center 🔴 精确报出「restoring size alone leaves it in the card's bottom-right corner」
修复后 🟢

npm test 全量退出码 0,cargo check --lib 通过。

🤖 Generated with Claude Code

bigsongeth and others added 2 commits August 19, 2026 13:25
卡片和录音胶囊是同一个窗口。弹卡片要把窗口挪到右下角,收卡片时
`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)
上一条修复(收起卡片时把胶囊位置也还回去)在 8 月丢过一次 —— 它只活在
一个没合并的本地分支上,主线从 PR Open-Less#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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant