Commit Graph
19 Commits
Author SHA1 Message Date
fanlusky 233ac9ef33 feat(engine,server,windows,ui-html,installer): 句中辅助码触发键可多选,新增分号
设置页「句中辅助码触发键」可勾选反引号和分号,至少保留一个。

- 分号只在 AcceptsMarkerAt 成立(这一节是偶数键)时才当触发键;微软/搜狗/紫光双拼的
  ing 韵母只跟在奇数键之后,两者不抢同一个位置,判断时分号先让给 ing 韵母
- 无论按哪个键,TSF 按键缓冲、Server raw 与引擎输入串里都记成反引号,引擎解析不变
- 新配置项 helpcode.shuangpin_mid_sentence_helpcode_backtick / _semicolon 同步写入出厂
  模板、工作配置与两个设置宿主
- opcode 28 保持「反引号是否触发」、载荷单个 "0"/"1"(TSF 会丢弃更长的帧);分号走新增
  的 opcode 29,旧 DLL 忽略它
2026-10-02 23:55:46 +08:00
Jia Sui f73a988cd5 fix(server): 按配置段拆分设置宿主分发链,修复 C1061 构建失败
configUpdate 的分发是一条 109 分支的 else-if 链,MSVC 以 error C1061
(编译器限制:块嵌套太深)拒绝编译,MetasequoiaImeServer Release 构建失败。
嵌套上限约 127 层,develop 上原本 105 个分支已贴近上限,本分支 69092b9d
新增 association.sentence_collocation_additive / _rerank 两个键后就顶穿了。

按 config.toml 的段名拆成 10 个 Apply<段名>Subkey,沿用文件内既有的
ApplySmartPunctuationSubkey 模式;各段名互不重叠,调用点依次调用全部函数,
至多一个命中。拆后每段最长 27 个分支。

拆链不是纯文本搬运:段内谓词重叠的分支会双跑。两处已恢复 else-if 并就地注明原因——
general 的 4 个 floating_toolbar_* 键与 custom_translation.enabled 各自都被同段末的
rfind 前缀分支覆盖,拆成并列 if 后会先正确写入一次、再被前缀分支用 value_to<bool>
读同一个值错误写入一次。tencent_tmt 的 target_language 判断在 rfind 分支体内部,不受影响。

验证:clang-format 18.1.8 零违规;Release 构建产出 Server 与 Settings 两个 exe;
按 else-if 链编号复验跨链重叠为 0、109 个分支不变;ctest 在同一隔离环境下与改动前
基线一致(2 个既有失败,均本地复现、CI 为绿,与本改动无关)。
2026-10-02 23:05:51 +08:00
Jia Sui 03c0a4246d feat(server,ui-html,engine,installer): 接入社区多 octagram 模型,分模型下载与单选激活 2026-10-02 20:15:40 +08:00
Jia Sui 8f22f68d4c refactor(server,ui-html): 万象模型撤下未评测说明与hash检查
有效性判定收敛为单一标准:能被引擎读取端打开(格式锁)即已下载——
「未经评测」的前提随上游更新不再成立,摘要比对、旁挂 .sha256 与
reviewed 字段不再有任何用途,全链路移除:

- collocation_model:删 kReviewedSha256、Sha256Hex(含 bcrypt 依赖)、
  DigestSidecar/ReadRecordedDigest;NOTICE.md 保留 CC-BY-4.0 署名四要素
  (来源/许可/文件名/字节数),删 SHA256 行与重训备注
- 双宿主快照序列化对称删 reviewed;设置页 ready 文案固定「模型已就绪」,
  删「上游已更新,未经评测」分支
- 顺删本文件从未使用的 <sstream>/<atomic>

行为变化:老用户数据目录里的 .sha256 旁挂文件不再被读取,按整个 models
目录的保留逻辑留到卸载才消失,无害。

验证:clang-format 18.1.8 --Werror、Server 双宿主 Release 构建、
pnpm test 91 例、tsc/vite build、全仓 grep 无残留。
2026-10-02 20:15:40 +08:00
Jia Sui 98f7d61284 refactor(server): 万象合并为单个总开关
sentence_collocation_additive + sentence_collocation_rerank 两个键、两个 UI 开关合并为
sentence_collocation_enabled + 一个「万象语法整句」。上游 RIME-LMDG 只有单开关形态
(__include: octagram + grammar/language),它公布的句子正确率 +15%(61.71% → 76.86%)
对应的正是解码期接入,没有「只开重排」的评测;两个开关对同一份模型是可分性的过度承诺。

顺带清掉一个死开关:引擎的 SentenceAssociationOptions 里根本没有 additive 字段,
加成分由 collocation_model 非空隐含开启。GetConfiguredAssocSentenceCollocationAdditive
的唯一消费者是设置页回显,写入的值没有任何读取方——这是「开关开了没反应」的第二处
断点,与模型 id 那处叠加。现在契约对齐:开关关则连路径都不解析,模型缺席则重排一并关闭。

保留 sentence_collocation_model(手动换模型包的逃生口)与两个权重(高级调参,本就
不进设置页)。

行为变化:MergeConfigIntoTemplate 以模板为骨架重建用户配置,模板里没有的键会被丢弃,
因此旧的两个键消失、新的 sentence_collocation_enabled 取模板默认 false,等于已开启的
用户被重置为关闭。可接受——该特性此前因模型 id 从未被写入而根本无法生效,不存在可被
重置掉的行为。

改动面 14 个文件:配置层四处、会话接线一处、双宿主序列化与 configUpdate 分发各两处、
两份 config 模板、设置页四处加测试。

验证:全仓旧键零残留,clang-format 18.1.8 --Werror 通过,Server/Settings 构建 exit 0,
pnpm test 91 例通过,tsc 通过。
2026-10-02 20:15:40 +08:00
Jia Sui e1a664a194 fix(server): 万象下载器改用格式锁,撤下无依据的评测宣称
锁定值与上游实际资产不符(字节数 409719852 vs 409412652,摘要 ae43724a… vs 873cbbb3…),下载在字节锁处失败。上游 LTS 会被原地重传且无不可变资产 URL,摘要硬锁意味着每次重训都要改代码发签名安装包,否则全用户下载失败,这个成本消不掉。

改为锁格式:删字节数常量,总长取响应 Content-Length 并在收完后比对一次(截断检测);新增 ValidateFormat 让引擎读取端自己开一次(魔数 + 双数组边界,不按文件自带长度做堆分配)。摘要不再参与放行,只判断 reviewed 并写入旁挂 .sha256(状态查询要频繁调用,不能每次重算 400MB 哈希)。真实上游字节按 open() 判据复核精确贴边通过:array_size=102353152,收尾正好在 409412652。

kReviewedSha256 保留旧值,摘要不符即 reviewed=false,设置页明示「上游已更新,未经评测」,NOTICE 记录实际摘要与字节数并附差异说明。契约无需重生成:sentence_collocation_model_status 是两个宿主各自拼的 ad-hoc JSON,不在 schema.json 内。

撤下无法验证的宣称:设置页删掉「评测 top1 +6 个百分点」(仓里没有评测工具、没有 docs/research,该数字无法复现),改为描述机制;清掉两处对从未进仓的文档与工具的引用(docs/research、tests/src/gram_reader、eval_lm_compare);修正 octagram_gram.h 里被本次改动失效的「摘要校验在下载侧做」。配置只改注释、未增删键,五件套映射不适用。
2026-10-02 20:15:39 +08:00
Jia Sui 69092b9d9a feat(server): 万象模型的配置链路、模型包解析与按需下载
- [association] 新增五键:sentence_collocation_additive/rerank(设置页开关,默认
  关)、sentence_collocation_model(模型包 id,下载器写回)、两个权重(配置文件
  高级项,不进设置页);出厂模板与工作配置同步更新,通过五件套映射下发
- 模型包确定性布局 <DataDir>/models/<id>/<id>.gram,解析只是一次 stat;开关开
  着但文件缺席即视为关闭,词格静默降级
- collocation_model.{h,cpp}:WinHTTP 按需下载(重定向放行、进度回报)+ BCrypt
  SHA256 校验 + 同卷原子落位 + CC-BY-4.0 NOTICE。SHA256/字节数在常量里钉死:
  上游 LTS release 会被原地重传,换字节必须显式更新并复跑离线评测
- CandidateSource::Collocation 落库/学习判定与神经整句同类,候选徽标〔万象〕
- 下载器编译进 Server 与独立设置进程两个宿主,状态以磁盘为准
2026-10-02 20:15:39 +08:00
fanlusky 79b0bf3aaa feat(server,ui-html,installer): 回车上屏英文入库加开关
设置页「输入」新增「回车上屏英文时入库」开关,对应 input.enter_learns_english_word,默认开启保持原有行为。关闭后回车仍上屏英文,但不再写入英文词库。
2026-10-02 18:45:08 +08:00
fanlusky 45c473158a feat(engine,server,windows,ui-html): 双拼预编辑可显示转换后的全拼
外观里行内预编辑与候选窗预编辑各加一个「双拼显示转换后的全拼」开关,默认关闭。
开启后在双拼方案下:行内原始按键显示不带分词符号的全拼,拼音分词显示带分词符号的
全拼;候选窗显示带分词符号的全拼。

- 引擎新增 get_shuangpin_preedit_forms,同时给出原串切分与全拼切分,不受
  shuangpin_preedit_mode 影响。
- 原始按键样式下 TSF 不等 Server 回包,开关开启且为双拼时 Server 下发的实际样式
  改为 pinyin,回包里再按用户样式去掉分词符号;换方案时重新下发。
- 转换后字母数与按键不再对应,Server 在预编辑末尾附光标映射表
  (engine/contracts/preedit_caret_map.h),DLL 用它定位光标和处理方向键,
  无表或长度不符时回落到原有字母映射。
2026-10-02 16:14:50 +08:00
fanlusky 3a7adb6f1a feat(server): 日期时间混入候选,混输查询合并到同一条线程
- 新增 MixedCandidates(src/mixed/):英文、emoji、颜文字、日期时间共用一条后台线程,
  按一次输入查完、一次回调交回任务线程合并,候选窗每个按键最多因混输刷新一次。
  删除 EmojiIme / KaomojiIme;EnglishIme 只保留英文状态、Shift+Y 和候选译文
- 日期时间混入候选:全拼 / 双拼下整串是 rq / riqi / date、sj / shijian / time、
  xq / xingqi / week(双拼按方案换算后的整拼也算)时,首个格式排在首个中文候选之后、
  云 / 英文 / emoji 之前,其余追加在末尾;选中直接上屏,不调频、不能固定或删除
- 新增 utility.mixed_candidates(候选混输总开关,默认开)与 utility.date_time_candidates
  (默认关);总开关关掉后英文、emoji、颜文字、快捷短语、日期时间都不混入,各自的值保留。
  两份配置模板与两个设置宿主同步读写
2026-10-02 03:39:23 +08:00
fanlusky fae80660c1 feat(engine,server,windows,ui-html): 双拼句中辅助码
在完整音节后敲反引号,接着的字母筛这个音节上的字:第一码大小写均可,
紧跟的大写字母是第二码,之后可以接着打,一个输入里可以有多段。开关
helpcode.shuangpin_mid_sentence_helpcode 默认关闭。

约束在整句解码时生效,而不是解完再筛:
- Google 解码器在 add_char_qwerty 里送 extend_mtrx_nd 之前按约束过滤词条,
  锁住的字带着上下文重新整句解码;约束变化时整段重解,不污染增量搜索。
- kenlm 词格建图时按约束筛边,受约束的跨度不截断地查词库;神经重排只在
  词格 n-best 里挑,自动满足约束。
- 普通词条、云和 AI 候选按同一规则收尾校验;满足不了就不出整句。

反引号段在请求里换成手动分隔符,原串留给宿主回读与预编辑显示;选词推进
时剩余音节上的约束随原串保留。输入形状规则放在 contracts/mid_sentence_helpcode.h,
TSF、Server、引擎共用,新增 Worker opcode MidSentenceHelpcodeChanged = 28。
googlepinyinime-rev 已与上游解绑,AGENTS 与 UPSTREAM.md 同步说明。
2026-10-02 02:20:37 +08:00
fanlusky dde06afd7f feat(server): 候选框显示翻页箭头并接入设置页预览
- D2D:横排在候选右侧多一窄列,竖排在最后一行下方、「‹」与序号左对齐;开关或排列方式变化时重建场景
- WebView2:页面按 data-page-arrows 显示箭头,点击经 candidatePage 翻页
- 箭头点击走 WM_PAGE_CANDIDATE_ARROW,不受鼠标滚轮翻页开关影响;页面快照带上一页 / 下一页可用状态
- 两个设置窗口宿主下发内置皮肤的 page_arrows,外部皮肤条目带解析后的 pageArrows
- 安装包把 skins/default 以 onlyifdoesntexist 放到数据目录,升级不覆盖用户改过的清单
2026-09-30 12:42:25 +08:00
fanlusky 2415da274f feat(server): 候选中混排快捷短语并参与调频
- 全拼 / 双拼(非五笔、无辅助码、非造词)编码精确匹配时,把快捷短语组放到固定位置候选
  之后,翻页扩展时重新放置;Shift+K 模式不变
- 新增 utility.quick_phrase_candidates(混入候选)和 utility.quick_phrase_frequency
  (参与调频),默认都开,utility.quick_phrase 仍只管 Shift+K;两份配置模板与两个设置宿主
  同步读写
- 选中快捷短语或越过它选普通候选时异步写入槽位,K 模式里的选择不学;英文最高权重提升
  忽略快捷短语
2026-09-29 14:31:51 +08:00
fanlusky c9b86e0026 feat(engine): 支持用户自定义双拼方案
用户在数据目录 shuangpin\custom\ 下放 TOML 文件,在设置页以 custom/<文件名>
选用。零声母规则从 o、a、first_letter、full_pinyin 四种里选,声母和韵母
映射由文件指定,可用 [zero_initials] 覆盖个别零声母音节。

- 引擎:新增 CustomShuangpinLayout 与 BuildCustomShuangpinProfile,校验缺键、
  声母撞键,并对全部音节做往返解码,冲突时给出可读的错误信息;新增运行时
  方案注册表,按需加载。
- Server:扫描并解析自定义方案,配置快照带上方案列表与目录;文件缺失或无效
  时回退到小鹤。
- 设置页:仿照自定义辅助码,新增打开目录和刷新按钮;无效方案列出但禁用,
  悬停时显示错误。
- 安装器:覆盖安装与迁移数据目录时保留 shuangpin 目录。
- 文档:docs/custom-shuangpin.example.toml 提供带详细注释的示例。
2026-09-29 11:33:23 +08:00
fanlusky 995174af47 feat(server): 造词时 Esc 可只清除未选拼音、保留已选词
新增 input.escape_keeps_selected_word(默认 false)。开启后,造词中按 Esc
只清掉还没选的拼音,保留已选的词,候选窗只显示该词作为 preedit;
再按一次 Esc 整体取消(小狼毫风格)。R 模式下仍整体取消。

- server:HandleCreatingWordEscape 以 CompositionRestored 帧回复
- windows:_HandleEscapeCancel 读取回复并应用,超时或不支持时回退到原取消
- ui-html:设置页「输入」增加开关
- 配置模板、两个设置宿主同步新增该键
2026-09-29 01:58:38 +08:00
Jia Sui 456fdb819c feat(engine,server,ui-html,installer): 五笔拼音混输改为同时混排并拆出独立开关
Signed-off-by: Jia Sui <jsfaint@gmail.com>
2026-09-28 17:55:36 +08:00
fanlusky 841b846f64 feat(server): 整句候选来源标签可在设置页关闭
新增 association.sentence_source_badge(默认开启,保持现有行为),
控制候选窗整句候选后的〔Trigram〕〔Unigram〕〔神经D〕〔神经K〕标签是否显示。
关闭只隐藏标签,不影响候选内容与排序;云/AI 角标不受影响。

同步更新出厂模板、开发配置、两个设置窗口宿主的读写映射和设置页开关。
2026-09-28 12:04:43 +08:00
fanlusky 4e17d27ea7 feat(server,ui-html,installer): 悬浮工具栏支持无交互自动隐藏
新增 general.floating_toolbar_auto_hide(默认关闭)和
general.floating_toolbar_auto_hide_delay(1–60 秒,默认 5)。开启后,工具栏显示满延时且
无交互时约 0.4 秒淡出并隐藏;鼠标悬停在工具栏上时倒计时重置,淡出中移入会立即恢复。
中英、全半角、标点、Caps Lock、英文模式、简繁或日文模式真正变化时重新显示并重新计时;
普通的焦点切换和 IME 激活不会把已自动隐藏的工具栏带回来。

悬停用 GetCursorPos 与 GetWindowRect 轮询判断,两者同为物理像素,不涉及 DPI 换算,
也不需要页面侧新增消息,D2D 与 WebView2 两种后端共用。预热、首帧宽限和 cloak 期间不计时。
淡出结束时以 alpha 0 隐藏,下次显示之前再恢复 255,避免在隐藏瞬间闪出一帧不透明画面。

两个新键已写入出厂模板和开发配置,两个设置宿主的字段列表与分发都已补上,并放在
general.floating_toolbar_ 按钮项前缀兜底之前。设置页在工具栏卡片内新增开关和缩进的
延时行,延时用新增的通用 −/+ 步进器,开关关闭时延时行置灰。
2026-09-28 01:09:10 +08:00
fanlusky f4aba3bcfc refactor(server): 按职责拆分 windows_webview2.cpp
windows_webview2.cpp 有 5500 行,小窗 WebView2 环境与 controller 的排队创建、
Topmost 分阶段提升、皮肤注入、候选窗脚本、托盘菜单、设置窗口和悬浮工具栏全挤
在一个文件里。按职责拆成以下几个翻译单元:

- windows_webview2.cpp:共享状态与全局变量、小窗 WebView2 environment 创建与
  controller 排队重试、导航就绪门控、lazy-topmost 分阶段提升、内容截断回退、
  DPI / RasterizationScale 辅助、可见性与状态查询,以及 GetAppdataPath、
  InitSmallWindowWebviews、PrepareTrayMenuWebviewForShow、ShutdownWebviews
- windows_webview2_skin.cpp:读取页面 HTML、外部候选皮肤 CSS 注入与资源内嵌、
  PrepareHtmlForWnds,以及候选窗、悬浮工具栏的布局 / 主题 / 外观应用
- windows_webview2_candidate.cpp:候选内容与测量区脚本、悬停锁与粘性卡片脚本、
  候选槽位增量更新、测量 bounds、OnControllerCreatedCandWnd 与页面消息处理
- windows_webview2_menu.cpp:托盘菜单 controller 创建与页面消息处理、
  SyncMenuFloatingToolbarToggle
- windows_webview2_settings.cpp:设置窗口的 DirectComposition 视觉树、
  composition controller、设置页消息与 configUpdate 分发、
  PostSettingsWindowState、PostSettingsConfig、InitWebviewSettingsWnd
- windows_webview2_toolbar.cpp:悬浮工具栏状态缓存与渲染脚本、
  OnControllerCreatedFtbWnd 与页面消息处理、各个 UpdateFtb*
- windows_webview2_internal.h:上述文件之间共享的声明,只给
  windows_webview2*.cpp 包含

纯搬移,不改行为:函数体、注释和格式都没动。原来在匿名命名空间里、现在
要跨文件用的符号(导航就绪标志、工具栏导航重试与绘制宽限标志、内容截断冷却
时间戳、JsonNumberAsDouble、HandleContentTruncatedMessage、
OnSmallWindowControllerSettled、NotifySmallWindowNavigationReady)放进
windows_webview2_detail 命名空间并在内部头文件里声明,避免与 Server 其他
翻译单元里的同名内部符号冲突;只被一个文件用到的仍留在该文件的匿名命名空间。
共享全局变量的定义仍在 windows_webview2.cpp。诊断日志的屏蔽宏、json 别名、
前置声明和 kTruncationSizeFactor 原样挪进内部头文件,每个拆出的文件都先包含
它,日志宏在各文件里仍是同样的空展开。

用脚本比对了原文件与拆分后全部文件的非空、非 #include 行多重集:原文件的
每一行都恰好出现一次,差异只有新增的声明、命名空间包装、using 指令、
#pragma once 和文件头注释。可用 git diff --color-moved 复核。
2026-09-27 15:38:01 +08:00