tokisaki galaxy
bb4c48505a
server: accept a semver prerelease suffix in the product version
...
The validation added in #1084 rejected anything but digits and dots, which is
narrower than semver: a prerelease suffix after a hyphen is part of the spec,
and the native about page's ParseVersion already strips such a suffix for the
update check while displaying it verbatim. Allow exactly one hyphen segment of
[0-9A-Za-z.-] after the numeric body, so dev builds can embed
0.7.1-dev.20261019.<hash> and the about page shows the full string.
version.txt itself stays bare MAJOR.MINOR.PATCH and every release passes
unchanged.
lcompile-release.ps1 feeds the same override locally, so its ValidatePattern
loosens in step.
2026-10-19 16:00:20 +08:00
小莫
dd018f7985
fix(server): 修复关于页显示旧版本 ( #1084 )
...
* fix(server): 从产品版本文件显示关于页版本
* fix(server): 使用发布版本更新关于页
2026-10-19 13:27:46 +08:00
Lu Fan(SonnyCalcr)
e701cebbe4
feat(server,ui,installer): 内置皮肤改由皮肤清单定义,新增微信绿(卷轴)内置皮肤 ( #1061 )
...
* feat(ui): CandidateList 支持等宽网格列与关闭选中态
- Appearance::gridColumns:横排时按可用宽度均分成等宽列(列数不超过 gridColumns,每列不窄于
minItemWidth),放不下一列的项占多列。
- Appearance::selectable:为 false 时不画任何选中态,首项也能正常显示悬停与按下效果。
* feat(server,installer): 内置皮肤改由皮肤清单定义,新增微信绿(卷轴)内置皮肤
内置皮肤数据化:
- skins/default/<id>/skin.toml 从只放开关改为完整的出厂清单(配色、圆角、版式、工具栏、名称与说明),
并新增 canvas 基底的清单。候选窗、悬浮工具栏、光标状态徽标不再在代码里写死任何内置皮肤的参数。
- 出厂清单在构建时编进程序;数据目录里的清单写了的键覆盖出厂值,缺失或写错时退回出厂外观。
安装包改为每次整份覆盖 skins\default。
- 皮肤清单新增版式键(padding、item_gap、number_inset 等,长度可写 DIP 或 "0.3em"),以及
[candidate_window.horizontal] / [candidate_window.vertical] 分排列覆盖、page_arrow_divider、
toolbar.icon_corner_radius_dip。
- test_candidate_skin_tokens 保留改动前的内置分支作参照,逐个皮肤、主题、排列与字号比对清单解析结果。
共用候选窗场景:
- 候选框的场景搭建与套皮肤抽成 CandidateSkinScene,候选窗与设置页的皮肤预览共用;设置页预览改用
真实的候选窗控件绘制,删除原先移植自网页样式的预览绘制与各皮肤硬编码。
卷轴展开(expand_panel)与微信绿(卷轴):
- 皮肤开关 expand_panel / expand_rows:横排候选行右端的展开按钮,展开后首行与后续候选网格一起滚动,
右侧细滚动条;展开与收起只认按钮,展开时滚轮上下滚动、不翻页。
- 网格点选按绝对下标上屏并在工作线程核对显示文字;滚到底部时自动再展开一批候选。
- 新增内置皮肤 wechat_scroll「微信绿(卷轴)」。
2026-10-18 20:42:06 +08:00
Mista and fanlusky
a0da400ae0
feat(server): 支持 Jev 基础候选接入 ( #1030 )
...
* feat(server): 支持 Jev 本地候选推荐
接入 TypeSafe Choice API,并在 AI 辅助设置中提供独立凭据和模型配置。
从本地候选中选择推荐项,保留候选元数据和前两项位置,丢弃取消或过期结果。
补充离线回归测试与数据发送说明。
* fix(server): 修正 Jev 候选重排与配置默认值
- Promote 只在未固定的词库候选槽位之间重排,不再挪动同文字的快捷短语、
云候选、英文和日期等异步行,也不因列表里有任何固定候选就跳过却仍打标记
- 选择无法应用时清除 Jev 选择,〔Jev〕标记只给词库候选
- 少于两个候选时不发 Jev 请求
- 出厂模板和工作配置里 endpoint_jev / model_jev 留空,跟随代码默认值
- 设置页 Jev 的接口地址固定为官方地址,默认值改用 Jev::kEndpoint / kModel
- 查询模型不再列出未经验证的 jev-latest / jev-preview
- 免费函数 Jev::Fetch 直接委托 Client::Fetch,不重复检查
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-17 21:33:35 +08:00
Lu Fan(SonnyCalcr)
cc868151a8
feat: 新增 canvas 基底,D2D 候选窗支持 v2 九宫格、挂件分层与 APNG/GIF 动图 ( #1014 )
...
* feat(ui): 新增 APNG/GIF 动图解码与动画帧时钟
AnimatedImage 按 APNG 规范解析 acTL/fcTL/fdAT 并校验序号、CRC 与帧边界,逐帧重建 PNG 交给 WIC 解码后按 dispose_op/blend_op 合成;GIF 走 WIC 多帧解码。延时规则与浏览器一致,超限或损坏的动画退回静态首帧。
DeviceResources::GetImageFrame 按动画时钟取帧并汇报下一次换帧时刻,动图帧按 64 MB 预算缓存;Scene 与 Window 据此按需重绘,可整体关闭动画。DrawNineSlice 按 CSS border-image 语义区分源切片与目标边宽、支持 fill,并用夹取画刷消除接缝渗色。
* feat(server): 新增 canvas 基础皮肤并要求 v2 皮肤基于它
canvas 是全贴图基底:透明面板、无描边、无圆角、无阴影,外观来自九宫格面板图与挂件。它只能作为外部皮肤的 base,不进内置皮肤列表;v1 皮肤用它直接报错。
schema_version = 2 而 base 不是 canvas 的皮肤按 v1 加载、忽略全部 v2 字段,并在扫描结果里记一条问题。v2 字段补齐校验:图片限 .png/.apng/.gif/.webp,fill 必须是布尔,偏移与 z_index 有范围,挂件最多 32 层。
* feat(server): D2D 候选窗补齐 v2 九宫格、挂件分层与动图
canvas 皮肤在 D2D 候选窗里按 CSS border-image 语义绘制九宫格面板:切片以源图像素计、边宽以 DIP 计、fill = false 不画中心;有面板图时卡片的底色、描边、圆角与阴影让给贴图。content_insets_dip 从面板外缘量起且不小于边框宽度,line_gap_dip 只作用于预编辑行与候选之间。
挂件拆成卡片前后两层:z_index < 1 在面板之下,其余在面板之上,同层按 z_index 与声明顺序稳定排序;图片在框内等比居中。动图按场景给出的下一次换帧时刻排一次性定时器续帧,只在候选窗显示时运行,隐藏、换皮肤时停;系统关闭动画效果时只显示首帧,v1 皮肤保持静态。
设置页原生预览认得 canvas 基底并绘制面板图。
* feat(examples): 新增基于 canvas 的 v2 全贴图皮肤样例
canvas-demo 用 2 倍九宫格面板、面板下层的叶子挂件和面板上层循环播放的 APNG 星光演示 v2 皮肤,素材均为程序生成的几何图形,随目录以 MIT 授权。样例测试同步校验 schema 2 与 canvas 互为前提、v2 图片扩展名与数值范围。
* docs(examples): 说明 canvas 基底、v2 九宫格与挂件分层及动图规则
schema 说明去掉「仅 WebView2」,写明 v2 必须以 canvas 为 base、canvas 只能当 base;九宫格切片按源图像素、边宽按 DIP,content_insets_dip 不小于边宽,line_gap_dip 只管预编辑行与候选之间;挂件以 z_index 1 为界分在面板上下两层;动图格式、帧时长、上限与系统减少动画时的行为。server/docs/ui-backends.md 与 skins/README.md 同步更新。
* docs(server): 候选窗与皮肤目录的注释不再引用已删除的 WebView2 页面
2026-10-17 02:57:33 +08:00
Lu Fan(SonnyCalcr)
1b875267db
refactor: 移除 WebView2,界面只保留 Direct2D ( #1013 )
...
* refactor(engine): 语音独立工具改用原生托盘菜单,移除 WebView2
托盘菜单改用 Win32 弹出菜单,设置项改为用关联编辑器打开 config.toml,关于页改为消息框。删除 window_webview2 与两份 HTML 资源,windows-app 特性不再依赖 webview2 端口。
* refactor(server): 移除 WebView2,候选窗、工具栏、托盘菜单与设置窗口只保留 Direct2D
删除 src/webview2 与 webview_utils,小窗口的置顶、皮肤重载和工具栏状态缓存移到 window/small_windows,presenter 成为唯一路径,D2D 下的置顶时序与 cloak 行为保持不变。设置进程去掉 WebView2 宿主、鼠标转发与启动图,只构建原生窗口;旧快捷方式传入的 --d2d / --webview2 被忽略。删除 appearance.ui_backend 与 appearance.settings_ui_backend(读取、写入、设置页下拉框与两份配置模板),升级时由模板合并丢弃旧值。设置窗口的图片资源移到 server/assets/settings,安装到数据目录的 assets\settings。不再链接 WebView2Loader,vcpkg 去掉 webview2 端口。
* build(installer): 打包与 CI 不再处理 WebView2 页面和运行时
Prepare-PackageFiles 去掉 -UiHtmlDirectory 与 html 暂存,改为暂存 server/assets/settings;安装器不再检查 WebView2 Runtime、不再创建 ProgramData 下的 WebView2 用户数据目录,升级和卸载时清理旧版留下的 webview2 / webview2-settings 与数据目录里的 html。CI 删除设置页构建、契约同步检查和候选窗 DOM 测试,release 的打包任务不再依赖网页产物;dependabot 去掉 npm。第三方声明去掉 WebView2Loader。
* refactor(ui-html): 删除 WebView2 页面与设置页前端工程
候选窗、工具栏、托盘菜单、设置页、主题和契约副本都已没有使用者;设置窗口用到的图片资源已移到 server/assets/settings。sync-contracts.py 与 pnpm/Vite/TypeScript 工程一并删除,契约仍以 engine/contracts/webview 为唯一来源。
* docs(server): 文档改为只描述 Direct2D 界面
仓库地图去掉 ui-html,组件边界、uiAccess 与置顶时序、设置窗口和配置读写的说明改写为 Direct2D 唯一界面;安装说明去掉 WebView2 Runtime 检查,打包命令去掉 -UiHtmlDirectory;engine/contracts/webview 的说明改为设置窗口是唯一使用者,名字是历史遗留。
* docs(skins): 皮肤说明不再区分 WebView2 与 D2D 渲染器
v2 字段去掉「仅 WebView2」,改为说明由 Direct2D 原生渲染、部分字段仍在补全;颜色必须带 # 改为清单格式约定。随包的 skins/niya-demo/skin.toml 不改,免得用户机器上的同名皮肤在升级时被当成已修改而改名备份。
* docs(ui): demo 说明文字不再提 WebView2
2026-10-17 02:48:02 +08:00
26e6896c5f
fix(voice): dismissible overlay, silent no-speech handling, reasoning strip + provider registry with mainstream presets ( #996 )
...
* fix(server): keep vendor chat fields off foreign endpoints and strip reasoning from responses
* fix(engine): strip inline reasoning from polished text and assistant responses
* fix(server): never block the voice control loop with modal dialogs
* feat(server): provider registry for AI assistant with mainstream provider presets
* feat(ui-html): settings pages for the provider registry, model fetch everywhere and new ASR providers
* test(server): vendor detection, reasoning strip, provider store and expanded provider defaults
* fix(server): keep the voice overlay dismissible at all times
* fix(server,ui-html,installer): 服务商注册表与语音收尾的评审修正
- 加载配置时先读 ai_providers.json 再校验当前服务商,用户添加的服务商不再被重置为 deepseek
- Token 不再明文写进 ai_providers.json,统一存为 config.toml 的 token_<id>(DPAPI 加密、升级时按凭证保留);旧文件里的明文 token 读取时清掉
- 配置重载同时检查 ai_providers.json 的修改时间,只改注册表也能同步到 Server
- 删除当前服务商时按切换服务商的流程更新 token、地址和模型
- 内置服务商列表收敛到 ProviderStore::BuiltinProviderIds 一处,新增的六个预置服务商同样以 config.toml 为准;去掉未使用的 MakeProviderId,前端生成 id 时避开内置 id
- 出厂模板和开发配置补上六个新预置服务商的 token_/endpoint_/model_ 键
- 录音回调失败时走完整的取消收尾,恢复系统静音并关闭豆包连接;删除不可达的第二段 Esc 处理
- 语音转写地址查询模型时正确推出 /models
- 设置页补上语音识别和润色的 Custom 选项、新预置服务商的显示名,修正添加服务商的地址提示
---------
Co-authored-by: alayahbridge319-boop <246594988+alayahbridge319-boop@users.noreply.github.com >
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-16 21:46:41 +08:00
Lu Fan(SonnyCalcr)
53649d9d12
feat(server,ui,ui-html): 新增实验性的 Direct2D 设置窗口 ( #999 )
...
* feat(ui): 动画时钟、渲染剔除与文本框扩展,并优化 msimeui 性能
- 新增 Animation.h:cubic-bezier 缓动、Tween、颜色插值,Window 提供
RequestAnimationFrame / GetFrameTime,动画帧随显示刷新率推进
- Window 可选 DirectComposition 渲染(含不透明模式以支持 ClearType)、
双击按下、捕获丢失时结束按压、悬停刷新、可关闭销毁时退出消息循环
- Scene 把滚轮交给光标下最内层可滚动元素并逐级冒泡
- DeviceResources:裁剪栈驱动的离屏剔除、按帧回收的笔刷缓存、SVG 栅格缓存
- ScrollViewer 可配置外观与平滑滚动;Popup 支持阴影通道与出现动画;
卡片阴影缓存抽成可复用的 ShadowCache
- TextBox 支持 SetText、密码掩码、多行滚动、字重/字间距/行高与外观设置
- 调试日志默认关闭(MSIMEUI_DEBUG_LOG=1 才写文件),不再在每次点击和
TSF GetTextExt 时打开文件
- 字体回退在 CJK/emoji 映射之后保留系统回退,缺字不再显示方框
- 新增动画与剔除单元测试
* feat(server): 新增实验性的 Direct2D 设置窗口
设置窗口可通过 appearance.settings_ui_backend 选择渲染方式:默认 webview2,
d2d 为实验性的 msimeui 原生实现(server/src/settings/native/),1:1 复刻
网页设置页,包括侧栏切换时竖向高亮条的滑动动画。
- 两个前端共用同一套 webview 契约消息与 HandleClientMessage,配置读写
映射仍只在 settings_app.cpp
- 切换渲染方式后设置进程自动以新方式重新打开;--d2d / --webview2 可临时指定
- 新配置项写入出厂模板与开发配置,UiBackendPolicy 与测试同步更新
- METASEQUOIA_IME_SETTINGS_INSTANCE 允许开发版与已安装的设置窗口并存
- 更新 ui-backends 文档与 server/AGENTS.md
* feat(ui-html): 外观页新增设置窗口渲染方式选项
2026-10-16 16:13:13 +08:00
HowGeGe and fanlusky
64fdc775f0
fix(settings): preserve mouse capture during WebView drags ( #979 )
...
* fix(settings): preserve mouse capture during WebView drags
* fix(server): 设置窗口鼠标抓取丢失时补发抬起
把鼠标转发的抓取状态机从 settings_app.cpp 拆到 settings_mouse_input,
只做决策不调 Win32,测试直接链接它,不再在 configure 时切源码文本。
- WM_CAPTURECHANGED 时给仍按着的键补发抬起,标题栏拖动、
Alt+Tab、系统弹窗抢走抓取后页面不会卡在按下状态
- 抓取期间吞掉 WM_MOUSELEAVE,最后一个键抬起后再重新登记离开跟踪
- 隐藏或关闭设置窗口前释放抓取
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-16 01:56:02 +08:00
Jia Sui and fanlusky
d414268b5f
[Feat] U 模式重构为部件拆字反查 ( #973 )
...
* feat(engine,server,windows,ui-html,installer): U 模式重构为部件拆字反查 (#971 )
把 U 模式从 Unicode 十六进制码位输入改成部件拆字反查:Shift+U 后输入各
组成部分的拼音连写(晶 = ririri)即可出字,候选上反显注音。
- 引擎:新增 local_modes/radical_query(前缀匹配、字频降序),数据表
engine/radical/decomposition.txt 与生成脚本;LocalInputMode::Unicode
改为 Radical;删除 unicode_query。
- Server:g_radical_mode_triggered + IsRadicalCompositionActive + 候选分发,
用 cpp-pinyin 反显注音;删除 Unicode 码位路径。
- 前端 TSF:删除 U 模式的十六进制/数字特例与影子预判,按普通组合处理。
- 配置:utility.unicode_mode 改为 utility.radical_mode。
- 部署:assets 登记 radical_decomposition,安装器与 CI 拷表。
数据来源 mirtlecn/rime-radical-pinyin (GPL-3.0),授权见 engine/radical/NOTICE.md。
* feat(server): U 模式反显拼音改为带调风格(jīng)
* fix(server,installer): 修复 U 模式 CI(拆字表打包夹具与 clang-format)
* fix(engine,server): 修正 U 模式按键错位、重复候选与配置迁移
- Server 在 U 模式不再吞掉撇号、分号、斜杠、大写字母等键:TSF 已把它们记进按键缓冲,
吞掉又不重同步会让两侧串和光标错位。照常插入,含非 a-z 的码查不出候选
- 拆字查询全码命中排在仅前缀命中之前,同一个字的几种拆法只出一次
- 升级合并把旧键 utility.unicode_mode 的值带到 utility.radical_mode,读取时也兜底旧键
- 生成脚本只收汉字,重建拆字表去掉 abc 码下的 51 行数字、字母与符号
- 开着 U 模式时在后台预热拆字表与 cpp-pinyin 词典,避开首个按键的冷加载
- Server 按会话的 RuntimePaths 查拆字表,删掉走全局数据目录的便捷重载
- cpp-pinyin 初始化合并到 utils/hanzi_pinyin,设置页导入注音共用;U 模式与快捷短语的收键分支合并
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-16 01:01:18 +08:00
luo jiyin and fanlusky
f62b383851
fix(server): 适配 OpenAI 推理模型请求参数 ( #968 )
...
* fix(server): 适配 OpenAI 推理模型请求参数
OpenAI 请求使用 max_completion_tokens。推理模型省略 temperature,并增加输出上限。
增加请求 JSON 回归测试。DeepSeek 与自定义服务保持原有参数。
Refs #882
* fix(server): 修复 OpenAI 配置测试参数
设置页探测与 AI 联想共用 token 字段选择规则。
增加 OpenAI、DeepSeek、SiliconFlow 和自定义服务的请求 JSON 测试。
Refs #882
* fix(server): 增加 OpenAI 推理模型配置探测额度
设置页为 OpenAI 推理模型预留 32 个输出 token。
其他模型和服务商保持原有探测额度,并补充 JSON 断言。
Refs #882
* refactor(server): 明确配置探测请求函数名称
将 BuildChatRequestBody 改为 BuildChatProbeRequestBody。
更新设置页调用点和请求 JSON 测试。
Refs #882
* fix(server): 适配 AI 翻译的 OpenAI 推理模型参数
* fix(server): OpenAI 推理模型降低推理强度并放宽超时
默认 medium 推理常超过 8 秒,AI 联想与翻译会静默失败。
推理模型显式发送 reasoning_effort: low,超时与 DeepSeek thinking 一致用 30 秒;
设置页探测带上同样的参数。合并三处重复的推理请求判断。
Refs #882
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-15 22:48:04 +08:00
Jia Sui and fanlusky
2f7335be3e
统计增加首词命中率 ( #963 )
...
* feat(engine): webview 契约给统计概览增加首词命中率字段并支持 null 分支
* feat(server,ui-html): 统计新增首词命中率(决策点记录、stats v2 迁移与设置页展示)
* fix(server): 首词命中计数补日语标点排除与并发迁移复查
* fix(ui-html): 统计卡片改用自适应列宽,避免长数字被省略号截断
* fix(server,installer): 修复首词命中率随附测试在 CI 的失败
三条 server 用例是首词命中率改动新加、但从未跑绿:RawDb 在迁移用例里对尚不存在的库开只读写连接,补上 SQLITE_OPEN_CREATE;09-19 写了一次次选,按契约它是一次真实测量(0%),断言却要求 nullopt,改为断言 0.0,并把 nullopt 覆盖移到迁移用例(total==0 的那天)。安装器 preserve-user-data 从 stats_pipe.cpp 取库名,而 EnsureStatsStoreOpen 连同 stats.db 字面量已挪到 stats_candidate_capture.cpp,改到新位置。
* fix(server,ui-html): 首词命中率写库移出按键路径,修正迁移与统计口径
- 选词计数改由后台 SerialTaskRunner 写库,标点收尾与选词路径不再同步等 SQLite 锁
- 迁移以表结构为准,不再被版本号挡住;缺列时在 IMMEDIATE 事务里逐列补齐
- 标点在日期入口上收尾时按实际上屏的首条候选计数
- 五笔四码唯一自动上屏不计入;多段造词任一段非首位即不算命中
- 只有选词计数的日线不算打字日,不影响天数、日均与连续天数
- 卡片复用 formatFirstWordRatio 并展示累计命中率,概览复用 DailyRow::FirstWordRatio
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-15 22:09:50 +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
時崎 ギャラクシー
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
時崎 ギャラクシー and fanlusky
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
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)
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)
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
Jia Sui and fanlusky
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
fanlusky
af34e23d07
feat(server): 语法模型支持浏览器下载与本地导入,新增网络代理设置
...
应用内下载 octagram 模型较慢时,设置页每个模型行提供「浏览器下载」直链,
下载后用「导入」选中 .gram 文件:后台复制进模型目录,与应用内下载走同一套
格式校验与原子落位,原文件不动。导入与下载共用单槽。
帮助页新增网络代理(跟随系统 / 直连 / 自定义 HTTP 代理),新增 [network]
配置段。云输入、模型下载、AI 联想、翻译、语音识别与润色、凭证测试统一经
NetworkProxy 选择代理,改动即时生效。WinHTTP 的「跟随系统」由 DEFAULT_PROXY
改为 AUTOMATIC_PROXY,真正读取用户的系统代理与 PAC。
契约新增 collocationModelImport 消息;旧设置宿主补上 openExternalUrl。
2026-10-03 22:49:07 +08:00
fanlusky
05d202ce5b
feat(server): 日期时间候选支持调频、置顶、固定位置,并加「📅 日期」格式入口
...
日期时间候选(Shift+T 模式与 rq / sj / xq 混输)按格式 ID 调频:日期文本每天都变,
所以给每种格式一个稳定 ID,学到的顺序记在用户库新表 date_time_format_order,固定位置
借用 fixed_candidate_positions。T 模式与混输共用一份顺序,混输取排第一的格式。右键
置顶、固定位置、取消固定对日期候选生效。
混输时在第三位放一个「📅 日期」入口(时间为「🕒 时间」),选中后不上屏,候选框整页换成
全部格式,与 Ctrl+Enter 译文页同一套子页机制。新增开关 utility.date_time_menu,默认
打开,出厂模板、工作配置、两个设置宿主与设置页同步更新。UILess 宿主不显示入口。
2026-10-03 20:44:22 +08:00
Jia Sui
2557ee4a63
docs: 修正 AGENTS.md 与注释中与代码不符的五处描述
...
spec 校对时逐条核对「文件:行号」锚点发现,五处文档描述与代码现状不符:
- windows/AGENTS.md:格式化遵循的是本目录 .clang-format,仓库没有根目录 .clang-format
- 根 AGENTS.md:engine/... 前缀 include 只对 server/ 成立,windows/ 无仓库根,走相对路径
- scripts/format.sh:注释声称 CI 会跑 generate.py --check,实际没有任何 CI 作业调用
- server/AGENTS.md:测试源文件数与三处行号锚点过期
- server/CMakeLists.txt:注释里的 "eleven add_test()" 实际是 24 个,删去具体数字
纯文档与注释改动,无行为变化。
2026-10-03 08:29:50 +08:00
luojiyin
b0b672ccf1
fix(ai): 限制建议缓存大小
2026-10-03 00:56:30 +08:00
Lu Fan(SonnyCalcr)
f7a78183ee
Merge pull request #584 from luojiyin1987/fix/ai-suggestion-cache-key
...
fix(ai): include semantic request inputs in suggestion cache key
2026-10-03 00:41:49 +08:00
luojiyin
add578fdeb
fix(ai): 修正建议缓存键语义输入
2026-10-02 21:30:30 +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
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
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
Lu Fan(SonnyCalcr)
bb8c5e9be8
Merge pull request #495 from fanlusky/refactor/split-config-and-controls
...
refactor(server,ui): 按职责拆分 ime_config.cpp 与 Controls.cpp
2026-09-27 18:06:05 +08:00
fanlusky
b5def6a2e3
refactor(server): 按职责拆分 ime_config.cpp
...
ime_config.cpp 有 4000 行,配置状态与加载、TOML 文本编辑、升级时的模板合并、
写回与通知 Server,以及上百对 GetConfigured* / SetConfigured* 全挤在一个文件里。
按职责拆成以下几个翻译单元:
- ime_config.cpp:全部配置全局变量的定义(顺序与初始值不变)、语音配置的读写锁
快照与发布、各项校验白名单、LoadImeConfig(读取与缺省值)、旧版语音配置迁移、
InitImeConfig / ReloadImeConfigIfChanged / InvalidateImeConfigWriteTime
- ime_config_toml.cpp:保留格式的键值替换与插入、跨行值定位、赋值遍历与解析、
读文件、原子写文件与 TOML 解析校验
- ime_config_template.cpp:凭证兜底重放、MergeTomlIntoTemplate /
MergeConfigIntoTemplate、损坏配置的留证与抢救、SyncConfigWithInstalledTemplate、
旧 ACP 乱码目录的配置找回
- ime_config_write.cpp:WriteConfiguredValues / WriteConfiguredValue 与
NotifyImeServer*(Aux 管道与窗口消息两条通知路径)
- ime_config_appearance.cpp:每页候选数、字体与字号、文字颜色、候选窗布局与跟随
光标、小窗 UI 后端、皮肤、预编辑样式、固定徽标、设置窗口保留时长
- ime_config_fonts.cpp:ResolveSystemFontFamilyForCss 与 GetSystemFontFamilies
- ime_config_theme.cpp:主题模式、各界面主题覆盖与 ResolveConfiguredTheme
- ime_config_input.cpp:输入方案与中日文模式、简繁、中英切换键、双拼与五笔方案、
上屏样式、辅助码、全拼纠错、模糊音、调频
- ime_config_keys.cpp:翻页键、方向键选词、以词定字、智能标点、配对标点、标点锁定
及发给 TSF 的载荷
- ime_config_toolbar.cpp:悬浮工具栏与光标状态角标
- ime_config_general.cpp:诊断日志、输入统计、中英与表情混输、候选翻译、云候选、
整句联想、工具模式与剪贴板历史
- ime_config_services.cpp:语音输入、AI 助手、腾讯 TMT、自定义翻译与小牛翻译
- ime_config_internal.h:上述文件之间共享的声明,只给 ime_config*.cpp 包含
纯搬移,不改行为:函数体、注释和格式都没动。原来在匿名命名空间里、现在
要跨文件用的符号放进 ime_config_detail 命名空间并在内部头文件里声明,
避免与 Server 其他翻译单元里的同名内部符号冲突;只被一个文件用到的仍留在
该文件的匿名命名空间,包括 g_session_backend、两个语音 token 槽位补写标记、
g_config_last_write_time、PublishVoiceInput、AiAssistantProviders、LoadImeConfig
和各文件自己的小工具。全局变量的定义都还在 ime_config.cpp,按原顺序排列,
初始化顺序不变;函数内的 static(字体缓存、DirectWrite 工厂、凭证键表、剪贴板
首次加载标记等)仍各自只有一份定义。取值范围常量、kFuzzyPinyinRuleKeys、
ConfigFileLock、ParseScheme 前置声明、模板文件名、TomlAssignmentVisitor 和
ConfigValueUpdate 原样挪进内部头文件。MetasequoiaImeServer、
MetasequoiaImeSettings 和测试工程都编译 ime_config.cpp,三处源文件列表一并
补上新文件。
非搬移改动只有两处:NotifyImeServer 的默认实参从定义挪到了头文件里的声明上;
测试里 kFuzzyPinyinRuleKeys 副本的注释改指 ime_config_internal.h。
用脚本比对了原文件与拆分后全部文件的非空、非 #include 行多重集:除上述
函数签名外,差异只有新增的声明、extern、命名空间包装、using 指令、
#pragma once 和文件头注释。可用 git diff --color-moved 复核。
2026-09-27 17:22:17 +08:00
fanlusky
4b30659fca
ci: 单独构建并运行引擎自己的测试
...
Server 任务以 BUILD_TESTING=OFF 把引擎当库引入(避免 EXCLUDE_FROM_ALL 下的测试
找不到可执行文件),于是引擎的 23 组测试从来没在 CI 里跑过,只在开发机上跑。
server/tests 覆盖不到的行为——比如用户日志的连接管理、关闭它时要放开数据目录里
所有缓存句柄——只有这些测试能发现;#491 的一个回归就是这样在本地才测出来的。
新增 Engine 任务:按 engine/vcpkg.json 单独配置引擎、构建、再用
scripts/ci/test-engine.ps1 在与 Server 相同的锁定词库上跑 ctest。数据目录与
LOCALAPPDATA 都隔离到临时目录,布局与 test-server.ps1 一致。同步更新
server/CMakeLists.txt 里「引擎测试经 server/tests 覆盖」的旧说法。
本地按同样命令(不设 BOOST_ROOT,只靠 vcpkg 清单)配置、构建并运行:23/23 通过。
2026-09-27 16:54:20 +08:00
fanlusky
8f2c81dd0f
perf(server,engine): 选词学习的写库挪出按键线程
...
权重调整(AdjustCandidateRanking)和输入过的英文词(LearnEnteredEnglishWord)
原本和按键排在同一个 worker 队列里:每次选非首位候选都要在下一个键之前付一次
用户日志的提交和 fsync,触发阈值时还有 msime.db 的一次提交。磁盘忙或杀软扫描
时,这就是肉眼可见的卡顿。
- server/src/utils/serial_task_runner:单线程、按提交顺序执行、低优先级,Stop()
先执行完队列再退出,选词不会在退出时丢失。附单元测试。
- 两类写入改为投递给这个执行器。它们只调用 user_dictionary,不碰输入会话;唯一
的会话副作用——权重变化后清引擎候选缓存——经 EnqueueResetInputSessionCacheTask
回到 worker。删除对应的 TaskType 和 Task 字段。main 在 worker join 之后调用
ShutdownDictionaryWriter()。
- user_dictionary:默认日志连接改为每线程一条。FULLMUTEX 只保证单次调用安全,
保证不了事务:连接只有一个事务状态,写线程的 BEGIN IMMEDIATE..COMMIT 会把
worker 的读卷进来,worker 侧 UserDatabase 析构时还会把它回滚。
close_default_user_database() 递增代号,各线程下次访问时关闭旧连接。
- WorkerThread 取任务改为 move,不再每次复制整个 Task。
行为变化:选词后如果写入还没完成就立刻再打同一串,会短暂看到旧顺序;写完后
worker 清缓存,下一次查询即是新顺序。此前的“下一个键之前一定写完”换成了不阻塞
按键。
2026-09-27 16:09:51 +08:00
fanlusky
a8b5246852
refactor(server): 按职责拆分 ime_windows.cpp
...
ime_windows.cpp 有 4600 行,窗口创建与总窗口过程、候选窗定位与裁剪、WebView2
候选窗显示流程、候选窗消息处理、悬浮工具栏、托盘菜单、设置窗口和光标状态角标
全挤在一个文件里。按职责拆成以下几个翻译单元:
- ime_windows.cpp:共享状态与全局变量、系统明暗切换判定、宿主 cloak 与状态
描述(DescribeCandidateHostState、DescribeTrayMenuHostState 等)、
SyncHostWebViewBounds、ApplyConfiguredInputScheme / ApplyConfiguredShuangpinSchema、
窗口类与消息注册、CreateCandidateWindow、WndProc 分发,以及光标状态角标的
WndProcCaretStateWindow
- ime_windows_candidate_layout.cpp:光标锚点、四分之一屏宿主尺寸、装饰偏移、
翻转与裁剪记忆、ReanchorCandidateHostToPaintedCard、
ClipCandidateWindowToContent / SetWindowRgn、KeepCandidateCardInsideHostAndMonitor
- ime_windows_candidate_show.cpp:RefreshCandidateClipAfterPaint、
PlaceCandidateHostNearCaret、槽位测量扩展裁剪、FinishCandidateShowAfterSlots
和两遍测量的 FineTuneWindow
- ime_windows_candidate.cpp:WndProcCandWindow(显示 / 隐藏 / 移动消息、去重与
隐藏宽限、DPI 变化、状态切换转发、配置同步定时器、候选鼠标动作)
- ime_windows_toolbar.cpp:悬浮工具栏的配置尺寸、拖动后收回可见屏幕、
LayoutFloatingToolbar、显隐决策与绘制宽限、ApplyConfiguredFloatingToolbarSize、
WndProcFtbWindow
- ime_windows_menu.cpp:托盘菜单的 DIP 到物理像素换算与 WndProcMenuWindow
- ime_windows_settings.cpp:设置窗口的前台激活与重试、非客户区与最大化按钮、
鼠标输入转发、WndProcSettingsWindow
- ime_windows_internal.h:上述文件之间共享的声明,只给 ime_windows*.cpp 包含
纯搬移,不改行为:函数体、注释和格式都没动。原来在匿名命名空间里、现在
要跨文件用的符号放进 ime_windows_detail 命名空间并在内部头文件里声明,
避免与 Server 其他翻译单元里的同名内部符号冲突;只被一个文件用到的仍留在
该文件的匿名命名空间,包括只有候选窗消息处理用到的强制重排、隐藏宽限与
去重签名状态,以及只有设置窗口用到的激活重试计数。其余共享全局变量的定义
仍在 ime_windows.cpp。WEBVIEW_DIAG_LOGF 宏、定时器 ID 与时长常量、
FineTuneWindow 等前置声明和 SuppressCandidateDpiChange 原样挪进内部头文件。
非搬移改动只有两处:LogCandidatePositionAudit 和 PlaceCandidateHostNearCaret
的默认实参从定义挪到了头文件里的声明上;DescribeTrayMenuHostState 上方注释
里的文件名改成 windows_webview2_menu.cpp,菜单的生命周期事件在上一次拆分后
已经搬到那里。
用脚本比对了原文件与拆分后全部文件的非空、非 #include 行多重集:除上述
两个函数签名和那一行注释外,差异只有新增的声明、命名空间包装、using 指令、
#pragma once 和文件头注释。可用 git diff --color-moved 复核。
2026-09-27 15:49:35 +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
fanlusky
b6ca8bd8ba
refactor(server): 按职责拆分 event_listener.cpp
...
event_listener.cpp 有 5700 行,按键、候选组页、异步候选合并、选词、任务
队列和命名管道服务端全挤在一个文件里。按职责拆成以下几个翻译单元:
- event_listener.cpp:共享状态(特殊模式开关、UILess、译文缓存、异步请求
来源、状态快照、候选 UI 归属)、模式判定与候选窗显隐辅助,以及
CancelCloudCandidateRequest、RegisterStatusSnapshotWindow、
SendCurrentDataToClient、SendUiLessCompositionToClient、ClearState
- event_listener_worker.cpp:Task 定义、任务队列、顶字 Hide 抑制记账、
WorkerThread 分发循环、ReplayDeferredClientActivation 和各个 Enqueue*
- event_listener_pipes.cpp:管道握手与日志辅助、各管道监听线程、主管道
客户端线程、RegisteredPipeMonitorThread、关停时唤醒监听
- event_listener_candidates.cpp:候选组页与发布(BuildCurrentCandidatePage、
RefreshCandidatePageUi 等)、调频排序键、翻页扩展、PrepareCandidateList
- event_listener_async_candidates.cpp:云联想、AI、神经重排、英文、译文、
emoji、颜文字的 Apply* 合并
- event_listener_keys.cpp:按键分类、ApplyCompositionEditKey、译文副候选页、
五笔顶字负载、PublishRestoredCompositionCandidates、HandleImeKey
- event_listener_selection.cpp:ResolveCandidateItem、
WaitForCandidateRenderSync、ProcessSelectionKey
- event_listener_internal.h:上述文件之间共享的声明,只给
event_listener*.cpp 包含
纯搬移,不改行为:函数体、注释和格式都没动。原来在匿名命名空间里、现在
要跨文件用的符号放进 event_listener_detail 命名空间并在内部头文件里声明,
避免与 Server 其他翻译单元里的同名内部符号冲突;只被一个文件用到的仍留在
该文件的匿名命名空间。共享全局变量的定义仍在 event_listener.cpp。唯一的
非搬移改动是 UpdateCloudInput/UpdateEnglishInput/UpdateEmojiInput/
UpdateKaomojiInput/UpdateAiInput 的默认实参从定义挪到了头文件里的声明上。
用脚本比对了原文件与拆分后全部文件的非空、非 #include 行多重集:除上述
五个函数签名外,差异只有新增的声明、命名空间包装、using 指令、
#pragma once 和文件头注释。可用 git diff --color-moved 复核。
2026-09-27 15:26:19 +08:00
fanlusky
030f2d4db8
feat(server): D2D 悬浮工具栏跟随候选窗皮肤配色
...
此前 D2D 工具栏只按明暗切换默认配色,不看皮肤。新增 floating_toolbar_skin,
按 ui-html/webview2/ftb/<skin>(_light).html 给出背景、边框、图标、悬停、分隔线、
拖动条颜色与圆角;外部皮肤沿用其 base 皮肤,因为 toolbar.css 只作用于 WebView2。
2026-09-26 21:49:04 +08:00
AaGMix
902989abc4
feat(server): 显示光标输入状态提示
2026-09-22 23:52:28 +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
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
Evan
497b46e067
feat(ipc): validate voice control leases
...
* feat(ipc): validate voice control leases
* style(ipc): format voice control dispatcher
* fix(ipc): use local engine contract path
2026-09-14 18:21:49 +08:00
fanlusky
529060a359
feat(settings): API Key 配置项增加连通性测试按钮
...
设置页中所有需要用户自填 API Key 的配置项(AI 助手、候选词翻译、
语音识别、语音润色)旁增加「测试配置」按钮,点击后用当前填写的凭据
向服务发一个最小请求,即时反馈配置是否有效。
- server: 新增 ApiCredentialTest::Run 统一分发(chat 发 1-token 请求、
ASR 传静音 WAV、翻译发单词校验、豆包走 DoubaoAsrClient::TestCredentials),
settings 可执行文件补上 curl/zlib/bcrypt/winhttp 及 cloud/voice 源。
- engine/contracts: 新增 apiCredentialTest / apiCredentialTestResult 消息,
已 generate.py 重新生成并 sync-contracts 同步至 ui-html/shared。
- ui-html: credential-test 模块负责发消息与渲染结果;测试按钮改小两号;
外观页补充字体下拉框加宽至 1.5 倍、加号按钮宽度减半。
- docs(agents): 补充格式化会卡 CI 的注意事项——clang-format 须为 18.1.8,
且 format.sh 在 Windows 上因 venv 布局失效,需直接调 clang-format 自查。
2026-09-13 22:11:09 +08:00
fanlusky
e350a94509
feat(translation): 候选词翻译支持小牛翻译(NiuTrans)
...
在线翻译服务由「腾讯云 / 自定义 DeepLX」扩展为三选一,新增小牛翻译。
- 新增 NiuTrans provider(/v2/text/translate):按官方规则对参数加 apikey
排序拼接后取 MD5 作为 authStr(BCrypt 实现,无新增依赖),表单编码逐条
翻译,解析 tgtText,errorCode/errorMsg 视为失败
- cloud_translation 调度改为 Provider 枚举,优先级 niutrans > custom > tencent,
缓存 key 带 provider 区分
- 配置层新增 [niutrans](enabled/app_id/apikey)及读取与读写接口
- 设置界面新增「小牛翻译」下拉项与 APP ID / API Key 输入框,切换时保持
provider 互斥
- 新增 test_niutrans_translation(凭据校验、鉴权 MD5、响应解析)
2026-09-13 20:54:45 +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
73ec6fe9c9
perf(ci): cache the Server MSVC build with sccache
...
The Server job's Build step dominates the pipeline (~6m37s of a ~9.5m job;
the dictionary/integration test is only ~43s). sccache caches individual
MSVC compiles across runs so an incremental change rebuilds only what it
touched instead of the whole server + engine tree.
sccache only caches an MSVC compile whose debug info is embedded in the
object, so switch the Server build's Release debug info from /Zi to /Z7.
The linker's /DEBUG still emits the beside-the-executable PDB the release
job validates, so the symbol layout is unchanged.
Its compiler-launcher hook is honoured only by the Ninja and Makefile
generators, so configure the Server build with Ninja Multi-Config (keeping
the --config Release / bin/Release layout the later steps depend on) and
add an MSVC-environment step, since Ninja does not locate cl.exe on its own.
2026-09-11 03:46:30 +08:00
AaGMix
e9a2c91214
feat(installer): include release PDB symbols
2026-09-07 02:10:38 +08:00
Evan Hu
87e842f6a3
build(windows): TSF、Server 与设置程序开启 /W4
...
/W4 已经是本仓的既有约定——ui/ 的五个 target 和 server/ 的四个辅助可执行文件都在用。漏掉的恰好是三个最大的:注入到 Chrome、QQ、Office 等宿主进程里的 TSF DLL,拥有全部网络调用、WebView2 宿主与皮肤加载的 Server 主进程,以及设置程序。这三个此前都跑在默认的 /W3 上。
暂不加 /WX:存量告警要先在 Windows 上读完并清掉,在那之前把告警变成错误只会逼出一堆 /wd 抑制。/permissive- 同样留到能在 Windows 上构建验证的后续改动,本次改动无法在本机编译验证这一项。
2026-09-06 18:11:37 +09:00
Evan Hu
680fdfa648
fix(skin): 丢弃第三方皮肤 CSS 里的远程引用,不再原样注入候选窗
...
皮肤是用户往 %LOCALAPPDATA%\metasequoiaime\skins 里丢的文件夹,是彻头彻尾的第三方内容,而它的 CSS 会被内联进候选窗文档。此前 IsRewritableSkinCssUrl() 对含 :// 的 URL 直接返回 false,而 false 分支把原始 url("…") 原封不动写回样式表——也就是说 `background-image: url(https://attacker/beacon.png )` 会在每一次候选窗渲染时向外发一个请求,泄露「此人此刻正在打字」以及 IP。这直接推翻了 PRIVACY.md 的 "Local conversion never contacts the network",而那句话是这个项目最强的信任资产。
问题出在这个判断只有两态:不可重写就等于放行。实际上有三类:
- Embed——皮肤包内的相对路径,读文件内联;
- Keep——本身就是惰性的 data: 负载和文档内 #fragment 引用,照原样保留;
- Drop——任何会让候选窗向皮肤包之外取数据的写法。丢掉 URL 后声明不完整,CSS 会整条丢弃,这正是想要的结果。
Drop 覆盖 http/https/ftp 等带 scheme 的、协议相对的 //host/x(不写 scheme 但照样出网)、路径穿越,以及会逃出皮肤包的绝对路径。
url() 不是唯一入口:`@import "https://…"` 根本不含 url() token,扫描器永远看不到它。补 StripRemoteImports() 做前置处理,只删远程 @import,本地的保留。
skin.preview 来自皮肤自己的 skin.toml,此前直接进 EmbedSkinCssUrl。现在同样过一遍分类,不因为它是通过 manifest 字段到达就被信任。
判断逻辑抽到 server/src/webview2/skin_css_policy.{h,cpp},不依赖任何 Windows API,配 6 个用例覆盖三类判定与两种 @import 写法。本机以 -Wall -Wextra 编译运行全部通过。
2026-09-06 18:05:09 +09:00
Evan
4fc16952b3
wip(windows): 补齐隐私与许可交付,修好外部人的构建入口
...
- 新增 PRIVACY.md:逐项写明每个联网端点、发送内容、默认开关与关闭方式,明确云候选默认开启、
token 在 config.toml 明文存储、无任何遥测
- 设置页「用户协议」「隐私政策」两个无 handler 的死按钮接上;前者改名为「开源许可协议」指向
LICENSE,开源产品的用户条款就是许可证本身,不再承诺一份不存在的 EULA
- 安装包随包装入 GPL 正文并在安装向导显示(GPLv3 §4/§6),补上打包测试断言
- release.yml 增加 build provenance attestation,并在发布后向 MSIME-Web 发 repository_dispatch,
让 msime.app/update.json 不再等两小时一次的 cron
- 所有 CMakePresets 的 vcpkg 路径改用 $env{VCPKG_ROOT},server 的 Boost 回退不再指向个人目录
- ui/README.md 指向本机绝对路径的 markdown 死链改成相对链接
- 新增 check-developer-paths.sh 并接入 required 的 quality 检查,防止路径回潮
- docs/installation.md 不再写死版本号
2026-09-06 12:05:06 +09:00