[windows][ime] TSF 失败后的静默 unicode 兜底按 Enter 发送换行并可能与迟到插入叠加,导致聊天输入框内容重复与拆分
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
Start in src-tauri/src/core_adapters.rs at the TSF submit failure branch, where the warning is logged and allow_non_tsf_fallback routes to windows_unicode_fallback. Then read insert_via_unicode_keystrokes, which currently logs nothing, and check how newline_mode = enter turns \n into Enter presses. A first step is adding the missing fallback log lines; done means the fallback path is visible in logs and a regression test asserts that a multi-paragraph insert under TSF NotDispatched sends the text once without submitting.
由索引模型根据 Issue 内容生成。
描述
现象
- 触发条件:
windowsInsertionMode = tsf、allowNonTsfInsertionFallback = true(默认开启)、windowsSendInputNewlineMode = enter(出厂默认);目标为聊天类前台输入框(实测 Codex Desktop 的 Chromium composer,进程 ChatGPT.exe);听写内容经 Structured 润色后包含多个段落/分条。 - 当前表现:一次听写后,目标输入框出现两份内容、尾部片段劈裂重复;内容逐段被以 Enter 提交为新消息("说完一条分出新的一条,结束后还会出现第三条")。记事本/普通网页不复现(TSF 正常
Committed,不走兜底;Enter 也不提交)。 - 证据(2026-10-31 本机日志,已脱敏,仅含长度/hash/session id):
- 正常路径:
submit begin op=commit ... payload_bytes=308(= 146 字符 ×2 + 16 头部,commit 只含一份文本)→status=Committed→Ok(Inserted)。 - 异常路径:
submit complete op=commit status=NotDispatched→[WARN] [core-adapter] TSF submit failed: TSF edit session was rejected before dispatch→ 0.4s 后insert_final result=Ok(Inserted),期间没有任何兜底路径日志。该会话 polish 85 字符,目标输入框实际出现约 2×85 字符(内容两份、尾部片段 ×2)。 - 近期统计:
probe deadline expired32 次、NotDispatched6 次,目标均为 Codex Desktop composer;失败后均未观察到第二次 submit 或兜底日志。
- 正常路径:
影响
- 聊天类应用中听写结果重复、被拆分成多条消息提交,需手工清理;核心听写链路在此类应用中不可信。
- 兜底路径零日志:故障时无法从日志定位(本次排查的主要成本),用户侧表现为"随机重复"。
- 观测:单日兜底路径触发最高 20 次(2026-10-24)。
复现步骤
- Windows 11 x64 + OpenLess 2.0.2-Beta.3+build.141-c9f49c60,保持
windowsInsertionMode=tsf、windowsSendInputNewlineMode=enter(出厂默认)。 - 前台置为 Codex Desktop 消息输入框(Chromium WebView composer)。
- 听写一段会被 Structured 风格润色成多段/分条的内容,松手。
- 观察目标输入框:内容两份 / 尾部片段重复 / 被按段提交为新消息;日志特征为
TSF submit failed后 0.2~2s 内Ok(Inserted)且无兜底日志。
根因定位(代码固定到 v2.0.2-Beta.3-tauri)
src-tauri/src/core_adapters.rsTSF 提交失败分支:log::warn!("[core-adapter] TSF submit failed: {error}")后break InsertStatus::Failed;allow_non_tsf_fallback为真时进入windows_unicode_fallback(&self.context, &text)。- 同文件
windows_unicode_fallback(约 :5240):insert_via_unicode_keystrokes(text, WindowsSendInputOptions { newline_mode: windows_sendinput_newline_mode }),函数内零日志;newline_mode = "enter"时每个\n转成 Enter 键按下——聊天类 composer 中即"提交消息"。 - TSF
NotDispatched/ rejected-before-dispatch 不保证文本未插入:异步 edit 仍可能落进文档(源码注释自证 "a timeout proves nothing: the edit can still commit"),与逐字打字兜底叠加 → 内容 ×2、尾部劈裂。 - payload 字节数学(chars×2+16)证明 commit 本身只含一份文本;重复由"迟到但真实落地的 TSF 插入 × 打字兜底"叠加造成,而非 commit 重复。
建议接受标准
-
windows_unicode_fallback进入/完成写入日志(建议并入 insert-diagnostics:operation=insert_fallback_unicode、chars、result),从日志可直接判定走了兜底。 - TSF
NotDispatched/outcome-unknown 时不再无条件报Ok(Inserted):至少在日志/用户可见文案中提示"文本可能已插入",并评估兜底前探测目标现有内容。 - 评估兜底打字换行默认值:
enter在聊天类输入框会提交消息;考虑默认shiftEnter或在文档标注enter模式风险。 - 回归用例:TSF NotDispatched + 多段文本 + 聊天类前台,断言目标只收到一份文本、无意外提交。
TODO / 不确定项
- Codex Desktop composer 对 TSF 异步 apply 的确切时序未做 instrumentation(黑盒);"TSF 失败但实际已插入"由内容 ×2 观测与源码注释佐证,未逐帧取证。
- 其他 Chromium/Electron 应用的 TSF 拒绝率未统计。
已验证的绕过
windowsInsertionMode = paste(剪贴板 + Ctrl+V 单次插入):本机实测不再出现重复/拆分,重启后配置稳定。- 或
windowsSendInputNewlineMode = shiftEnter:兜底打字不再提交消息,但 TSF × 兜底双插入风险仍在。
环境:Windows 11 Pro 10.0.26200 x64;OpenLess 2.0.2-Beta.3+build.141-c9f49c60。本 issue 不含转写正文,仅含长度、hash、session id 与日志结构。
- 主要语言
- Rust
- 星标
- 3.7k
- 派生
- 349
- 平均合并
- 2 天 14 小时
- 30 天内合并 PR
- 97
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
Open-Less/openless 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
[windows] 录音快捷键还不支持设置为ScrllLock键可能已有人在做 @HKLHaoBin 于 9 天前认领。 未关闭bug
难度 2/5 1-3 小时 新手友好度 75/100
Open-Less/openless#1241 · 5 条评论 ·
维护者通常 1 天内回复
-
[omni] 多模态模式开启翻译后,译文时有时无,或混在 JSON 字段里可能已有人在做 @Breadswim 于 2 天前认领。 未关闭
难度 4/5 3-5 天 新手友好度 52/100
维护者通常 1 天内回复
-
[omni] 多模态「验证」对 OpenAI gpt-audio-1.5 必然返回 400可能已有人在做 @Breadswim 于 2 天前认领。 未关闭
难度 3/5 1-2 天 新手友好度 58/100
维护者通常 1 天内回复
-
[omni] OpenAI gpt-audio-1.5 多模态听写输出被包成 JSON 对象(final_text 等字段)后直接上屏可能已有人在做 @Breadswim 于 2 天前认领。 未关闭
难度 4/5 3-5 天 新手友好度 52/100
维护者通常 1 天内回复
查看 Open-Less/openless 的全部 Issue
相似的 Issue
-
example
难度 2/5 1-3 小时 新手友好度 72/100
rust-lang-nursery/rust-cookbook#954 ·
维护者通常 1 天内回复
-
难度 1/5 1-3 小时 新手友好度 70/100
OpenZeppelin/openzeppelin-relayer#967 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
src/runs.rs:1987: a missing --source-run-id now answers 400 "Source run ... does not exist", not 404未关闭nightly-audit
难度 2/5 1-3 小时 新手友好度 62/100
antithesishq/snouty#602 ·
维护者通常 1 天内回复