From a8bce9f985d67cd212bd0fb57841848bb207b861 Mon Sep 17 00:00:00 2001 From: ssurui Date: Mon, 5 Oct 2026 17:24:10 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=AE=B0=E5=BD=95=E8=A7=A6=E5=B1=8F=20?= =?UTF-8?q?hover=20=E7=B2=98=E6=BB=9E=E8=A6=86=E7=9B=96=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E8=89=B2=E7=9A=84=E8=B8=A9=E5=9D=91=E4=B8=8E=20CDP=20=E6=8E=92?= =?UTF-8?q?=E6=9F=A5=E6=96=B9=E6=B3=95=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充语音按钮"绿框"bug 的真实根因(CSS 优先级,非 focus/outline) 和弯路,以及用 CDP 直接读计算样式定位"样式写了但没生效"类问题的 排查方法,供以后排查类似 CSS 问题参考。 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QeoAy5KmkHuKWHAktpEsaN --- CLAUDE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 71212f3..32a292d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -218,6 +218,16 @@ const vectors = await Promise.all(texts.map((t, i) => fetchOne(t, i))) 另外,`queryHandler`/`visionHandler` 里 `chunks.length === 0` 的兜底文案说的是"知识库还没有文档,请先导入"而不是"我不知道"——`search()` 是全表余弦排序后截断,只要库里有文档就一定返回结果,走到那个分支只可能是库为空。 +### 触屏 `:hover` 粘滞导致状态色被覆盖(ChatPanel.vue 语音按钮) + +`.voice-btn`(语音输入/语音输出/拍照提问)点击切换"开启"状态后,按钮背景该变成纯 `--green` 填充,真机上却只有边框变绿、背景还是浅灰——看起来像凭空多了一个"焦点框"。 + +**排查弯路**:一开始以为是浏览器原生 focus outline,加了 `outline:none` + `:active` 反馈,真机复测毫无变化。 + +**真正根因是 CSS 优先级,跟 outline/focus 完全无关**:`.voice-btn.on`(2 个选择器)想把背景铺满 `--green`,但 `.voice-btn:not(:disabled):hover`(3 个选择器)优先级更高。触屏点击后没有鼠标可以"移开",`:hover` 状态会一直粘在刚点过的按钮上不消失(触屏设备通病),于是 hover 规则的浅灰 `background` 把 `.on` 的绿色背景盖掉,只剩下没被 hover 碰到的 `border-color: var(--green)` 还在——视觉上就是一个孤零零的绿框。修复:hover 规则加 `:not(.on)` 排除已开启状态。 + +**排查方法论**:真机截图只能看到表象("好像是焦点框"),容易被误导。改用 CDP(Chrome DevTools Protocol)——启动时加 `--remote-debugging-port=9222`,写个不依赖 `ws` 包的最小原生 WebSocket 客户端(`net`+`crypto` 手搓握手帧)连上去,用 `Input.dispatchMouseEvent` 模拟真实点击、`Runtime.evaluate` 读点击后的 `getComputedStyle()`,直接看到 `background` 实际渲染值是 `--panel-3` 不是 `--green`,才一次性定位到真正被覆盖的是哪条规则。**以后排查"样式明明写了但没生效"类问题,CDP 读计算样式比盯着截图猜更可靠。** + ### Vue Proxy IPC 序列化 Pinia 响应式对象直接传给 `ipcRenderer.invoke()` 会触发结构化克隆错误。**所有传入 IPC 的响应式对象必须先序列化**: