Files
msime-windows/server
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
..
2026-03-12 01:34:41 +08:00
2025-04-16 02:03:11 +08:00
2025-11-10 21:10:36 +08:00

Metasequoia IME Server(水杉输入法 Server 端)

This is the server end of MSIME-Windows.

How to build

Prerequisites

  • Visual Studio 2026
  • CMake
  • vcpkg
  • Python3.10+
  • Boost
  • Node.js and pnpm 10 (settings page build)

Make sure vcpkg and Boost are installed by Scoop.

Build steps(For Dev)

Clone the product and provision the reviewed dictionary release from its root:

git clone --recursive https://github.com/metasequoiaime/MSIME-Windows.git
cd MSIME-Windows
python scripts/product_lock.py fetch-dictionaries --staging-root .
if ($LASTEXITCODE -ne 0) { throw 'Dictionary verification failed' }
$devLocalData = Join-Path $PWD 'build/dev-local'
$devData = Join-Path $devLocalData 'metasequoiaime'
New-Item -ItemType Directory -Force $devData | Out-Null
Copy-Item MetasequoiaImeDict/out/* $devData -Force
Copy-Item engine/helpcode/helpcodes $devData -Recurse -Force
Copy-Item server/assets/tables/* $devData -Force
Copy-Item server/assets/config/config.toml $devData -Force
Push-Location ui-html/webview2/settings/ime-settings
try {
    pnpm install --frozen-lockfile
    if ($LASTEXITCODE -ne 0) { throw 'Settings dependencies failed' }
    pnpm build
    if ($LASTEXITCODE -ne 0) { throw 'Settings page build failed' }
} finally { Pop-Location }
$devWebview = Join-Path $devData 'html/webview2'
New-Item -ItemType Directory -Force $devWebview | Out-Null
foreach ($part in @('shared', 'candwnd', 'ftb', 'menu')) {
    Copy-Item "ui-html/webview2/$part" $devWebview -Recurse -Force
}
$devSettings = Join-Path $devWebview 'settings/ime-settings'
New-Item -ItemType Directory -Force $devSettings | Out-Null
Copy-Item ui-html/webview2/settings/ime-settings/dist $devSettings -Recurse -Force
python server/scripts/prepare_env.py
if ($LASTEXITCODE -ne 0) { throw 'Server environment setup failed' }
$env:LOCALAPPDATA = $devLocalData
$env:METASEQUOIA_IME_DATA_DIR = $devData
cd server

Use a dedicated PowerShell terminal. These steps stage the same locked data as CI and the current product pages in a separate development directory. Both LOCALAPPDATA (Windows config/pages) and METASEQUOIA_IME_DATA_DIR (Engine data) are set for that terminal; setting only the latter would still use installed Windows configuration and pages. Close the terminal after development. Dictionary source changes belong in Engine's dictionary/; use its root build_profile.py to build data instead of calling internal stages in the archived Dict repository.

Then, build and run,

.\scripts\lcompile.ps1
.\scripts\lrun.ps1

If you want to build and run in one step, run the following command,

.\scripts\llaunch.ps1

Watchdog

MetasequoiaImeWatchdog.exe is built next to the server. Starting the server directly also starts the watchdog, which monitors the server from the same directory and restarts it after an unexpected exit. Repeated early crashes use an exponential restart delay (up to 30 seconds) to avoid a restart storm.

The developer terminate shortcut stops both processes. The restart shortcut asks the watchdog to start a fresh server after the current process exits.

English prefix candidates

Place english.db next to msime.db in %LOCALAPPDATA%\metasequoiaime. Set general.cn_en_mixed_input = true in config.toml to enable asynchronous English prefix candidates for Quanpin and Shuangpin.

Developer Shortcuts

The server process includes a few built-in global shortcuts that are useful during development:

  • Ctrl + Shift + Alt + T
    • terminate MetasequoiaImeServer immediately
  • Ctrl + Shift + Alt + R
    • restart MetasequoiaImeServer
    • Settings exposes the same restart under 外观 → 重启输入法进程; it reaches the server over the Aux pipe because Settings runs in its own process
  • Ctrl + Shift + Alt + C
    • clear IME engine cache
  • Ctrl + Shift + Alt + 1 to Ctrl + Shift + Alt + 8
    • delete candidate 1 to 8 from the current candidate window
    • this only works while the candidate window is visible

Screenshots

Shared voice providers

The server links Engine VoiceCapture and Voice. WAV encoding and batch transcription/polish codecs come from the public library; Windows keeps provider configuration, SiliconFlow padding/retry behavior and Doubao streaming. Batch uploads are bounded to 20 MiB and responses to 1 MiB. Failed or empty polish responses retain the original transcript.

本地 Release 构建脚本可通过 -ManifestTool <mt.exe 路径> 使用其他 Windows SDK;未指定时沿用现有 SDK 路径。配置、编译或 manifest 嵌入失败都会中止,避免安装器收集旧产物。