Commit Graph
1480 Commits
Author SHA1 Message Date
Evan Hu a4d583bf2d ci: sign uiAccess server and installer 2026-09-06 17:04:08 +09:00
Evan Hu ef62de3f7e ci: sign only the release installer 2026-09-06 17:01:04 +09:00
Evan Hu 85d462e16c ci: deduplicate release signing targets 2026-09-06 16:58:51 +09:00
Evan ef55ff67bb Merge branch 'main' into ci/self-hosted-runner-signing 2026-09-06 16:55:18 +09:00
Evan Hu e574a2381e ci: use self-hosted Windows signing runner 2026-09-06 16:54:49 +09:00
Lu Fan(SonnyCalcr) 6c7379d003 Merge pull request #191 from fanlusky/fix/webview2-candidate-edge-position
Fix the position issue of the WebView2 candidate window when it touches the screen edge
2026-09-06 15:22:01 +08:00
github-actions[bot] 26b3fc9442 Merge pull request #190 from metasequoiaime/release-please--branches--main--components--MetasequoiaImeWindows
chore(main): release 0.3.1
2026-09-06 07:00:10 +00:00
fanlusky 8544e74dc5 fix: Fix the position issue of the WebView2 candidate window when it touches the screen edge. 2026-09-06 14:59:19 +08:00
Evan fa198af3e1 chore(main): release 0.3.1 2026-09-06 15:49:49 +09:00
Evan c56ce04fc0 Merge pull request #184 from metasequoiaime/feat/public-session-adoption
refactor(server): carry Engine online query identity through candidate tasks
2026-09-06 15:49:26 +09:00
Evan 688972b28c Merge remote-tracking branch 'origin/main' into pr184-merge
# Conflicts:
#	server/src/session/input_session.h
2026-09-06 15:31:05 +09:00
Evan b65063a155 Merge pull request #183 from metasequoiaime/chore/oss-hardening
chore: harden the repository against mainstream OSS standards
2026-09-06 15:21:25 +09:00
Neptrue_Lin ebc0531614 Merge pull request #186 from metasequoiaime/issue-template-revision
Revise feature request template
2026-09-06 12:43:39 +08:00
Neptrue_Lin 3a1a56633c Revise feature request template
Updated options for feature request template to improve clarity.
2026-09-06 12:32:14 +08:00
Evan Hu ac805078e4 refactor(server): carry engine online queries through asynchronous candidate tasks 2026-09-06 13:24:03 +09:00
Evan Hu fd4993f7cc chore(server): pin merged Session caret editing interface 2026-09-06 13:18:43 +09:00
Evan Hu 9785ecd4bf fix(server): revalidate queued online candidate generations on apply 2026-09-06 13:17:50 +09:00
Evan Hu 7c44bd18d1 chore(server): prepare Session adoption with merged engine interfaces 2026-09-06 13:13:15 +09:00
Evan b835131338 style: 格式化合并进来的 engine_input_session.cpp
main 上新合入的这一行不符合 server/.clang-format。这正是新增的格式检查要拦的东西——它在这次
合并里第一次生效就抓到了一处。
2026-09-06 13:07:28 +09:00
Evan Hu a533d5aa2b Merge remote-tracking branch 'origin/main' into chore/oss-hardening
# Conflicts:
#	AGENTS.md
2026-09-06 13:06:54 +09:00
Evan 12515e96ad docs: 加英文 README
全组织此前零英文入口。这个仓库 1248 star,必然有非中文读者从 Trending 进来,而 README、
CONTRIBUTING、组织主页全是中文。

只翻译定位、安装、功能列表、隐私、构建入口和许可这几节,不翻那 230 行辅助码手册——用户主体
是中文用户,英文优先级不该抬到那个程度。同时如实说明大部分 issue 和注释是中文的。
2026-09-06 13:00:40 +09:00
Evan 31ff9f6762 chore: 加 .mailmap 统一提交身份
同一个人以多种 name/email 出现(fanlusky / fanlumaster / Lu Fan(SonnyCalcr),Evan / Evan Hu ×
两个邮箱),git shortlog 与贡献图因此失真。

不改写历史:三个产品仓按 SHA 钉住引擎 submodule,任何 rewrite 都会让这些 pin 失效。.mailmap
只告诉 git 哪些身份是同一个人,提交本身不变。
2026-09-06 12:56:04 +09:00
Evan 598b641dd1 docs: 校正签名配额的前提,README 加 badge 与 star 趋势
四处文档把「签名证书次数有限」当作设计理由,其中 docs/product-release.md 据此建议把若干修复攒成
一个版本以省额度。证书实际允许每月 3000 次签名,这个项目的发布频率远低于该量级,前提不成立。
改为如实说明,并把攒版本的真实理由(发布节奏与 release notes 的碎片化)写清楚——那个考量仍然
成立,只是和配额无关。
2026-09-06 12:43:16 +09:00
github-actions[bot] 9e11a0464b Merge pull request #182 from metasequoiaime/release-please--branches--main--components--MetasequoiaImeWindows
chore(main): release 0.3.0
2026-09-06 03:41:25 +00:00
Evan 0421eb612f chore(main): release 0.3.0 2026-09-06 12:28:34 +09:00
Evan 2d51c53839 Merge pull request #181 from metasequoiaime/ci/build-channel
feat(release): separate the automatic build channel from the release channel
2026-09-06 12:26:17 +09:00
Evan 5bd82e9c5f ci(windows): 在 CI 里执行 clang-format
移植 MSIME-Linux 的 scripts/format.sh,接进已经是 required 的 Workflow validation 作业。
四个组件各自的 .clang-format 一直在仓库里,但此前没有任何一处强制执行。

.git-blame-ignore-revs 指向上一个重排提交,git blame 会跳过它。
2026-09-06 12:17:29 +09:00
Evan Hu d0e7cfdbdf feat(release): separate the automatic build channel from the release channel
Automatic per-merge builds shared one list with deliberately cut releases, and the previous change only made them identifiable rather than separate: both were still prereleases, which is the thing the issue objects to. Labelling was the wrong answer to it.

GitHub has no channel concept, but it has two published states, and there are exactly two kinds of release here. A push-triggered build is now a prerelease; a workflow_dispatch build is an ordinary release and the newest one carries the Latest badge. The Releases page shows two lists instead of one list with two kinds of title in it, and /releases/latest resolves for the first time.

The prerelease flag therefore means "automatic and uncurated" rather than "beta". The product being 内测 is stated in the release notes and in docs/installation.md, which is where a claim about stability belongs; the flag cannot carry that meaning while it is also the only thing separating the channels. docs/installation.md now tells readers to take the Latest badge, describes both channels, and says explicitly that being in the release channel means somebody picked it, not that it is stable.

Already published releases are not touched, so the Latest badge stays empty until the first dispatch after this lands.

Refs MSIME-Windows#167
2026-09-06 12:16:28 +09: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 24adac667a chore(windows): 截图自托管,issue chooser 补安全上报入口
8 张 README 截图从 i.imgur.com 迁到 docs/images/:大陆网络直连不了 imgur,等于对最核心的受众
隐藏了产品截图,同时把门面绑在随时可能失效的第三方图床上。

issue chooser 此前三条 contact link 没有安全条目,而 blank issue 是关掉的——发现漏洞的人看到的
唯一路径就是提公开 bug,恰好是 SECURITY.md 明令禁止的。本仓 private vulnerability reporting 已启用,
直接指向 advisory 表单。文档链接改指 msime.app/docs。
2026-09-06 12:11:35 +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 46e48d219e chore(main): release 0.2.1 (#179) 2026-09-06 11:58:08 +09:00
Evan 6341b4fffc fix(server): 出厂配置的占位凭据不再被当作真凭据
IsPlaceholderToken 与 IsUsableSecret 都只识别 `<YOUR_OWN_` 前缀,而 config.default.toml 实际使用
`<YOUR_XXX>` 和 `FAKESECRET_` 两种写法,18 个占位符无一被识别。叠加 ai_assistant.enabled、
voice_input.voice_input、tencent_tmt.enabled 三项默认为 true,全新安装在用户未做任何配置的情况下
就会把输入串发往 api.deepseek.com、把候选文本发往 tmt.tencentcloudapi.com——请求虽然鉴权失败,
内容已经离开本机。

两处判定改为按占位符的形状判断,并新增 test_default_config_credentials:它直接读安装器实际打包的
config.default.toml,逐条校验没有任何凭据被当成可用值,因此换一种占位符写法也会被拦下。
2026-09-06 11:56:11 +09:00
Evan 2b8bf937d9 fix(server): adopt isolated engine sessions and helpcode annotations (#178) 2026-09-06 11:44:30 +09:00
github-actions[bot] acff323ac1 Merge pull request #173 from metasequoiaime/release-please--branches--main--components--MetasequoiaImeWindows
chore(main): release 0.2.0
2026-09-06 02:29:13 +00:00
Neptrue_Lin 41ef263f3c Merge pull request #177 from metasequoiaime/docs/template-refinement
chore: Issue template refinement
2026-09-06 10:19:28 +08:00
Evan aee9f2e5ef chore(main): release 0.2.0 2026-09-06 11:18:06 +09:00
Neptrue_Lin 78dee8cec0 Merge branch 'main' into docs/template-refinement 2026-09-06 10:14:51 +08:00
Evan 5a4484feea fix(ci): stop the engine bump triage from executing engine file names (#174)
The Hold-for-review step built its comment with printf and spliced ${{ steps.classify.outputs.reason }} into the command text inside single quotes. That value is the list of file names the engine changed. GitHub substitutes ${{ }} textually before bash parses the line, so a path containing a single quote closes the quoting and the rest runs as a command — on a runner holding a contents:write and pull-requests:write token for this repository. Merge rights in MSIME-Engine became command execution here. Non-adversarially the same splice makes the step die under set -euo pipefail on any engine path containing an apostrophe, so a routine bump gets a red triage job and no review comment.

Every value now reaches the shell through env: and the comment body goes through --body-file. The $GITHUB_OUTPUT heredoc that carries those same file names gets a random delimiter, since a path equal to the literal EOF would otherwise close the block early.

Separately, the classifier could auto-merge a range it had not inspected. With an empty file list, echo emits one blank line, grep -v prints it, and command substitution strips it, so the emptiness test read "nothing suspicious changed" and enabled auto-merge. The compare API returns an empty file list whenever head is already an ancestor of base — an engine force-push, or the pin sitting ahead — which is exactly the case where a bump silently reverts engine source this frontend depends on. It now fails closed on any compare status other than a plain fast-forward, and on an empty file list.

Found while auditing MSIME-Apple, which carried a byte-identical copy of this workflow. Fixed there in metasequoiaime/MSIME-Apple#269.
2026-09-06 11:14:01 +09:00
Neptrue_Lin 54aa158991 Refine bug report template labels and descriptions
Updated labels and descriptions in the bug report template to clarify the problem and actual behavior sections.
2026-09-06 10:12:47 +08:00
Evan ee4334dddd fix(ci): attach automatic release checks to the pull request (#175)
* ci(codeql): 避免版本元数据 PR 产生待人工批准的扫描

* fix(ci): attach release validation to the pull request
2026-09-06 11:10:05 +09:00
Neptrue_Lin cdbc6a7710 Change bug type from 'bug' to 'Bug' in template 2026-09-06 10:09:15 +08:00
Evan fd9236f175 Merge pull request #176 from metasequoiaime/ci/build-channel-labelling
feat(release): mark automatically built releases in the title and notes
2026-09-06 11:09:07 +09:00
Neptrue_Lin 1538b2b33a Revise problem label to include motivation
Updated the label for the problem section to include motivation.
2026-09-06 10:08:11 +08:00
Neptrue_Lin b5c3355ece Refine bug report template fields and options
Updated bug report template with improved labels and descriptions.
2026-09-06 10:02:19 +08:00
Evan Hu cc5af5356d fix(ci): stop the dependency review from skipping the release pull request
Dependency review was guarded on the pull_request event, which made it report "skipped" on every other trigger. A required status check that reports "skipped" blocks a merge rather than passing it, and #166 made this one of the nine contexts main requires, so the automatic release could no longer land its own release pull request: land-release-pr.sh watched ci.yml go green and then got "the base branch policy prohibits the merge" (run 34004706430, pull request #173).

The guard cannot simply move to the release path either. ci.yml ignores the release pull request by path, because a pull_request run started by GITHUB_TOKEN is created in action_required and never executes, so the release workflow dispatches ci.yml on the release branch and that dispatch is the only run reporting checks on that commit.

The job now runs on every trigger. A pull_request event supplies both refs on its own; every other trigger compares the default branch against the checked-out commit, which is the same question the pull request asks.
2026-09-06 10:58:00 +09:00
Evan Hu 568fd3742c feat(release): mark automatically built releases in the title and notes
GitHub has three release states and no notion of a channel, so an automatic per-merge build and a release somebody deliberately cut both land in the same Pre-release list that docs/installation.md presents to users as the 内测版 channel. Before every releasable merge produced a release that did not matter; now it does.

Releases published from a push get 「(自动构建)」 in the title and a note saying they were not curated. Releases published from a workflow_dispatch keep the bare tag as their title, which is what release-please already set. docs/installation.md tells readers which is which and why they share one list.

This is deliberately a labelling change and not a second release track. Giving automatic builds their own tag namespace would need a parallel versioning mechanism alongside the one release-please owns, and stacking that on a pipeline that is currently failing and shipping unsigned artifacts is the wrong order.

Refs MSIME-Windows#167
2026-09-06 10:50:56 +09:00
Evan 947b5e9fa2 ci(release): 分离版本分支与历史 tag 的令牌权限 (#171) 2026-09-06 10:45:17 +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