Commit Graph
150 Commits
Author SHA1 Message Date
fanlusky 1348ca8eb7 fix(server): D2D 托盘菜单项圆角与外框同心对齐,放宽行高与内边距 2026-09-27 11:19:07 +08:00
fanlusky 573169105f fix(ui): D2D 预编辑光标不再贴住首项高亮
光标原先取整行高度并下移 8%,底部越过预编辑行、碰到候选列表首项的
高亮背景。改为与 CSS 相同的几何:高 1.2em,底边在基线下 0.12em。
2026-09-26 23:38:37 +08:00
fanlusky 7d66b2556c feat(server,ui,ui-html): 候选翻译文本支持在皮肤中单独配色
外部皮肤 skin.toml 的 [candidate.dark] / [candidate.light] 新增 translation 键。
不写时沿用候选文字色叠 62% 不透明度;写了就按原值绘制(含选中行)。
D2D(CandidateList::Appearance::translationColor)、WebView2 注入 CSS
和设置页外部皮肤预览三处一致;预览顺带补上此前漏掉的 number 配色。
2026-09-26 23:38:37 +08:00
fanlusky 0b4d9172f8 feat(ui): CandidateList 让贴着列表四角的项使用外框圆角
Appearance 新增 outerCornerRadius 与 outerTopCornersEnabled:选中或悬停的
填充里,真正落在列表四角上的角改用外框半径,其余角保持 cornerRadius。
既支持内缩高亮与外框同 R,也支持整行铺满、只在外框四角处贴合的皮肤。
2026-09-26 21:49:04 +08:00
fanlusky 3135af32ea fix(ui): 菜单项回调不再读已释放的闭包
在 D2D 候选框上右键删除一个候选,server 进程必定闪退重启,
0xc0000005 访问违例。

MenuFlyoutItem::OnMouseUp 直接调 onClick_()。候选框右键菜单的四个
动作都是「先 CloseContextMenu(true) 再 PostMessageW」的形状,而
CloseContextMenu 会同步 RemovePopup 并 reset 掉 contextMenu,也就是
在 operator() 还在栈上时销毁了这个 MenuFlyoutItem,连带销毁正在执行
的 std::function —— 小闭包按 MSVC 的 small-object optimization 就存
在 MenuFlyoutItem 那个堆块里。紧接着的 RestoreHostAfterMenu() 同步
走 SetWindowPos,派发 WM_SIZE/WM_PAINT 触发重新分配,把那块内存回收
掉;lambda 体回来再读 this 时读到的已经是别人的数据,mov rcx,[rcx+8]
(hwnd_)就炸在这里。

四个动作形状相同,都有这个缺陷;置顶碰巧不崩只是因为 MSVC 没有在那
一处重新从闭包存储里加载捕获,不是它安全。

改法是调用前把 ClickHandler 拷到栈上,让可调用对象活过自己这次调用。
Button::OnClick 是同样的形状,一并改掉。

回归测试不去捕捉内存破坏——能不能复现取决于分配器是否恰好回收了那一
块、以及编译器有没有重新加载捕获,而且读已释放内存本身就是未定义行为。
改为断言修复所建立的不变式:句柄在自己 operator() 执行期间始终存活。
2026-09-19 15:45:25 +08:00
fanlusky a54e2e08e8 fix(ui): 候选窗高亮行的辅助码与翻译跟随选中文字色
微信绿皮肤下高亮行的辅助码压在 #07C160 绿底上仍是暗色,与 webview 不一致。

候选行绘制里 rowTextSelected/rowLabelSelected 只覆盖候选文字和序号,辅助码
(annotation)和翻译一直无条件使用 appearance_.annotationColor。而皮肤 CSS 里
这两者是 .text 的子节点(.cand-content / .cand-translation),颜色继承自 .text,
选中行的 `.first .text { color: ... }` 会一并覆盖它们——D2D 端分开绘制,这层继承
关系就丢了。

改为在高亮行显式跟随 rowTextSelected,翻译再乘 CSS 的 opacity .62 保持原有观感。
判据是 rowTextSelected 的 alpha,所以各皮肤自动与各自 CSS 对齐:wechat 与
willow_green 白色,graphite #111827/#f1f3f5,fluent 没有 .first .text 覆盖,
沿用普通文字色不变。
2026-09-14 14:45:11 +08:00
Jia Sui 3dea733f64 fix(ui,server): 候选窗 D2D 阴影按皮肤对齐 webview——Card 支持 pass 列表
D2D 候选窗的 Card 阴影是内置 Win11 漫射三层(σ11/6/2.8,α0.42/0.30/0.48):
四边对称、无偏移,紧贴边缘合成 α≈0.49、暗晕铺到 15~20 DIP。webview 端候选窗
皮肤 CSS 用的是带右下偏移的双层软阴影(light: 8px 10px 24px rgba(0,0,0,.18)
+ 2px 3px 8px rgba(0,0,0,.10))。用户实机截图剖面实测:webview 底边峰值 α≈0.16、
D2D 0.41,差 2.5 倍,即「D2D 阴影偏重」的根因。对称三层无论怎么调参都拟合不了
带偏移的衰减曲线;浮动工具栏已有的 SetShadowScale 只缩模糊半径压不了深度
(高斯在边缘恒贡献 α/2)。

给 Card 拆出数据驱动的阴影 API,阴影参数跟皮肤走(与颜色/圆角同模式):

- ui/include/msimeui/Layout.h、ui/src/Layout.cpp:ShadowPass{σ,α,offset} +
  SetShadowPasses(空列表保持内置 Mist 三层,逐位不变)、SetShadowEnabled
  (整体开关,语义对齐 Popup 同名 API)、SetShadowOpacity(uniform 缩 α,与
  SetShadowScale 正交);渲染统一走 DrawGaussianShadowPass,无 device context
  时兜底 DrawLayeredMistShadow
- server/src/window/candidate_presenter.cpp:ApplySkin 按皮肤设阴影——内置四皮肤
  与自定义包用皮肤 CSS 标准对(σ=blur÷2)light {12,0.18,(8,10)}+{4,0.10,(2,3)}、
  dark α .34/.22;默认皮肤 webview 端 body HTML 无 box-shadow,D2D 同步关掉
- 浮动工具栏(scale=0.45)与托盘菜单(全默认)不触新 API,观感不变
- 透明垫余量重验:新 pass 可见范围(α>0.02 阈值)左16/上14/右32/下34 DIP,
  均在左32/上20/右32/下40 垫内,无裁切切痕
2026-09-14 12:34:10 +08:00
fanlusky ee5b50133f fix(ui): 竖排候选窗选中项高亮铺满整行
d2d 渲染下,竖排候选窗被卡片最小宽度撑大时,候选项的自然宽度小于卡片内宽,
选中高亮与命中区域只覆盖到文字宽度,右侧留下大片空白(候选项较长、宽度超过
卡片最小宽度时表现正常)。

根因:候选项行矩形取自按自然宽度测量得到的 itemGeometry。列表虽被拉伸到卡片
内宽,但绘制前 Present 阶段会以窗口宽度重新测量,把每项宽度还原为自然宽度,而
此时排布命中缓存(矩形未变)被跳过,铺满逻辑无法重新生效。

改为在 ItemRect 中直接以列表最终布局宽度 bounds_ 计算竖排行矩形,不再依赖逐项
自然宽度,绘制与命中同时受益,且不受测量/排布缓存影响。右侧留白由卡片内边距
提供,与左侧保持一致。横排路径不变。

新增 GetItemBounds 访问器与三个回归测试,其中一个复刻了 Present 重新测量的时序,
可捕获此前方案的退化。
2026-09-14 02:04:58 +08:00
Lu Fan(SonnyCalcr) e6136ddcd2 Merge pull request #337 from Komeiji-Shiki/codex/fix-317-candidate-wrap
fix(ui): 修复候选列表溢出与换行后的点击区域
2026-09-12 14:52:52 +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
古明四季 42bfd9fc49 fix(ui): 修复候选列表溢出与换行后的点击区域 2026-09-11 23:34:57 +08:00
Jia Sui 62b4688aa1 feat(ui): DeviceResources 与 Window 支持渲染 DPI 覆写 2026-09-11 22:21:57 +08:00
Jia Sui bd7cf1114d fix(server): D2D 内置皮肤补齐 light 配色,对齐 WebView2 CSS
Signed-off-by: Jia Sui <jsfaint@gmail.com>
2026-09-11 22:19:24 +08:00
Carry aa56486cc6 fix(ui): pass /utf-8 to msimeui-tests on MSVC to prevent C4819 on CP936 2026-09-11 21:03:03 +08:00
Neptrue_Lin 2b6bfafeec Merge pull request #327 from ouchihao/codex/fix-295-retained-dpi
fix(ui): refresh DPI when reusing render targets
2026-09-11 20:34:43 +08:00
Ouchihao f3fcd322e9 fix(ui): refresh DPI when reusing render targets 2026-09-11 19:35:07 +08:00
Ouchihao f9a021dc02 fix(ui): collapse text selections before moving the caret 2026-09-11 19:32:34 +08:00
Aegon8719 dea9292814 issue-55 候选窗支持可配置的有序回落字体 2026-09-09 18:03:56 +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
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
Ouchihao 3fc7ba295f feat(server): show translations in horizontal candidate windows 2026-09-06 18:09:01 +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 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
Evan 2562026a2a fix: 修复审计发现的候选页竞态、管道权限、TSF 空指针与皮肤 CSS 逃逸等八项缺陷 (#169)
* fix(server): publish candidate pages as immutable snapshots

Global::candidate_ui is a plain global with no lock. The IPC worker rebuilds and destroys its vectors while the UI thread paints from them, and the handoff is an asynchronous PostMessage, so the worker returns to its loop and pops the next task without waiting. A second keystroke, or an async english.db candidate arrival, could free the page_views buffer and its strings while FillItemsFromUi held a reference into it. ShowItemContextMenu reads the same vectors from a mouse callback with no message handoff at all.

The worker now copies each finished page into an immutable snapshot and publishes it at the single point where the rebuild completes; every UI-thread reader loads that snapshot once and reads only its own copy. PostMessage is deliberately left alone: the worker also serves UI-originated tasks, so a synchronous handoff would invite a cross-thread deadlock.

* fix(server): bound the aux and diagnostic pipe reads

Both listeners serve a single pipe instance inline and polled a NOWAIT handle for as long as the process ran, breaking only on a read that succeeded or failed with something other than ERROR_NO_DATA. A client that connected and never wrote held the only instance forever, so the loop never returned to ConnectNamedPipe and no later client was accepted, permanently losing the langbar right-click channel and the terminal-deactivation teardown fallback.

Give both loops the same deadline the hello reads already use and go back to accepting when it expires.

* fix(server): snapshot the voice config and replace config.toml atomically

GetConfiguredVoiceInput handed out a reference to a global that LoadImeConfig rewrites from the IPC worker thread, clearing and refilling two std::map members and around twenty strings. The voice control thread and the low-level keyboard hook, which copies the struct on every keystroke, walked those nodes as they were freed. LoadImeConfig now parses into a local and publishes it in one step under a process-local shared_mutex, and the getter returns a snapshot by value. The file lock is deliberately not reused for readers: it is a named cross-process mutex with a five second wait, and taking it in a keyboard hook would stall typing system-wide.

WriteConfiguredValue also reopened the live config.toml with ios::trunc, so a crash mid-write destroyed the user settings and could leave the file unparseable for good. It now goes through WriteFileTextAtomically, which this file already uses elsewhere. That makes the directory entry swap atomic; it does not flush, so it is not a durability guarantee.

* fix(ipc): restrict the server pipes to the owning account

The pipe DACL granted Everyone FILE_ALL_ACCESS. That bit set includes FILE_CREATE_PIPE_INSTANCE, so any local account could add an instance to a name this server already owned and answer part of the connections in its place. The names are machine-global with no per-user component, and the TSF client opened them with no SQOS, so a squatter could also impersonate the connecting thread.

Build the descriptor from the process owner SID and admit only LocalSystem, that account, and app containers with connect-only rights so UWP text input still works; fail pipe creation outright when the SID cannot be resolved rather than falling back to something permissive. Pass FILE_FLAG_FIRST_PIPE_INSTANCE on the first instance of each name so a squatted name fails loudly, and request SECURITY_IDENTIFICATION on the client side.

Authenticating the peer process after connect is a larger change to the handshake and is not attempted here.

* fix(tsf): guard the null focus paths and unadvise the compartment sink

_CandidateChangeNotification treated ITfThreadMgr::GetFocus and ITfDocumentMgr::GetTop as if they could only fail through a FAILED HRESULT. Both return S_OK with a null out-pointer when nothing has focus or the document stack is empty, and the results were dereferenced unconditionally, so an external UILess host calling Finalize while focus moved away crashed the host application. The sibling _RequestCancelComposition in the same class already checks both.

CLangBarItemButton::CleanUp only unregistered the compartment inside the SUCCEEDED branch of a CoCreateInstance, then deleted the sink regardless. This TIP registers COM-less activation support, where that call does fail, and the sink was freed while TSF still held it advised. Unadvising never needed the thread manager, so it now runs unconditionally before the deletes.

* fix(ui): drop the cached visuals when the scene is replaced

Window caches focused, hovered and captured Visual pointers into the scene tree, and SetScene destroyed that tree without clearing them. Toggling the input mode rebuilds the floating toolbar scene, so the next mouse move dispatched a virtual call through a freed object.

Clear all three before the assignment, and release the capture only when this window actually holds it: an adopted HWND may never have called SetCapture, and capturedVisual_ is set even when it did not.

* fix(settings): scope external skin CSS through the CSS parser

injectScopedSkinCss concatenated a skin toolbar.css into an @scope block as text. Block nesting is textual, so a leading close brace in the skin ended the block early and the rest styled the whole settings window. The host validates only the skin file name, never the body, and candidatePreviewCss interpolated unvalidated colour strings into rule bodies the same way.

Create the scope rule empty and re-insert every rule parsed out of the skin as a child of it, so nothing lands outside however the braces are balanced, and accept a colour only in the plain notations we actually render.

* fix(release): gate the dictionary source commit on its default branch

verify_published iterated only data["repositories"], but the dictionary block pins a source_commit exactly like they do. The manifest check merely proves the release agrees with the lock about that commit, which a branch commit satisfies just as happily, so a dictionary built from an unmerged branch shipped with the provenance the gate exists to refuse.

Run the dictionary through the same loop under its own name, resolving its repository from the lock. A retired dictionary repository that no longer answers is reported rather than failing the release: its published artifacts are immutable history, and being unable to re-check them is not the same as finding them unmerged.
2026-09-06 10:31:48 +09:00
Evan Hu 49aab62fe5 docs: name the current locations rather than the archived repositories
The notices listed MSIME-UI and MSIME-UiHtml as third-party components with their own repository URLs. They are directories of this repository, so the notice said the product bundles something from elsewhere when it bundles its own code; the GPL section now says where they are and what they used to be. The bundled data attribution follows the dictionary and helpcodes to MSIME-Engine, matching what product-lock.json already names, with every upstream term unchanged. The miniaudio entry described a vendored copy this branch deletes.

The server build steps still told a developer to clone the archived MSIME-Dict and build the databases by hand, through a directory layout that no longer exists. The product lock names a published release and records the SHA256 of every asset, which is the same data all three platforms ship, so the steps fetch that instead. The worked example lost a hardcoded path into one developer's machine.
2026-09-06 08:48:30 +09:00
Evan Hu 05036a763d docs: describe one repository instead of six
The product README was under windows/ after the move, but it was never a component readme: it is the download page, the feature guide and the contribution guide for the whole product, so it goes back to the root and windows/ gets a real component readme.

AGENTS.md at the root now carries the repository map, the component boundaries that used to be cross-repository rules, and the release process, since the scripts that process drives live at the root. The component files keep what is genuinely theirs and say which paths they are relative to. References that named sibling repositories now name sibling directories, and the lock documentation records that Server, the pages, the GUI framework and the installer are no longer inputs to pin.
2026-09-06 02:40:51 +09:00
Evan Hu 32e722c176 ci: build every component from the one checkout
The product lock existed because five repositories had to be pinned to reproduce one product. Four of them are directories now, so a commit of this repository already pins them and there is nothing left for the lock to say about them. What it still records is what the product consumes from outside: the engine submodule, the helpcodes and the dictionary release.

CI gains a job per component instead of a reusable workflow that checked out locked commits and rebuilt them. The release workflow no longer stages five sibling checkouts under historical names and no longer carries a second sparse checkout for its own scripts; Prepare-PackageFiles.ps1 already took the source directory names as parameters, so it is told the new ones, plus where the product-wide notice now lives.

Two checks the removed cross-repository pins used to provide are replaced in kind: the pages are verified against the engine submodule's web contract in CI rather than by locking UiHtml and Engine together, and the engine bump triage that Server carried moves here and now also says so when a bump leaves the lock behind.
2026-09-06 02:35:49 +09:00
Evan Hu 8a2ec3dfee chore(boundaries): guard the reusable GUI library against product dependencies 2026-09-06 00:33:31 +09:00
Evan d9e2d08dea Merge pull request #4 from metasequoiaime/dependabot/github_actions/actions-7a5a078ad4
chore(deps): bump actions/checkout from 6.1.0 to 7.0.1 in the actions group
2026-09-05 18:07:13 +09:00
Evan Hu 210c38faff test: fail an empty suite and report how many tests ran
CI 日志里 ctest 只显示「1 个测试通过」,因为它把整个套件当作一个测试项,而 --output-on-failure 只在失败时打印用例明细。这样一个一个用例都没注册的套件,看起来和全部通过完全一样。

runner 改为:注册数为零时直接判失败,并在结尾打印实际跑了多少个用例。ctest 改用 --verbose,让用例明细进入 CI 日志。
2026-09-05 17:23:34 +09:00
Evan Hu b8ac8e0b00 test: cover the layout core with unit tests
本仓库此前没有任何测试,CI 只能证明它编译得过。布局系统是这个框架里被所有控件依赖、也最容易在重构中被改坏的部分,而且是纯几何计算,不需要 D2D 设备即可测。

新增 13 个用例,覆盖 Visual 的测量与排列语义(margin 收缩可用尺寸并回填外框、显式尺寸覆盖测量值、min/max 夹取、可用空间封顶、测量缓存的命中与失效、Stretch/Center/Trailing 对齐定位),以及 StackPanel 与 HorizontalStackPanel 的堆叠、间距和 padding 行为。

断言值全部按 src/Layout.cpp 的现有实现推导,用于锁定当前行为,不是按我认为应该如何来写。测试框架沿用 MSIME-Server 的极简写法,但去掉了 fmt 依赖,本仓库不引入任何新依赖。

Grid、WrapPanel 和 ScrollViewer 本次未覆盖,留作后续。
2026-09-05 17:21:10 +09:00
dependabot[bot] 3baf1cf014 chore(deps): bump actions/checkout in the actions group
Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6.1.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/d23441a48e516b6c34aea4fa41551a30e30af803...3d3c42e5aac5ba805825da76410c181273ba90b1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-05 08:09:55 +00:00
Evan 7e5d584c03 chore: track dependency updates with dependabot 2026-09-05 16:52:31 +09:00
Evan 94b805105f chore(ci): follow the default branch rename to main 2026-09-05 15:31:11 +09:00
Evan Hu 798ff3c90c fix(build): compile the handwriting demo as C++20
新加的 CI 在 windows-2025 上暴露出 msimeui-handwriting-demo 编译失败:MSVC 14.51 的 STL 对 <experimental/coroutine> 直接 static_assert 报错(STL1011)。

根因是这个 demo 用 C++/WinRT(winrt/Windows.UI.Input.Inking.h)做墨迹识别,而项目全局是 C++17;C++/WinRT 在 C++17 下走 <experimental/coroutine> 回退路径,新版 MSVC STL 已经不再允许。把这一个 target 提到 C++20 即可,库和其余三个 demo 不受影响。
2026-09-05 14:13:40 +09:00
Evan Hu 80c62ab403 ci: build on Windows for every pull request
本仓库此前没有任何 CI,PR 是否能编译只能靠人工在本机验证。加一个最小门禁:windows-2025 上用 MSVC 配置并 Release 构建静态库和四个 demo。

本仓库没有 vcpkg 依赖,只链系统库,所以不需要装依赖步骤。没有测试目标,因此暂不跑 ctest。push 只在 master 上触发,避免特性分支与 PR 重复跑。
2026-09-05 14:11:57 +09:00
Evan Hu 2a2785ab7f chore: add GPL-3.0 license
本仓库此前没有 LICENSE 文件,GitHub 上显示为无许可,外部贡献者无法确认使用和分发条件。补上与水杉输入法其余仓库相同的 GPL-3.0 文本(与 MSIME-Windows、MSIME-Engine 的 LICENSE 逐字节一致),并在 README 末尾加一节说明。
2026-09-05 14:07:12 +09:00
fanlusky a0c4daa9ad Allow Card window shadows to scale for compact surfaces
Add Card::SetShadowScale so the Win11 mist shadow can shrink with the
control. The floating toolbar can then keep the same shadow style without
clipping or a oversized host.
2026-09-02 19:12:01 +08:00
fanlusky 4e66d19614 Optimize the shadow style of the candidate window. 2026-09-02 17:23:03 +08:00
fanlusky 2ac86ea0e2 Stop mapping all Unicode through CJK fonts and cut D2D working-set growth
Custom font fallback no longer covers the whole BMP/SIP, so Segoe UI Emoji
can paint color glyphs again; CJK still prefers Noto Sans SC then YaHei.
Drop GaussianBlur compatible targets during BeginDraw (they retained GPU
memory as the candidate window grew), grow the composition swapchain in
64px steps without shrinking, and reuse Card chrome plus cached text
formats instead of reallocating them every layout.
2026-09-02 16:25:54 +08:00
fanlusky 5421c1dd1b Add SVG icon rendering, toggle switch, and font fallback for MenuFlyoutItem
MenuFlyoutItem gains leading SVG icons (ID2D1SvgDocument), an optional
trailing toggle switch, and content-based width measurement so tray-style
menu items match the HTML menu layout. Default UI font is now Noto Sans SC
with Microsoft YaHei fallback via IDWriteFontFallback; CreateTextFormat
gracefully falls back when the primary family is missing.
2026-09-02 16:03:57 +08:00
fanlusky d16f9f3fea Add DirectComposition hosting and candidate-list chrome for the IME window
Adopt an existing HWND, render through D3D11/DXGI/DirectComposition, and
extend CandidateList with horizontal layout, skin colors, click-to-commit,
and a D2D context menu. Preedit TextBlock draws a caret in a fixed insert
slot so neighboring glyphs reflow instead of using letter-spacing; the
default end caret sits slightly after the last character.
2026-09-02 15:29:14 +08:00
fanlusky 3d7b8a3c24 Add support for svg render. 2026-08-17 04:42:53 +08:00
fanlusky d65928e3de Resolve clangd errors. 2026-08-15 03:11:26 +08:00
fanlusky 64c08a7c02 Adjust some controls for emoji panel. 2026-08-15 02:32:00 +08:00
fanlusky f3c7a9557a Set the initial position setting method for the window. 2026-07-20 23:22:05 +08:00
fanlusky f0759db93e Optimize the rendering smoothness of strokes in the handwriting input area. 2026-07-20 01:37:15 +08:00
fanlusky 4e8e85afd8 Make handwriting recognize demo 2026-07-20 01:32:31 +08:00
fanlusky 548330b8cf Make a demo of an on-screen keyboard. 2026-07-15 18:22:50 +08:00
fanlusky 8bc914b258 Make the scrollbar of the emoji panel demo scrollable. 2026-07-15 18:09:32 +08:00