Commit Graph
1604 Commits
Author SHA1 Message Date
Evan e2a4f3dcfb fix(tsf): remove unused client router shim (#303) 2026-09-10 07:49:31 +09:00
Evan db166187d9 feat(tsf): convert edit session keys for client router (#301)
* feat(tsf): add client key router boundary

* feat(tsf): convert edit session keys for client router
2026-09-10 07:41:53 +09:00
Evan e45b9fb7b9 feat(tsf): add client key router boundary (#300) 2026-09-10 07:40:28 +09:00
Neptrue_Lin 86abcf8987 Merge pull request #289 from Aegon8719/feat/issue-52-shortcuts
issue-288 修复候选窗在光标坐标无效时提前显示的问题
2026-09-09 19:29:27 +08:00
Aegon 8a78b02271 Merge branch 'develop' into feat/issue-52-shortcuts 2026-09-09 18:18:21 +08:00
Neptrue_Lin cffbefa6d3 Merge pull request #292 from metasequoiaime/issue-template-revision
Issue template revision
2026-09-09 15:04:44 +08:00
Neptrue_Lin 2d304168b5 Revise feature request template
Updated feature request template for clarity.
2026-09-09 14:53:19 +08:00
Neptrue_Lin 11c6b88e51 Revise bug report template
Updated the bug report template for clarity.
2026-09-09 14:50:23 +08:00
Aegon8719 848cf6a288 issue-288 修复候选窗在光标坐标无效时提前显示的问题 2026-09-09 11:40:26 +08:00
Lu Fan(SonnyCalcr) f7cd4e6174 Merge pull request #284 from fanlusky/fix/safe-directory-config-race
fix(ci): stop the self-hosted jobs racing on the global git config
2026-09-09 03:03:36 +08:00
fanlusky d7e7b3f0d9 fix(ci): stop the self-hosted jobs racing on the global git config
The three self-hosted release jobs are separate runner instances on one
machine, so they share C:\Users\runner\.gitconfig and all reach "Trust the
runner workspace" in the same second. Two win the config lock and the third
dies with "could not lock config file ...: File exists", taking Build TSF x64
down before it compiles anything and skipping the installer job.

Retry the write, and re-read safe.directory before each attempt so a peer
instance having already trusted this path counts as success.
2026-09-09 03:00:23 +08:00
Lu Fan(SonnyCalcr) 7e9f0cd892 Merge pull request #281 from fanlusky/fix/self-hosted-runner-git-and-vcpkg
fix(ci): unblock packaging on the self-hosted runners
2026-09-09 02:31:32 +08:00
fanlusky 28689361ad fix(ci): unblock packaging on the self-hosted runners
Two independent failures kept the v0.6.5 installer from being built after
the pipe probe was fixed.

The package job died in "Verify the release commit is on main" with git
exiting 128 on dubious ownership: runner instance 2 leaves _work owned by
SYSTEM while the job runs as `runner`. actions/checkout is unaffected, so
the first plain git call is what fails, and only jobs scheduled onto that
instance see it. Mark the workspace as safe in the three self-hosted jobs.

Neither vcpkg manifest carried a builtin-baseline, so vcpkg resolved ports
against the registry at HEAD and every build depended on a live fetch of
microsoft/vcpkg. When that fetch degraded, the ports simply vanished:

    Fetching registry information from https://github.com/microsoft/vcpkg (HEAD)...
    fmt does not exist
    utfcpp does not exist

Build TSF x64 failed this way twice and passed twice on the same runner,
which is what an unpinned registry looks like. Pin both manifests to
04a9d8e5, the commit vcpkg-lock.json already records as resolved.
2026-09-09 02:18:57 +08:00
Lu Fan(SonnyCalcr) 1c4643b0e4 Merge pull request #278 from fanlusky/fix/pipe-probe-stale-server
fix(ci): stop the pipe probe from handshaking with a leftover Server
2026-09-09 01:41:15 +08:00
fanlusky f91dae7644 fix(ci): stop the pipe probe from handshaking with a leftover Server
release.yml runs the Server job on a persistent self-hosted runner while
ci.yml runs it on a fresh windows-2025 image, so only the release path
kept a MetasequoiaImeServer.exe alive between jobs. That leftover holds
Local\MetasequoiaImeServer_SingleInstance, the Server started by
probe-server.ps1 returns 0 straight out of the guard, and the probe then
completes its handshakes against the older build. Probes 1-4 pass because
they do not ask for CharacterSetShortcut; probe 5 does, the pre-6ff1cff
Server never advertises it, and the run dies on a capability mismatch that
nothing in the tree can explain. v0.6.3 and v0.6.4 both failed this way.

Stop any Server and Watchdog before starting one, and fail loudly if the
Server this script starts exits on its own.

Also stop test-server.ps1 from reporting every non-zero verify-contracts
exit as a lock mismatch. A dubious-ownership git failure on the same
runner surfaced as "The engine submodule does not match the product lock"
while the lock was correct.

Also ignore installer/LICENSE.txt, which Prepare-PackageFiles.ps1
generates from the repository LICENSE on every local packaging run.
2026-09-09 01:29:29 +08:00
Lu Fan(SonnyCalcr) a763580a0c Merge pull request #275 from fanlusky/fix/candidate-stall-under-load
fix: stop candidate-window flicker and dropped commits when the machine is loaded
2026-09-09 00:22:50 +08:00
fanlusky 0bf0928fef perf(deps): pick up the cached user-dictionary journal connection
Engine da61ca4 keeps one sqlite connection open for the default journal
instead of opening one and rebuilding the schema per call.
apply_fixed_positions runs on every keystroke, and that open was 90% of the
server-side candidate build under disk load, stalling the task thread long
enough for composition commits to time out and be dropped.

This also advances the engine from the commit develop currently pins to the
merged tip of engine main. Content-wise that is a small step -- 29 files,
mostly additive: fuzzy pinyin options, dictionary state, and their tests.

product-lock.json follows the submodule, so the artifact manifest records the
engine the build actually used and the release gate still sees a merged
engine commit.
2026-09-09 00:02:57 +08:00
fanlusky 0e003fa095 perf(server): time each segment of the per-keystroke candidate build
PrepareCandidateList runs on the shared task thread, so a stall inside it
delays the hide/show messages queued behind it and reaches the screen as
candidate-window flicker. The diagnostic log showed an identical workload
take 8 ms once and 688 ms moments later, but not which segment paid for it.

Split the function into measured segments and emit one "candidate build"
line per keystroke, separating the engine candidate lookup from the
user-dictionary fixed-position pass -- the latter opens a SQLite
connection and runs its schema DDL on every call.

Measurement only; no behaviour change.
2026-09-08 22:53:59 +08:00
fanlusky bb53763e66 fix(server): only grace-delay a candidate hide the worker delivered late
The unconditional 50ms hide grace cost typing its snap: every commit left the
candidate window up for an extra frame or three, and when the next word started
inside the window the pending hide was cancelled outright, so the box stopped
disappearing between words and just morphed instead.

Only a hide that waited behind a stalled task thread can describe state the user
has already typed past, and only that one has a show queued behind it worth
waiting for. Measure the queue wait where the task is dispatched and mark the
posted message; normal typing never reaches the threshold, so it hides
immediately exactly as before.

The wait is now logged per hide task, so the threshold can be checked against a
real capture rather than assumed.
2026-09-08 22:53:59 +08:00
fanlusky cc81f794bb fix(server): stop candidate strobing when the worker queue backs up
Diagnostic logs from a busy machine show the candidate host repainting far
more often than the input changes. Three separate causes, all in the UI
thread's handling of the worker's posted messages:

- A HideCandidate for one keystroke of a burst (backspacing the composition
  empty, then typing again) cloaks a visible window and un-cloaks it ~30ms
  later. Defer the hide on a short grace timer and drop it when a show
  arrives first; a real commit or focus loss still hides, 50ms later.
- Each keystroke posts two WM_SHOW_MAIN_WINDOW messages, since the candidate
  rebuild and the English/cloud merge both request a show. Skip a repaint
  whose preedit, page, selection and caret match the frame already on screen
  within a short window.
- When the worker stalls it drains several keystrokes at once and posts a
  show for each. The handler always renders the newest published state, so an
  older queued show can only repaint what the newer one is about to; drop it
  when a newer show is already queued.

The stall itself (PrepareCandidateList blocking the shared task thread, worst
case 688ms against a median of 11ms) is untouched here.
2026-09-08 22:53:59 +08:00
fanlusky 58fa715921 fix(tsf): stop the candidate window flickering when the host lags
Two host-timing paths could hide or displace the candidate window between
ordinary keystrokes. Both get much more likely while the machine is busy,
which is when the flicker is reported.

TS_E_NOLAYOUT from ITfContextView::GetTextExt means "not laid out yet, ask
again", but _GetTextExt reported it as a valid measurement at INVALID_Y.
Server then parks the host off-screen for that frame and the next successful
measurement pulls it back. Chromium-based hosts answer TS_E_NOLAYOUT
whenever their renderer is behind, so under load this repeats per keystroke.
Keep the anchor the session already has instead, once it has one; composition
start still falls back to the sentinel rather than reusing a stale anchor
from another document.

TF_LC_DESTROY was treated as candidate-session teardown for every process
except a hardcoded Telegram.exe. Hosts that transiently destroy and recreate
their context view mid-composition therefore got a HideCandidate between
keystrokes, which clears the live composition on the Server side: the window
blinks and the pending candidate can no longer be committed. Gate on a live
composition instead of a process name. Commit, cancel, focus loss, and
presenter cleanup all end the composition first, so they still tear the
session down through their own paths.
2026-09-08 22:53:59 +08:00
linyanmandlinym 6e03f57747 fix(server): 全拼导入按汉字数选择切分并识别文件编码 (#270)
无分隔拼音按汉字数另选完整切分,避免西安/xian 误报音节数不一致。设置页按 BOM 识别 UTF-8/UTF-16,无效 UTF-8 再回退 GBK。带撇号的水杉导出三列 TSV 本身已合法,回归测试钉住该格式。

Co-authored-by: linym <lin.yanming@foxmail.com>
2026-09-08 22:52:45 +09:00
Lu Fan(SonnyCalcr) df532db042 Merge pull request #266 from fanlusky/fix/icon-font-followups
Fix/icon font followups
2026-09-08 17:40:23 +08:00
fanlusky 4b3da57134 fix(emoji-panel): use Chinese nav titles to match the tab labels
The sub-page nav titles were English ("Sticker", "GIF", "Kaomoji",
"Symbols") while the Clipboard page was already Chinese, and the text
labels the tabs fall back to when no icon font carries the glyph are all
Chinese. On a Windows 10 machine that actually hits that fallback, the user
taps a tab labelled 贴纸 / 颜文字 / 符号 and lands on a page headed
"Sticker" / "Kaomoji" / "Symbols".

Translate the titles to match the tab labels and drop the per-page font and
size branch, which only existed because Clipboard was the one Chinese title.
Rename kClipboardTitleFontSize to kNavTitleFontSize now that every page uses
it, and move it next to the other nav metrics. "GIF" stays as is: it is an
acronym and the tab label already reads GIF.
2026-09-08 16:33:18 +08:00
fanlusky 43576b75ca fix(ui): make the icon font resolver recover from DirectWrite failures
ResolveIconGlyph() guarded its cache with a mutex, but the work under that
lock reached SharedFactory(), whose lazy DWriteCreateFactory() call was
unsynchronized -- and UiFontFamily()/ApplyUiFontFallback() call the same
helper without that mutex from render paths. Two window threads could both
see a null factory, both create one into the same ComPtr, leak one and race
on the raw pointer. Give SharedFactory() a lock of its own.

IconFonts() memoized its result in a function-local static initialized
exactly once. If SharedFactory() returned null or GetSystemFontCollection()
failed on that first call, the empty list was cached for the process
lifetime and every icon degraded to its text label permanently, with no
retry -- while SharedFactory() itself retried on each call, making the
failure mode asymmetric and hard to diagnose. Keep the list only once the
probe finds a font, and skip the ResolveIconGlyph cache while it is empty
so the retry is actually reachable.
2026-09-08 16:11:16 +08:00
Lu Fan(SonnyCalcr) 880c04f6bb Merge pull request #264 from fanlusky/fix/icon-font-fallback-win10
fix(server): fall back to text when the icon font lacks the glyph
2026-09-08 16:01:03 +08:00
Lu Fan(SonnyCalcr) 305ee60ffd Merge pull request #260 from Aegon8719/feat/issue-52-shortcuts
issue-52 加入简繁切换快捷键;增加候选词翻页快捷键组合、增加以词定字快捷键组合;issue-55 候选词与辅助码间保留一个半角空格。
2026-09-08 15:55:22 +08:00
Evan 99ab7804ef docs(.github): 为新用户补充反馈模板示例与填写提示 (#263) 2026-09-08 07:42:07 +00:00
Aegon8719 6ff1cff42d issue-52 加入简繁切换快捷键;增加候选词翻页快捷键组合、增加以词定字快捷键组合;
issue-55 候选词与辅助码间保留一个半角空格。
2026-09-08 14:07:48 +08:00
fanlusky 43555460db fix(server): fall back to text when the icon font lacks the glyph
The floating toolbar and the emoji panel hardcoded "Segoe Fluent Icons",
which ships with Windows 11 only. DirectWrite substitutes a font silently
instead of failing, so on Windows 10 every private-use codepoint the toolbar
draws (U+E982, U+F110, U+F138, ...) rendered as a blank box: the buttons
still worked but no longer showed any state.

Resolve the font per codepoint rather than per family, because "Segoe MDL2
Assets" is a subset of Segoe Fluent Icons and swapping the family alone would
still draw tofu wherever a glyph is missing:
- add msimeui::ResolveIconGlyph(), which probes the installed icon fonts with
  IDWriteFont::HasCharacter() and returns the first family that really carries
  the glyph, or no family at all;
- give every toolbar and tab icon a text label to fall back on (中/英, 半/全,
  简/繁, ...) when neither font has the glyph;
- drop the unused ToolbarIconButton::SetGlyph/SetLabel, which would bypass the
  resolved family.

Add unit tests for the resolver, covering the missing-glyph path that cannot
be reached on a host that has Segoe Fluent Icons installed.
2026-09-08 12:41:01 +08:00
Lu Fan(SonnyCalcr) 0ef8b70e9b Merge pull request #253 from fanlusky/fix/config-non-ascii-username-path
fix(config): handle non-ASCII (e.g. Chinese) user profile paths
2026-09-08 02:34:56 +08:00
fanlusky de4d3acf45 fix(config): handle non-ASCII (e.g. Chinese) user profile paths
std::filesystem::path(std::string) and path::string() convert through the
system ANSI code page, so a non-ASCII (e.g. Chinese) user profile path on a
non-UTF-8 (GBK 936) system was corrupted and every config read/write failed
with "设置保存失败"; on a code page that cannot represent the characters
path::string() threw, crashing the Server at startup in a watchdog restart loop.

Never route config paths through the ANSI narrow encoding:
- build g_config_path and the legacy voice path from the wide accessors;
- read config by opening the wide path + toml::parse(text) instead of
  toml::parse_file(path.string()) (LoadImeConfig, MigrateLegacyVoiceInputConfig);
- build and open the TSF shared config path as a wide std::filesystem::path.

Add a regression test that round-trips config read/write under a Chinese
profile path.
2026-09-08 02:20:37 +08:00
Evan 2fb4560d99 fix(release): use beta tags for automatic builds (#252)
* fix(release): separate ci snapshots from stable tags

* fix(release): use beta tags for automatic builds
2026-09-07 17:56:34 +09:00
Evan 0025e79b45 chore(ci): run CodeQL only on nightly schedule
Run CodeQL daily at 00:00 Asia/Tokyo and remove all other workflow triggers.
2026-09-07 17:55:09 +09:00
Evan 560b730ea1 Merge pull request #250 from metasequoiaime/fix/release-vcpkg-lock
fix(scripts): keep automatic releases as prereleases
2026-09-07 17:30:28 +09:00
Evan Hu b777008abb fix(scripts): keep automatic releases as prereleases 2026-09-07 17:25:55 +09:00
Evanandgithub-actions[bot] d1ac5273e4 fix(ci): guard the release vcpkg selection (#247)
* chore(main): release 0.6.1

* release (#245)

* fix(installer): test server before embedding uiAccess manifest (#243)

* fix(ci): align release matrix with selected vcpkg (#244)

* fix(ci): guard the release vcpkg selection

* fix(ci): report the release runner in vcpkg errors

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-07 16:38:19 +09:00
Evan fbd6b390f1 fix(ci): align release matrix with selected vcpkg (#244) 2026-09-07 16:03:23 +09:00
Evan f62e8e5fc8 fix(installer): test server before embedding uiAccess manifest (#243) 2026-09-07 15:37:08 +09:00
Evan ba62a6a720 Merge pull request #239 from metasequoiaime/fix/release-followup
fix(server): make release probes non-interactive
2026-09-07 13:46:58 +09:00
Evan Hu 84ef90cc8d fix(server): test valid escaped CSS URLs 2026-09-07 13:37:22 +09:00
Evan Hu 8972593b3e style(server): format skin CSS policy 2026-09-07 13:32:09 +09:00
Evan Hu 818069f9c3 fix(server): make release probes non-interactive 2026-09-07 13:28:25 +09:00
Evan e2d68b9d4c Merge pull request #238 from metasequoiaime/main
Main
2026-09-07 13:22:34 +09:00
github-actions[bot] cd22ff21f2 Merge pull request #237 from metasequoiaime/release-please--branches--main--components--MetasequoiaImeWindows
chore(main): release 0.6.0
2026-09-07 04:16:18 +00:00
Evan 51e9124128 chore(main): release 0.6.0 2026-09-07 13:04:37 +09:00
Evan 99f790366f Merge pull request #236 from metasequoiaime/develop
release
2026-09-07 13:04:18 +09:00
Neptrue_Lin 4b95c90fde Merge pull request #228 from AaGMix/feat/installer-pdb
feat(installer): include release PDB symbols
2026-09-07 09:46:06 +08:00
Evan ad48153c8e Merge pull request #230 from metasequoiaime/fix/release-vcpkg-lock
fix(server): embed uiAccess manifest before signing
2026-09-07 07:53:08 +09:00
Evan Hu 84de68fd1b fix(server): embed uiAccess manifest before signing 2026-09-07 07:42:14 +09:00