fanlusky
44caaefcec
perf(windows): 健康路径去掉多余的排空投递,删除死掉的 50 ms 硬读取
...
健康路径上 OnKeyDown 把键入队后立即同步排空(Excel 首键修复,保留),但入队时
仍先投递一条 WM_DrainDeferredKeyDown。同步排空已经把这颗键取出:它要么还在
飞行中、要么已完成,那条消息运行时只会看到「有键在飞」或「队列为空」而直接
返回;后面排队的键由这颗键完成时自己安排排空。所以现在入队时不再投递,同步
排空之后只在键仍留在队列里(焦点会话未连上或刚开了重置)时才补一次投递,
这两种情况与原先的行为一致。每个健康按键省掉一次 PostMessage 和一次空转的
窗口消息。PeekNamedPipe 存活探测不动。
同时删除已确认没有调用者的硬读取路径:ReadDataFromServerViaNamedPipe、
单参数的 TryReadDataFromServerPipeWithTimeout(50 ms 超时即拆管道),以及
ClearNamedpipeDataIfExists——它唯一存活的调用不带 force,函数体什么也不做。
2026-09-27 19:05:59 +08:00
fanlusky
5f05c15a52
refactor(windows): 去掉按键回放,失败一律丢键并按策略重置
...
回放随 4f2fe01d 引入,当时唯一点名的动机是 Win+. 往返后候选窗拉不起来;
之后 #259、#404、#427 都是回放本身造成的(闪烁、重复上屏、重试循环),
PR #275 的讨论也已经指出整缓冲区回放在架构上就是错的。维护者的决定是:
会话在组合中途断掉时清空组合、从空开始,失败那一刻已被吃掉并排队的键
一律吞掉,既不交还宿主也不重发。偶尔需要重打一段拼音,换来的是不再闪烁、
不会双上屏、没有重试循环。
删除的机制:恢复前缀 _deferredAppliedPrefix、_RefreshDeferredRecoveryPrefix、
_ArmDeferredRecoveryForTransport、完成时追加/改写前缀的逻辑、重试计数与
kMaxDeferredKeyReplayAttempts,以及只为二次提交存在的 preservedApplied 分支。
_RetryDeferredKeyReplay 与 _DropAmbiguousDeferredKey 合并为 _FailDeferredKey:
永不重新分发这颗键,由 DeferredKeyFailurePolicy 决定代价——
- Stale(焦点 token、组合纪元、重置 token 已被取代):静默丢键,不再发起重置;
- Resync(宿主拒绝 edit session、RequestEditSession 失败、异步投递失败):
丢键、吞队列、本地取消组合并在当前 token 上让 Server 清组合;
- Transport(写失败、回复读失败、TransportUnavailable、DefinitelyNotSent、
DeliveryAmbiguous、提交回复 300 ms 超时、worker 断开):丢键、吞队列、完整重置;
- Offline(离线原始通道里的宿主失败):丢键、吞队列,保留本地组合。
DeliveryAmbiguous 绝不重发;DefinitelyNotSent 也不再重排队,因为已经没有回放。
保留:FIFO、屏障与投影、所有 token/纪元/请求号校验、先重置再排空、离线原始
通道、300 ms 提交预算、Excel 首键同步排空、失焦 300 ms 延迟、连续 6 次失败
拉起 Server(计数只来自排空路径的 Ensure 失败与离线通道,回放从未单独喂它)。
2026-09-27 18:58:48 +08:00
fanlusky
9c1c4b82b2
refactor(windows): 按职责拆分 KeyEventSink.cpp
...
KeyEventSink.cpp 有 3100 多行,同步按键判定、延迟按键队列与回放、输入模式
热键全挤在一个文件里。按职责拆成以下几个翻译单元:
- KeyEventSink.cpp:_IsKeyEaten、ConvertVKey、_IsKeyboardDisabled、退格
长按与 CapsLock 处理、直通字符统计,以及 ITfKeyEventSink 各回调
(OnSetFocus、OnTestKeyDown、OnKeyDown/_DispatchKeyDown、OnTestKeyUp、
OnKeyUp、OnPreservedKey/_DispatchPreservedKey)和 sink 的注册/注销
- KeyEventSink_DeferredKeys.cpp:延迟按键屏障与容量、预测状态投影、恢复
前缀、_ClassifyDeferredKeyDown,以及入队、清空、完成、重试、丢弃歧义键、
排空调度和 Server 不可用回退
- KeyEventSink_Hotkeys.cpp:mintty 的裸 Shift 键盘钩子、裸修饰键武装、
组合键与修饰键松开热键匹配、_QueueInputHotkey
- KeyEventSinkInternal.h:上述文件之间共享的声明,只给
KeyEventSink*.cpp 包含
纯搬移,不改行为:函数体、注释和格式都没动。原来在匿名命名空间里、现在
要跨文件用的函数(SegmentEditFunction、CaptureIpcModifiers、
IsEnglishInputModeToggle、IsTranslationCommitShortcut、
IsCharacterSetInputModeToggle、PostOwnerMessageWithSyncFallback、
IsShiftVk)仍定义在 KeyEventSink.cpp,改放进 key_event_sink_detail 命名
空间并在内部头文件里声明;只被一个文件用到的仍留在该文件的匿名命名空间。
THIRDPARTY_NEXTPAGE/THIRDPARTY_PREVPAGE 宏和本来就是 inline 的
VKeyFromVKPacketAndWchar 原样挪进内部头文件。唯一的非搬移改动是
PostOwnerMessageWithSyncFallback 的默认实参从定义挪到了头文件里的声明上。
没有新增全局构造器或函数内静态变量。
用脚本比对了原文件与拆分后全部文件的非空、非 #include 行多重集:除上述
函数签名外,差异只有新增的声明、命名空间包装、using 指令、#pragma once
和文件头注释。可用 git diff --color-moved 复核。
2026-09-27 17:38:47 +08:00
fanlusky
d852a05687
fix(windows,server): 选词回复超时不再上屏未显示的候选
...
电池模式下数字选词常上屏与所见不同的候选。链路是:TSF 只等 50ms
回复;Server 在回复前同步写调频,渲染同步 30ms 到点后按未绘制的页面
选词;超时后 TSF 断管重放按键,Server 又调一次频。
- server:选词调频(拼音/五笔/英文)改为 worker 队列任务,在回复之后、
下一键之前执行,不再占用回复时间。
- server:渲染同步改为条件变量,绘制回显即唤醒;上限 30ms 提到 80ms。
- windows:上屏类回复等待 300ms;请求已送达仍超时时视为投递不确定,
只重建前缀并丢弃上屏键,不再无条件重放。
- server:同一选择 3s 内在新连接上再次到达时跳过调频,避免重放重复计数。
2026-09-27 00:47:36 +08:00
fanlusky
b3b3b3303c
feat(windows): 焦点切换到另一个输入框时提示当前中英文状态
...
新增开关 general.caret_state_indicator_on_focus(默认关闭,需同时开启光标
状态提示)。
- DLL 把以下情况视为“换了输入框”:焦点窗口变化(Win32 编辑框、对话框、
其他应用各有自己的窗口);焦点经过一段没有可编辑文档的间隙后落到可
编辑文档上(Chromium、WPF 等单窗口宿主在字段之间就是先失焦再聚焦);
焦点会话真正结束后重新开始。Chromium 打字时在两个可编辑文档之间直接
替换文档管理器,不会触发。从 Win+. 面板返回不计入;线程焦点在真正的
应用切换后返回计入。一次切换的多次回调由定时器合并为一次提示;组字
过程中、以及没有光标的控件都保持静默。每次判断在 TSF 诊断日志里记录
判定标志,不含任何文本
- 协议复用 IMESwitch:wch 改为 FanyImeCaretStateTrigger(UserToggle /
CapsLockEdge / FocusEntered),CapsLockEdge 保持首发时的 VK_CAPITAL
取值,Server 对未知值按 UserToggle 处理
- Server 在开关打开时显示当前的有效模式(Caps Lock 开启时为“英”);
配置项同步写入两份模板和两个设置窗口宿主的读写映射
- 设置页在光标状态提示卡片中增加“切换输入框时提示”开关
2026-09-26 17:09:34 +08:00
fanlusky
597d087580
fix(server): 光标状态提示仅由用户快捷键触发并收紧协议边界
...
- DLL 只在 Shift/Ctrl 切换、Shift+Space、Ctrl+.、Caps Lock 与
Ctrl+Shift+F 处显式请求提示;Server 下发的模式同步、宿主转换模式
同步和激活初始化不再弹出提示,移除 compartment 回调里的推断逻辑
- 恢复文档切换时候选窗的 OnKillThreadFocus,改用 COM 身份比较
- 能力位改名为 CaretStateIndicator,DLL 仅在 Server 确认后发送切换
事件;Server 端这些事件只驱动提示,不再改写工具栏或状态快照
- 标点提示的中英模式由 DLL 随 PuncSwitch 携带
- UILess 宿主不显示提示,会话转为 UILess 时隐藏已显示的提示
- Ctrl+Shift+F 的锚点就地读取,不再覆盖 Global::Point,且只采信
协商了该能力的客户端
- 撤回对所有包默认 point 的修改;提示窗口创建失败不再阻断 Server
启动;提示内容改为结构化 CaretStateBadge
- Caps Lock 在同一按键重复进入 TestKeyDown 时只处理一次
2026-09-26 13:58:22 +08:00
AaGMix
a855a9c7f4
feat(windows): 发送光标状态切换事件
2026-09-22 21:20:07 +08:00
fanlusky
94df9a7917
fix(stats): 修复统计功能审查发现的 8 个问题
...
- Server 退出时唤醒统计管道监听线程,避免从未有客户端连接时 join 永久阻塞
- DLL 发送批次时对 ERROR_FILE_NOT_FOUND 重试,覆盖服务端重建管道的窗口
- 新增 StatisticsEnabledChanged(26) 推送开关,DLL 侧在采集入口直接短路:
关闭时不读 range、不分类、不开管道
- stats_meta 的 schema_version 写入正确的键名(此前键与值都是 "1")
- dropped_count 仅在实际发送的批次中消费,空批次不再吞掉丢弃计数
- _CaptureCompositionStats 改为遍历 composition range 的克隆,避免移动宿主的活动区间
- FanyImeStatsEvent 显式声明 reserved 尾部,不再把未初始化的填充字节写入管道
- 透传统计去重标记在抑制一次后即消费,避免同一 tick 内的连击被漏计
2026-09-20 20:34:50 +08:00
Jia Sui
ffcd1aee28
feat: 输入统计(DLL 采集、Server 聚合存储与设置页展示)
...
- windows: 三上屏出口 + OnTestKeyDown 直通字符采集,修复半角数字恒零
- engine: 统计 Web 消息契约(statsRequest/statsResponse)
- server: 统计管道接收、聚合、SQLite 存储、保留策略与每日自动清理
- ui-html: 设置页「统计」模块(卡片、热图、明细、开关、清理)
- docs: PRIVACY / README 改为内置可选统计
2026-09-20 19:08:16 +08:00
fanlusky
1640cb353f
fix(punctuation): 终端里智能标点改写恢复 SendInput 兜底
...
重构智能标点(#413)之后,「英文/数字后面连按两次同一个英文标点变回中文
标点」在 Windows Terminal 里失效:输入 `1.` 再按一次 `.`,屏幕上变成
`1.。`。
#413 把原来的 SendInput(合成 VK_BACK + KEYEVENTF_UNICODE)改写换成了编
辑会话里的 ITfRange::SetText 原地改写。原地改写要求宿主背后有一份真正的
文档文本存储;终端类宿主的 TSF 上下文只是一层代理,不保存已上屏的文字,
于是 ShiftStart(-1) 拿不到前一个字符,代码退回 fallback
insertChinesePunctuation(),把中文标点追加在 ASCII 标点后面而不是替换掉
它。SendInput 走的是系统输入队列,与宿主实现无关,这也是老方案在哪儿都
能用的原因。
两条路并存:先试原地改写,失败才排队 SendInput 改写,两条都不成立时才是
原来的 fallback。区分宿主不靠进程名,而靠写入前把待改写字符读回来校验——
空壳存储会照单全收 ShiftStart 和 SetText 并报告成功,只有把字符交还回来
的存储才真的能被原地改写。
SendInput 改写从编辑会话里 PostMessage 出去,执行前校验焦点会话 token、
前台窗口和 500 ms 期限,任一不符就放弃,而不是对着已经挪走的光标盲目退
格;每个清除 action 的地方一并取消排队中的改写。合成事件带自生成标记,本
输入法自己的按键钩子据此放行。
顺带补上一个洞:FUNCTION_SMART_PUNCTUATION_CONVERT/REVERT 在 replay 与
prevalidated 路径上没有处理者,按键会被吃掉后静默丢弃,现在走同一个编辑
会话。
两条改写路径各加一行诊断日志,走已有的 tsf_diagnostic_log 开关,默认关
闭:宿主用的是哪种存储从外面看不出来,出问题时不用靠猜。
2026-09-19 17:21:30 +08:00
Lu Fan(SonnyCalcr)
4dea1cd12f
Merge pull request #420 from metasequoiaime/feat/ctrl-arrows-segment-caret
...
feat(input): 组词内按分词的光标与删除(#35 / #187 / #347)
2026-09-18 12:35:52 +08:00
Jia Sui
2576986881
feat(windows): Ctrl+左右键按分词移动光标
...
组词中 Ctrl+Left/Right 按一个字的拼音(音节段)移动光标,
与 Ctrl+Backspace 的删除粒度共用同一套引擎分段边界。
- 闸门归纳为 SegmentEditFunction(code, modifiers):Ctrl-only + 组词存在,
VK_BACK/LEFT/RIGHT 分别映射到 segment 函数;三处判据与延迟投影共用
- 新增 FUNCTION_MOVE_LEFT_SEGMENT / FUNCTION_MOVE_RIGHT_SEGMENT,
编辑会话只按 CompositionRestored 的 caret 移动本地光标,不重建 buffer
- Server 用 segment_raw_boundaries() 取前/后边界,能力位与非 UiLess 时回包;
非拼音/本地模式/UiLess/旧端仍按单字符 ±1 回退
Refs #187
2026-09-17 23:36:34 +08:00
Jia Sui
b1ec3ab1e0
feat(server): Ctrl+Backspace 按分词删除前置拼音(#187)
...
组词中 Ctrl+Backspace 删除光标前的一个分词单元:剩余拼音非空时删
最后一个字的拼音(音节段),剩余拼音为空时删除最后一段已选分词。
engine:
- InputSession::segment_raw_boundaries() 作为按段删除/移动的唯一分段来源:
全拼把纠错显示串的分隔符映回 raw 下标,双拼与 pinyin_segmentation 共用贪心谓词,
2 字辅助码自成一段;非拼音方案与本地模式返回空表示无单位模型
server:
- IsSegmentBackspaceKey(Ctrl-only)、整段丢弃 / 边界截断 / 单字符回退三分支,
截断点残留分隔符清理
- 复用 CompositionRestored 回包(含 caret),仅协商能力位且非 UiLess 时发送;
新出现「只剩已选汉字、无拼音」的中间态
windows:
- 两处 Ctrl 闸门对组词中的 Ctrl+VK_BACK 放行,新增 FUNCTION_BACKSPACE_SEGMENT
与 _HandleCompositionBackspaceSegment(读回包重建,失败回退单字符)
- 空 raw 状态渲染、终态清理 word_for_creating_word,延迟投影失效处理
Closes #187
2026-09-17 23:01:31 +08:00
Jia Sui
62c9f5cf6b
fix(windows): Backspace 清空预编辑后长按不外溢(#347)
...
从组词里起手的那一次 Backspace 按住,一旦 composition 被删空,
同一次按住的后续 auto-repeat 一并吞掉;松开后重新按下才恢复退格。
- 新增 KeyRepeatGuard 纯谓词(repeat 位判定,不依赖 keyup、不用计时器)+ x64/x86 单测
- OnTestKeyDown / _DispatchKeyDown / _ClassifyDeferredKeyDown 三处同判据:
吞键不发 IPC、不请求编辑会话,命中的排队键完成 replay token
- 延迟投影把 inputLength 打到 0 时置位,慢 Server 下重放不会漏到宿主
- 焦点/上下文清理路径复位(OnSetFocus、_ClearDeferredKeyDowns、
ThreadFocusSink::OnKillThreadFocus),不在 Chromium 文档切换路径误复位
Refs #347
2026-09-17 21:02:07 +08:00
fanlusky
a898c3b11c
feat(candidate): Ctrl+Enter 上屏候选右侧的译文
...
候选框开着且高亮候选带译文时,Ctrl+Enter 上屏那条译文而不是候选本身。
译文只有一条就直接上屏;有多条时把候选框整个换成这几条译义,空格/数字键
照常选一条上屏。
TSF 端原先在分类器最前面把所有 Ctrl 组合都还给应用,Ctrl+Enter 到不了
Server。现在在那条拦截之前先判一次:候选框开着才认,否则组合仍归应用。
认下来的键走既有的 FUNCTION_SERVER_CANDIDATE_KEY 通道,由 Server 决定
是上屏(CommitExactText)还是只开一个新候选页(NavigationIgnored)。
译文页复用同一份 candidate_ui.items,因此云/AI/英文/emoji/颜文字这些异步
回调和候选扩展都要在这个子模式下让路,否则会把译文冲掉。输入串全程没动,
退出时把存下来的 items 和页码原样放回去即可。译文页上的空格/数字键只回
Normal / OutofRange——SELECT_BY_NUMBER 的 TSF 路径只认这两种。
2026-09-17 20:21:56 +08:00
Jia Sui
d4a0796400
feat(windows): 重构智能标点
...
原「智能标点」是小狼毫 digit_separators 的语义:字母或数字后的 , . : 直出英文,
2 秒内再按同一标点转回中文——后半段靠 SendInput 往应用里补一个 Backspace 再打
字符,落点全靠光标当时在哪。这次换掉主路径:输入中文标点后按空格,用 TSF 编辑
会话就地把它改写成对应英文(。,!?;:、 → . , ! ? ; : /),2 秒内再按同一标点
键撤回中文;原来的直出降为子开关,整个家族默认全关、改为显式开启。
- 按键与会话:转换/撤回是本地文档改写,不进 IPC、不进延迟队列;判据落在
_IsKeyEaten,OnTestKeyDown 的预判与 _DispatchKeyDown 的第二遍预判共用同一份,
声称吃掉的键不交还宿主。改写走编辑会话,请求不上时另起一个最小会话把键本身
的字符写回去,避免已吃掉的键静默丢失。
- 状态:原来散落的九个成员收成一个 SmartPunctuationAction(None / ChineseCommitted
/ AsciiConverted),带焦点令牌、前台窗口和 beforeChar 指纹;清理以分类结果为准,
丢焦点与上下文栈变化一并清掉。
- 转换范围:。,!?;:、 与单独出现的 “”‘’【】《》() 都是映射表里的单字符
条目,路径完全一致;成对补全产生的一对(autoClosedPair)不 arm,按空格只落空格,
不会留下 <》 这类孤儿。
- 开关:总开关 + 中文标点后按空格转换 + 同键转回中文 + 数字后直出 + 字母后直出,
五键全部默认关;直出拆成数字/字母两个独立子开关(IPC 24 收窄为数字、25 追加为
字母),TSF 按前字符类型分别查键。
- 命名:子开关「独立符号」改名「中文标点后按空格转换」,配置键
smart_punctuation_standalone → smart_punctuation_space_convert,IPC 22 只改常量名
(SmartPunctuationSpaceConvertChanged),编号与语义不变、新旧测试包互通;设置页
文案同步重写,五处配置链路与死宿主一并跟改。
- 兼容:旧键未出厂、不迁移(升级合并按模板缺键丢弃、取默认 false);编号 23 随
「按空格整体转换一对」的设计退役并留空不复用。
- 验证:TSF x86/x64 与 Server Release 编译、Server 335 单测、windows_ipc_contract
x64/x86、设置页 build + 27 测试、clang-format 18.1.8 全绿。真机未跑。
Signed-off-by: Jia Sui <jsfaint@gmail.com >
2026-09-17 09:29:57 +08:00
fanlusky
5a5dea6b7e
feat(japanese): 日语模式禁用 -/= 翻页并用 - 打长音符
...
日语模式下把 - 让给长音符(ー)输入,同时禁掉 -/= 的候选翻页:
- 罗马字方案接受 VK_OEM_MINUS,set_raw_input 不再把 - 过滤掉;
罗马字表里本来就有 - 到 ー 的条目,只缺按键准入。
- n 后面接 - 时按辅音处理,让前面的 n 收成 ん,避免 n- 卡在 pending。
- 空编码时单独按 - 也起头组合,候选框第一项是 ー、第二项是普通 -。
- 组词流程同样适用:判据只看日语模式加组合状态,不再走翻页和词转字。
- -/= 在日语模式下不翻页,_ = + 这些没有假名写法的字符退回标点上屏,
并把 = _ 补进服务端的标点集合,和 TSF 侧保持一致,否则会等不到回包。
TSF 侧的影子分类、IsVirtualKeyNeed(ForFreshComposition) 和服务端按键策略
三处同时改,统一以配置项 input_mode == japanese 为判据(R 模式不在范围内,
TSF 侧看不到临时会话,用会话类型判断会让两侧分类打架)。
2026-09-15 01:57:50 +08:00
fanlusky
c9a5d0fd2b
feat(tsf): 成对标点补全加修饰键闸门与配对栈
...
问题:成对标点上屏后靠 PostMessage + SendInput(VK_LEFT) 把光标送回中
间。(〈《""' 全是 Shift 组合键,而 PostMessage 是异步的,合成的
VK_LEFT 常常在物理 Shift 仍按住时抵达宿主,于是变成 Shift+Left——右
半边标点被选中。机器差异只是竞态窗口大小的差异,【】这类不需要 Shift
的键从不复现,可作对照。另外按右半边标点会再插一个,得到(内容));
而引号方向由左右交替维护,右引号根本打不出来。
改动:
- 修饰键闸门:送键前用 GetAsyncKeyState 查 Shift/Ctrl/Alt/Win 是否物
理按下,按住就挂 15ms 重试定时器推迟,而不是合成一次 Shift 抬起
(WindInput 的源码注释记录了那条路走不通)。Win 键同样要拦,Win+Left
是贴靠窗口的快捷键,箭头落进按住的 Win 组合会重排桌面。带 2s 超时
和焦点会话校验,过期即放弃并清栈。
- 自生成按键标记:合成的 INPUT 带 dwExtraInfo =
PAIRED_PUNCTUATION_SENDINPUT_EXTRA_INFO,四个按键 sink 入口和
_BareShiftKeyboardHookProc 在开头识别后放行,IME 不会把自己发出去
的键再处理一遍,跳过配对时也就不会清掉正在走的那个栈。
- 配对栈:参照 WindInput 的 pair_tracker,记录 {左半, 右半, 焦点令牌},
深度上限 16。按右半边标点时先读文档确认光标右边确实是那个字符,确
认后跳过而不是再插一个;焦点切换、中英与标点模式切换、任何结构性
按键(方向键、Backspace、回车等)都清栈。
- 引号在成对模式下每次按都开一对新的,不再走 GetPunctuation 的左右交
替,右引号因此可达。
- 消息窗口销毁时走 _CancelPairedPunctuationCaretMove 而不是裸
KillTimer:重试定时器额外挂着 _pairedCaretRetryTimerActive,只杀定
时器会把标志停在 true,消息窗口重建后就再也装不上定时器,等于延迟
送键这条路被静默废掉。
已知限制:连续按住 Shift 跨两个成对标点键(如 " 接 ()会得到 ""()
而不是 "()"。这是延迟送键设计的固有取舍,WindInput 同样如此;要消
掉需走 TSF SetSelection 兜底,或合成一次 Shift 抬起。
Backspace 整对删除未实现:判断左右两半必须读文档,而读文档需要编辑
cookie,不吃键的 Backspace 路径没有。不验证就合成 VK_DELETE,在栈因
鼠标点击而失效时会删掉无关字符。
验证:Debug 与 Release 的 x64、x86 四种组合均编译通过;clang-format
对改动文件无差异;本机实测成对标点上屏后光标落在成对标点中间。
2026-09-13 17:48:41 +08:00
fanlusky
a7779d2b05
refactor(engine): 将 engine 从 submodule 内嵌为仓内一等组件
...
`vendor/MetasequoiaImeEngine` submodule 改为 `engine/` 目录,导入的是
product-lock.json 原本钉住的提交 c810d201,逐文件核对过 blob,因此这是一次
搬迁而不是引擎升级。引擎自己的嵌套 submodule(googlepinyinime-rev、utfcpp、
miniaudio)按各自 gitlink 一并展开,来源与提交见 engine/UPSTREAM.md。
Windows 专门化裁掉了不属于本产品的部分:whisper.cpp 与本地推理 provider、
macOS 示例宿主、整个 dictionary/ 建库流水线、上游 CI 与 release-please 配置。
词库仍按 product-lock.json 在构建时取发布产物。
引擎不再进 product-lock.json:没有 gitlink 就没有可比对的东西,锁里写一个上游
commit 等于宣称这棵树仍等于上游,而第一个 Windows 专门化改动落地时这句话就假了。
verify-contracts / verify-checkout 随之移除,verify-published 保留。
CI 与工具链:引擎源码并入根 scripts/format.sh(删掉引擎自己的那份,避免两套排除
列表分叉);引擎 voice.yml 里的 silero_vad.onnx 摘要校验移到本仓 lock job;
engine-bump-triage 工作流与其测试删除;gitsubmodule 的 dependabot 频率由 daily
改为 monthly。
README、README.en、AGENTS.md、CONTRIBUTING、docs/product-release.md 及各子目录
文档同步说明新的位置与边界。
2026-09-12 03:40:48 +08:00
fanlusky
0febd575ac
refactor(windows): clear the TSF /W4 warnings
...
The bulk of them were left behind when the keystroke latency logging was
removed: PerfTimer objects and *ElapsedMs doubles that nothing reads any more
(C4189). Dropping them also takes the QueryPerformanceCounter calls back out of
the keystroke path. The timers that still feed DebugTsfKeyLatency (Ipc.cpp,
KeyEventSink.cpp, KeyHandlerEditSession.cpp) are untouched.
The rest, all behaviour-preserving:
- C4463: the four CompositionProcessorEngine configuration flags were one-bit
BOOL fields, so every `= TRUE` stored -1. They are plain BOOLs now.
- C4456: renamed the shadowing inner locals (pLI -> pItem, focusToken ->
expectedFocusToken).
- C4100: commented out the parameter names that are part of a TSF interface but
unused in the implementation.
- C4702: dropped the unreachable `return 0;` after FreeLibraryAndExitThread,
which is __declspec(noreturn).
- C4102: removed the two vestigial NoPresenter labels and the empty branches
that used to jump to them.
- C4267/C4244: made the narrowing conversions in the pipe write and the offline
key handler explicit.
- C4996: SharedConfigPath now uses GetEnvironmentVariableW instead of the
deprecated _wgetenv.
x64 and x86 Release both build clean; ctest passes in both.
2026-09-12 00:55:56 +08:00
Ouchihao
1d2431add6
fix(windows): commit highlighted candidates with numpad punctuation
2026-09-11 19:32:17 +08:00
fanlusky
580ac98d0a
fix(windows): match bare-Shift release on the arming latch, not GetKeyState
...
Word does deliver the bare-Shift release to the key-event sink; what it
does not deliver is a fresh GetKeyState() snapshot alongside it. Hosts
that call into ITfKeystrokeMgr outside the message dispatch see the
modifier state as of the last message the thread pulled, so
Global::PureShiftKeyUp/IsShiftKeyDownOnly -- both derived from
GetKeyState() -- can still describe the previous keystroke. The release
match then silently fails even though OnTestKeyUp and OnKeyUp both ran.
Match on the released virtual key plus _shiftHotkeyArmed instead.
The latch already means "this Shift went down alone and nothing else has
been pressed since" (_TrackModifierHotkeyArming disarms on any other
key-down) and it self-clears, so TestKeyUp/KeyUp cannot double-toggle.
Arming reads GetAsyncKeyState(), which reports the real keyboard rather
than the host thread's queue. Same treatment for bare Ctrl.
This is how Weasel and WindInput handle it -- neither installs a
keyboard hook for the toggle -- so revert the hook back to mintty-only.
mintty is a genuinely different failure: it offers no bare modifier
key-up to the sink at all, so there is no event to match against.
2026-09-11 03:07:14 +08:00
fanlusky
0838681465
fix(windows): restore bare-Shift CN/EN toggle in Word
...
70930df stopped eating the bare-Shift release so JetBrains double-Shift
keeps working, and moved the toggle into OnTestKeyUp because TSF does not
call OnKeyUp after a FALSE test result. Word never completes a bare-Shift
release through ITfKeyEventSink at all, so neither callback runs and the
CN/EN toggle was lost there.
The mintty workaround already solves exactly this shape: a thread-local
WH_KEYBOARD hook watches the real bare-Shift press/release and feeds it
back into the deferred hotkey path, with _MarkBareShiftHandled() latching
presses the key-event sink already toggled so hosts that do deliver the
release never toggle twice. Install that hook for every host instead of
only mintty.exe, and rename its members off the mintty-specific naming.
Also log the three bare-Shift release paths under [issue47] -- key-up was
previously uninstrumented, which is why the Word diagnostic log could not
distinguish "release never arrived" from "toggle queued but dropped".
2026-09-11 01:34:00 +08:00
Aegon8719
6ff1cff42d
issue-52 加入简繁切换快捷键;增加候选词翻页快捷键组合、增加以词定字快捷键组合;
...
issue-55 候选词与辅助码间保留一个半角空格。
2026-09-08 14:07:48 +08:00
Evan
42238a2af6
style: 用 .clang-format 重排 server / windows / ui / log
...
一次性重排,无任何行为改动。四个目录各自的 .clang-format 放在仓库里已久,但全组织只有
MSIME-Linux 在 CI 里执行 clang-format 检查,这边 149 个文件里有 149 个不符合自己声明的风格。
已逐文件核对语义等价:去掉空白、注释与相邻字符串字面量拆分之后,全部 149 个文件与重排前逐字节
相同。差异只有三类——缩进与换行、clang-format 补的 } // namespace 结尾注释、以及长字符串被拆成
相邻字面量(C++ 保证自动拼接)。
这个提交会写进 .git-blame-ignore-revs。
2026-09-06 12:13:47 +09:00
Evan Hu
253b068984
refactor: move the Windows TSF frontend under windows/
...
Preparation for folding Server, UI, UiHtml, Installer, ImeLog and TsfEditControl into this repository. The Windows tip's own sources, build files and local build scripts move under windows/ so every component sits at the same level. Product-level files stay at the root: the lock, the release manifest, the shared CI scripts, the integration tests, the docs and the Engine submodule.
2026-09-06 02:24:27 +09:00