LazyMau/dsh-shuangpin-reminder ↗★ 0

@local/shuangpin-reminder

双拼键位提醒:可拖动、可拉伸的悬浮面板,自动收集打字速度与卡顿,给键位加热力标记,卡住时亮键,并把卡过的字做成练习。内置多种常见双拼方案,也可自定义键位。

パッケージ
@local/shuangpin-reminder
互換性
未検証
バージョン
1.5.0
ライセンス
MIT
最終更新
2026/11/05

インストール

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:LazyMau/dsh-shuangpin-reminder

ドキュメント

README 全文を読む ↗

双拼键位提醒(Shuangpin Keys)

Harness Web UI 插件。默认嵌入在会话内容与输入框之间(占真实布局,不遮挡任何内容),也可一键切换为可拖动的浮窗;它在你正常打字时自动收集速度与卡顿,把常卡的键标红,打字时在键盘上白框提示候选键位,并把「卡过的字」变成练习。

支持 小鹤双拼 / 自然码 / 微软双拼 / 智能ABC / 拼音加加 五种常见方案,以及自定义键位。

  • 插件包:@local/shuangpin-reminder(v1.5.0)
  • 双挂载:conversation.input.dock(嵌入模式,会话与输入框之间)+ shell.overlay(浮窗模式)
  • 默认收起成一条细栏(点「展开」即出键盘),嵌入/浮窗状态都会被记住

方案切换

面板左上角标题就是方案下拉框(显示当前方案名,如「小鹤双拼」),点开即可换方案;键位页本身不再放方案栏。选中「自定义…」会打开键位编辑器:可先选一个内置方案作模板,再逐个韵母改键,另可切换零声母规则(照全拼 / o 打头)。

键位表不是手写的,而是从 rime 官方 schema 的 speller/algebra 规则机械推导而来,并用 src/derive-check.cjs 校验,避免抄错:

node src/derive-check.cjs  --check   # 与 src/schemes.cjs 内置表比对

界面(嵌入模式 = 默认)

(会话内容)
────────────────────────────────────────
⠿ 小鹤双拼 ▾ [键位][练习][记录]              ◐ 📌 ⧉   ← 悬停面板才出现
┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│Q │W │E │R │T │Y │U │I │O │P │   ← 打 x 时:X[下] K[行] L[想] N[小] R[先] 白框提示
├──┴──┴──┴──┴──┴──┴──┴──┴──┤
│A │S │D │F │G │H │J │K │L │
│Z │X │C │V │B │N │M │
▸ 声母 / 韵母提示
────────────────────────────────────────
(输入框)
  • 嵌入模式(默认):键盘插在会话与输入框之间,宽度自动跟随输入框区域,键位很大;不覆盖、不遮挡对话。展开/收起只用工具行(模型选择旁)的「鹤」开关,收起后完全不渲染。
  • 浮窗模式:点标题栏的 🗗(U+1F5D7)切换(原地浮起)——可拖动(按住标题栏)、右下角 ⤡ 拉伸、滑杆调透明度、📌 固定;切回嵌入点 🗖(U+1F5D6)。
  • 悬停显影(两种模式一致):平时只显示三行键盘;鼠标移上面板,控制行才出现。
  • 展开/收起:只用工具行(模型选择旁)的「鹤」开关——两种模式收起后都完全不渲染,恢复也用这个开关。
  • 热力标记:只有出问题的键才有标记——黄条=不稳(卡壳率 25%–45%),红条+红边+韵母变红=常卡(≥45%);掌握较好的键不做任何标记。样本不足 2 次不显示。

提醒是怎么"即时"的

先说边界:双拼跑在系统输入法里,网页拿不到按键流。浏览器在合成时只给 compositionstart/update/end 和上屏文本,拿不到"你按了哪几个键"。所以能做到的是:

  1. 上屏后立刻(约 0.1 秒)在输入框上方弹出小条:学 xt,同时键位图上 X、T 两个键高亮 2.6 秒。慢的会带「慢」。
  2. 合成过程中读输入法的预编辑文本:如果它暴露的是拼音(例如 xue),面板上会实时显示 正在打:xue → xt。这条依赖输入法行为——有的输入法只显示双拼码或直接显示候选,那就只显示原文。获取方式是标准 DOM 事件:合成期间输入法会通过 compositionupdate 主动把预编辑文本报给页面,插件在 document 上捕获(此时 keydown 只有 keyCode 229,拿不到按键)。每次回显会带相对毫秒数记入「记录」页的「输入法回显采样」,用来确认你的输入法到底给了什么。
  3. 练习里才是真正提前的:卡住 1.2 秒自动亮出两个键(可关),答错直接给正确键位。
  4. 键盘框选(主要提醒面):面板上的键盘直接高亮,不用看别处——
    • 打了声母、韵母还没打(如 x):常用字(下/行/想/小/先…)对应的韵母键被白框框选,键上标出候选字;
    • 打完一个字:按你的「上一个字 → 下一个字」历史(不足时用常用接续兜底),把下一个字的双拼键框选出来,首选键加重;
    • 合成中已完成的音节键会轻框确认。 原先输入框上方的小条已移除,提醒统一收敛到键盘区域。

也就是说:真实输入是"上屏即提示",练习是"提前提示"。想更提前,只能靠练习把键位练熟。

数据收集(先收集,再变准)

  • 每次上屏记录:哪些字、这次合成花了多久、中间退格几次。
  • 卡壳过程:合成期间预编辑串一旦变短(你退格改码),整条轨迹会被记下来,如 q→qi→qin→qi→qing(想打情,韵母忘了先按了 N)。
  • 按键混淆:从卡壳轨迹里推断「该按的键 vs 实际按的键」,如 该按 K,实按 B ×4。仅在输入法预编辑暴露拼音时可归因;只暴露码时保留轨迹。
  • 字级二元:记录「上一个字 → 下一个字」的接续频率,驱动键盘框选的下一个字预测(个人历史优先,内置常用接续兜底,上限 500 组)。
  • 单字耗时 > 你近期均值的 1.5 倍 + 120ms,或退格 ≥ 2 次 → 记为一次「卡」。
  • 字 → 拼音 → 双拼两键(内置 3755 个常用字的表),于是「哪些键常出现在卡顿的字里」就出来了。
  • 「记录」页显示:收集进度、最容易卡的键与常按错的键(并排)、卡过的字,以及最近输入 / 输入法回显采样 / 卡壳过程三栏并列。面板高度有上限、只滚正文,所以记录页再长也能随时点回「键位」页。
  • 记录按方案分开存:同一套键位在不同双拼方案下含义不同(比如 k 在小鹤是 ei、在自然码是 iao), 混在一起的热力图没有意义。所以在标题栏切换方案后,进度、弱键、错键、卡过的字、最近输入都只算当前方案; 换回来时原来的记录还在。记录页标题旁的小标签标出这份数据属于哪个方案。
  • 旧记录归谁:1.5.0 之前的统计没有记录方案(那时插件只支持小鹤), 升级后统一归到小鹤。不归给「升级那一刻停留的方案」——否则你切到别的方案再重启, 小鹤攒下的记录就会跑到那个方案下,看起来像「换方案后记录还在」。 早期中间版本确实犯过这个错,现在会自动把错放的数据挪回小鹤(只搬一次)。
  • 「练习」页可以切到 我的错字,只练你卡过的字。

隐私

不联网、不上传、无第三方请求。 全部数据只留在本机浏览器里,可用「记录」页的「清空」一键清除。

这个插件需要在输入框上统计打字,因此有必要说清楚它读了什么、存在哪:

读什么:只监听宿主的可编辑区域(textarea、文本输入框、contenteditable), 读取输入法上屏的文本与合成期间暴露的预编辑串,用于计算速度、退格与卡顿。 不读按键内容之外的任何东西:不注入输入框、不改写你的输入、不拦截按键默认行为、 不读取其它页面、不做任何网络请求。插件自己的面板(如「改键位」输入框)已被显式排除, 不会被计入统计。

存在哪:

位置内容说明
localStorage dsh.shuangpin-reminder.prefs.v4位置、宽度、透明度、页签、方案、自定义键位纯偏好
localStorage dsh.shuangpin-reminder.stats.v2按方案分桶的逐字/逐键统计、最近 30 条输入、回显采样、卡壳过程完整数据,仅此端口可见
cookie dsh.shuangpin.mirror.*仅聚合计数:键次数、错键次数、进度基数见下

cookie 镜像的存在理由:GUI 端口每次启动会变,而 localStorage 按端口隔离、 cookie 不分端口,所以用一条小 cookie 让统计在换端口后还能接上。 镜像刻意不含用户输入原文——不带打过的字、不带拼音、不带卡壳序列, 因为 cookie 会随同源请求自动发送、且同域脚本可读。镜像约 1KB、1 条 cookie, SameSite=Lax,https 下自动加 Secure。旧版本曾把原文写进镜像, 升级后会检测并自动重写清除。

关于旧存储键:1.5.0 之前包名为 @local/xiaohe-shuangpin-reminder, 存储键前缀是 dsh.xiaohe.*。改名后首次启动会自动把旧键的数据迁移到 dsh.shuangpin-reminder.*(新键已有数据时不覆盖),老用户的偏好与统计不会丢。

键位表(小鹤双拼;其余方案在面板标题里切换)

键韵母例键韵母例
Qiu求 qiú → qqAa大 dà → da
Wei杯 bēi → bwSong / iong中 zhōng → vs
Ee饿 è → eeDai买 mǎi → md
Ruan转 zhuǎn → vrFen人 rén → rf
Tue / üe学 xué → xtGeng能 néng → ng
Yun云 yún → yyHang上 shàng → uh
U声母 sh;韵母 u是 shì → uiJan看 kàn → kj
I声母 ch;韵母 i吃 chī → iiKing / uai平 píng → pk
Ouo / o多 duō → doLiang / uang想 xiǎng → xl
Pie写 xiě → xpZou走 zǒu → zz
V声母 zh;韵母 ui / ü中 zhōng → vs · 女 nǚ → nvXia / ua下 xià → xx
Bin心 xīn → xbCao好 hǎo → hc
Niao小 xiǎo → xnMian天 tiān → tm
  • 每个音节两个键:声母键 + 韵母键。
  • 小鹤零声母:单字母双写(啊 aa、鹅 ee),双字母照全拼(爱 ai、安 an、欧 ou、恩 en、而 er),ang/eng 用 ah、eg。
  • ü 一律按 v:女 nv、绿 lv、略 lt;ju/qu/xu/yu 里的 u 也按 v:去 qv。

其他方案的差异(键位细节见面板内的键帽):

方案声母 zh/ch/sh零声母特点
小鹤双拼v / i / u照全拼本表
自然码v / i / u照全拼ai=l、ei=z、ing=y、un=p
微软双拼v / i / uo 打头ing=; 键、ü=y
智能ABCa / e / vo 打头ian=w、in=c、iao=z
拼音加加v / u / io 打头ao=d、an=f、en=r

目录

package.json            插件清单
cordis.patch.yml        插入 Host 行
index.js                Host 半边(空实现)
client.js               生成的 Client 模块(含拼音表与方案表)
src/client.template.js  可读源码(UI 与业务逻辑,只改这里)
src/schemes.cjs         双拼方案数据层(键位表 / 编码 / 拼音解析,可独立测试)
src/derive-check.cjs    从 rime schema 推导键位并校验内置表
src/schemes.test.cjs    方案数据层断言测试
src/dict-src.txt        字 + 拼音数据(3755 条)
src/build-client.cjs    生成脚本(内联方案层与拼音表)
src/smoke.cjs           本地渲染/捕获冒烟测试
src/smoke-scheme.cjs    方案切换渲染测试
src/smoke-picker.cjs    自绘下拉交互测试(展开/选中/收起)
src/smoke-height.cjs    新会话高度回归(面板/键盘不被压扁)
src/smoke-mirror.cjs    cookie 镜像行为(不含原文 / 可恢复 / 旧数据清除)
src/smoke-migrate.cjs   旧存储键迁移(不丢偏好与统计)
src/smoke-per-scheme.cjs 记录按方案隔离(切方案不串数据)
src/smoke-records-switch.cjs 记录页文本随方案变化
src/smoke-switch-live.cjs 同一 store 上实时切换方案
src/smoke-repair.cjs    旧统计错放到其他方案时的修复
src/mirror-size.cjs     镜像体积与隐私体检(估算 cookie 条数与上限)
src/smoke-proto.cjs     恶意统计数据不污染原型
src/add-fallbacks.cjs   给主题变量补兜底色(幂等,人工执行)
src/lint-locale.cjs     文案体检(死字符串 / 重复 key),构建时自动跑
src/layout-check.cjs    记录页三栏布局估算(窄面板是否会被挤扁)
src/key-size.cjs        键位尺寸换算表(人工核对用)
locale/                 zh/en 标题与描述
icon.svg                图标
LICENSE                 MIT(含第三方数据说明)

改完源码后:

node src/build-client.cjs src client.js
node --check client.js
node src/schemes.test.cjs         # 方案数据层
node src/smoke.cjs . map          # 还有 drill / stats / float / fab / capture
node src/smoke-scheme.cjs .       # 方案切换与自定义编辑器
node src/smoke-picker.cjs .       # 下拉展开/选中/收起
node src/smoke-height.cjs .       # 面板/键盘不可被压扁
node src/smoke-mirror.cjs .       # cookie 镜像不含输入原文、可跨端口恢复
node src/smoke-migrate.cjs .      # 旧存储键迁移不丢数据
node src/smoke-per-scheme.cjs .   # 记录按方案隔离
node src/smoke-records-switch.cjs . # 记录页文本随方案变化
node src/smoke-switch-live.cjs .  # 同一 store 上实时切换方案
node src/smoke-repair.cjs .       # 旧统计错放时能被修回
node src/mirror-size.cjs          # 镜像体积与隐私体检
node src/smoke-proto.cjs .        # 恶意统计数据不污染原型
node src/layout-check.cjs         # 记录页三栏布局估算
node src/derive-check.cjs  --check

也可以直接 npm test(等价于上面除 derive-check 的全部检查)与 npm run build。

已知限制

  • 拿不到输入法按键流;真实输入只能"上屏即提示",无法在按键前预测。
  • 「卡顿」是按时长/退格估算的;拼音表外的生僻字只计时间,不更新键位。
  • 透明度过低时文字一起变淡(要的就是"看得见但不挡事")。
  • 面板宽度 340–860px;更窄的窗口会按屏幕宽度自动限制。