Merge remote-tracking branch 'origin/main' into chore/oss-hardening
# Conflicts: # AGENTS.md
This commit is contained in:
commit
a533d5aa2b
13 files changed
+135
-24
No files matched your search
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "0.2.0"
|
||||
".": "0.3.0"
|
||||
}
|
||||
@@ -28,6 +28,10 @@ Windows 端的全部一方源码在本仓。**合仓改变的是仓库数量,
|
||||
- **候选与输入状态的权威在 Server 和引擎**,页面只负责展示和发出用户动作,不要在网页侧复制状态机。
|
||||
- `ui-html/webview2/shared/` 是 Engine web 契约的副本,由 `ui-html/scripts/sync-contracts.py` 生成,CI 用 `--check` 验证它和 submodule 一致。手改这个目录会被 CI 拦下来,改契约要改 Engine。
|
||||
|
||||
Server 当前仍使用 Engine 的兼容 `InputSession`,尚未迁移公共 `Session` facade。
|
||||
辅助码筛选与候选提示都按会话配置,禁止用全局默认码表驱动活动会话。
|
||||
`RuntimePaths::legacy()` 在适配器创建时捕获现有安装布局;完整资源包和用户数据代际切换仍待接入。
|
||||
|
||||
## 构建
|
||||
|
||||
每个组件是独立的 CMake 工程,各自带 `vcpkg.json` 和 preset,从仓库根目录指定 `-S`:
|
||||
@@ -67,7 +71,16 @@ cmake -S ui -B ui/build -A x64 # GUI 框架
|
||||
|
||||
**每次可发布的合并都花掉一次签名额度**,这正是之前的手动设计要避免的事。取舍的理由记在 [docs/product-release.md](docs/product-release.md)。
|
||||
|
||||
`workflow_dispatch` 保留下来,现在是修复通道而不是常规路径:run 在建出 draft 之后失败时,拿那个 tag 重跑即可。同一个 tag 重复触发会被 `validate-draft-release.sh` 挡下——发布之后它就不是 draft 了,不会重复发布同一个版本。
|
||||
`workflow_dispatch` 保留下来,它同时是修复通道和发布频道的入口:run 在建出 draft 之后失败时,拿那个 tag 重跑即可。同一个 tag 重复触发会被 `validate-draft-release.sh` 挡下——发布之后它就不是 draft 了,不会重复发布同一个版本。
|
||||
|
||||
**两个频道由触发方式决定,`publish-release.sh` 据此选状态**([#167](https://github.com/metasequoiaime/MSIME-Windows/issues/167)):
|
||||
|
||||
| 频道 | 触发 | 发布状态 | 标题 |
|
||||
|---|---|---|---|
|
||||
| 自动构建 | push | `--prerelease` | `v<版本>(自动构建)`,说明里带提示 |
|
||||
| 发布 | `workflow_dispatch` | `--prerelease=false --latest` | `v<版本>` |
|
||||
|
||||
GitHub 没有自定义频道,只有 Latest / Pre-release / Draft 三种状态,所以这里用 `prerelease` 标志承载「自动、未经挑选」,而不是承载「内测」。产品仍在内测这件事写在 release 说明和 [docs/installation.md](docs/installation.md) 里——那才是该做稳定性声明的地方,而这个标志同时还得用来分隔频道,兼不了两份职责。
|
||||
|
||||
`windows/src/IME/MetasequoiaIME.rc` 的 `FILEVERSION` / `PRODUCTVERSION` 不由 release-please 直接改(它是逗号和点号两种写法),而是由 `scripts/apply_version.py` 从 `version.txt` 注入,release 构建在 configure 之前执行:
|
||||
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## [0.3.0](https://github.com/metasequoiaime/MSIME-Windows/compare/v0.2.1...v0.3.0) (2026-09-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **release:** separate the automatic build channel from the release channel ([2d51c53](https://github.com/metasequoiaime/MSIME-Windows/commit/2d51c5383925ce8174a60f5527cbda5ff03b7d9f))
|
||||
* **release:** separate the automatic build channel from the release channel ([d0e7cfd](https://github.com/metasequoiaime/MSIME-Windows/commit/d0e7cfdbdfaae9e5c296c83e58707932128c9b71))
|
||||
|
||||
## [0.2.1](https://github.com/metasequoiaime/MSIME-Windows/compare/v0.2.0...v0.2.1) (2026-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **server:** adopt isolated engine sessions and helpcode annotations ([#178](https://github.com/metasequoiaime/MSIME-Windows/issues/178)) ([2b8bf93](https://github.com/metasequoiaime/MSIME-Windows/commit/2b8bf937d96e5ff80e19f97de5be594802d90b5d))
|
||||
|
||||
## [0.2.0](https://github.com/metasequoiaime/MSIME-Windows/compare/v0.1.4...v0.2.0) (2026-09-06)
|
||||
|
||||
|
||||
|
||||
+9
-11
@@ -24,20 +24,18 @@
|
||||
## 下载
|
||||
|
||||
1. 打开 [Releases 页面](https://github.com/metasequoiaime/MSIME-Windows/releases)。
|
||||
2. 下载最新的安装包 `MetasequoiaIME_Setup_v<版本号>.exe`(例如 `MetasequoiaIME_Setup_v0.0.9.2.exe`)。
|
||||
2. 下载带 **Latest** 徽章那个版本的 `MetasequoiaIME_Setup_v<版本号>.exe`。
|
||||
|
||||
> 注意:当前发布版本均标记为 **Pre-release(内测版)**,功能与稳定性可能变动,重要环境建议先在虚拟机或非主力机试用。
|
||||
Releases 页面上有两类发布:
|
||||
|
||||
| 频道 | 页面上的样子 | 来源 | 谁该用 |
|
||||
|---|---|---|---|
|
||||
| 发布 | 正常发布,最新的一个带 **Latest** 徽章 | 人工挑选后发布 | **一般都用这个** |
|
||||
| 自动构建 | 标记 **Pre-release**,标题带「(自动构建)」 | 每次合并到 `main` 后由 CI 自动发布,未经人工挑选 | 只在需要某个尚未进入发布频道的修复、且能接受未经验证的改动时用 |
|
||||
|
||||
> 注意:**两个频道当前都还是内测阶段**,版本号仍在 `0.x`,功能与稳定性可能变动,重要环境建议先在虚拟机或非主力机试用。「发布」频道的含义是「有人挑过」,不是「已经稳定」。
|
||||
> 参与内测与反馈:[Telegram 群](https://t.me/msimegroup)、QQ 群 829919142。
|
||||
|
||||
Releases 页面上有两类发布,都标记为 Pre-release,靠标题区分:
|
||||
|
||||
| 标题形式 | 来源 | 建议 |
|
||||
|---|---|---|
|
||||
| `v<版本号>(自动构建)` | 每次合并到 `main` 后由 CI 自动构建发布,未经人工挑选 | 想要最新修复、且能接受未经验证的改动时用 |
|
||||
| `v<版本号>` | 人工挑选后发布 | 一般情况下用这个 |
|
||||
|
||||
GitHub 的发布只有 Latest / Pre-release / Draft 三种状态,没有自定义频道,因此两类共用同一个 Pre-release 列表。
|
||||
|
||||
## 安装与注意事项
|
||||
|
||||
双击安装包,按安装向导提示完成安装。
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"repositories": {
|
||||
"engine": {
|
||||
"repository": "metasequoiaime/MSIME-Engine",
|
||||
"commit": "cc21e42916cf93ce43051fec474e87eeeba305bf"
|
||||
"commit": "c63ba774ac03c9ff8ba82776d50e97d27fd459a3"
|
||||
}
|
||||
},
|
||||
"dictionary": {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Attach the installer to the draft release, append the artifact details to its notes and publish.
|
||||
#
|
||||
# GitHub offers 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. Every releasable merge now produces one of these, so the two are told apart in the title and the notes rather than by where they appear (MSIME-Windows#167).
|
||||
# GitHub has no notion of a release channel, so the two kinds of release this pipeline produces are carried by the two states it does have (MSIME-Windows#167). An automatic per-merge build is a prerelease; a build somebody deliberately dispatched is an ordinary release, and the newest one carries the Latest badge. Users therefore see two lists rather than one list with two kinds of title in it, and /releases/latest finally resolves.
|
||||
#
|
||||
# The prerelease flag means "automatic and uncurated" here, not "beta". The product being 内测 is stated in the notes and in docs/installation.md, which is where a claim about stability belongs; it is not something the flag can express while the flag is also the only thing separating the channels.
|
||||
#
|
||||
# Requires GH_TOKEN, GH_REPO, TAG_NAME, ASSET_PATH, ASSET_NAME, ASSET_SHA256, SIGNING_ENABLED,
|
||||
# DICTIONARY_TAG, PRODUCT_MANIFEST, RELEASE_TRIGGER.
|
||||
@@ -12,10 +14,12 @@ gh release upload "$TAG_NAME" "$ASSET_PATH" "$PRODUCT_MANIFEST" --repo "$GH_REPO
|
||||
if [[ "$RELEASE_TRIGGER" == push ]]; then
|
||||
# Braces are required: bash takes the full-width bracket that follows as part of the name otherwise.
|
||||
title="${TAG_NAME}(自动构建)"
|
||||
banner='本版本由 CI 在合并到 `main` 后自动构建发布,未经人工挑选。想要经过挑选的内测版本,请选择标题不带「自动构建」的发布。'
|
||||
banner='本版本由 CI 在合并到 `main` 后自动构建发布,未经人工挑选,标记为 Pre-release。想要经过挑选的版本,请下载页面上带 Latest 徽章的那个。'
|
||||
channel=(--prerelease)
|
||||
else
|
||||
title="$TAG_NAME"
|
||||
banner=''
|
||||
channel=(--prerelease=false --latest)
|
||||
fi
|
||||
|
||||
gh release view "$TAG_NAME" --repo "$GH_REPO" --json body --jq .body > notes.md
|
||||
@@ -34,5 +38,5 @@ gh release view "$TAG_NAME" --repo "$GH_REPO" --json body --jq .body > notes.md
|
||||
fi
|
||||
} >> notes.md
|
||||
|
||||
gh release edit "$TAG_NAME" --repo "$GH_REPO" --draft=false --prerelease --title "$title" --notes-file notes.md
|
||||
echo "Published $TAG_NAME as '$title' with $ASSET_NAME"
|
||||
gh release edit "$TAG_NAME" --repo "$GH_REPO" --draft=false "${channel[@]}" --title "$title" --notes-file notes.md
|
||||
echo "Published $TAG_NAME as '$title' (${channel[*]}) with $ASSET_NAME"
|
||||
@@ -920,7 +920,7 @@ std::string BuildCurrentCandidatePage()
|
||||
if (show_helpcodes && item.source != CandidateSource::EnglishDictionary &&
|
||||
item.source != CandidateSource::QuickPhrase && item.source != CandidateSource::Emoji &&
|
||||
item.source != CandidateSource::Kaomoji && item.source != CandidateSource::Generated)
|
||||
view.annotation = HelpcodeUtils::compute_helpcodes(item.word, uppercase_all_helpcodes);
|
||||
view.annotation = g_inputSession->get_helpcode_annotation(item.word, uppercase_all_helpcodes);
|
||||
if (item.source == CandidateSource::CloudSuggestion)
|
||||
view.badge = " ☁️";
|
||||
else if (item.source == CandidateSource::AiSuggestion)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#include "config/ime_config.h"
|
||||
#include "MetasequoiaImeEngine/common/helpcode_utils.h"
|
||||
|
||||
EngineInputSession::EngineInputSession(SchemeType scheme, const ShuangpinProfile &profile) : session_(scheme, profile)
|
||||
EngineInputSession::EngineInputSession(SchemeType scheme, const ShuangpinProfile &profile)
|
||||
: paths_(metasequoia::RuntimePaths::legacy()), session_(scheme, profile, paths_)
|
||||
{
|
||||
ApplyConfiguration();
|
||||
}
|
||||
@@ -11,8 +12,21 @@ void EngineInputSession::ApplyConfiguration()
|
||||
{
|
||||
const auto scheme = session_.scheme();
|
||||
if (scheme == SchemeType::Quanpin || scheme == SchemeType::Shuangpin)
|
||||
HelpcodeUtils::select_helpcode_schema(scheme == SchemeType::Quanpin ? GetConfiguredQuanpinHelpcodeSchema()
|
||||
: GetConfiguredShuangpinHelpcodeSchema());
|
||||
{
|
||||
const auto &schema = scheme == SchemeType::Quanpin ? GetConfiguredQuanpinHelpcodeSchema()
|
||||
: GetConfiguredShuangpinHelpcodeSchema();
|
||||
if (schema != helpcode_schema_)
|
||||
{
|
||||
// Keep filtering and annotations on this session's captured resource layout.
|
||||
// Applying unchanged settings on each key must not reload the tables.
|
||||
auto keymap = HelpcodeUtils::load_helpcode_keymap(paths_.resources, schema);
|
||||
if (session_.set_helpcode_schema(schema))
|
||||
{
|
||||
helpcode_schema_ = schema;
|
||||
helpcode_keymap_ = std::move(keymap);
|
||||
}
|
||||
}
|
||||
}
|
||||
session_.set_shuangpin_helpcode_enabled(GetConfiguredShuangpinHelpcodeEnabled());
|
||||
session_.set_quanpin_helpcode_enabled(GetConfiguredQuanpinHelpcodeEnabled());
|
||||
session_.set_quanpin_autocorrect_enabled(GetConfiguredQuanpinAutocorrectEnabled());
|
||||
@@ -160,3 +174,11 @@ IInputSession::CreatingWordProgress EngineInputSession::update_creating_word_pro
|
||||
{
|
||||
return session_.update_creating_word_progress(current_pinyin, current_word, selected_word, selection_transition);
|
||||
}
|
||||
|
||||
std::string EngineInputSession::get_helpcode_annotation(const std::string &word, bool uppercase_all) const
|
||||
{
|
||||
const auto scheme = session_.scheme();
|
||||
if (!helpcode_keymap_ || (scheme != SchemeType::Quanpin && scheme != SchemeType::Shuangpin))
|
||||
return {};
|
||||
return HelpcodeUtils::compute_helpcodes(word, uppercase_all, helpcode_keymap_.get());
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "input_session.h"
|
||||
#include "MetasequoiaImeEngine/common/helpcode_utils.h"
|
||||
#include "MetasequoiaImeEngine/core/input_session.h"
|
||||
|
||||
class EngineInputSession : public IInputSession
|
||||
@@ -19,6 +20,7 @@ class EngineInputSession : public IInputSession
|
||||
|
||||
const std::vector<WordItem> &get_candidates() const override;
|
||||
bool expand_initial_candidates() override;
|
||||
std::string get_helpcode_annotation(const std::string &word, bool uppercase_all) const override;
|
||||
std::optional<WordItem> find_candidate(const std::string &key, const std::string &value) override;
|
||||
|
||||
const std::string &get_pinyin_sequence() const override;
|
||||
@@ -49,5 +51,8 @@ class EngineInputSession : public IInputSession
|
||||
|
||||
private:
|
||||
void ApplyConfiguration();
|
||||
const metasequoia::RuntimePaths paths_;
|
||||
metasequoia::InputSession session_;
|
||||
std::string helpcode_schema_;
|
||||
HelpcodeUtils::SharedKeymap helpcode_keymap_;
|
||||
};
|
||||
@@ -28,6 +28,7 @@ class IInputSession
|
||||
|
||||
virtual const std::vector<WordItem> &get_candidates() const = 0;
|
||||
virtual bool expand_initial_candidates() = 0;
|
||||
virtual std::string get_helpcode_annotation(const std::string &word, bool uppercase_all) const = 0;
|
||||
virtual std::optional<WordItem> find_candidate(const std::string &, const std::string &)
|
||||
{
|
||||
return std::nullopt;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "tests/includes/test_framework.h"
|
||||
#include "src/session/engine_input_session.h"
|
||||
#include "src/config/ime_config.h"
|
||||
#include "MetasequoiaImeEngine/common/helpcode_utils.h"
|
||||
#include "MetasequoiaImeEngine/quanpin/quanpin_query.h"
|
||||
#include "MetasequoiaImeEngine/shuangpin/shuangpin_dictionary.h"
|
||||
@@ -764,3 +765,55 @@ TEST_CASE(EngineShuangpinSegmentedInitialCandidatesExpandInPlace)
|
||||
REQUIRE(count_initials(expanded_candidates) > 24);
|
||||
REQUIRE_EQ(count_other_candidates(expanded_candidates), other_count);
|
||||
}
|
||||
|
||||
TEST_CASE(EngineSessionsKeepHelpcodeFilteringAndAnnotationsTogether)
|
||||
{
|
||||
// The integration runner supplies an isolated config and the locked release dictionaries.
|
||||
InitImeConfig();
|
||||
struct RestoreConfiguration
|
||||
{
|
||||
std::string quanpin = GetConfiguredQuanpinHelpcodeSchema();
|
||||
std::string shuangpin = GetConfiguredShuangpinHelpcodeSchema();
|
||||
bool quanpin_enabled = GetConfiguredQuanpinHelpcodeEnabled();
|
||||
bool shuangpin_enabled = GetConfiguredShuangpinHelpcodeEnabled();
|
||||
~RestoreConfiguration()
|
||||
{
|
||||
SetConfiguredQuanpinHelpcodeSchema(quanpin);
|
||||
SetConfiguredShuangpinHelpcodeSchema(shuangpin);
|
||||
SetConfiguredQuanpinHelpcodeEnabled(quanpin_enabled);
|
||||
SetConfiguredShuangpinHelpcodeEnabled(shuangpin_enabled);
|
||||
}
|
||||
} restore;
|
||||
REQUIRE(SetConfiguredQuanpinHelpcodeEnabled(true));
|
||||
REQUIRE(SetConfiguredShuangpinHelpcodeEnabled(true));
|
||||
REQUIRE(SetConfiguredQuanpinHelpcodeSchema("lantian"));
|
||||
REQUIRE(SetConfiguredShuangpinHelpcodeSchema("ziranma"));
|
||||
|
||||
EngineInputSession quanpin(SchemeType::Quanpin);
|
||||
EngineInputSession shuangpin(SchemeType::Shuangpin);
|
||||
REQUIRE_EQ(quanpin.get_helpcode_annotation("你", true), std::string("(RX)"));
|
||||
REQUIRE_EQ(shuangpin.get_helpcode_annotation("你", false), std::string("(rE)"));
|
||||
|
||||
InputLetters(quanpin, "niRX");
|
||||
REQUIRE(!quanpin.get_candidates().empty());
|
||||
REQUIRE(std::any_of(quanpin.get_candidates().begin(), quanpin.get_candidates().end(),
|
||||
[](const auto &item) { return item.word == "你"; }));
|
||||
InputLetters(shuangpin, "ni");
|
||||
shuangpin.recompute_candidates();
|
||||
quanpin.recompute_candidates();
|
||||
REQUIRE_EQ(quanpin.get_helpcode_annotation("你", true), std::string("(RX)"));
|
||||
REQUIRE_EQ(shuangpin.get_helpcode_annotation("你", false), std::string("(rE)"));
|
||||
REQUIRE(std::any_of(quanpin.get_candidates().begin(), quanpin.get_candidates().end(),
|
||||
[](const auto &item) { return item.word == "你"; }));
|
||||
|
||||
// A settings change is adopted by the next refresh, including candidate annotations.
|
||||
REQUIRE(SetConfiguredQuanpinHelpcodeSchema("ziranma"));
|
||||
quanpin.reset_state();
|
||||
InputLetters(quanpin, "niRE");
|
||||
REQUIRE_EQ(quanpin.get_helpcode_annotation("你", true), std::string("(RE)"));
|
||||
REQUIRE(std::any_of(quanpin.get_candidates().begin(), quanpin.get_candidates().end(),
|
||||
[](const auto &item) { return item.word == "你"; }));
|
||||
REQUIRE_EQ(shuangpin.get_helpcode_annotation("你", false), std::string("(rE)"));
|
||||
quanpin.switch_scheme(SchemeType::Shuangpin);
|
||||
REQUIRE_EQ(quanpin.get_helpcode_annotation("你", false), std::string("(rE)"));
|
||||
}
|
||||
Vendored
+1
-1
Submodule vendor/MetasequoiaImeEngine updated: cc21e42916...c63ba774ac.
+1
-1
@@ -1 +1 @@
|
||||
0.2.0
|
||||
0.3.0
|
||||
Reference in new issue
Block a user