feat(dictation): 落字失败时把话交还给用户,换行不再劈开聊天消息 - #943
Open
bigsongeth wants to merge 1 commit into
Open
Conversation
Collaborator
两件事,本质是同一个:说出来的话没落到该去的地方。 **一、换行会把聊天消息从中间劈开发出去。** macOS 把 U+000A 当 Return 键,而聊天框里 Return 就是「发送」。一条带空行 的两段话逐字上屏时,第一个换行直接把上半句发了出去,下半句留在输入框里。 改成发真实的 Shift+Return:聊天框里是软换行不发送,编辑器、终端、网页输入 框里就是普通换行。 做成设置而不是硬编码——风格市场里有靠换行把一段话拆成多条消息的风格包, 那种效果要的正是真回车。默认 Shift+Return,需要的人切回 Return。 **二、落字失败时,那段话会悄无声息地消失。** 密码框挡着(Secure Input)、辅助功能掉了权限、粘贴被拒——这些时候唯一的 兜底是把文本写进剪贴板。可它既依赖一个默认可关的开关,就算开着,屏幕上也 没有任何东西告诉你文本在那儿。用户看到的只是胶囊一闪而过,然后是个空输入框。 现在屏幕底部会弹一张卡片,里面是**完整**的那段话,一键复制。20 秒后自己 消失,鼠标停在上面就不走。 顺带修正一处数据失真:逐字上屏打到一半断掉时,此前一律报 InsertStatus:: Inserted(屏幕上明明只有半截),history 里的 insertStatus 因此是错的。
bigsongeth
force-pushed
the
fix/macos-streaming-newline-sends-message
branch
from
August 12, 2026 10:44
4c5158e to
fec1545
Compare
Contributor
Author
|
手打这么长也太牛了😂 三点都收到,PR 已经更新。 换行做成设置了 这个结论其实项目里早就有 —— Windows 侧的 有个差异想请你定:Windows 默认是 Enter,我这边默认给了 Shift+Return。 理由是 macOS 这次是真出了事故——一条带空行的两段话,上半句直接飞出去成了一条微信消息,只能撤回。默认值想站在「不误发」这边;靠换行拆多条消息的风格包,让用户去设置里切 Return,风格包说明里写一句就行。如果你觉得两边默认值该保持一致,我改成 Enter 也可以——那样默认用户还是会遇到误发。 焦点守卫整个撤掉了 你说的那种使用习惯确实会被它干扰。顺着想下去发现更根本的问题是:它在猜用户的意图。拿「app 变了」当「字会打错地方」的信号,可技术上分不清「不小心切走了」和「故意切过去让它落在那」——同一个动作,意图相反。 撤掉之后卡片不受影响。它靠的是「落字确实失败了」这个事实:Secure Input、辅助功能掉权限、粘贴被拒。这些没有猜测成分。 性能那条也就不用担心了,守卫没了。 你提的「没有光标才弹卡片」我觉得方向是对的——判断「这儿能不能打字」是事实,比猜意图靠谱。不过要动的东西比较多,这个 PR 先不碰。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
两件事,本质是同一个:说出来的话没落到该去的地方。
一、换行会把聊天消息从中间劈开发出去
说一段话,润色把它分成了两段。逐字上屏到微信的时候,中间那个换行直接把上半句当成一条消息发了出去,下半句留在输入框里。只能撤回。
原因是 macOS 把 U+000A 当 Return 键,而聊天框里 Return 就是「发送」。
改成发真实的 Shift+Return——聊天框里是软换行不发送,编辑器、终端、网页输入框里就是普通换行。
做成设置而不是硬编码:风格市场里有靠换行把一段话拆成多条消息的风格包,那种效果要的正是真回车。默认 Shift+Return,需要的人在设置里切回 Return(只在 macOS + 开了流式插入时显示,结构对齐已有的
WindowsSendInputNewlineMode)。二、落字失败时,那段话会悄无声息地消失
密码框挡着、辅助功能掉了权限、粘贴被拒——这些时候文本落不进去,而唯一的兜底是把它写进剪贴板。
问题是:这个兜底依赖一个默认可关的开关;而且就算开着,屏幕上也没有任何东西告诉你文本在剪贴板里。你看到的只是胶囊一闪而过,然后是个空输入框,话就这么没了。
现在屏幕底部会弹一张卡片,里面是完整的那段话,一键复制。20 秒后自己消失,鼠标停在上面就不走(你还在读的时候它不该跑)。
实现
技术细节
换行(
unicode_keystroke.rs):用kVK_Return虚拟键码 + shift flag,代替原来CGEventKeyboardSetUnicodeString送 U+000A 的做法。\r吞掉不发键(CRLF 里它只是 LF 的前缀,发出去会变成两个换行),但仍计入typed_chars——调用方拿它和 delta 的字符数比对,少一个就会判定「部分失败」并丢弃后续所有 delta。顺带把重复的 CGEvent 资源管理收口到一个post_key_event。卡片触发:
InsertStatus::CopiedFallback | Failed。PasteSent是 Windows / Linux 上的成功语义,不能算失败,否则每次正常听写都会弹卡片。逐字上屏打到一半断掉时,
run_streaming_polish把完整文本放进Inner::insert_fallback_text,收尾据此把 status 从Inserted纠正为CopiedFallback——此前那里一律报成功,history 里的insertStatus也跟着失真。卡片内容是完整文本,不是屏幕上那半截。
final_text遵守「所见即所存」的约定(#412 确立),但用户要拿回的是整段。卡片窗口复用词条建议卡片那一套(复用胶囊窗口、关穿透、缩尺寸、重定位),包括两个容易漏的缓存同步:
capsule_cursor_passthrough不同步,后续emit_capsule会以为「没变化」而跳过该调的那次,表现是胶囊之后一直挡着屏幕;capsule_layout不清,下次录音时胶囊会留在卡片挪过去的位置。时序:卡片在会话收尾那一刻弹出,而收尾自己安排了一次 idle 隐藏。照搬
selection_polish_capsule_active已有的让路机制,否则卡片刚出现就被自己这轮会话的尾巴收掉。复制走后端而不是
navigator.clipboard:卡片浮在别的 app 上,按钮刻意不抢焦点(抢了就把用户正在写的地方的光标弄没了),而未聚焦的文档调那个 API 会抛Document is not focused。测试
cargo test --lib1119 通过,tsc --noEmit通过。还没验证的