987 Commits
Author SHA1 Message Date
Jia Suiandfanlusky 456b23ed94 feat(engine,server): 五笔支持 T 模式与 V 模式 (#942)
* feat(engine,server): 五笔支持 T 模式与 V 模式

五笔方案此前被排除在 Shift+T 日期时间模式与 V 模式之外。根因是方案层就把
raw 归一成小写 a–y、丢弃数字与运算符,Server 的形状判定读不到大写 T/V 前缀。

- 引擎 WubiScheme 新增 raw_input_with_cases_:保留大小写与 T/V 模式的非字母键
  (数字、运算符、: /),但按码字域丢掉越域字母(默认配置下 z)。查询码仍由
  normalize_wubi_code 派生,普通码下两者长度相等——Server 用原样串长度数四码。
- Server:T 触发新增独立谓词 date_time_scheme(chinese_scheme || Wubi),不并入
  chinese_scheme 本身;FormatVModeWorkerPayload 对五笔返回 UppercaseOnly。
- 自动上屏 / 顶字两个谓词新增 special_mode_active 前置:T 模式关键词归一后可能与
  合法四码重合(Tdate → tdat),不挡会在第四个字母上结束模式。
- 引擎与 Server 测试、README 与两份配置模板文案同步。

* fix(engine,server,ui-html): 五笔 T/V 模式由宿主标注字面输入

原实现让五笔原样串不再裁到码长,普通码下原样串可以比码长(混输关、z 通配时 wqazb 对 wqaz),
Server 读回后光标、退格和上屏都会错位;守卫也只认 T 模式的全小写主体,TdatE 仍会顶出 tdat。

- 引擎:InputSession::set_wubi_literal_input 随 pending 序列一起下发,只作用于同一次设置。
  普通码的原样串就是带大小写的码,与查询码逐位对应、一起截断;字面输入原样保留,
  不查码表与拼音,也不算完整四码。退格从原样串重新派生码,字母键两份视图取同一个字母。
- Server:ApplyCompositionEditKey 按 IsWubiLiteralComposition(T 模式触发标记或 V 模式形状)
  标注字面输入;两个五笔上屏策略恢复原签名,不再需要 special_mode_active。
- 设置页 T/V 模式说明补上五笔。

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-15 02:08:36 +08:00
Jia Suiandfanlusky 1499027f04 fix(engine,server): 光标落在拼音首单元内部不再收起候选窗 (#940)
* fix(engine,server): 光标落在拼音首单元内部不再收起候选窗

quantized_prefix_end() 把光标 floor 到完整单元边界后,prefix_end==0 同时表示「真串首」与「首单元内部」。后者被 Server 的 IsCaretPrefixEmpty 当成串首,候选窗整窗收起(全拼 nihaoya 选你好后剩 ya 左移即消失)。

floor 结果为 0 且 caret>0(首单元内部)时改返回 raw.size(),落回整串解码;prefix_end()==0 当且仅当 caret==0,只有真串首才隐藏。segment_raw_boundaries() 与 #187「前缀只切完整单元」的契约不变。

* fix(engine,server): 首单元内部的光标向上取整到首单元末尾

上一提交让首单元内部的光标落回整串解码,prefix_end() 因此报告整串已解码,
与实际光标位置不符:Server 选词时 caret_prefix_selection 为假,不复位组合
光标,造词后剩余串带着旧光标位置;首单元与后续单元的取整方向也不一致。

改为向上取整到首单元末尾(只有一个单元时即整串)。prefix_end() == 0 仍当且
仅当 caret == 0,前缀选词走正常的光标复位路径。同步更新引擎与
input_key_policy.h 中描述旧规则的注释和相应测试。

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-15 01:37:58 +08:00
Ten-Shine 850ad772a1 feat(server,ui-html,windows): 添加任务栏语言栏按钮显隐配置
任务栏语言栏(输入法指示器旁)此前固定注册中/英模式、全/半角、中文标点三个按钮,
用户无法隐藏。新增 [language_bar] 配置节:enabled 是总开关,关闭时不再向
ITfLangBarItemMgr 注册任何按钮;show_ime_mode / show_double_single_byte /
show_punctuation 分别控制单个按钮。键缺失或共享配置不存在时默认全部显示,
旧配置与全新安装的行为不变。

- 出厂模板 config.default.toml 与工作配置 config.toml 新增 [language_bar] 节,
  四个键默认 true;升级时 MergeConfigIntoTemplate 以出厂模板为骨架重建用户配置
- Server:LoadImeConfig 读取四个开关,ime_config_general 提供成对读写入口;
  设置进程 BuildConfigMessage(configSnapshot)与 ApplyConfigUpdate(path 分发)
- 设置页「工具」分区新增语言栏卡片:总开关 + 三个子开关。总开关关闭时子开关
  置灰禁用但保留勾选展示,交互与候选混输一致;快照回填与点击上报均有测试覆盖
- TSF DLL:SetupLanguageBar 在注册时机读取共享配置,不注册的按钮不会出现在
  语言栏。按钮对象仍创建以保持析构对称;模式与标点切换走 compartment,
  隐藏按钮不影响输入
- 改动需重启生效(按钮在 TSF 激活时注册),设置页已注明
2026-10-14 13:54:11 +08:00
Lu Fan(SonnyCalcr) e1f9e26767 fix(server,ui-html): 收紧微软皮肤的候选尺寸、选中条与翻页箭头 (#934)
- 候选行上下内边距 0.235em → 0.12em(行高约 1.82em → 1.59em),左右内边距 0.76em/0.9em → 0.4em/0.3em
- 横排每项最小宽 4.35em → 3.5em,项间距与换行行距 0.28em → 0.15em
- 选中条 max(3px, 0.24em) × 1.1em → 3px × 0.9em
- 翻页三角 0.47em × 0.56em → 0.44em × 0.52em,圆角由高度的 1/4 加到 3/8,两三角间距减半
- 候选窗 CSS、设置页预览与 D2D 端同步修改
2026-10-14 02:20:39 +08:00
Lu Fan(SonnyCalcr) e46294308d fix: CS2 / Dota 2 / 三角洲行动等游戏里的输入法兼容(加载、候选、翻页、独占全屏、启动崩溃) (#925)
* fix(installer): TSF DLL 改装到系统目录,CS2 等 Trusted Mode 游戏里可以切出输入法

CS2 的 Trusted Mode 只放行位于系统目录且有代码签名的第三方 in-proc DLL,
装在 Program Files 下的 DLL 会被拒绝加载,游戏里切不出输入法。
x64 DLL 改装到 System32\IME\MetasequoiaIME\msime_v*,x86 装到 SysWOW64 下同名目录,
语言配置图标随之放到两处根目录;升级时清掉旧版留在 Program Files 的版本目录。

* fix(windows): CS2、Dota 2 不理会 UI-less 声明,照常显示候选窗

起源 2 引擎的 Valve 游戏以 UI-less 激活 TSF,但只替白名单里的输入法绘制候选。
我们据声明收起了自己的候选窗,游戏又不画,结果只剩左上角的系统默认 IME 小框,只能盲打。
对 cs2.exe、dota2.exe 忽略 UI-less,照常由 Server 绘制候选窗。

* fix(server): 前台为 D3D 独占全屏时不弹候选窗与光标状态提示

独占全屏下别的进程的窗口画不上去,弹出去反而可能把游戏踢出独占模式(闪黑、切分辨率、卡死)。
在 UI 线程真正显示前用 SHQueryUserNotificationState 判一次(300ms 缓存),独占全屏就收起候选窗,
并清掉已渲染可见标记,数字键与空格选词不再等待不会到来的绘制。

* fix(engine,server,windows): 游戏自绘候选时后台改动的候选列表实时同步给宿主

英雄联盟等 UI-less 宿主自己画候选。云候选、AI 候选、模型重排、英文混排在按键回复之后才改
候选列表,却从不通知 DLL,游戏还画着上一键读到的旧列表,数字键和空格却按新列表上屏,
于是「选的和上屏的不一样」。

- 契约新增 Worker opcode 35 UiLessCandidatesChanged,载荷与 UiLessComposition 同形,
  由 uiless_page.h 统一格式化与解析,并附候选页代号;旧 DLL 读选中位时在第三个制表符处停下,兼容。
- Server 在上述后台改动之后把新页推给候选归属的客户端。
- DLL 收到后回到 TSF 线程核对焦点与组合纪元,更新候选 UI 元素并通知宿主重读。推送与按键回复
  走不同管道,按代号只接受不旧于当前的页,避免旧页盖掉新页。
- 从共享内存重读到不同的页时高亮回到第一项,与 Server 每次重建都选中首项一致,
  不再保留旧高亮造成「高亮这个、空格上屏那个」。

* feat(windows,server,ui-html,installer): 游戏兼容开关,CS2 / Dota 2 独占全屏打字不再黑屏

CS2、Dota 2 只替内置白名单(按输入法登记名称全等比对)里的输入法处理输入法消息。不在名单里时
组合落到 Windows 默认组合窗 CiceroUIWndFrame,左上角弹出编码小框;独占全屏下这个窗口一出现
游戏就被踢出全屏,表现为一打字就黑屏。清风输入法未开兼容时同样如此。

- 设置页帮助页新增「游戏兼容(CS2 / Dota 2)」:开启后把水杉在系统里登记的名称换成白名单里的
  名字(默认「拼音输入法」),游戏会自己画候选。需要管理员权限:设置进程以
  --apply-game-compat-name 提权重启自身,写 HKLM 记录值并改语言配置的 Description。
- TSF DLL 注册时以记录值为准,安装/升级重新注册不再把用户选的名称冲掉;卸载时删除记录值。
- 开启时不再对 cs2.exe / dota2.exe 忽略「宿主自己画候选」的声明,避免两个候选框。
- Server 的独占全屏判断覆盖进出独占模式时的 QUNS_BUSY 间隙,不在切换途中弹候选窗。
- TSF 诊断日志在组合开始/结束与 BeginUIElement 时记录宿主窗口状态(只有窗口与标志,没有文本)。

* fix(windows): 后台刷新回调持有加载器引用,宿主卸载 TSF DLL 后不再崩溃

当前输入法是水杉时,CS2 启动途中会加载再卸载 TSF DLL。诊断日志的线程池回调
FlushTsfDiagnosticLogs 正在 Sleep(250),只靠 DllAddRef 撑着:那只回答 DllCanUnloadNow,
挡不住宿主直接卸载。DLL 卸载后 SDL3 映射进同一段地址,回调醒来跑进 SDL3 的代码,
游戏崩溃(0xc0000005,崩溃转储里卸载模块的地址范围与崩溃地址吻合)。

- 诊断日志与统计上报的线程池回调改为用 GetModuleHandleEx 取加载器引用,
  回调里 FreeLibraryWhenCallbackReturns,函数完全返回后才放手。
- 析构函数把 DllRelease 挪到最后,不在等待 IPC 线程退出之前就让 DLL 可卸载。

* fix(windows): 游戏自绘候选时不再把 Server 的 HTML 候选页当成候选交给游戏

CS2 开了游戏兼容后打一个码,游戏里的候选只有一项且是乱码。宿主要等 BeginUIElement 才说
自己画候选,而那时组合首键已经不带 UI-less 标记发给了 Server;Server 往共享内存写的是给
自己候选窗用的 HTML 页(辅助码、译文 span),DLL 的候选元素从共享内存回读后整串交给了游戏。

- 开了游戏兼容名称的 cs2.exe / dota2.exe 在 ActivateEx 就认定由游戏画候选,每个按键都带标记。
- 候选会话里收到过 Server 的 UI-less 页之后才从共享内存回读,之前一律不读。

* fix(windows): 游戏自绘候选时翻页能刷新,部分上屏后不再出现乱码候选

三角洲行动(虚幻引擎,以 TF_TMF_UIELEMENTENABLEDONLY 激活)里按 = / - / PageUp / PageDown
翻页,Server 已翻到第 N 页,游戏却还画着第一页;接着按空格上屏的是第 N 页的首项(多为单字),
剩下的拼音继续组合时候选框里出现乱码。

- 翻页、移高亮等键走异步的 WM_AsyncServerCandidateKey,以前把回复当作「只是确认」丢掉;
  UI-less 下这份回复就是新的候选页,现在应用并通知宿主重读。
- UI-less 的 _SetText 拿不到 Server 的候选页时不再退回本地引擎的占位列表——那不是候选,
  宿主会画成一条乱码。部分上屏后从共享内存回读 Server 写好的新页;_ClearList 同时清掉上次
  的页记录,页文本相同也会重新载入。
2026-10-13 21:58:15 +08:00
Hao Qu e39ea0b11c feat(server): 添加候选译文词性显示开关 (#915) 2026-10-13 21:57:46 +08:00
Lu Fan(SonnyCalcr) 119c439d7a fix(engine,server): 日文候选不再写进拼音词库,升级回放跳过已残留的日文词 (#907)
日文 provider 出的候选没有设置 scheme,默认是 Quanpin。在日文模式下选非首选
候选时,Server 按拼音调频,用罗马音作键(pan、amarini)写进拼音日志。msime.db
里没有这些行,UPDATE 改不到任何东西,但 update_pinyin_weight 照样记了日志;
升级回放时 apply_pinyin 找不到行就插入,日文词于是出现在全拼候选里。

- 日文候选标上 SchemeType::JapaneseRomaji
- Server 的选词调频、置顶、造词与云候选落库跳过日文候选
- update_pinyin_weight 没改到行时不写日志
- 回放跳过、打开日志时清掉自动学习来的带假名拼音条目;手动加的词不动

Fixes #869
2026-10-13 12:54:29 +08:00
時崎 ギャラクシー bcbbdcf74c Merge pull request #903 from Tokisaki-Galaxy/feat/skin-schema-v2
feat(server): 外部皮肤新增 schema_version 2,WebView2 与 D2D 双后端支持九宫格贴图与自由挂件
2026-10-13 00:57:14 +08:00
luo jiyin d58e706c9c test(perf): measure per-key model path resolution overhead (#846)
* test(perf): measure per-key model path resolution overhead

* docs(server): correct model benchmark binary path

* ci(server): upload model path latency sample

* ci(server): repeat model path benchmark three times

* test(perf): measure first-touch missing model paths

* fix(server): isolate model benchmark through CRT environment

* ci(server): measure paths before integration tests

* test(server): isolate model path benchmark from CI
2026-10-12 21:38:16 +08:00
Jia Suiandfanlusky 60c2b26698 fix(engine,server): 全拼纠错收窄「多字」纠错并让开关真正关闭纠错 (#889)
* fix(engine,server): 全拼多字纠错收窄为仅 QWERTY 邻键插入

多字(插入)纠错只保留「误插 QWERTY 邻键」:生成器 insertion_variants 不再生成重复相邻键(双击),生成式空间不再生成任意字母插入(远键插入)。autocorrect_table.h 随生成器重新生成。

* fix(engine): 全拼纠错开关关闭时真正不再改写切分

纠错开关关掉后真正不纠错:把手误拼写别名层从常开改为跟掩码走(ü 归一 jv->ju / nue->nve 仍常开);两个早于掩码可知的切分入口 QuanpinScheme::build_request 与 QuanpinDictionary::get_or_compute_segments 显式带上掩码,切分缓存键随之隔离。

* fix(engine,server): 纠错开关关闭时辅助码、写回与排序上下文也按字面切分

关掉全拼纠错后,只有方案的 build_request 和查询自身切分不再跑手误别名层,
其余几条路径仍用默认(带别名)的切分,和查询落在不同的键上:

- 辅助码路径自己切底串作为显式切分交给词典,haoyonga 仍被切成 hao'yong'ga;
- 调频、造词、删词、建 SQL、校验去掉分隔符后重切,开关关掉时选中 hao'yong'a
  的词会去更新 hao'yong'ga,调频静默失败;
- 排序 / 固定位置的上下文 key(引擎与 server 两处)仍按别名切,laing 与 liang
  的固定位置互相串。

改动:
- cut_pinyin_by_mode 的 include_correction_aliases 改为必填,每个调用点显式决定;
  双拼词典、设置页词库校验和离线工具传 true,保持原行为。
- 新增 quanpin::correction_aliases_enabled,方案、词典、辅助码与排序 key 共用
  (只看换位、邻键两位),修正只带删除 / 插入位的掩码在方案与词典两层判断不一致。
- 词典按最近一次 query 的掩码记录别名开关,写回路径统一走 cut_for_write。
- 会话改为在换方案、改开关时 bind_quanpin_scheme 一次,不再每次 refresh 下发。
- 方案只存 bool;jv -> ju 放回纠错表,去掉合并表。
- 在 CI 实际执行的 test_input_session 中补回归用例(写回、辅助码、固定位置、
  方案掩码),每条都确认回退修复后会失败。

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-12 20:41:23 +08:00
Jia Suiandfanlusky fc90cb1f2c fix(engine): 纯简拼读法否决权,纠错不再劫持 nbn 等简拼候选 (#881)
* fix(engine): 纯简拼读法否决权,纠错不再劫持 nbn 等简拼候选

纯简拼形输入(切分全是单字母声母)若词库 jp 列有该输入读法,纠错不得接管:
nbn 是「能不能」、nqn 是「女青年」、bqng 是「并且能够」,纠错却把它们改写成
字母更近的错串候选(难/帮),顶掉真实命中的简拼词。

输入形状分不干净——sahng 也全是合法声母却必须保留乱序纠错;唯一分得开的是
词库有没有这个简拼读法,故判定落在同时持有 DB 与纠错掩码的查询层。

- 新增 quanpin::has_pure_jianpin_reading(jp 列 LIMIT 1 查询)
- QuanpinDictionary::effective_autocorrect_types 统一 query_exact 与
  insert_word_to_series_cache 的有效掩码,避免 series 缓存键分歧
- 非纯简拼(shng=[sh,n,g]、shbng、sahng/shangg/sjhang)纠错不变

* fix(engine): 纯简拼整词改为排在纠错前面,不再整个关掉纠错

原方案在词库有纯简拼读法时把纠错掩码置 0,漏了元音的真手误(bng 想打 bang)
也随之纠不了,而且只有词库层知道纠错被关,预编辑与选词消耗仍按原掩码判断。

改为不动掩码:纠错照常进行,纠错分支里字面结果中的纯简拼整词
(literal_jianpin_pinyin_match:两个及以上单字母声母盖住整串,逐个是正读首字母,
a/e/o 不算)和混合简拼一样放进领衔组。纠错读法排在后面,仍可选、仍带纠错标记。
判定直接看已查回的字面结果,不新增 SQL;掩码不变,缓存键也不变,
因此删掉 has_pure_jianpin_reading 与 effective_autocorrect_types。

测试补上预编辑(n'b'n)、选前缀单字只消耗自己的声母、bng 仍保留「帮」。

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-12 17:23:30 +08:00
Lu Fan(SonnyCalcr) 341ca93be5 fix(server): 升级不再关掉老用户的候选词翻译 (#884) 2026-10-12 16:47:31 +08:00
時崎 ギャラクシーandfanlusky add122fcc6 feat: 候选词翻译新增 AI 辅助提供方,收敛为单一 translation.provider (#876)
* feat(engine): 翻译凭据测试新增 translation.ai 服务类型

* feat(server): 候选词翻译新增 AI 辅助提供方,收敛为单一 translation.provider

* feat(ui-html): 候选词翻译新增 AI 辅助提供方并跟随单一 provider

* fix(server): 将 ai_translation 加入编译目标与测试套件

* fix(server): 在 AiTranslation 命名空间导出 ParseTranslations 供测试调用

* fix(server): 解析模型返回时剥除 Markdown 代码块标记

* fix(server): AI 候选词翻译校验占位 Token、收紧解析,翻译配置改读快照

- AI 翻译的 Token 改用 IsUsableSecret 校验,出厂模板的占位符不再被当成可用凭据
- 解析模型回复时,合法 JSON 但键不对、数组长度不符或残缺 JSON 一律作废,不再把原文当译文显示和写进释义库
- 从旧布尔推导出腾讯云时保留 tencent_tmt.enabled 门禁;在设置页明确选择时才打开,升级不会替用户向腾讯云发送候选词
- 候选词翻译读取的 provider 与各服务配置合成一份加锁快照,配置重载时整份替换,候选线程和翻译 worker 不再直接读会被改写的全局字符串;每个任务只解析当前服务的配置
- provider 与三个旧布尔一次原子写入;旧的 niutrans/custom enabled 开关改为切换 provider
- AI 联想与 AI 翻译共用 ChatCompletions 传输层,翻译超时与 AI 联想一致改为 8 秒
- 设置页凭据测试与运行时共用 AiTranslation::FromConfiguredTranslation,provider 白名单只保留 IsValidTranslationProvider 一处

* fix(ui-html): 候选词翻译的 AI 配置提示随 AI 辅助页实时更新

AI 辅助页改服务商、地址、Token 或模型后,翻译区的提示和摘要立刻刷新,不用重开设置窗口;可用性判断与服务端一致,占位 Token 不算已配置。

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-12 16:47:04 +08:00
Jia Sui d495ea2ad0 fix(engine,server,installer): octagram 加成收敛为单一注入,校正量纲与参数,修复〔万象〕同音乱码 2026-10-12 13:12:21 +08:00
fanlusky 1fd6bebbe2 fix(server): 设为默认改为把水杉排到中文第一位
原实现只写「替代默认输入法」(InputMethodOverride),水杉在中文(简体)的键盘列表里
仍排在末尾,系统设置里看不到它是默认,Win + Space 切到中文时也不是水杉。

- SetAsDefault 不在列表时先 InstallLayoutOrTip 加入,再 SetDefaultLayoutOrTip
  挪到第一位并复核;不改语言列表顺序,也就不动界面语言
- 不再写替代默认输入法,旧版写下且仍指向水杉的在设置成功后清掉
- QueryStatus 改为判断水杉是否在所在语言排第一
- 加入失败时区分「输入法未注册」与「未添加中文(简体)语言」
- 「系统设置」按钮改开「语言和区域」
- 卸载时清理仍指向水杉的替代默认输入法
2026-10-12 03:56:28 +08:00
fanlusky 797c0f5652 feat(server,ui,ui-html): 候选窗可严格按文字宽度收紧
皮肤新增 [candidate_window] fit_content,设置页「外观」新增「候选窗口按文字收紧宽度」
(appearance.candidate_window_fit_content),两者取或。打开后:

- D2D 不再垫 160 DIP 卡片最小宽度和列表 80 DIP 下限,行尾不再多留 6 DIP,
  右侧留白与序号左侧留白相同;
- WebView2 去掉 .container 的 7em 下限;
- 同一输入里宽度不再只增不减,高度仍保持。

皮肤的 min_width_dip 与装饰图宽度照常生效;内置皮肤清单出厂为 false。
2026-10-12 02:03:59 +08:00
MSIME(MetaSequoia IME) fe12f90b2a Merge pull request #857 from fanlusky/fix/toolbar-trilingual-cycle
fix(server): 工具栏语言按钮在开启中日英循环时按循环切换
2026-10-12 01:23:06 +08:00
fanlusky 29adbe76f0 fix(server): 工具栏语言按钮在开启中日英循环时按循环切换 2026-10-12 01:19:32 +08:00
fanlusky c821e5addc fix(installer,server): 新用户默认关闭候选词翻译 2026-10-12 01:04:52 +08:00
Lu Fan(SonnyCalcr) f64da66e6c fix(server): 混输 emoji 最匹配的一个放首页末位,其余排在所有候选末尾 (#853)
此前所有混输 emoji 都追加在候选列表末尾,候选一多就全落到最后一页。现在知道每页候选数时,
查询结果里最匹配的那个 emoji 放在首页末位,其余 emoji 仍排在末尾,颜文字照旧紧随其后。

- 补全词的默认位置和学到的英文槽位在有 emoji 时让到首页倒数第二位,两者都留在首页;
  用户固定位置的英文仍然优先。
- CurrentEnglishPlacement 在没有英文候选时也带上每页候选数,emoji 才能定位。
- 选中 emoji、颜文字不再算越过英文,不会让英文槽位后退;否则首页末位的 emoji 每选一次
  都会把英文往后推。
2026-10-12 01:02:02 +08:00
Lu Fan(SonnyCalcr) 19543ead61 fix(server,installer): 升级不再改掉老用户设置和效果档模型,SimplySign 打包前构建词库 (#852)
* fix(server): 升级不再把只给新装用户的出厂默认值推给老用户

* fix(installer): 升级时把 0.9.x 随包的效果档模型挪到 models\neural-desktop,不再删掉

* fix(installer): SimplySign 完整包打包前从同级 msime-dictionary 构建词库并备好模型
2026-10-12 00:27:59 +08:00
Lu Fan(SonnyCalcr) 0d7ae4ca79 feat(server): 翻译里的词性与分隔符单独配色 (#848)
候选翻译(如 "adj. spreading; n. sound of laughter")由 Server 切成单词、词性、
分隔符三段:WebView2 端包成 .cand-pos / .cand-sep,D2D 端经
CandidateList::Item::translationRuns 用分段画刷绘制,两端同一切法。

新增皮肤键 translation_pos / translation_separator 及选中行的
selected_translation_pos / selected_translation_separator,留空时与翻译同色。

内置皮肤的翻译改用 Notion 调色板:词性取相邻的另一个色相并降到 50% 不透明度,
分隔符为单词色 45%;铺实色高亮的选中行另配一组。仓库内的外部皮肤同步补上词性与
分隔符配色。
2026-10-11 23:28:30 +08:00
Lu Fan(SonnyCalcr) 0c754b4a04 fix(server): 辅助码会话用例显式打开辅助码开关 (#850)
#847 把出厂模板的 shuangpin_helpcode / quanpin_helpcode 改成默认关闭后,
测试辅助码行为的会话用例沿用模板默认值,在辅助码关着的情况下运行,CI 的
Server 测试有 10 个用例失败。

新增 ScopedHelpcodesEnabled,在这些用例里先于会话构造把两个开关打开,结束时
还原。只改内存里的开关、不写配置文件,开发机上跑也不会改动用户配置。
2026-10-11 23:21:02 +08:00
Lu Fan(SonnyCalcr) 715028e1b9 feat(server): AI 联想上下文跳过密码框、换应用清空、过滤类凭证串 (#845)
AI 联想会把最近的上屏文本作为上下文发到云端,以前不区分输入框,也跨应用累积。

- 密码 / PIN 输入框:TSF 在一段输入的首键上读 InputScope(IS_PASSWORD、IS_NUMERIC_PIN 等),
  按键包带上 FanyImePipeFlags::SensitiveField;Server 收到后不发 AI 请求,上屏也不进上下文
- 只在 AI 联想开启时才检测:Server 通过新的 worker opcode AiContextUploadChanged(34)
  告诉 TSF 开关状态,关着时按键路径只多一次 atomic load
- 上下文按焦点会话归属:client_id 或 activation_epoch 变了就清空,不再把 A 应用里打的字带进
  B 应用的请求
- 发送前去掉像凭证的 ASCII 串:不含空白、长度 >= 8 且混了字母 / 数字 / 符号中两类以上
2026-10-11 21:58:34 +08:00
沐雪清泽andmushiyuki 93c7b8121d fix(engine): consume Japanese nn as a single nasal (#669)
Co-authored-by: mushiyuki <248113054+muxueqingze@users.noreply.github.com>
2026-10-11 20:39:55 +09:00
luo jiyin 950649a3a4 fix(server): include page state in candidate frame deduplication (#834)
* fix(server): include page state in candidate frame deduplication

* test(server): cover existing candidate frame state
2026-10-11 20:39:46 +09:00
Lu Fan(SonnyCalcr) 331d0012f5 feat(server): config.toml 中的 API 凭证改用 DPAPI 加密存储 (#840)
翻译、语音、AI 助手的 token / key / secret 以前明文写在 config.toml 里。现在落盘时
用 DPAPI(当前用户范围)加密,存成 "dpapi:<base64>",加载后在内存中还原成明文,
业务代码和设置页的行为都不变。

- 加密统一在 WriteConfiguredValues 里做,所有凭证 setter 自动生效
- 启动时把旧版本留下的明文凭证就地换成密文,用户无需重新填写
- 出厂占位符和空值保持明文,升级时的模板合并仍能识别「未填写」
- 解不开的密文(配置被拷到别的账户或机器)按未填写处理,不会把密文当 token 发出去
- IsCredentialKey 从 ime_config_template.cpp 移到新的 ime_config_credentials.cpp 共用
2026-10-11 18:15:12 +08:00
Lu Fan(SonnyCalcr) 649747f8cd feat(server): 神经网络整句效果优先模型改为按需下载 (#838)
效果优先模型(sentence-model-desktop.safetensors,约 25 MB)不再随安装包分发,
改为和八股文模型一样在设置页下载后才能使用。

- engine:SentenceAssociationOptions 新增 neural_desktop_model 路径,留空即模型缺席、
  静默不重排;词典不再从资源目录固定加载效果档。load_file 按 UTF-8 打开路径,
  数据目录在非 ASCII 用户目录下也能读到模型
- server:新增 settings/neural_model,下载到 <数据目录>\models\neural-desktop\,
  下载与导入都按 neural-model/lock.json 的字节数和 SHA256 校验后原子落位;
  会话层只在开关打开时解析模型路径
- ui-html:设置页增加下载 / 导入 / 删除 / 浏览器下载,模型未就绪时开关置灰
- installer:不再打包效果档模型;旧版装进数据目录根下的那份在覆盖安装与卸载时清掉

原有配置项 sentence_neural_desktop 保持不变,用户的开关状态升级后保留。
2026-10-11 18:08:33 +08:00
Lu Fan(SonnyCalcr) 7825e93038 feat(installer): 翻译默认使用小牛翻译 (#836)
出厂模板和开发配置都改为默认启用小牛翻译(niutrans.enabled = true),
开发配置同时关掉腾讯云,与出厂模板保持一致。设置页的翻译供应商按
niutrans.enabled 推导,新装用户打开即是小牛翻译;升级用户保留原选择。
2026-10-11 17:32:22 +08:00
MSIME(MetaSequoia IME) 40d24b7341 Merge pull request #827 from chr331/fix/mixed-pinyin-candidate-priority
fix(engine): 保留原样混拼候选优先级
2026-10-11 16:18:56 +08:00
Lu Fan(SonnyCalcr) f273dccd18 feat(server): AI 联想与语音润色支持设置 DeepSeek 思考强度 (#821) (#824)
新增 [ai_assistant] thinking 与 [voice_input] polish_thinking,可选 disabled / low / high / max,
默认 disabled 保持原行为。非 disabled 时向 DeepSeek 发送 thinking: enabled 与 reasoning_effort,
其他提供商不受影响。开启思考时 AI 联想超时放宽到 30 秒、max_tokens 提到 8192,润色超时放宽到 30 秒;
AI 联想缓存键纳入思考强度。设置页 AI 辅助与文本润色 API 各加「思考强度」下拉框。

Closes #821
2026-10-11 15:23:46 +08:00
Lu Fan(SonnyCalcr) f019cbdad0 fix(server): 悬浮工具栏的大小写状态不再与 Caps Lock 反向 (#822)
Server 在低级键盘钩子里逢 Caps Lock 按下就翻转一次自记的状态,从不与系统
对账。长按 Caps Lock 时钩子会收到不带重复标记的自动重复,而 Windows 只在
首次按下时切换,重复次数为奇数时状态就此反向;锁屏、UAC 等安全桌面上的
切换钩子看不到,也会造成同样的漂移。反向的状态还会经 CapsLockChanged 广播
回 TSF,连带英文标点跟随和语言栏图标一起错。

- 钩子记录 Caps Lock 是否按住,松开前的重复按下不再翻转。
- TSF 在 Caps Lock 切换与焦点进入时发来的 IMESwitch 带有前台线程读到的
  真实状态,Server 据此校正自记状态,有变化时刷新工具栏并通知 TSF。
2026-10-11 15:03:15 +08:00
chr331 36ab259c43 fix(engine): 保留原样混拼候选优先级 2026-10-11 13:56:14 +08:00
Lu Fan(SonnyCalcr) 42c2666a4d feat(server): 悬浮工具栏改用候选窗的柔和阴影,开着阴影也能贴边,并可在设置中关闭阴影 (#809)
* feat(server): 悬浮工具栏改用候选窗的柔和阴影,并可在设置中关闭阴影

- D2D 工具栏改用与候选窗相同的两层阴影(light α .18/.10,dark α .34/.22),
  透明边距与候选窗一致(16/14/32/34 DIP)
- 新增配置 general.floating_toolbar_shadow(默认开启),设置页「悬浮工具栏」
  加「工具栏阴影」开关;关闭后去掉阴影边距,工具栏可贴住屏幕边缘
- 关闭阴影时保留 1 DIP 边距,避免居中描边的外半边被窗口边缘裁掉
- WebView2 工具栏:开关同样生效;默认页阴影改为候选窗的近接层(宿主只留 8px)

* feat(server): 悬浮工具栏开着阴影也能贴住屏幕边缘

- 阴影边距允许伸出屏幕:拖动结束、改尺寸和默认位置都只要求工具栏本体
  (含描边)留在工作区内;伸出工作区的部分用窗口区域裁掉,不绘制、
  不挡任务栏点击,拖动时实时更新
- 伸出量按宿主实际像素尺寸和卡片位置计算,消除向上取整留下的 1-3 px
  缝隙;关掉阴影时同样贴边
- 切换阴影时保持工具栏本体位置不动,并按工作区重新判定边界,
  避免工具栏一部分跑到任务栏下面
- 设置页「工具栏阴影」说明改为「在工具栏周围显示与候选窗一致的柔和阴影」
2026-10-11 13:07:27 +08:00
Lu Fan(SonnyCalcr) b4479974b8 fix(server): 改用 SetInputMethodOverride 设置默认输入法 (#807)
input.dll 的 SetDefaultLayoutOrTip 在 Windows 11 上返回成功,但只改了语言内排序、
Preload 和 CTF 的语言默认项,不写 InputMethodOverride,默认输入法实际不变,复核
因此报「系统没有应用这项设置」。改为调用 bcp47langs.dll 的 SetInputMethodOverride,
与系统设置「替代默认输入法」及 Set-WinDefaultInputMethodOverride 写同一项;仅在缺少
该导出的旧系统上回退到 SetDefaultLayoutOrTip。

同时给两个按钮单独的样式:原先复用的 restart-server-button 在浅色主题下悬停前后
背景几乎相同,看不出是按钮。「设为默认」改为强调色主按钮,两者都有悬停、按下和
键盘焦点反馈。
2026-10-11 02:53:36 +08:00
Lu Fan(SonnyCalcr) 85edddc988 feat(server): 设置页支持把水杉设为默认输入法 (#802)
帮助页新增「默认输入法」卡片:显示当前是否为默认,提供「设为默认」和
「系统设置」两个按钮。设置走 input.dll 的 InstallLayoutOrTip /
SetDefaultLayoutOrTip,完成后按 HKCU International\User Profile 复核;
EnumEnabledLayoutOrTip 在 Windows 10/11 上对 TSF 输入法返回空列表,
因此状态直接读注册表。只在用户点击时执行,不在启动或升级时改写。

新增 defaultImeRequest / defaultImeResponse WebView 契约并同步 ui-html。

同时在帮助页加入 Wiki 与 FAQ 链接,在关于页加入官网 msime.app。
2026-10-11 02:22:01 +08:00
Lu Fan(SonnyCalcr) 5aed0fcbf0 fix: 修复部分用户打不开设置页,WebView2 缺失时仍可正常打字 (#801)
* fix(installer): VC++ 运行库要求提到 14.40 并给出直接下载链接

服务端程序用 MSVC 14.40+ 编译并导入 msvcp140.dll,配上更旧的运行库,
std::mutex 首次加锁即访问冲突、进程无声闪退,设置页因此打不开;原先
14.20 的门槛放过了装着 2019 / 早期 2022 运行库的机器。

- 缺失与「已装但过旧」分开提示,并显示当前版本
- 「是」直接下载 VC++ x64 运行库与 WebView2 Evergreen Bootstrapper
- 更正 WebView2 缺失的影响:只有设置页需要它,默认 D2D 候选窗照常打字

* fix(server): WebView2 不可用时设置页给出提示,候选窗退回 D2D

- 设置进程不再忽略 WebView2 environment / controller 创建失败(运行时缺失
  时同步失败、回调不来,界面会一直停在启动画面),改为弹框说明原因、错误码
  与下载直链后退出
- ui_backend = webview2 而检测不到运行时时,本次进程改用 D2D 渲染,保证能
  正常打字;不改配置,装好运行时后下次启动自动恢复
- WebView2 下载地址统一为 Evergreen Bootstrapper 直链
2026-10-11 02:21:40 +08:00
luo jiyinandfanlusky 66e0a6dc76 fix(server): 保留候选文本中的逗号和私用字符 (#766)
* fix(server): 保留候选文本中的逗号和私用字符

* fix(server): render candidate frame from one snapshot

* fix(server): use captured preedit for sticky card key

* fix(server): dedup candidate frames by item-aware signature

Candidate text now keeps raw commas, so the comma-joined candidate_string
could make ["a,b","c"] and ["a","b,c"] share one frame signature and drop
a real page change as a duplicate repaint. Length-prefix each item instead.

Also serialize the WebView candidate items with the replace error handler
so an invalid UTF-8 translation gloss cannot throw on the UI thread, and
let CandidateWindowContent own its preedit and page snapshot rather than
referencing caller locals.

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-11 02:08:25 +08:00
fanlusky f414570505 test(server): anchor input mode lookup to line start in legacy path migration test 2026-10-10 23:14:20 +08:00
fanlusky 90e812fe4e fix(installer): start fresh installs in Chinese and ship opt-in features off
- input.default_ime_mode defaults to chinese, matching the Server and TSF fallbacks
- utility.emoji_mode / kaomoji_mode default off so Shift+E / Shift+M no longer swallow
  English words that start with a capital letter
- voice_input, ai_assistant and tencent_tmt default off; without a token they only pop a
  prompt (voice on Right Alt, which is also AltGr) or do nothing
- doubao_enable_ddc defaults off, matching its comment and the code fallback
- drop general.enable_emoji, which nothing reads

cloud_candidates stays on: the installer's network page asks the user and only writes
false when unchecked. Code fallbacks in ime_config are aligned with the template.
2026-10-10 23:02:15 +08:00
Jia Suiandfanlusky e10f845e04 refactor(server): 移除服务端内嵌设置宿主死代码 (#764)
* refactor(server): 移除服务端内嵌设置宿主死代码

InitWebviewSettingsWnd 全仓无调用点,webviewSettingsWnd 恒空,这条内嵌设置 WebView 宿主及其上的 configUpdate 分发、PostSettingsConfig() 空转调用链从不执行。设置页由独立进程 MetasequoiaImeSettings.exe 承载并自行轮询配置,删死路径与 7 个空转调用点不改变可观察行为。

- 整删 windows_webview2_settings.cpp、ime_windows_settings.cpp
- 清除仅服务该宿主的全局/声明/常量,及 windows_webview2.cpp、windows_webview2_skin.cpp 中的引用
- 删 PostSettingsConfig() 调用点,两处 if 块塌缩为直接调用 setter
- 同步根 AGENTS.md 与 server/AGENTS.md 的「两个设置宿主」表述

* docs: 配置同步规则补回设置页 configSnapshot 字段列表

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-10 21:33:25 +08:00
Lu Fan(SonnyCalcr) 5e03628a7e feat(installer): build cn-en translations into the local dictionary (#784)
* fix(server): drop part-of-speech labels from Ctrl+Enter translation senses

The machine-translated Chinese-to-English glosses prefix each sense with a
label ("n. silver medal", "proper n. Wanshan District"). The candidate
window keeps showing the whole gloss, but splitting it for Ctrl+Enter now
strips the label so it never reaches the committed text.

* feat(installer): build cn-en translations into the local dictionary

build-dictionary.py gains a cn-en-translations stage that loads
sources/cn-en/machine-translations.txt from msime-dictionary into
zh_en_glosses verbatim, keeping every sense instead of the two the ECDICT
derivation keeps. It runs after english-glosses and before
custom-translations, so it overrides ECDICT and custom/translations.txt
still overrides it.

Local full test packages now always build the dictionary from the sibling
msime-dictionary checkout instead of downloading the product-lock release:
Invoke-LocalTest.ps1 and prepare-package-inputs.ps1 call
build-dictionary.py --if-stale, which skips only when out/ was built from
the current clean source commit by the current builder. CI and releases
still take the dictionary from product-lock.json.
2026-10-10 21:17:03 +08:00
Ouchihao 777fb2aef4 fix(engine): protect repeated-initial jianpin 2026-10-10 17:05:09 +08:00
Ouchihao 29c8917e8c fix(engine): preserve leading-initial mixed jianpin 2026-10-10 14:46:13 +08:00
Lu Fan(SonnyCalcr) 49f22b5c25 fix(server): 手写识别板按钮改用图标字体,修复撤销图标显示为缺字方框 (#749)
撤销按钮的 ↶ (U+21B6) 直接写在文字里,Noto Sans SC 没有这个字形,
DirectWrite 也回退不到,画成了缺字方框。

两个按钮的图标改由 ResolveIconGlyph 解析:Windows 11 用 Segoe Fluent Icons,
Windows 10 回退 Segoe MDL2 Assets(撤销 U+E7A7,重写 U+E711),并以 SemiBold
模拟加粗与旁边文字协调;两种字体都没有该字形时只画文字标签,与悬浮工具栏一致。
2026-10-09 21:33:03 +08:00
xiaozhu1337andfanlusky 9c0f6da9be feat(windows): add opt-in per-app Vim Escape mode (#737)
* feat(windows): add opt-in per-app Vim Escape mode

* fix(windows): 改用 config.toml 的 vim_mode_apps 并移除 yaml-cpp

TSF DLL 会注入所有宿主进程,约定保持 /Zc:threadSafeInit- 且不加全局构造器;
yaml-cpp 带命名空间级 std::string 数组和线程安全局部静态,违背这一约定。
配置改为 [input] vim_mode_apps = [...],由 DLL 内的小型解析器读取,
沿用 config.toml 的时间戳缓存;键写入出厂模板与工作配置,升级合并保留用户值。

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-09 16:59:05 +08:00
AkaneIwasaandfanlusky 568d07f513 feat(windows): 添加中日英三语轮换 (#717)
* feat(windows): 添加中日英三语轮换

* fix(windows): 三语轮换改为快捷键开关并修复两端状态不同步

- 开关移到「快捷键 → 中英文切换」下的「允许中日英循环切换」,配置键改为
  [keybindings] trilingual_cycle;输入模式恢复为中文/日文,二者互不影响
- 循环请求带显式标记位和请求方自身的中英/日文状态,Server 据此计算目标语言;
  普通 VK_SHIFT 仍是中英切换,不再被误当成循环
- Server 开关在请求途中被关闭时仍回复,按中英切换处理,TSF 只丢弃后续排队按键,
  不再超时重连
- 已发出的循环回复未被应用(含 Superseded)时走完整重连,避免日文状态两端不一致
- 循环切换补上光标处状态提示和标点同步
- 切换语言时复用暂存的另一语言会话,不在按键路径上重建引擎和打开 msime.db
- 配置读取的语言与开关改为原子量;TrilingualCycleChanged 的能力过滤移到发送方
- 中日切换不再重发 TrilingualCycleChanged;结束组合被拒绝时不计入统计

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-09 15:44:41 +08:00
時崎 ギャラクシー aa5c632ea9 Merge pull request #711 from Tokisaki-Galaxy/feat/llm-model-fetch
feat: AI 辅助新增获取模型、测试连接与 Custom 提供商
2026-10-08 23:13:01 +08:00
normaltank c2bd59d448 fix(server): 翻页扩展时保留混输候选并同步 Emoji 末尾排序 2026-10-08 22:10:13 +08:00
normaltank 6489d4be17 feat(server): 调整 Emoji 混输候选项位置至候选列表末尾 2026-10-08 22:10:13 +08:00