fanlusky
bc37ac9a9e
feat(installer): 安装时可自定义数据目录位置
...
词库、用户配置和皮肤此前固定在 %LOCALAPPDATA%\metasequoiaime,整包几百 MB
全压在 C 盘。安装向导新增一页,允许把这部分数据放到任意盘。
程序本体仍固定装在 Program Files:MetasequoiaImeServer.exe 带 uiAccess=true,
只有在受信任目录里这个标志才生效。
运行期的权威来源是 HKLM\Software\Metasequoia\MetasequoiaIME 的 DataDir。
Server、TSF DLL 和引擎三个组件各自按同一顺序解析:METASEQUOIA_IME_DATA_DIR
环境变量 -> 该注册表值 -> %LOCALAPPDATA%\metasequoiaime。三处各持一份实现,
因为组件边界规则禁止 windows/ 与 server/ 互相 include。32 位 TSF DLL 用
KEY_WOW64_64KEY 读,避开 WOW64 重定向。
安全相关的两个约束:
- 安装器在数据目录里写 .metasequoiaime-data 标记。覆盖安装的清理和卸载的整目录
删除只在看到这个标记(或目录就是历史默认位置)时才执行——用户可能把数据目录
指到一个本来就有自己文件的文件夹。
- 选择页与 /DATADIR= 走同一套校验:拒绝相对路径、盘符根目录、不存在的盘、程序
目录内部,以及 Windows/Program Files/各 AppData/用户主目录本身。
升级时改了位置,PrepareToInstall 在停掉输入法进程后用 robocopy /MOVE 搬运用户
拥有的数据(msime_user.db 及其 WAL/SHM/journal、config.toml、config.base.toml、
skins\),词库和前端资源由本次安装重新写入。跨卷移动目录 MoveFile 会失败,所以用
robocopy。用户词库搬运失败会中止安装:否则新版本对着空的 msime_user.db 启动,
ssPostInstall 的回放只记一句 skipped,用户的自造词就静悄悄没了。
测试侧新增 METASEQUOIA_IME_CONFIG_DIR,只改 config.toml 的位置而不搬词库。原先
靠重定向 LOCALAPPDATA 做隔离的办法在装了正式版的机器上已不再成立——注册表值
的优先级高于它。
2026-09-17 22:49:19 +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
426bb9d544
feat(server): WebView2 候选窗口支持滚轮翻页,并加上开关
...
PR #351 只给 D2D 候选窗口加了滚轮翻页,WebView2 后端还没有。
网页端只上报一个以 WHEEL_DELTA 为单位的原始 delta,notch 累加仍然
交给 CandidateWheel::ConsumeWheelDelta,两个宿主最终都汇聚到
WM_PAGE_CANDIDATE,翻页逻辑一份不重复。Chromium 的 wheelDeltaY 本身
就是 ±120/格,正是原生累加器期望的单位,deltaY 作标准回退。
同时把滚轮翻页做成可配置的 general.paging_mouse_wheel,默认关闭:
候选宿主是 NOACTIVATE 且从不获得焦点,滚轮只有在 Windows「当我悬停
在非活动窗口上方时滚动它们」开启时才会送达,无法无条件承诺,所以设为
opt-in,设置页里也写明了这个前提。开关只在 WM_PAGE_CANDIDATE 一处
判断;D2D 那边在 WM_MOUSEWHEEL 顶部提前返回 false,关闭时消息照旧
交给 DefWindowProc,与功能加入前一致。
2026-09-13 01:02:02 +08:00
fanlusky
e860eaab43
fix(server): 避免 WebView2 候选窗阴影被裁切
2026-09-12 23:50:31 +08:00
fanlusky
2717000084
fix(server): 候选窗右侧截断——去重的 show 不再让待应用的裁剪区失效
...
WebView2 后端下候选窗右边总是被竖切一刀,且多半切在最后一个候选上。
根因在 WndProcCandWindow:g_candidate_content_generation 在处理函数最顶
部无条件自增,早于下面的去重判断。IME 每次按键会投递两次 show,于是每一
次按键的时序都是:
gen N -> path=content-only,InflateCandWnd 带着捕获 gen N 的回调发出
DOM 更新;
gen N+1 -> 帧签名相同,走 path=dedup,什么都没画、什么都没排,但把计数
器留在了 N+1;
gen N 的 dom-callback 触发,发现 N != N+1,直接 return。
结果 SetWindowRgn 只会被完整的 FineTune 刷新,区域一直冻结在上一次
FineTune 量到的宽度上。后面的候选页一旦更宽,就会画到过期区域之外 —— 右
边缘的硬切;更窄时只是多出一块看不见的透明留白,所以这个 bug 时隐时现。
诊断日志(851 行)里的证据:path=content-only 18 次,dom-callback 世代
不匹配 18/18,clip-measure-submit / apply / drop 全为 0,ui-region 7 次且
全部来自 path=full-layout。区域算术本身在 7 个样本里都正确。
修复:去重分支把世代还回去(compare_exchange,仅在期间无人自增时生效),
因为一次去重的 show 什么都没画,DOM 里仍然正是那次在途测量对应的帧。上面
的 path=superseded 出于同样的原因在自增之前就返回。
顺带修掉同一个 bug 的另一半与它为何一直查不出来:
- 去掉 #msime-fast-layout 那张给 .container/.row-wrapper 强加
white-space:nowrap!important 的样式表(并在应用外观时移除历史残留),
它让卡片长过 --msime-max-width-dip 的上限,再被 overflow-x:hidden 从字
中间切开;
- ApplyCandidateFrame 与测量脚本改用 max-content,不再写死 nowrap;
- 量测宽度纳入 scrollWidth,并把 rw/ow/sw/cw 一并回传;
- webview_utils.cpp 里被 #define 成空操作的 ui-measure 日志恢复,裁剪路径
的静默 return 补上 clip-refresh-skip 一行 —— 正是这两处的沉默让「区域
从不刷新」在日志里毫无痕迹。
新增的日志只记录数字、坐标与状态,不含任何用户输入或候选文本。
Release / Debug 构建通过,ctest -C Debug 2/2 通过,打包安装后人工验证不再截断。
2026-09-12 18:42:04 +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
54e5c68f94
refactor(server): clear the Server and test /W4 warnings
...
Most of them come from logging that is compiled out. The disabled DIAG_LOGF /
FANY_IPC_LOG* macros expanded to `((void)0)`, so every HRESULT and parameter
that exists only to be logged looked unreferenced (C4189/C4100). They now
expand to `((void)sizeof(DiscardLogArgs(__VA_ARGS__)))`: the arguments stay in
an unevaluated context, so nothing is computed and no side effect runs, but the
names count as used.
The rest:
- C4100: commented out the parameter names that are genuinely unused
(WinMain's hPrevInstance/nCmdShow, AdjustWndPosition's hwnd/height, the
WebView2 callbacks' sender, and the no-op logging sinks).
- C4189/C4101: removed the dead locals and the unused catch bindings left over
with the log statements.
- C4244: made the DIP-to-pixel and int-to-float conversions explicit in
globals.h, ime_windows.cpp and the menu placement.
- C4456: renamed the shadowing `hwnd` and `lock` locals.
- C4005: guarded the tests' `#define NOMINMAX` with #ifndef; the test targets
already define it on the command line.
Release builds clean and ctest passes for the server, the tests and msimeui.
Warnings under vendor/ are untouched: those are submodules with their own
repositories.
2026-09-12 00:55:57 +08:00
Lu Fan(SonnyCalcr)
0eaa35eed1
Merge pull request #274 from linyanm/fix/issue-155-settings-lag
...
fix(server,ui-html): 降低设置窗口拖动与工具栏首屏卡顿
2026-09-10 21:33:31 +08:00
fanlusky
093b485124
Merge branch 'develop' into fix/issue-242-settings-cannot-save
...
冲突解决:
- windows/src/Utils/FanyUtils.h:develop 通过新增的 .gitattributes 把整个文件
从 CRLF 规范化成 LF,与本分支删除 GetIMEDataDirPath() 声明的改动整文件冲突。
取 develop 的 LF 版本,再删掉该声明。
- server/tests/src/test_config_non_ascii_path.cpp:仅 include 块冲突,取两侧并集
(algorithm/filesystem/fstream/iterator/string/system_error)。另修正一处错误的
自动合并——develop 新增的候选回退字体断言本属于
config_round_trips_under_non_ascii_profile_path,git 却按上下文并进了本分支新增的
config_migrates_legacy_acp_mangled_path,已移回原测试。
2026-09-10 19:33:43 +08:00
Lu Fan(SonnyCalcr)
184444cf7f
Merge pull request #272 from linyanm/fix/issue-267-d2d-toolbar-size
...
fix(server): 设置变更时重建 D2D 悬浮工具栏外观
2026-09-10 19:18:07 +08:00
Aegon8719
dea9292814
issue-55 候选窗支持可配置的有序回落字体
2026-09-09 18:03:56 +08:00
linym
c07aa8e878
fix(server,ui-html): 降低设置窗口激活卡顿 ( #155 )
...
激活时不再重推整份配置;HTML 按 UTF-8 二进制读取;契约脚本移到标记之后;最大化按钮矩形用 rAF 合并。
2026-09-08 22:47:26 +08:00
linym
075fbb2a84
fix(server): persist settings after leftover non-ASCII config paths
...
Elevated setup left high-IL config.toml that medium-IL Settings never
rewrote on machines hit by the ANSI path bug. Make the data directory
writable, migrate ACP-mangled leftovers, rebuild unparseable files, and
keep remaining asset paths on wchar_t.
Fixes #242
2026-09-08 22:42:26 +08:00
linym
f588b7f5e8
fix(server): rebuild D2D toolbar when scale or icon size changes
...
D2D bakes user scale into Visuals at RebuildScene. RelayoutHost alone left the live toolbar unchanged after settings updates.
2026-09-08 22:26:31 +08:00
Aegon8719
6ff1cff42d
issue-52 加入简繁切换快捷键;增加候选词翻页快捷键组合、增加以词定字快捷键组合;
...
issue-55 候选词与辅助码间保留一个半角空格。
2026-09-08 14:07:48 +08:00
Evan Hu
e645c9abaa
fix(skin): 修复 GCC 下的符号比较告警与 clang-format
...
两处 CI 反馈:
towlower() 在 glibc 上返回 wint_t(无符号),与 wchar_t 比较触发 -Wsign-compare。macOS 上 wchar_t 是 32 位有符号、towlower 返回 int,所以本机以 -Wall -Wextra 编译不会告警。显式转回 wchar_t 再比较。
windows_webview2.cpp 里那处调用的换行不符合仓库的 clang-format 配置,按配置重排。
重新以 -Wall -Wextra -Wpedantic -Werror + ASan/UBSan 构建运行,6 个用例全过。
2026-09-06 18:28:30 +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
fanlusky
8544e74dc5
fix: Fix the position issue of the WebView2 candidate window when it touches the screen edge.
2026-09-06 14:59:19 +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
b5d07f11eb
fix(server): reduce WebView message and first-paint overhead
2026-09-06 03:05:36 +09:00
Evan Hu
3c24076fad
feat(webview): validate shared message contracts before native dispatch
2026-09-05 23:07:50 +09:00
fanlusky
b0156540c0
Revert the WebView2 changes and temporarily make it switchable between rendering backends.
2026-09-04 01:42:10 +08:00
fanlusky
4c6b72a04e
Add an option for [ / ] page turning.
2026-09-02 21:13:48 +08:00
fanlusky
82048fb8be
Add a setting to always use Chinese/English punctuation.
2026-09-02 20:53:04 +08:00
fanlusky
65b7516ea3
Replace the floating toolbar WebView2 with a D2D presenter
...
Draw the status bar with Direct2D and Segoe Fluent Icons, keep the existing
toggle and click behavior, and size the host so the compact mist shadow is
not clipped.
2026-09-02 19:12:16 +08:00
fanlusky
ea9218ceff
Replace the tray menu WebView2 with a D2D presenter
...
TrayMenuPresenter renders the language-bar right-click menu through
Direct2D on the existing menu HWND, matching the HTML version 1:1:
floating toolbar toggle, emoji/handwriting/keyboard/voice launchers,
settings, and about. The WebView2 menu path is kept as a fallback but
no longer created by default. Preedit caret reflow now uses an inline
object slot instead of letter-spacing, and cloud/AI candidate badges
are restored on D2D items.
2026-09-02 16:04:29 +08:00
fanlusky
5c02503139
Render the candidate window with D2D instead of WebView2
...
Drive the existing candidate HWND through CandidatePresenter and msimeui,
keep WebView2 for the menu, floating toolbar, and settings, and load
external skins from skin.toml color tokens. Restore cloud/AI badges on
D2D items, and keep click, pin, fix-position, and delete on the same
window messages as before.
2026-09-02 15:30:03 +08:00
fanlusky
30fc78f534
Improve candidate window stability, positioning, and pointer interaction
2026-09-01 00:45:19 +08:00
fanlusky
464406beac
Fix the regression issue introduced after the previous fix for slow candidate window response during high CPU usage: candidate window flickering and candidates being highlighted without mouse hover movement.
2026-08-30 17:54:02 +08:00
fanlusky
2effb7c393
Add support for the DeepLX translation service.
2026-08-30 15:39:02 +08:00
fanlusky
7c5b7505ea
Adjust some styles.
2026-08-29 21:48:50 +08:00
fanlusky
2ca14df0a5
Add support for a skin system.
2026-08-29 19:36:12 +08:00
fanlusky
dafc7d6040
Optimize the logging on both the TSF and Server sides to facilitate issue diagnosis.
2026-08-25 13:53:30 +08:00
fanlusky
f85d77e6a1
Add multilingual candidate translation.
2026-08-25 12:40:55 +08:00
fanlusky
b159d8c9ab
Update local path. Optimize audio interruption.
2026-08-23 23:37:00 +08:00
fanlusky
0e19f06da2
Fix the issue where the candidate window is truncated.
2026-08-23 18:58:35 +08:00
fanlusky
06e19fb14f
Add some logs.
2026-08-23 18:10:47 +08:00
fanlusky
0049a18cdd
Fix the issue where the language bar and FTB are out of sync for the Chinese-Japanese status.
2026-08-22 19:40:51 +08:00
fanlusky
2e44cc4e24
Add logs.
2026-08-22 18:34:13 +08:00
fanlusky
e42cc92b8e
Attempt to fix the flickering issue with Microsoft Shuangpin. Add an R mode for temporary Japanese input.
2026-08-22 16:54:29 +08:00
fanlusky
bade05e5aa
Provide an entry for setting the Tencent Cloud Translation API key.
2026-08-22 13:38:08 +08:00
fanlusky
1cf7846382
There should also be a corresponding status indicator when the keyboard Caps Lock is turned on.
2026-08-21 23:09:03 +08:00
fanlusky
6960a74fce
Add dynamic switching of Japanese icons.
2026-08-21 17:33:37 +08:00
fanlusky
27b0974be1
Add preliminary support for Japanese input.
2026-08-21 16:55:41 +08:00
fanlusky
e885856124
Add Chinese-English translation functionality during typing.
2026-08-21 16:04:50 +08:00
fanlusky
1ca62aa947
Add the Y mode feature, which provides temporary English input functionality.
2026-08-19 19:40:18 +08:00
fanlusky
20194244bb
Add fine-grained control over the character count threshold for triggering Chinese-English mixed input.
2026-08-19 13:40:54 +08:00
fanlusky
943b6fbd4b
Add J mode, the shorthand pinyin mode.
2026-08-19 08:05:44 +08:00
fanlusky
fe649e706f
Implement the clipboard functionality in the emoji panel.
2026-08-19 00:00:05 +08:00
fanlusky
8565b0531b
Add kaomoji to mixed candidates (after emoji) behind general.kaomoji_mixed_input
...
kaomoji_ime: async worker mirroring emoji_ime; KaomojiQuery::QueryPrefix
gains a db_path override. NormalizeMixedCandidateOrder places kaomoji right
after emoji, shifting with cloud/AI. event_listener wires
UpdateKaomojiInput/ApplyKaomojiCandidates/EnqueueKaomojiCandidates and the
PrepareCandidateList trigger; config general.kaomoji_mixed_input is plumbed
through settings_app/webview2; main.cpp starts/stops KaomojiIme; ordering test
added.
2026-08-17 00:08:24 +08:00