From aac6d3e0f406ecdd7ffd412a51ba1117104ce73c Mon Sep 17 00:00:00 2001
From: laoyang
Date: Tue, 29 Sep 2026 08:10:22 +0800
Subject: [PATCH] =?UTF-8?q?=E8=81=94=E6=83=B3=E8=AF=8D=E9=94=AE=E7=9B=98?=
=?UTF-8?q?=E9=80=89=E8=AF=8D=E5=BD=BB=E5=BA=95=E4=BF=AE=E5=A4=8D=EF=BC=9A?=
=?UTF-8?q?=E4=B8=8A=E4=B8=8B=E9=94=AE=20/=20=E9=80=97=E5=8F=B7=E5=8F=A5?=
=?UTF-8?q?=E5=8F=B7=20/=20=E8=BE=93=E5=85=A5=E6=B3=95=E7=BB=84=E5=90=88?=
=?UTF-8?q?=E4=B8=8A=E5=B1=8F=E5=9B=9B=E6=9D=A1=E8=B7=AF=E5=BE=84=E5=85=A8?=
=?UTF-8?q?=E9=80=9A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
背景:用户反馈「上下键和逗号句号都无法选择候选词,只能用鼠标」。逐条查下去
发现是**三个各自独立的原因**叠在一起,任何一个没修都会让人觉得「还是坏的」。
① 单组按键模式让另一组键静默失效
线上 settings.suggest_keys 停在 'punct'(只认 , 和 .)—— 是该模式的正确
行为,但用户不知道那是设置项在起作用,只当坏了。
改法:默认值改为 'both'(↓/↑ 与 , . 都认)。'both' 是 'arrows' 与 'punct'
各自行为的**严格超集**,升级不会让任何人的习惯失效,只是多了一组。
老库用**一次性迁移**(哨兵键 mig_suggest_keys_both 把关)把已知单组值并成
'both'。⚠️ 迁移必须只跑一次:db_init() 每次请求都执行,无条件 UPDATE 会把
用户在后台特意选回的 'arrows' 于下一个请求改回去 —— 设置项直接失效。
同时同步三处默认值:index.php / admin/index.php / admin/actions.php。
② 【元凶】beforeinput 只认 insertText,漏了 insertCompositionText
输入法把标点当**组合内容**提交时,beforeinput 的 inputType 是
insertCompositionText,旧代码直接 return → 这条路径完全失效。
修法:两种 inputType 都认。
③ 【真机实测的硬限制】insertCompositionText 的 beforeinput cancelable = false
Chromium 里这条事件**不允许** preventDefault(CDP 实测记录:
beforeinput it:insertCompositionText comp:true data:"," pd:false cc:false),
所以「命中就拦」对这条路根本无效 —— 标点会被真的插进搜索框。
修法:加「事后收口」——beforeinput 记下快照与该移到的目标下标,
等这一帧事件洪峰过去(setTimeout 0)再把标点从输入框里撤掉、并补上选词移动。
目标下标必须**当场算好**:收口跑在宏任务里,那时 input 的 `active = -1`
已经把选中清掉了,再从 active 起算会每次都回到第一条(不累加)。
顺带把标点判定抽成共用函数 punctDir/keyPunctDir(e.key 与 e.code 互为兜底),
并修掉「方向键也无差别置去重闸门」——方向键不产生 beforeinput,置了纯属副作用,
反而会误挡紧随其后的真实标点。
测试与验证(全部实跑,非推断):
- jsdom 交互测试 89 项全过,新增「真实浏览器形态」场景(cancelable=false +
组合上屏),并**纠正了旧版一条在断言 bug 本身的断言**(原写「组合上屏不该被拦」)。
- 反向验证 6 变体全部精确挂掉对应断言(含新增的「只认 insertText」与
「方向键置闸门」两个变体);旧变体引用的 isComma/isPeriod 已随重构更新。
- **真实浏览器**(Playwright + msedge + CDP IME)21/21:上下键 / 半角 / 全角
insertText / IME 组合上屏四条路径全部能选词。
- 真实浏览器反向验证:基准全绿,删掉「事后收口」或改回只认 insertText 都精确报红。
- 迁移语义测试 16 项全过(真实 db_init,含「用户改回 arrows 后不再被覆盖」)。
- 静态断言空跑 155 条全过;线上部署 0 FAIL,诊断页 57 条断言全过。
- 线上真机直连 n.yangyq.net 21/21。
---
admin/actions.php | 6 +-
admin/index.php | 17 ++---
assets/js/app.js | 174 ++++++++++++++++++++++++++++++++++++----------
db.php | 25 ++++++-
index.php | 8 +--
5 files changed, 174 insertions(+), 56 deletions(-)
diff --git a/admin/actions.php b/admin/actions.php
index e393a79..21969c5 100644
--- a/admin/actions.php
+++ b/admin/actions.php
@@ -268,9 +268,9 @@ try {
// 打开方式:'0' = 当前页面;其余(含字段缺失、被篡改成怪值)一律回到 '1'(新标签页)
// ——默认必须等于改造前的旧行为,这样老库里已有的值上线后不会突然变样。
$open = (string)($_POST['open_blank'] ?? '1') === '0' ? '0' : '1';
- // 联想词按键:arrows(↓/↑)/ punct(, 和 .)/ both;非白名单一律回到 arrows
- $keys = (string)($_POST['suggest_keys'] ?? 'arrows');
- if (!in_array($keys, ['arrows', 'punct', 'both'], true)) { $keys = 'arrows'; }
+ // 联想词按键:both(都认,默认)/ arrows(↓/↑)/ punct(, 和 .);非白名单一律回到 both
+ $keys = (string)($_POST['suggest_keys'] ?? 'both');
+ if (!in_array($keys, ['arrows', 'punct', 'both'], true)) { $keys = 'both'; }
$map = [
'site_name' => trim((string)($_POST['site_name'] ?? '')),
'subtitle' => trim((string)($_POST['subtitle'] ?? '')),
diff --git a/admin/index.php b/admin/index.php
index b87578c..84e7b25 100644
--- a/admin/index.php
+++ b/admin/index.php
@@ -325,18 +325,19 @@ else: // settings ?>
各用各的(没恢复好的引擎会自动回退到百度的建议)。
- 决定用哪组键在联想下拉里选词:选中某条后回车才按该词搜索;不选就直接回车 = 用你输入的原文。
- 「, 和 .」跟输入法一样,手不用离开主键区,习惯中文输入法的话更顺手。
- ⚠️ 选它之后,联想下拉打开时逗号与句号会当作选词键,不再直接输入 ——
- 想搜「python, pandas」这类带逗号的词,先按 Esc 关掉下拉再输。
+ 决定用哪组键在联想下拉里选词:选中某条后回车才按该词搜索;不选就直接回车 = 用你输入的原文。
+ 推荐「两者都可以」——手不用离开主键区,习惯中文输入法的话按 , . 更顺手,同时也保留方向键。
+ ⚠️ 只要联想下拉打开着,逗号与句号就会当作选词键、不再直接输入 ——
+ 想搜「python, pandas」这类带逗号的词,先按 Esc 关掉下拉再输。
+ 若只留一组键,另一组会完全没反应(不是坏了,是这里的选择),所以除非你明确只想用一组,建议保持默认。