* feat(server,ui-html): AI 联想候选徽标支持切换 🤖 / 🐳 AI 联想候选的来源角标此前硬编码为 🤖,用户无法更改。按固定排位徽标 (#463)的既有做法把它做成可配置项 appearance.candidate_ai_badge_style, 取值 "robot"(🤖,出厂默认)或 "whale"(🐳)。 - 默认值保持 🤖 不变,升级不改变默认观感 - 与固定排位徽标一致,配置里存枚举名而不是字面 emoji,TOML/日志/diff 里不出现图形字符;新增样式只改白名单与展示层映射 - 非法取值一律回退到出厂 🤖,手改 TOML 写错不会让 AI 候选失去来源标识 - 展示层新增纯函数 AiSuggestionBadge(style),沿用既有 badge 字段通道, D2D 与 WebView2 两个后端同时生效,不新增渲染路径 - 配置改动经既有 ConfigChanged → WM_APPLY_IME_CONFIG 链路即时生效, 无需重启输入法进程 - 出厂模板 config.default.toml 与开发配置 config.toml 同步补齐, 避免升级时用户选择被 MergeConfigIntoTemplate 静默丢弃 验证: - server/tests/portable(Linux,g++,无 vcpkg):57 test(s), 0 failure(s) - ui-html settings:vitest 167 passed,tsc --noEmit 通过,vite build 通过 - clang-format 18.1.8 --dry-run --Werror --style=file:改动文件全部通过 - scripts/ci/check-developer-paths.sh 通过;sync-contracts.py --check 通过 * fix(server): 补齐 IsValidCandidateAiBadgeStyle 的跨编译单元声明 ime_config_appearance.cpp 是独立编译单元,无法看到定义在 ime_config.cpp 里的 IsValidCandidateAiBadgeStyle,MSVC 报 C3861。补上 ime_config_internal.h 里的声明——IsValidCandidateFixedBadgeStyle 等同类校验函数都声明在那里。 * refactor(server,ui-html): AI 徽标样式改为单一映射表并精简测试 - AiSuggestionBadgeGlyph 作为样式到字形的唯一映射,配置层白名单 IsValidCandidateAiBadgeStyle 直接查它,新增样式不会两边漂移 - 测试去掉与首条断言重复的一行和恒真的 cloud 断言,改为检查映射表 对已知/未知样式的判定 - 设置页描述写明两种样式对应的 emoji --------- Co-authored-by: Caiyi Liao <216114941+cxk1993@users.noreply.github.com> Co-authored-by: fanlusky <1730976608@qq.com>
Tests
Run the candidate DOM test from the repository root:
node --test server/tests/test-candidate-dom-render.cjs
The test runs the JavaScript renderer from windows_webview2_candidate.cpp.
It checks text, order, badges, translations, and fixed candidate color.
This project now includes a lightweight regression test target wired into the main CMake build.
What It Covers
Current tests focus on high-value IME behaviors that are easy to accidentally break during refactors:
shuangpinscheme request constructionquanpinscheme request construction- case-preserving shuangpin segmentation
- full-helpcode detection
engine + shuangpincontinuation behavior- cloud suggestion trigger timing for shuangpin
The goal is not to fully simulate the whole IME UI pipeline yet. The goal is to protect the core input rules and session behavior.
Build and run
Run these commands from the MSIME-Windows repository root after preparing
vcpkg and Boost with python server/scripts/prepare_env.py:
python scripts/product_lock.py fetch-dictionaries --staging-root .
if ($LASTEXITCODE -ne 0) { throw 'Dictionary verification failed' }
$env:LOCALAPPDATA = Join-Path $PWD 'build/test-data/user-local'
$env:METASEQUOIA_IME_DATA_DIR = Join-Path $env:LOCALAPPDATA 'metasequoiaime'
New-Item -ItemType Directory -Force $env:METASEQUOIA_IME_DATA_DIR | Out-Null
Copy-Item MetasequoiaImeDict/out/* $env:METASEQUOIA_IME_DATA_DIR -Force
Copy-Item engine/helpcode/helpcodes $env:METASEQUOIA_IME_DATA_DIR -Recurse -Force
Copy-Item server/assets/tables/* $env:METASEQUOIA_IME_DATA_DIR -Force
Copy-Item server/assets/config/config.toml $env:METASEQUOIA_IME_DATA_DIR -Force
.\server\tests\scripts\llaunch.ps1 -Configuration Release
Use a dedicated PowerShell terminal: these environment variables isolate both Windows helpers and the shared Engine from the installed user's data for the rest of that session. The scripts run CTest; they do not launch the interactive Server, install or register the input method. CI additionally runs the native Win32/x64 pipe probe after building the full Server.
Tests belong to the Server CMake project, which provides the Engine, GUI and
third-party targets. server/tests/CMakeLists.txt is an add_subdirectory input,
not an independent project. The obsolete test presets have been removed; an old
locally generated server/tests/CMakePresets.json is no longer used.
Once test data is prepared, the convenience scripts work from any directory:
.\server\tests\scripts\llaunch.ps1 -Configuration Release # build, then CTest
.\server\tests\scripts\lrun.ps1 -Configuration Release # CTest only
Omitting -Configuration selects Debug in server/build; Release uses
server/build-release. Both compile MetasequoiaImeServerTests and
test_webview_contract in the parent build tree. Configuration, compilation,
test failures and an empty CTest registry return failure rather than launching a
stale executable or reporting success.
The equivalent direct CTest command from the repository root is:
ctest --test-dir server/build-release -C Release --output-on-failure --no-tests=error
Test Layout
tests/src/main.cpp- tiny custom test runner
tests/includes/test_framework.h- lightweight assertion helpers
tests/src/test_shuangpin_scheme.cpp- shuangpin scheme unit tests
tests/src/test_quanpin_scheme.cpp- quanpin scheme unit tests
tests/src/test_shuangpin_query.cpp- query/helper function tests
tests/src/test_engine_shuangpin_session.cpp- session-level behavior tests
Model path benchmark
Build the Server test target in Release mode. Run the benchmark from a Windows terminal:
cmake --build server/build-release --config Release --target MetasequoiaImeServerTests
server/build-release/bin/Release/MetasequoiaImeServerTests.exe --benchmark-model-path 10000 > model-path.csv
The executable creates small model files in a temporary data directory.
It restores METASEQUOIA_IME_DATA_DIR when it exits.
It does not load model contents.
Each row reports P50, P95, P99, and worst latency in nanoseconds.
hot repeats one path.
rotating cycles through 128 paths.
The rotating set increases metadata lookup pressure.
It does not guarantee an operating system cache miss.
first-touch checks each missing path once without benchmark warmup.
The operating system can still cache shared parent directories.
contended adds a thread that creates and removes files in the model directory.
This tests directory activity on the same volume.
It does not simulate all disk workloads.
The benchmark calls the production path resolvers used by ApplyConfiguration().
It measures their cost per call.
It does not measure full handle_key() latency.
The benchmark sets METASEQUOIA_IME_DATA_DIR to an isolated temporary directory.
Each resolver call reads this override and builds a path.
Normal installations do not set this override. Their directory lookup takes a different path.
The disabled-empty-id row returns before data_directory() runs.
The data-directory-only row measures data_directory() with the temporary override.
Median differences give only a rough scale for the remaining resolver work.
Do not subtract percentiles from separate rows to assign costs to path joining or file checks.
Use the rows to inspect latency distributions and tail latency across path access patterns.
They do not establish the same cost or performance loss in production.
Run each case more than once on the target Windows machine.
Record the storage type and active disk workload with the CSV results.
Recommended Practice
When you change input behavior, add a regression test if the change affects:
- input segmentation
- space/enter/backspace behavior
- helpcode handling
- continuation after candidate selection
- create-word flow
- cloud suggestion timing
The best tests in this project are the ones that capture a concrete input example that previously broke.