Commit Graph
246 Commits
Author SHA1 Message Date
Ouchihao 3fc7ba295f feat(server): show translations in horizontal candidate windows 2026-09-06 18:09:01 +08:00
Evan Hu 1ec7bdd690 fix(settings): 检查更新时消费 update.json 的校验值与签名状态
update.json 一直发布四个关于安装包的字段(installerName / installerUrl / installerSha256 / signed),而设置页的 UpdateManifest 类型只声明了 version 和 releaseUrl,另外两个字段发布出来却没有任何人消费。结果是:在安装包未签名的当下,SHA256 是用户唯一的完整性防线,服务端早已算好,却要用户自己去官网翻。

现在发现新版本的对话框会显示:signed 为 false 时给出明确警告(SmartScreen 会拦截、uiAccess 失效导致候选窗无法浮在提权程序之上),以及 Get-FileHash 命令和应得到的摘要,可选中复制。

「不做后台自动检查」是 PRIVACY.md 明示的隐私设计,没有改动——改的只是用户按下检查更新之后看到什么。

校验与展示逻辑抽到 update-manifest.ts:validateManifest 收紧了 releaseUrl 的判断(此前 startsWith 前缀匹配会放过 …/releases.attacker.example 这类同前缀域名),并对三个可选字段做格式校验——安装包名会进入一段让用户粘贴的命令,限制成发布流水线实际产出的形状;格式不对的可选字段按缺失处理,不让整次更新检查失败,老版本 manifest 仍然可用。

接入 vitest,11 个用例覆盖版本解析与排序(0.10.0 > 0.9.0、0.3 == 0.3.0)、manifest 校验的拒绝路径、以及签名/未签名/未声明三种状态下的展示。CI 在 build 之前加一步 pnpm test。
2026-09-06 18:08:34 +09:00
Evan Hu 5d5b661e14 fix(ui): 给候选窗、悬浮工具栏与托盘菜单加 Content-Security-Policy
这些页面会内联第三方皮肤的 CSS,而全仓此前没有任何 CSP,也没有 WebResourceRequested 过滤。上一个提交在注入阶段丢掉了远程引用,这一条是第二道防线:即使将来某条路径漏过,浏览器层面也不会真的把请求发出去。

策略按这些页面实际用到的资源来定,没有多给:default-src 'none' 关闭其余一切(包括 connect-src,这些页面不含任何 fetch/XHR/WebSocket);script-src 放行 https://msime-contracts(schema.js 与 runtime.js)与内联脚本;style-src 放行内联样式;img-src 与 font-src 放行 data: 以及 https://appassets、https://candidate-skins 两个虚拟主机——皮肤被内联时正是变成这两种形式。

覆盖 candwnd、ftb、menu、default-themes 下的 15 个完整文档。body/ 下的 8 个文件是注入宿主文档的片段,没有 <head>,不适用。
2026-09-06 18:05:09 +09:00
Evan f70f64acaa fix(ci,ui): 修掉 6 条 CodeQL high 告警,补上发布目标的祖先校验
- validate-draft-release.sh 增加「target_sha 必须在 main 历史上」的校验。此前有 write 权限的人可以
  建一个指向任意 commit 的 draft release 再 dispatch,那份代码就会被编译并签进正式安装包;唯一的
  祖先检查跑在 checkout 之后的不可信工作树里,而负责编译的 TSF job 根本没有这道检查。校验放在
  prepare(默认分支上下文)里,所有构建 job 都 needs 它。
- 字体名引用只转义了 `"` 没转义反斜线,以 `\` 结尾的字体名能吃掉闭合引号让后续内容按 CSS 解析。
  两处(appearance.ts、shared.ts)都改为先转义转义符。
- voice_input_service.cpp: float 平方先提升到 double
- engine-bump-triage.yml 顶层权限从 write 收到 read,写权限下放到 job;runner 与同仓其他 workflow
  统一为 ubuntu-24.04

新增 CONTRIBUTING.md,把散在各 AGENTS.md / 组件 README 里的环境要求、构建顺序、打包步骤和提交前
检查串成外部贡献者能照着走完的一条路径,并从 README 首屏链出。
2026-09-06 12:09:50 +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 06bba15d4f fix(server): keep settings I/O off UI thread and bound dictionary rendering 2026-09-06 03:31:40 +09:00
Evan Hu b5d07f11eb fix(server): reduce WebView message and first-paint overhead 2026-09-06 03:05:36 +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 373f8d1473 refactor: collapse the duplicated submodules into one vendor/ directory
Windows, Server and UiHtml each carried their own gitlink to MSIME-Engine, all three pinned to the same commit, and Server carried a gitlink to MSIME-UI that is now the ui/ component. Git only reads the root .gitmodules, so the nested ones had to go regardless.

The engine and the two third-party dependencies now live once at vendor/MetasequoiaImeEngine, vendor/opencc and vendor/cpp-pinyin. Server reaches them through MSIME_VENDOR_DIR and builds ui/ as an out-of-tree subdirectory; the Windows tip's relative contract includes gained the extra level; the ui-html contract sync and the Server env/table generators follow the engine to its new location.
2026-09-06 02:27:52 +09:00
Evan Hu f31084162a chore(deps): pin verified partial-composition flush semantics 2026-09-06 01:34:33 +09:00
Evan Hu c0866292fd chore(deps): pin the shared portable composition contract 2026-09-06 01:30:07 +09:00
Evan Hu 7a91c1d20d fix(contracts): allow the tenth mouse candidate and pin shared format contracts 2026-09-06 00:47:05 +09:00
Evan Hu ea9e8797a0 chore: remove the orphaned Sciter resources
sciter/ held two HTML files and a .gitkeep for a rendering backend that no longer exists. Nothing selects them: appearance.ui_backend in MSIME-Server accepts only "d2d" and "webview2", no source file in any repository of the organisation mentions Sciter, and Prepare-PackageFiles.ps1 never packaged the directory, so the files did not reach an installation either.

The README described the directory as the old backend's resources while calling WebView2 the current default, which reads as though Sciter were still selectable. It is not, so the row goes with the files.

The history keeps the markup if the old candidate window layout is ever wanted as a reference.
2026-09-05 23:43:30 +09:00
Evan Hu 059957a491 feat(protocol): use pinned typed messages across WebView pages 2026-09-05 23:09:37 +09:00
Evan eafa4c6ca6 Merge pull request #2 from metasequoiaime/dependabot/npm_and_yarn/webview2/settings/ime-settings/npm-cb03e01d6e
chore(deps): bump the npm group in /webview2/settings/ime-settings with 4 updates
2026-09-05 21:53:38 +09:00
Evan 330cdd0cc8 fix(settings): point the About and Feedback links at MSIME-Windows (#3)
The releases link in the About page and both the href and the visible link
text in the Feedback page still pointed at MetasequoiaImeTsf, which only
resolves through GitHub's rename redirect. These are links users click from
inside the settings window, and the visible text showed the old repository
name back to them.

The TSF frontend has been MSIME-Windows for a while; every other cross-repo
reference in the organization already uses that name.
2026-09-05 21:16:08 +09:00
dependabot[bot] 9bd33c8f61 chore(deps): bump the npm group
Bumps the npm group in /webview2/settings/ime-settings with 4 updates: [@types/chrome](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chrome), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [typescript](https://github.com/microsoft/TypeScript) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `@types/chrome` from 0.1.29 to 0.2.8
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chrome)

Updates `@types/node` from 24.10.1 to 26.4.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `typescript` from 5.9.3 to 7.0.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v7.0.2)

Updates `vite` from 7.2.2 to 8.2.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.2.2/packages/vite)

---
updated-dependencies:
- dependency-name: "@types/chrome"
  dependency-version: 0.2.8
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/node"
  dependency-version: 26.4.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: vite
  dependency-version: 8.2.2
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-05 10:51:59 +00:00
Evan Hu e11d69c754 ci: build the settings page on every change, and unbreak pnpm 11
This repository had no CI and no dependabot, while the settings page under
webview2/settings/ime-settings is a Vite and TypeScript project that the Windows release workflow
builds. Until now pnpm build only ever ran while cutting a release, so a break here surfaced at
the worst possible moment.

pnpm-workspace.yaml declared only onlyBuiltDependencies. pnpm 10 honours that key, which is why
releases worked, but pnpm 11 reads allowBuilds instead: it refuses to run esbuild's postinstall,
exits 1, and rewrites the file with a "set this to true or false" placeholder. So pnpm install
--frozen-lockfile fails outright for anyone on a current pnpm. Declaring allowBuilds alongside the
existing key satisfies both, verified against 10.34.5 and 11.25.0.

packageManager pins the pnpm version so a local checkout and CI agree through corepack.

dependabot now covers npm as well as actions. The template used across the other repositories only
declares github-actions and gitsubmodule, so the settings page dependencies were unwatched: they
sit one to two majors behind, with typescript at 5.9 against 7.0 and vite at 7.2 against 8.2.
Those bumps are left to dependabot rather than done here.

The README described only the symlink debug trick, through one developer's absolute paths, and
never mentioned that the settings page has to be built at all.
2026-09-05 17:39:12 +09:00
fanlusky 1709735ddd Revert the WebView2 changes and temporarily make it switchable between rendering backends. 2026-09-04 01:42:38 +08:00
fanlusky e82c5ce633 Add translations for Korean and German. 2026-09-03 00:49:13 +08:00
fanlusky db2efbaa0d Add an option for [ / ] page turning. 2026-09-02 21:13:13 +08:00
fanlusky 3d0dfd1aef Add a setting to always use Chinese/English punctuation. 2026-09-02 20:54:25 +08:00
fanlusky 30f2446cee Preview external skins from TOML color tokens instead of cand.css
Generate settings candidate previews from [candidate.dark]/[candidate.light]
and document skin.toml. Candidate HTML remains for the settings mockup;
the runtime window is no longer WebView2.
2026-09-02 15:30:29 +08:00
fanlusky a38989f52e Add candidate window follow-cursor setting and pointer diagnostics 2026-09-01 00:47:36 +08:00
fanlusky 6486e9ec4c 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:14 +08:00
fanlusky 27c388dffc Add support for the DeepLX translation service. 2026-08-30 15:39:16 +08:00
fanlusky a81940faac Adjust some styles. 2026-08-29 23:39:56 +08:00
fanlusky bcd5ebe8f7 Optimize niya skin. 2026-08-29 22:52:46 +08:00
fanlusky 799e6d3d04 Optimize settings cold start speed. 2026-08-29 22:33:22 +08:00
fanlusky ba1922e33f Adjust some styles. 2026-08-29 21:48:17 +08:00
fanlusky 10aaa42626 Add support for a skin system. 2026-08-29 19:36:42 +08:00
fanlusky b22990c95f Optimize the logging on both the TSF and Server sides to facilitate issue diagnosis. 2026-08-25 13:54:13 +08:00
fanlusky b8f0e91103 Add multilingual candidate translation. 2026-08-25 12:40:34 +08:00
fanlusky 7895d3a9cc Further standardize the issue of batch importing dictionaries. 2026-08-25 11:58:54 +08:00
fanlusky 20779511b0 Add some logs. 2026-08-23 18:11:03 +08:00
fanlusky 4088cc213f Add log switch. 2026-08-22 18:33:46 +08:00
fanlusky 4b95102501 Attempt to fix the flickering issue with Microsoft Shuangpin. Add an R mode for temporary Japanese input. 2026-08-22 16:54:51 +08:00
fanlusky edcd29d4bb Provide an entry for setting the Tencent Cloud Translation API key. 2026-08-22 13:38:42 +08:00
fanlusky 79b5f6dad2 There should also be a corresponding status indicator when the keyboard Caps Lock is turned on. 2026-08-21 23:09:15 +08:00
fanlusky cb925e4600 Adjust ftb font style. 2026-08-21 22:19:33 +08:00
fanlusky 19d41a007b Keep the Chinese/English/Japanese status icons in the plain-text toolbar in a non-selectable state. 2026-08-21 22:11:09 +08:00
fanlusky ccc20fb623 Optimize the tray icon of the language bar and the Japanese icon of the toolbar. 2026-08-21 22:06:29 +08:00
fanlusky b3cac70a00 Fix the issue where Chinese-Japanese switching fails after the settings window has been idle for a long time. 2026-08-21 17:52:17 +08:00
fanlusky 65d5508f64 Add dynamic switching of Japanese icons. 2026-08-21 17:33:54 +08:00
fanlusky cf67568bd9 Add preliminary support for Japanese input. 2026-08-21 16:56:08 +08:00
fanlusky 96040385bc Add Chinese-English translation functionality during typing. 2026-08-21 14:44:10 +08:00
fanlusky 14c5467fc6 Adjust the style of dropdown boxes to make them clearly distinguishable from the background. 2026-08-21 13:06:10 +08:00
fanlusky 5d9c3799e9 Provide more slots for custom prompts. 2026-08-21 12:58:56 +08:00