diff --git a/README.md b/README.md index c76b79a0..b6f4f09e 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Windows 端的全部一方源码都在本仓,一次 clone 就能拿到完整 - 中文输入:全拼、双拼(小鹤 / 自然码 / 首道 / 微软)、86 五笔。 - 日文输入:罗马字方案,提供平假名、片假名和日语词库候选;中文状态下也可用临时日语(R 模式)。 -- 拼音方案支持辅助码(蓝天小雨点、自然码、首右 2.0、首右 Plus、小鹤、加加)。 +- 拼音方案支持辅助码(蓝天小雨点、自然码、首右 2.0、首右 Plus、小鹤、拼音加加),也可以导入自己的辅助码方案。 - 支持谷歌云候选、AI 联想(DeepSeek / OpenAI / SiliconFlow / Groq)。 - 竖排候选窗支持中英互译释义;本地未命中时可走腾讯云机器翻译。 - 中英混输、emoji 混输、颜文字混输;独立英文候选输入模式。 @@ -124,7 +124,7 @@ Windows 端的全部一方源码都在本仓,一次 clone 就能拿到完整 - 首右 2.0 - 首右 Plus - 小鹤 -- 加加 +- 拼音加加 辅助码有“单码”和“双码”两种使用方式,也可以分别用于全拼和双拼。 diff --git a/engine/common/helpcode_utils.cpp b/engine/common/helpcode_utils.cpp index 87b5df94..917f0c3d 100644 --- a/engine/common/helpcode_utils.cpp +++ b/engine/common/helpcode_utils.cpp @@ -30,21 +30,144 @@ bool is_han_code_point(std::uint32_t code_point) (code_point >= 0x20000 && code_point <= 0x2FA1F) || (code_point >= 0x30000 && code_point <= 0x323AF); } +constexpr char kUtf8Bom[] = "\xEF\xBB\xBF"; + +void strip_line(std::string &line, bool first_line) +{ + if (first_line && line.rfind(kUtf8Bom, 0) == 0) + line.erase(0, 3); + if (!line.empty() && line.back() == '\r') + line.pop_back(); +} + +std::string trim(const std::string &text) +{ + const auto begin = text.find_first_not_of(" \t"); + if (begin == std::string::npos) + return {}; + const auto end = text.find_last_not_of(" \t"); + return text.substr(begin, end - begin + 1); +} + +// Empty when the schema is not a well-formed custom schema. The stem must stay a single file name +// inside the custom directory. +std::string custom_schema_stem(const std::string &schema) +{ + const std::string prefix = HelpcodeUtils::kCustomHelpcodeSchemaPrefix; + if (schema.size() <= prefix.size() || schema.compare(0, prefix.size(), prefix) != 0) + return {}; + std::string stem = schema.substr(prefix.size()); + if (stem.front() == '.' || stem.find_first_of("/\\:*?\"<>|") != std::string::npos) + return {}; + return stem; +} + +std::filesystem::path custom_schema_file(const std::filesystem::path &resources, const std::string &stem) +{ + return HelpcodeUtils::custom_helpcode_directory(resources) / metasequoia::path_from_utf8((stem + ".txt").c_str()); +} + +bool is_builtin_schema(const std::string &schema) +{ + return std::any_of(metasequoia::assets::helpcodes.begin(), metasequoia::assets::helpcodes.end(), + [&](const auto &entry) { return entry.schema == schema; }); +} + +// Reads the leading comment block: "# name: 中文名" and "# name_en: English name". +void read_custom_header(const std::filesystem::path &file, HelpcodeUtils::CustomHelpcodeSchema &schema) +{ + std::ifstream input(file); + std::string line; + bool first_line = true; + while (std::getline(input, line)) + { + strip_line(line, first_line); + first_line = false; + line = trim(line); + if (line.empty()) + continue; + if (line.front() != '#') + break; + const std::string body = trim(line.substr(1)); + auto separator = body.find(':'); + auto separator_size = 1; + if (const auto full_width = body.find("\xEF\xBC\x9A"); full_width < separator) // U+FF1A ':' + { + separator = full_width; + separator_size = 3; + } + if (separator == std::string::npos) + continue; + const std::string key = boost::algorithm::to_lower_copy(trim(body.substr(0, separator))); + const std::string value = trim(body.substr(separator + separator_size)); + if (key == "name") + schema.name = value; + else if (key == "name_en") + schema.name_en = value; + } +} + } // namespace namespace HelpcodeUtils { +std::filesystem::path custom_helpcode_directory(const std::filesystem::path &resources) +{ + return resources / "helpcodes" / "custom"; +} + +std::vector list_custom_helpcode_schemas(const std::filesystem::path &resources) +{ + std::vector result; + std::error_code error; + for (std::filesystem::directory_iterator it(custom_helpcode_directory(resources), error), end; !error && it != end; + it.increment(error)) + { + if (!it->is_regular_file(error) || + boost::algorithm::to_lower_copy(metasequoia::path_to_utf8(it->path().extension())) != ".txt") + continue; + CustomHelpcodeSchema schema; + schema.file_stem = metasequoia::path_to_utf8(it->path().stem()); + schema.schema = kCustomHelpcodeSchemaPrefix + schema.file_stem; + if (custom_schema_stem(schema.schema).empty()) + continue; + read_custom_header(it->path(), schema); + result.push_back(std::move(schema)); + } + std::sort(result.begin(), result.end(), [](const auto &a, const auto &b) { return a.file_stem < b.file_stem; }); + return result; +} + +bool is_helpcode_schema_available(const std::filesystem::path &resources, const std::string &schema) +{ + if (is_builtin_schema(schema)) + return true; + const std::string stem = custom_schema_stem(schema); + std::error_code error; + return !stem.empty() && std::filesystem::is_regular_file(custom_schema_file(resources, stem), error); +} + SharedKeymap load_helpcode_keymap(const std::filesystem::path &resources, const std::string &schema) { + std::filesystem::path file; const auto found = std::find_if(metasequoia::assets::helpcodes.begin(), metasequoia::assets::helpcodes.end(), [&](const auto &entry) { return entry.schema == schema; }); - if (found == metasequoia::assets::helpcodes.end()) + if (found != metasequoia::assets::helpcodes.end()) + file = resources / found->path; + else if (const std::string stem = custom_schema_stem(schema); !stem.empty()) + file = custom_schema_file(resources, stem); + else throw std::invalid_argument("Unknown helpcode schema"); auto result = std::make_shared(); - std::ifstream input(resources / found->path); + std::ifstream input(file); std::string line; + bool first_line = true; while (std::getline(input, line)) { + strip_line(line, first_line); + first_line = false; + if (!line.empty() && line.front() == '#') + continue; const auto pos = line.find('='); if (pos == std::string::npos || pos == 0) continue; @@ -296,8 +419,8 @@ bool matches_double_helpcodes(const std::string &word, const std::string &help_c bool is_supported_helpcode_schema(const std::string &schema) { - return std::any_of(metasequoia::assets::helpcodes.begin(), metasequoia::assets::helpcodes.end(), - [&](const auto &entry) { return entry.schema == schema; }); + // Syntax only: whether a custom file exists is is_helpcode_schema_available's question. + return is_builtin_schema(schema) || !custom_schema_stem(schema).empty(); } bool select_helpcode_schema(const std::string &schema) diff --git a/engine/common/helpcode_utils.h b/engine/common/helpcode_utils.h index b6cd5133..ec3e04b4 100644 --- a/engine/common/helpcode_utils.h +++ b/engine/common/helpcode_utils.h @@ -17,6 +17,21 @@ const Keymap &helpcode_keymap(); bool is_supported_helpcode_schema(const std::string &schema); bool select_helpcode_schema(const std::string &schema); +// User tables dropped into /helpcodes/custom/*.txt are schemas named +// "custom/". Leading "# name: ..." / "# name_en: ..." comment lines give display names. +inline constexpr char kCustomHelpcodeSchemaPrefix[] = "custom/"; +struct CustomHelpcodeSchema +{ + std::string schema; + std::string name; // Chinese display name; empty when the file does not declare one + std::string name_en; // English display name; empty when the file does not declare one + std::string file_stem; +}; +std::filesystem::path custom_helpcode_directory(const std::filesystem::path &resources); +std::vector list_custom_helpcode_schemas(const std::filesystem::path &resources); +// Built-in schemas are always available; a custom schema only while its file exists. +bool is_helpcode_schema_available(const std::filesystem::path &resources, const std::string &schema); + std::string get_first_han_char(const std::string &words); std::string get_last_han_char(const std::string &words); std::string::size_type count_han_chars(const std::string &words); diff --git a/engine/contracts/webview/messages.json b/engine/contracts/webview/messages.json index af9edf1c..fc8dae6c 100644 --- a/engine/contracts/webview/messages.json +++ b/engine/contracts/webview/messages.json @@ -16,6 +16,11 @@ "settings" ] }, + "openHelpcodeDirectory": { + "surfaces": [ + "settings" + ] + }, "openHandwritingPanel": { "surfaces": [ "settings" diff --git a/engine/contracts/webview/messages.ts b/engine/contracts/webview/messages.ts index 474769a7..c7e2b7e9 100644 --- a/engine/contracts/webview/messages.ts +++ b/engine/contracts/webview/messages.ts @@ -5,6 +5,7 @@ export type ClientMessage = | { "type": "configRequest"; "protocolVersion"?: 1 } | { "type": "skinCatalogRequest"; "protocolVersion"?: 1 } | { "type": "openSkinDirectory"; "protocolVersion"?: 1 } + | { "type": "openHelpcodeDirectory"; "protocolVersion"?: 1 } | { "type": "openHandwritingPanel"; "protocolVersion"?: 1 } | { "type": "restartServer"; "protocolVersion"?: 1 } | { "type": "copyText"; "protocolVersion"?: 1; "data": string } @@ -59,6 +60,7 @@ export type SettingsMessage = | { "type": "configRequest"; "protocolVersion"?: 1 } | { "type": "skinCatalogRequest"; "protocolVersion"?: 1 } | { "type": "openSkinDirectory"; "protocolVersion"?: 1 } + | { "type": "openHelpcodeDirectory"; "protocolVersion"?: 1 } | { "type": "openHandwritingPanel"; "protocolVersion"?: 1 } | { "type": "restartServer"; "protocolVersion"?: 1 } | { "type": "copyText"; "protocolVersion"?: 1; "data": string } diff --git a/engine/contracts/webview/schema.h b/engine/contracts/webview/schema.h index 1fe82c92..24c7091e 100644 --- a/engine/contracts/webview/schema.h +++ b/engine/contracts/webview/schema.h @@ -2,5 +2,5 @@ // Generated by generate.py; edit messages.json. namespace metasequoia::webview { inline constexpr int Version = 1; -inline constexpr char SchemaJson[] = R"msime({"version":1,"client":{"configRequest":{"surfaces":["settings"]},"skinCatalogRequest":{"surfaces":["settings"]},"openSkinDirectory":{"surfaces":["settings"]},"openHandwritingPanel":{"surfaces":["settings"]},"restartServer":{"surfaces":["settings"]},"copyText":{"surfaces":["settings"],"data":{"type":"string"}},"openExternalUrl":{"surfaces":["settings"],"data":{"type":"string"}},"dragStart":{"surfaces":["settings","toolbar"]},"focus":{"surfaces":["settings"]},"resizeStart":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"resizeHitTest":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"windowControl":{"surfaces":["settings"],"data":{"type":"string","enum":["minimize","maximize","restore","close"]}},"maximizeButtonRect":{"surfaces":["settings"],"data":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"dpr":{"type":"number","minimum":0.01}},"required":["x","y","width","height","dpr"],"additionalProperties":false}},"configUpdate":{"surfaces":["settings"],"data":{"type":"object","properties":{"path":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},"required":["path","value"],"additionalProperties":false}},"apiCredentialTest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"service":{"type":"string","enum":["translation.tencent","translation.niutrans","translation.custom","voice.asr","voice.polish","ai.assistant"]},"config":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"required":["requestId","service","config"],"additionalProperties":false}},"dictionaryRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"dictionary":{"type":"string","enum":["quanpin","wubi","english","quick"]},"action":{"type":"string","enum":["query","create","update","delete","import","importHans","export"]},"word":{"type":"string"},"code":{"type":"string"},"content":{"type":"string"},"weight":{"type":"integer"},"oldWord":{"type":"string"},"oldCode":{"type":"string"},"display":{"type":"string"},"oldDisplay":{"type":"string"},"offset":{"type":"integer","minimum":0,"maximum":2147483647},"limit":{"type":"integer","minimum":1,"maximum":200}},"required":["requestId","dictionary","action"],"additionalProperties":false}},"statsRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"action":{"type":"string","enum":["overview","clearAll","openDirectory"]}},"required":["requestId","action"],"additionalProperties":false}},"openKeyboardPanel":{"surfaces":["settings","toolbar"]},"candidate":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"delete":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"pin":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"clearPosition":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"candidateWheel":{"surfaces":["candidate"],"data":{"type":"integer","minimum":-32768,"maximum":32767}},"fixPosition":{"surfaces":["candidate"],"data":{"type":"object","properties":{"index":{"type":"integer","minimum":1,"maximum":10},"position":{"type":"integer","minimum":1,"maximum":5}},"required":["index","position"],"additionalProperties":false}},"contextMenuResize":{"surfaces":["candidate"],"data":{"type":"object","properties":{"width":{"type":"integer","minimum":0,"maximum":65535},"height":{"type":"integer","minimum":0,"maximum":65535},"topExpansion":{"type":"integer","minimum":0,"maximum":65535},"leftExpansion":{"type":"integer","minimum":0,"maximum":65535}},"required":["width","height"],"additionalProperties":false}},"contextMenuClosed":{"surfaces":["candidate"]},"candidatePointerArmed":{"surfaces":["candidate"]},"candidatePointerMotion":{"surfaces":["candidate"]},"candidateFrameProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"revision":{"type":"integer"},"stage":{"type":"integer"},"performanceMs":{"type":"number","minimum":0}},"required":["revision","stage","performanceMs"],"additionalProperties":false}},"candidatePointerProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"probe":{"type":"integer"},"screenX":{"type":"integer"},"screenY":{"type":"integer"},"clientX":{"type":"integer"},"clientY":{"type":"integer"},"movementX":{"type":"integer"},"movementY":{"type":"integer"},"armed":{"type":"boolean"}},"required":["probe","screenX","screenY","clientX","clientY","movementX","movementY","armed"],"additionalProperties":false}},"contentTruncated":{"surfaces":["candidate","toolbar","menu"],"data":{"type":"object","properties":{"surface":{"type":"string","enum":["candidate","toolbar","menu","ftb","candwnd","cand"]},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0}},"required":["surface","width","height"],"additionalProperties":true}},"floatingToggle":{"surfaces":["menu"],"data":{"type":"boolean"}},"settings":{"surfaces":["menu"]},"about":{"surfaces":["menu"]},"emojiSymbols":{"surfaces":["menu"]},"keyboardPanel":{"surfaces":["menu"]},"handwritingPanel":{"surfaces":["menu"]},"voiceInput":{"surfaces":["menu"]},"ready":{"surfaces":["toolbar"]},"exitEnglishInputMode":{"surfaces":["toolbar"]},"changeCharacterSet":{"surfaces":["toolbar"]},"openSettings":{"surfaces":["toolbar"]},"openEmojiPanel":{"surfaces":["toolbar"]},"changeIMEMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["cn","en"]}},"changeCharMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["fullwidth","halfwidth"]}},"changePuncMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["puncEn","puncCn"]}}},"server":{"configSnapshot":{"data":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"windowState":{"data":{"type":"object","properties":{"isMaximized":{"type":"boolean"}},"required":["isMaximized"],"additionalProperties":false}},"maxButtonEvent":{"data":{"type":"object","properties":{"event":{"type":"string","enum":["enter","leave","down","up","click"]}},"required":["event"],"additionalProperties":false}},"apiCredentialTestResult":{"properties":{"requestId":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["requestId","ok","message"],"additionalProperties":false},"dictionaryResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"dictionary":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"code":{"type":"string"},"display":{"type":"string"},"weight":{"type":"integer"}},"required":["word"],"additionalProperties":false}},"content":{"type":"string"},"filename":{"type":"string"},"offset":{"type":"integer","minimum":0},"hasMore":{"type":"boolean"}},"required":["requestId","ok","message","rows"],"additionalProperties":true},"statsResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"overview":{"type":"object","properties":{"hasData":{"type":"boolean"},"firstDayKey":{"type":"integer"},"lastDayKey":{"type":"integer"},"days":{"type":"integer"},"totalChars":{"type":"integer"},"totalActiveMs":{"type":"integer"},"todayDayKey":{"type":"integer"},"todayChars":{"type":"integer"},"todayActiveMs":{"type":"integer"},"averagePerDay":{"type":"number"},"currentStreak":{"type":"integer"},"longestStreak":{"type":"integer"},"bestDayKey":{"type":"integer"},"bestDayChars":{"type":"integer"},"todaySpeed":{"type":"number"},"averageSpeed":{"type":"number"},"fastestSpeed":{"type":"number"},"fastestDayKey":{"type":"integer"},"categories":{"type":"object","properties":{"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"}},"required":["cjk","latin","digit","punct","other"],"additionalProperties":false},"todayHourly":{"type":"array","items":{"type":"integer"}},"daily":{"type":"array","items":{"type":"object","properties":{"dayKey":{"type":"integer"},"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"},"activeMs":{"type":"integer"}},"required":["dayKey","cjk","latin","digit","punct","other","activeMs"],"additionalProperties":false}}},"required":["hasData","firstDayKey","lastDayKey","days","totalChars","totalActiveMs","todayDayKey","todayChars","todayActiveMs","averagePerDay","currentStreak","longestStreak","bestDayKey","bestDayChars","todaySpeed","averageSpeed","fastestSpeed","fastestDayKey","categories","todayHourly","daily"],"additionalProperties":false},"removedDays":{"type":"integer"}},"required":["requestId","action","ok","message"],"additionalProperties":true}}})msime"; +inline constexpr char SchemaJson[] = R"msime({"version":1,"client":{"configRequest":{"surfaces":["settings"]},"skinCatalogRequest":{"surfaces":["settings"]},"openSkinDirectory":{"surfaces":["settings"]},"openHelpcodeDirectory":{"surfaces":["settings"]},"openHandwritingPanel":{"surfaces":["settings"]},"restartServer":{"surfaces":["settings"]},"copyText":{"surfaces":["settings"],"data":{"type":"string"}},"openExternalUrl":{"surfaces":["settings"],"data":{"type":"string"}},"dragStart":{"surfaces":["settings","toolbar"]},"focus":{"surfaces":["settings"]},"resizeStart":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"resizeHitTest":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"windowControl":{"surfaces":["settings"],"data":{"type":"string","enum":["minimize","maximize","restore","close"]}},"maximizeButtonRect":{"surfaces":["settings"],"data":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"dpr":{"type":"number","minimum":0.01}},"required":["x","y","width","height","dpr"],"additionalProperties":false}},"configUpdate":{"surfaces":["settings"],"data":{"type":"object","properties":{"path":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},"required":["path","value"],"additionalProperties":false}},"apiCredentialTest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"service":{"type":"string","enum":["translation.tencent","translation.niutrans","translation.custom","voice.asr","voice.polish","ai.assistant"]},"config":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"required":["requestId","service","config"],"additionalProperties":false}},"dictionaryRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"dictionary":{"type":"string","enum":["quanpin","wubi","english","quick"]},"action":{"type":"string","enum":["query","create","update","delete","import","importHans","export"]},"word":{"type":"string"},"code":{"type":"string"},"content":{"type":"string"},"weight":{"type":"integer"},"oldWord":{"type":"string"},"oldCode":{"type":"string"},"display":{"type":"string"},"oldDisplay":{"type":"string"},"offset":{"type":"integer","minimum":0,"maximum":2147483647},"limit":{"type":"integer","minimum":1,"maximum":200}},"required":["requestId","dictionary","action"],"additionalProperties":false}},"statsRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"action":{"type":"string","enum":["overview","clearAll","openDirectory"]}},"required":["requestId","action"],"additionalProperties":false}},"openKeyboardPanel":{"surfaces":["settings","toolbar"]},"candidate":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"delete":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"pin":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"clearPosition":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"candidateWheel":{"surfaces":["candidate"],"data":{"type":"integer","minimum":-32768,"maximum":32767}},"fixPosition":{"surfaces":["candidate"],"data":{"type":"object","properties":{"index":{"type":"integer","minimum":1,"maximum":10},"position":{"type":"integer","minimum":1,"maximum":5}},"required":["index","position"],"additionalProperties":false}},"contextMenuResize":{"surfaces":["candidate"],"data":{"type":"object","properties":{"width":{"type":"integer","minimum":0,"maximum":65535},"height":{"type":"integer","minimum":0,"maximum":65535},"topExpansion":{"type":"integer","minimum":0,"maximum":65535},"leftExpansion":{"type":"integer","minimum":0,"maximum":65535}},"required":["width","height"],"additionalProperties":false}},"contextMenuClosed":{"surfaces":["candidate"]},"candidatePointerArmed":{"surfaces":["candidate"]},"candidatePointerMotion":{"surfaces":["candidate"]},"candidateFrameProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"revision":{"type":"integer"},"stage":{"type":"integer"},"performanceMs":{"type":"number","minimum":0}},"required":["revision","stage","performanceMs"],"additionalProperties":false}},"candidatePointerProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"probe":{"type":"integer"},"screenX":{"type":"integer"},"screenY":{"type":"integer"},"clientX":{"type":"integer"},"clientY":{"type":"integer"},"movementX":{"type":"integer"},"movementY":{"type":"integer"},"armed":{"type":"boolean"}},"required":["probe","screenX","screenY","clientX","clientY","movementX","movementY","armed"],"additionalProperties":false}},"contentTruncated":{"surfaces":["candidate","toolbar","menu"],"data":{"type":"object","properties":{"surface":{"type":"string","enum":["candidate","toolbar","menu","ftb","candwnd","cand"]},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0}},"required":["surface","width","height"],"additionalProperties":true}},"floatingToggle":{"surfaces":["menu"],"data":{"type":"boolean"}},"settings":{"surfaces":["menu"]},"about":{"surfaces":["menu"]},"emojiSymbols":{"surfaces":["menu"]},"keyboardPanel":{"surfaces":["menu"]},"handwritingPanel":{"surfaces":["menu"]},"voiceInput":{"surfaces":["menu"]},"ready":{"surfaces":["toolbar"]},"exitEnglishInputMode":{"surfaces":["toolbar"]},"changeCharacterSet":{"surfaces":["toolbar"]},"openSettings":{"surfaces":["toolbar"]},"openEmojiPanel":{"surfaces":["toolbar"]},"changeIMEMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["cn","en"]}},"changeCharMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["fullwidth","halfwidth"]}},"changePuncMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["puncEn","puncCn"]}}},"server":{"configSnapshot":{"data":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"windowState":{"data":{"type":"object","properties":{"isMaximized":{"type":"boolean"}},"required":["isMaximized"],"additionalProperties":false}},"maxButtonEvent":{"data":{"type":"object","properties":{"event":{"type":"string","enum":["enter","leave","down","up","click"]}},"required":["event"],"additionalProperties":false}},"apiCredentialTestResult":{"properties":{"requestId":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["requestId","ok","message"],"additionalProperties":false},"dictionaryResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"dictionary":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"code":{"type":"string"},"display":{"type":"string"},"weight":{"type":"integer"}},"required":["word"],"additionalProperties":false}},"content":{"type":"string"},"filename":{"type":"string"},"offset":{"type":"integer","minimum":0},"hasMore":{"type":"boolean"}},"required":["requestId","ok","message","rows"],"additionalProperties":true},"statsResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"overview":{"type":"object","properties":{"hasData":{"type":"boolean"},"firstDayKey":{"type":"integer"},"lastDayKey":{"type":"integer"},"days":{"type":"integer"},"totalChars":{"type":"integer"},"totalActiveMs":{"type":"integer"},"todayDayKey":{"type":"integer"},"todayChars":{"type":"integer"},"todayActiveMs":{"type":"integer"},"averagePerDay":{"type":"number"},"currentStreak":{"type":"integer"},"longestStreak":{"type":"integer"},"bestDayKey":{"type":"integer"},"bestDayChars":{"type":"integer"},"todaySpeed":{"type":"number"},"averageSpeed":{"type":"number"},"fastestSpeed":{"type":"number"},"fastestDayKey":{"type":"integer"},"categories":{"type":"object","properties":{"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"}},"required":["cjk","latin","digit","punct","other"],"additionalProperties":false},"todayHourly":{"type":"array","items":{"type":"integer"}},"daily":{"type":"array","items":{"type":"object","properties":{"dayKey":{"type":"integer"},"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"},"activeMs":{"type":"integer"}},"required":["dayKey","cjk","latin","digit","punct","other","activeMs"],"additionalProperties":false}}},"required":["hasData","firstDayKey","lastDayKey","days","totalChars","totalActiveMs","todayDayKey","todayChars","todayActiveMs","averagePerDay","currentStreak","longestStreak","bestDayKey","bestDayChars","todaySpeed","averageSpeed","fastestSpeed","fastestDayKey","categories","todayHourly","daily"],"additionalProperties":false},"removedDays":{"type":"integer"}},"required":["requestId","action","ok","message"],"additionalProperties":true}}})msime"; } diff --git a/engine/contracts/webview/schema.js b/engine/contracts/webview/schema.js index e39c8897..4eb28495 100644 --- a/engine/contracts/webview/schema.js +++ b/engine/contracts/webview/schema.js @@ -1,6 +1,6 @@ // Generated by generate.py; edit messages.json. (function(root) { - const schema = {"version":1,"client":{"configRequest":{"surfaces":["settings"]},"skinCatalogRequest":{"surfaces":["settings"]},"openSkinDirectory":{"surfaces":["settings"]},"openHandwritingPanel":{"surfaces":["settings"]},"restartServer":{"surfaces":["settings"]},"copyText":{"surfaces":["settings"],"data":{"type":"string"}},"openExternalUrl":{"surfaces":["settings"],"data":{"type":"string"}},"dragStart":{"surfaces":["settings","toolbar"]},"focus":{"surfaces":["settings"]},"resizeStart":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"resizeHitTest":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"windowControl":{"surfaces":["settings"],"data":{"type":"string","enum":["minimize","maximize","restore","close"]}},"maximizeButtonRect":{"surfaces":["settings"],"data":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"dpr":{"type":"number","minimum":0.01}},"required":["x","y","width","height","dpr"],"additionalProperties":false}},"configUpdate":{"surfaces":["settings"],"data":{"type":"object","properties":{"path":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},"required":["path","value"],"additionalProperties":false}},"apiCredentialTest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"service":{"type":"string","enum":["translation.tencent","translation.niutrans","translation.custom","voice.asr","voice.polish","ai.assistant"]},"config":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"required":["requestId","service","config"],"additionalProperties":false}},"dictionaryRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"dictionary":{"type":"string","enum":["quanpin","wubi","english","quick"]},"action":{"type":"string","enum":["query","create","update","delete","import","importHans","export"]},"word":{"type":"string"},"code":{"type":"string"},"content":{"type":"string"},"weight":{"type":"integer"},"oldWord":{"type":"string"},"oldCode":{"type":"string"},"display":{"type":"string"},"oldDisplay":{"type":"string"},"offset":{"type":"integer","minimum":0,"maximum":2147483647},"limit":{"type":"integer","minimum":1,"maximum":200}},"required":["requestId","dictionary","action"],"additionalProperties":false}},"statsRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"action":{"type":"string","enum":["overview","clearAll","openDirectory"]}},"required":["requestId","action"],"additionalProperties":false}},"openKeyboardPanel":{"surfaces":["settings","toolbar"]},"candidate":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"delete":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"pin":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"clearPosition":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"candidateWheel":{"surfaces":["candidate"],"data":{"type":"integer","minimum":-32768,"maximum":32767}},"fixPosition":{"surfaces":["candidate"],"data":{"type":"object","properties":{"index":{"type":"integer","minimum":1,"maximum":10},"position":{"type":"integer","minimum":1,"maximum":5}},"required":["index","position"],"additionalProperties":false}},"contextMenuResize":{"surfaces":["candidate"],"data":{"type":"object","properties":{"width":{"type":"integer","minimum":0,"maximum":65535},"height":{"type":"integer","minimum":0,"maximum":65535},"topExpansion":{"type":"integer","minimum":0,"maximum":65535},"leftExpansion":{"type":"integer","minimum":0,"maximum":65535}},"required":["width","height"],"additionalProperties":false}},"contextMenuClosed":{"surfaces":["candidate"]},"candidatePointerArmed":{"surfaces":["candidate"]},"candidatePointerMotion":{"surfaces":["candidate"]},"candidateFrameProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"revision":{"type":"integer"},"stage":{"type":"integer"},"performanceMs":{"type":"number","minimum":0}},"required":["revision","stage","performanceMs"],"additionalProperties":false}},"candidatePointerProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"probe":{"type":"integer"},"screenX":{"type":"integer"},"screenY":{"type":"integer"},"clientX":{"type":"integer"},"clientY":{"type":"integer"},"movementX":{"type":"integer"},"movementY":{"type":"integer"},"armed":{"type":"boolean"}},"required":["probe","screenX","screenY","clientX","clientY","movementX","movementY","armed"],"additionalProperties":false}},"contentTruncated":{"surfaces":["candidate","toolbar","menu"],"data":{"type":"object","properties":{"surface":{"type":"string","enum":["candidate","toolbar","menu","ftb","candwnd","cand"]},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0}},"required":["surface","width","height"],"additionalProperties":true}},"floatingToggle":{"surfaces":["menu"],"data":{"type":"boolean"}},"settings":{"surfaces":["menu"]},"about":{"surfaces":["menu"]},"emojiSymbols":{"surfaces":["menu"]},"keyboardPanel":{"surfaces":["menu"]},"handwritingPanel":{"surfaces":["menu"]},"voiceInput":{"surfaces":["menu"]},"ready":{"surfaces":["toolbar"]},"exitEnglishInputMode":{"surfaces":["toolbar"]},"changeCharacterSet":{"surfaces":["toolbar"]},"openSettings":{"surfaces":["toolbar"]},"openEmojiPanel":{"surfaces":["toolbar"]},"changeIMEMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["cn","en"]}},"changeCharMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["fullwidth","halfwidth"]}},"changePuncMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["puncEn","puncCn"]}}},"server":{"configSnapshot":{"data":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"windowState":{"data":{"type":"object","properties":{"isMaximized":{"type":"boolean"}},"required":["isMaximized"],"additionalProperties":false}},"maxButtonEvent":{"data":{"type":"object","properties":{"event":{"type":"string","enum":["enter","leave","down","up","click"]}},"required":["event"],"additionalProperties":false}},"apiCredentialTestResult":{"properties":{"requestId":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["requestId","ok","message"],"additionalProperties":false},"dictionaryResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"dictionary":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"code":{"type":"string"},"display":{"type":"string"},"weight":{"type":"integer"}},"required":["word"],"additionalProperties":false}},"content":{"type":"string"},"filename":{"type":"string"},"offset":{"type":"integer","minimum":0},"hasMore":{"type":"boolean"}},"required":["requestId","ok","message","rows"],"additionalProperties":true},"statsResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"overview":{"type":"object","properties":{"hasData":{"type":"boolean"},"firstDayKey":{"type":"integer"},"lastDayKey":{"type":"integer"},"days":{"type":"integer"},"totalChars":{"type":"integer"},"totalActiveMs":{"type":"integer"},"todayDayKey":{"type":"integer"},"todayChars":{"type":"integer"},"todayActiveMs":{"type":"integer"},"averagePerDay":{"type":"number"},"currentStreak":{"type":"integer"},"longestStreak":{"type":"integer"},"bestDayKey":{"type":"integer"},"bestDayChars":{"type":"integer"},"todaySpeed":{"type":"number"},"averageSpeed":{"type":"number"},"fastestSpeed":{"type":"number"},"fastestDayKey":{"type":"integer"},"categories":{"type":"object","properties":{"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"}},"required":["cjk","latin","digit","punct","other"],"additionalProperties":false},"todayHourly":{"type":"array","items":{"type":"integer"}},"daily":{"type":"array","items":{"type":"object","properties":{"dayKey":{"type":"integer"},"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"},"activeMs":{"type":"integer"}},"required":["dayKey","cjk","latin","digit","punct","other","activeMs"],"additionalProperties":false}}},"required":["hasData","firstDayKey","lastDayKey","days","totalChars","totalActiveMs","todayDayKey","todayChars","todayActiveMs","averagePerDay","currentStreak","longestStreak","bestDayKey","bestDayChars","todaySpeed","averageSpeed","fastestSpeed","fastestDayKey","categories","todayHourly","daily"],"additionalProperties":false},"removedDays":{"type":"integer"}},"required":["requestId","action","ok","message"],"additionalProperties":true}}}; + const schema = {"version":1,"client":{"configRequest":{"surfaces":["settings"]},"skinCatalogRequest":{"surfaces":["settings"]},"openSkinDirectory":{"surfaces":["settings"]},"openHelpcodeDirectory":{"surfaces":["settings"]},"openHandwritingPanel":{"surfaces":["settings"]},"restartServer":{"surfaces":["settings"]},"copyText":{"surfaces":["settings"],"data":{"type":"string"}},"openExternalUrl":{"surfaces":["settings"],"data":{"type":"string"}},"dragStart":{"surfaces":["settings","toolbar"]},"focus":{"surfaces":["settings"]},"resizeStart":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"resizeHitTest":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"windowControl":{"surfaces":["settings"],"data":{"type":"string","enum":["minimize","maximize","restore","close"]}},"maximizeButtonRect":{"surfaces":["settings"],"data":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"dpr":{"type":"number","minimum":0.01}},"required":["x","y","width","height","dpr"],"additionalProperties":false}},"configUpdate":{"surfaces":["settings"],"data":{"type":"object","properties":{"path":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},"required":["path","value"],"additionalProperties":false}},"apiCredentialTest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"service":{"type":"string","enum":["translation.tencent","translation.niutrans","translation.custom","voice.asr","voice.polish","ai.assistant"]},"config":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"required":["requestId","service","config"],"additionalProperties":false}},"dictionaryRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"dictionary":{"type":"string","enum":["quanpin","wubi","english","quick"]},"action":{"type":"string","enum":["query","create","update","delete","import","importHans","export"]},"word":{"type":"string"},"code":{"type":"string"},"content":{"type":"string"},"weight":{"type":"integer"},"oldWord":{"type":"string"},"oldCode":{"type":"string"},"display":{"type":"string"},"oldDisplay":{"type":"string"},"offset":{"type":"integer","minimum":0,"maximum":2147483647},"limit":{"type":"integer","minimum":1,"maximum":200}},"required":["requestId","dictionary","action"],"additionalProperties":false}},"statsRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"action":{"type":"string","enum":["overview","clearAll","openDirectory"]}},"required":["requestId","action"],"additionalProperties":false}},"openKeyboardPanel":{"surfaces":["settings","toolbar"]},"candidate":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"delete":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"pin":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"clearPosition":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"candidateWheel":{"surfaces":["candidate"],"data":{"type":"integer","minimum":-32768,"maximum":32767}},"fixPosition":{"surfaces":["candidate"],"data":{"type":"object","properties":{"index":{"type":"integer","minimum":1,"maximum":10},"position":{"type":"integer","minimum":1,"maximum":5}},"required":["index","position"],"additionalProperties":false}},"contextMenuResize":{"surfaces":["candidate"],"data":{"type":"object","properties":{"width":{"type":"integer","minimum":0,"maximum":65535},"height":{"type":"integer","minimum":0,"maximum":65535},"topExpansion":{"type":"integer","minimum":0,"maximum":65535},"leftExpansion":{"type":"integer","minimum":0,"maximum":65535}},"required":["width","height"],"additionalProperties":false}},"contextMenuClosed":{"surfaces":["candidate"]},"candidatePointerArmed":{"surfaces":["candidate"]},"candidatePointerMotion":{"surfaces":["candidate"]},"candidateFrameProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"revision":{"type":"integer"},"stage":{"type":"integer"},"performanceMs":{"type":"number","minimum":0}},"required":["revision","stage","performanceMs"],"additionalProperties":false}},"candidatePointerProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"probe":{"type":"integer"},"screenX":{"type":"integer"},"screenY":{"type":"integer"},"clientX":{"type":"integer"},"clientY":{"type":"integer"},"movementX":{"type":"integer"},"movementY":{"type":"integer"},"armed":{"type":"boolean"}},"required":["probe","screenX","screenY","clientX","clientY","movementX","movementY","armed"],"additionalProperties":false}},"contentTruncated":{"surfaces":["candidate","toolbar","menu"],"data":{"type":"object","properties":{"surface":{"type":"string","enum":["candidate","toolbar","menu","ftb","candwnd","cand"]},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0}},"required":["surface","width","height"],"additionalProperties":true}},"floatingToggle":{"surfaces":["menu"],"data":{"type":"boolean"}},"settings":{"surfaces":["menu"]},"about":{"surfaces":["menu"]},"emojiSymbols":{"surfaces":["menu"]},"keyboardPanel":{"surfaces":["menu"]},"handwritingPanel":{"surfaces":["menu"]},"voiceInput":{"surfaces":["menu"]},"ready":{"surfaces":["toolbar"]},"exitEnglishInputMode":{"surfaces":["toolbar"]},"changeCharacterSet":{"surfaces":["toolbar"]},"openSettings":{"surfaces":["toolbar"]},"openEmojiPanel":{"surfaces":["toolbar"]},"changeIMEMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["cn","en"]}},"changeCharMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["fullwidth","halfwidth"]}},"changePuncMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["puncEn","puncCn"]}}},"server":{"configSnapshot":{"data":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"windowState":{"data":{"type":"object","properties":{"isMaximized":{"type":"boolean"}},"required":["isMaximized"],"additionalProperties":false}},"maxButtonEvent":{"data":{"type":"object","properties":{"event":{"type":"string","enum":["enter","leave","down","up","click"]}},"required":["event"],"additionalProperties":false}},"apiCredentialTestResult":{"properties":{"requestId":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["requestId","ok","message"],"additionalProperties":false},"dictionaryResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"dictionary":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"code":{"type":"string"},"display":{"type":"string"},"weight":{"type":"integer"}},"required":["word"],"additionalProperties":false}},"content":{"type":"string"},"filename":{"type":"string"},"offset":{"type":"integer","minimum":0},"hasMore":{"type":"boolean"}},"required":["requestId","ok","message","rows"],"additionalProperties":true},"statsResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"overview":{"type":"object","properties":{"hasData":{"type":"boolean"},"firstDayKey":{"type":"integer"},"lastDayKey":{"type":"integer"},"days":{"type":"integer"},"totalChars":{"type":"integer"},"totalActiveMs":{"type":"integer"},"todayDayKey":{"type":"integer"},"todayChars":{"type":"integer"},"todayActiveMs":{"type":"integer"},"averagePerDay":{"type":"number"},"currentStreak":{"type":"integer"},"longestStreak":{"type":"integer"},"bestDayKey":{"type":"integer"},"bestDayChars":{"type":"integer"},"todaySpeed":{"type":"number"},"averageSpeed":{"type":"number"},"fastestSpeed":{"type":"number"},"fastestDayKey":{"type":"integer"},"categories":{"type":"object","properties":{"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"}},"required":["cjk","latin","digit","punct","other"],"additionalProperties":false},"todayHourly":{"type":"array","items":{"type":"integer"}},"daily":{"type":"array","items":{"type":"object","properties":{"dayKey":{"type":"integer"},"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"},"activeMs":{"type":"integer"}},"required":["dayKey","cjk","latin","digit","punct","other","activeMs"],"additionalProperties":false}}},"required":["hasData","firstDayKey","lastDayKey","days","totalChars","totalActiveMs","todayDayKey","todayChars","todayActiveMs","averagePerDay","currentStreak","longestStreak","bestDayKey","bestDayChars","todaySpeed","averageSpeed","fastestSpeed","fastestDayKey","categories","todayHourly","daily"],"additionalProperties":false},"removedDays":{"type":"integer"}},"required":["requestId","action","ok","message"],"additionalProperties":true}}}; root.MsimeMessageSchema = schema; if (typeof module !== 'undefined' && module.exports) module.exports = schema; })(globalThis); diff --git a/engine/helpcode/README.md b/engine/helpcode/README.md index e859a66d..cbf370ce 100644 --- a/engine/helpcode/README.md +++ b/engine/helpcode/README.md @@ -9,10 +9,12 @@ - `shouyou2_0`:`shouyou2_0_helpcode.txt`(首右2.0) - `shouyouplus`:`shouyouplus_helpcode.txt`(首右plus) - `xiaohe`:`xiaohe_helpcode.txt`(小鹤) -- `jiajia`:`jiajia_helpcode.txt`(加加) +- `jiajia`:`jiajia_helpcode.txt`(拼音加加) 公共数据位于 Engine 的 `helpcode/helpcodes/`,平台打包时复制需要的文件到应用资源目录。运行时路径由平台传给引擎,不依赖作者机器的绝对路径。合仓后的消费端无需再单独检出 HelpCode 仓库。 +`helpcodes\custom\` 是留给用户的目录:运行时放进去的每个 `.txt` 都是一套方案,方案名为 `custom/<文件名>`,由 `HelpcodeUtils::list_custom_helpcode_schemas` 扫描。文件开头的 `# name:` / `# name_en:` 注释给出中英文显示名,缺省用文件名。面向用户的格式说明见 [helpcodes/custom/README.md](./helpcodes/custom/README.md),它随安装包一起放进用户的数据目录。安装器升级时保留这个目录。 + ## 取码规则 各方案的取码规则不同,写文档时不要互相套用: diff --git a/engine/helpcode/helpcodes/custom/README.md b/engine/helpcode/helpcodes/custom/README.md new file mode 100644 index 00000000..6c4c3713 --- /dev/null +++ b/engine/helpcode/helpcodes/custom/README.md @@ -0,0 +1,26 @@ +# 自定义辅助码 + +把自己的辅助码方案放进本目录,在设置窗口「辅助码」页点「刷新方案」(或重新打开设置窗口)后,就能在双拼和全拼方案下拉框里选到它。修改正在使用的方案文件后,要切换一次方案或重启输入法才会生效。 +升级安装会保留本目录里的文件;卸载输入法时会随数据目录一起删除,请自行备份。 + +## 文件要求 + +- 扩展名必须是 `.txt`,编码必须是 UTF-8(带不带 BOM 都可以)。 +- 每行一个字:`字=辅助码`,辅助码取等号后的前两个小写字母(`a`–`z`),例如 `妈=nm`。 +- 以 `#` 开头的行是注释,会被忽略。 +- 方案在配置里记作 `custom/<文件名去掉 .txt>`,改文件名等于换了一个方案;当前选中的方案文件被删掉或改名后,会回退到蓝天小雨点。 + +## 方案名称 + +设置里默认按文件名显示。想换个名字,在文件开头加上注释: + +```text +# name: 我的辅助码 +# name_en: My Helpcode +阿=ek +锕=jk +``` + +- `name` 是中文名,设置页显示它;`name_en` 是英文名。 +- 只写其中一个时,另一个用同一个名字;两个都不写就用文件名。 +- 冒号用半角 `:` 或全角 `:` 都行。名称注释只在文件开头(第一个非注释行之前)生效。 diff --git a/engine/tests/src/test_input_session.cpp b/engine/tests/src/test_input_session.cpp index ae513c28..8167d440 100644 --- a/engine/tests/src/test_input_session.cpp +++ b/engine/tests/src/test_input_session.cpp @@ -1,4 +1,5 @@ #include "../../core/input_session.h" +#include "../../common/helpcode_utils.h" #include "../../core/data_path.h" #include "../../user_dictionary/user_dictionary_journal.h" #include "test_directory_cleanup.h" @@ -1033,6 +1034,38 @@ int run_test() require(!metasequoia::InputSession::is_supported_helpcode_schema("unknown") && !metasequoia::InputSession::select_helpcode_schema("unknown"), "An unknown helpcode schema was accepted."); + // User tables in helpcodes/custom: UTF-8 file names, a BOM, CRLF and a full-width colon in the header. + const std::filesystem::path custom_directory = data_directory / "helpcodes" / "custom"; + write_file(custom_directory / std::filesystem::u8path("我的码.txt"), + "\xEF\xBB\xBF# name\xEF\xBC\x9A 我的辅助码\n# name_en: Mine\n你=cb\n拟=ab\n好=ef\n"); + write_file(custom_directory / "plain.txt", "你=ab\n"); + write_file(custom_directory / "README.md", "# name: not a schema\n"); + const auto custom_schemas = HelpcodeUtils::list_custom_helpcode_schemas(data_directory); + require(custom_schemas.size() == 2 && custom_schemas[0].schema == "custom/plain" && + custom_schemas[0].name.empty() && custom_schemas[0].name_en.empty() && + custom_schemas[1].schema == "custom/我的码" && custom_schemas[1].name == "我的辅助码" && + custom_schemas[1].name_en == "Mine", + "Custom helpcode schemas were not discovered with their header names."); + require(HelpcodeUtils::is_helpcode_schema_available(data_directory, "custom/我的码") && + HelpcodeUtils::is_helpcode_schema_available(data_directory, "lantian") && + !HelpcodeUtils::is_helpcode_schema_available(data_directory, "custom/missing"), + "Custom helpcode availability did not follow the files on disk."); + for (const std::string schema : {"custom/", "custom/../helpcode", "custom/a\\b", "custom/.hidden"}) + { + require(!metasequoia::InputSession::is_supported_helpcode_schema(schema), + "A custom helpcode schema escaping its directory was accepted."); + } + const auto custom_keymap = HelpcodeUtils::load_helpcode_keymap(data_directory, "custom/我的码"); + require(custom_keymap->size() == 3 && custom_keymap->at("你") == "cb", + "A custom helpcode table was not loaded past its header and BOM."); + { + metasequoia::InputSession custom_helpcode(SchemeType::Quanpin); + custom_helpcode.set_quanpin_helpcode_enabled(true); + require(custom_helpcode.set_helpcode_schema("custom/我的码"), "A custom helpcode schema was not selected."); + type(custom_helpcode, "nihaoC"); + require(!custom_helpcode.candidates().empty() && custom_helpcode.candidates().front().word == "你好", + "A custom helpcode table did not drive candidate reordering."); + } metasequoia::InputSession quanpin_helpcode(SchemeType::Quanpin); quanpin_helpcode.set_quanpin_helpcode_enabled(true); diff --git a/installer/default_config/config.default.toml b/installer/default_config/config.default.toml index 14a08ed6..62c4d8eb 100644 --- a/installer/default_config/config.default.toml +++ b/installer/default_config/config.default.toml @@ -251,13 +251,15 @@ fuzzy_uan_uang = false [helpcode] # 双拼辅助码 shuangpin_helpcode = true -# 双拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia" +# 双拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia", +# 或 "custom/<文件名>"(数据目录 helpcodes\custom\<文件名>.txt 中的自定义方案) shuangpin_helpcode_schema = "lantian" # 是否在候选窗口中显示双拼辅助码 show_sp_helpcode_in_candidate_window = true # 全拼辅助码 quanpin_helpcode = true -# 全拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia" +# 全拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia", +# 或 "custom/<文件名>"(数据目录 helpcodes\custom\<文件名>.txt 中的自定义方案) quanpin_helpcode_schema = "ziranma" # 是否在候选窗口中显示全拼辅助码 show_qp_helpcode_in_candidate_window = false diff --git a/installer/msime_setup.iss b/installer/msime_setup.iss index 7382e2ae..03bcf199 100644 --- a/installer/msime_setup.iss +++ b/installer/msime_setup.iss @@ -77,6 +77,8 @@ Name: "{commonpf64}\metasequoiaime\server" ; 用户数据(词库、配置、皮肤、前端资源)。默认在 LocalAppData,安装时可以改到别的盘, ; 选择写进 HKLM 的 DataDir,Server / TSF DLL / 引擎三方都从那里读。 Name: "{code:GetDataDir}"; Permissions: users-modify +; 用户自己的辅助码方案放这里,Server 扫描后列进设置页,升级时保留。 +Name: "{code:GetDataDir}\helpcodes\custom" ; WebView2 子进程是中完整性,写不进内置 Administrator 的高完整性 LocalAppData。 Name: "{commonappdata}\metasequoiaime" Name: "{commonappdata}\metasequoiaime\webview2"; Permissions: users-modify @@ -899,6 +901,35 @@ begin DelTree(Path, True, True, True); end; +{ helpcodes 里的内置表由本次安装重写,custom 子目录是用户自己放的辅助码,升级不得清掉。} +procedure CleanHelpcodesExceptCustom(const HelpcodesPath: String); +var + FindRec: TFindRec; + ItemPath: String; +begin + if FindFirst(AddBackslash(HelpcodesPath) + '*', FindRec) then + begin + try + repeat + if + (FindRec.Name <> '.') and + (FindRec.Name <> '..') and + (CompareText(FindRec.Name, 'custom') <> 0) + then + begin + ItemPath := AddBackslash(HelpcodesPath) + FindRec.Name; + if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then + TryDeleteTree(ItemPath) + else + DeleteFile(ItemPath); + end; + until not FindNext(FindRec); + finally + FindClose(FindRec); + end; + end; +end; + procedure CleanAppDataExceptUserFiles; var AppDataPath: String; @@ -924,10 +955,12 @@ begin then begin ItemPath := AddBackslash(AppDataPath) + FindRec.Name; - if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then - TryDeleteTree(ItemPath) + if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then + DeleteFile(ItemPath) + else if CompareText(FindRec.Name, 'helpcodes') = 0 then + CleanHelpcodesExceptCustom(ItemPath) else - DeleteFile(ItemPath); + TryDeleteTree(ItemPath); end; until not FindNext(FindRec); finally @@ -1091,6 +1124,19 @@ begin ResultCode ) and (ResultCode < 8) and Moved; + { 自定义辅助码也是用户自己放进来的。} + if DirExists(AddBackslash(OldDir) + 'helpcodes\custom') then + Moved := + Exec( + ExpandConstant('{sys}\robocopy.exe'), + '"' + AddBackslash(OldDir) + 'helpcodes\custom" "' + AddBackslash(NewDir) + 'helpcodes\custom" ' + + '/E /MOVE /R:2 /W:1 /NJH /NJS /NP /NFL /NDL', + '', + SW_HIDE, + ewWaitUntilTerminated, + ResultCode + ) and (ResultCode < 8) and Moved; + if not Moved then begin Log('User data migration reported failures; leaving ' + OldDir + ' in place.'); diff --git a/server/assets/config/config.toml b/server/assets/config/config.toml index e60437e6..4ca621a5 100644 --- a/server/assets/config/config.toml +++ b/server/assets/config/config.toml @@ -251,13 +251,15 @@ fuzzy_uan_uang = false [helpcode] # 双拼辅助码 shuangpin_helpcode = true -# 双拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia" +# 双拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia", +# 或 "custom/<文件名>"(数据目录 helpcodes\custom\<文件名>.txt 中的自定义方案) shuangpin_helpcode_schema = "ziranma" # 是否在候选窗口中显示双拼辅助码 show_sp_helpcode_in_candidate_window = true # 全拼辅助码 quanpin_helpcode = true -# 全拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia" +# 全拼辅助码方案,可选:"lantian", "ziranma", "shouyou2_0", "shouyouplus", "xiaohe", "jiajia", +# 或 "custom/<文件名>"(数据目录 helpcodes\custom\<文件名>.txt 中的自定义方案) quanpin_helpcode_schema = "ziranma" # 是否在候选窗口中显示全拼辅助码 show_qp_helpcode_in_candidate_window = true diff --git a/server/src/config/ime_config.cpp b/server/src/config/ime_config.cpp index 642b41e1..96c7a64c 100644 --- a/server/src/config/ime_config.cpp +++ b/server/src/config/ime_config.cpp @@ -28,6 +28,7 @@ #include "defines/defines.h" #include "ipc/ipc.h" #include "engine/common/helpcode_utils.h" +#include "engine/core/data_path.h" #include "statistics/stats_store.h" #include "voice-input/voice_providers.h" @@ -46,6 +47,12 @@ constexpr int kEnglishMixedInputMinCharsMin = 1; constexpr int kEnglishMixedInputMinCharsMax = 8; constexpr int kEnglishMixedInputMinCharsDefault = 2; +// A custom schema whose file was removed falls back to the default instead of loading an empty table. +bool IsHelpcodeSchemaAvailable(const std::string &schema) +{ + return HelpcodeUtils::is_helpcode_schema_available(metasequoia::data_directory(), schema); +} + std::string g_session_backend = "legacy"; SchemeType g_input_scheme = SchemeType::Shuangpin; std::string g_input_mode = "chinese"; @@ -1012,13 +1019,12 @@ bool LoadImeConfig() g_quanpin_helpcode_enabled = tbl["helpcode"]["quanpin_helpcode"].value_or(true); const std::string shuangpin_helpcode_schema = tbl["helpcode"]["shuangpin_helpcode_schema"].value_or(std::string("lantian")); - g_shuangpin_helpcode_schema = HelpcodeUtils::is_supported_helpcode_schema(shuangpin_helpcode_schema) - ? shuangpin_helpcode_schema - : "lantian"; + g_shuangpin_helpcode_schema = + IsHelpcodeSchemaAvailable(shuangpin_helpcode_schema) ? shuangpin_helpcode_schema : "lantian"; const std::string quanpin_helpcode_schema = tbl["helpcode"]["quanpin_helpcode_schema"].value_or(std::string("lantian")); g_quanpin_helpcode_schema = - HelpcodeUtils::is_supported_helpcode_schema(quanpin_helpcode_schema) ? quanpin_helpcode_schema : "lantian"; + IsHelpcodeSchemaAvailable(quanpin_helpcode_schema) ? quanpin_helpcode_schema : "lantian"; g_show_shuangpin_helpcode_in_candidate_window = tbl["helpcode"]["show_sp_helpcode_in_candidate_window"].value_or(true); g_show_quanpin_helpcode_in_candidate_window = @@ -2335,7 +2341,7 @@ const std::string &GetConfiguredShuangpinHelpcodeSchema() bool SetConfiguredShuangpinHelpcodeSchema(const std::string &schema) { - if (!HelpcodeUtils::is_supported_helpcode_schema(schema)) + if (!IsHelpcodeSchemaAvailable(schema)) return false; if (!WriteConfiguredValue("helpcode", "shuangpin_helpcode_schema", EscapeTomlBasicString(schema))) return false; @@ -2469,7 +2475,7 @@ const std::string &GetConfiguredQuanpinHelpcodeSchema() bool SetConfiguredQuanpinHelpcodeSchema(const std::string &schema) { - if (!HelpcodeUtils::is_supported_helpcode_schema(schema)) + if (!IsHelpcodeSchemaAvailable(schema)) return false; if (!WriteConfiguredValue("helpcode", "quanpin_helpcode_schema", EscapeTomlBasicString(schema))) return false; @@ -2477,6 +2483,24 @@ bool SetConfiguredQuanpinHelpcodeSchema(const std::string &schema) return true; } +std::vector GetCustomHelpcodeSchemas() +{ + std::vector result; + for (const auto &schema : HelpcodeUtils::list_custom_helpcode_schemas(metasequoia::data_directory())) + { + const std::string &name = schema.name.empty() ? schema.name_en : schema.name; + const std::string &name_en = schema.name_en.empty() ? schema.name : schema.name_en; + result.push_back( + {schema.schema, name.empty() ? schema.file_stem : name, name_en.empty() ? schema.file_stem : name_en}); + } + return result; +} + +std::string GetCustomHelpcodeDirectory() +{ + return metasequoia::path_to_utf8(HelpcodeUtils::custom_helpcode_directory(metasequoia::data_directory())); +} + bool GetConfiguredShowShuangpinHelpcodeInCandidateWindow() { return g_show_shuangpin_helpcode_in_candidate_window; diff --git a/server/src/config/ime_config.h b/server/src/config/ime_config.h index 81705d45..8a736f10 100644 --- a/server/src/config/ime_config.h +++ b/server/src/config/ime_config.h @@ -242,6 +242,17 @@ metasequoia::FuzzyPinyinOptions GetConfiguredFuzzyPinyinRuleStates(); bool SetConfiguredFuzzyPinyinRule(const std::string &key, bool enabled); const std::string &GetConfiguredQuanpinHelpcodeSchema(); bool SetConfiguredQuanpinHelpcodeSchema(const std::string &schema); +// User helpcode tables under /helpcodes/custom, rescanned on every call so a file dropped +// there shows up the next time the settings page asks. Names fall back to the file stem. +struct CustomHelpcodeSchemaInfo +{ + std::string schema; + std::string name; + std::string name_en; +}; +std::vector GetCustomHelpcodeSchemas(); +// UTF-8 path of the folder GetCustomHelpcodeSchemas scans. +std::string GetCustomHelpcodeDirectory(); bool GetConfiguredShowShuangpinHelpcodeInCandidateWindow(); bool SetConfiguredShowShuangpinHelpcodeInCandidateWindow(bool enabled); bool GetConfiguredShowQuanpinHelpcodeInCandidateWindow(); diff --git a/server/src/settings/settings_app.cpp b/server/src/settings/settings_app.cpp index 85b9c11a..0994e2bd 100644 --- a/server/src/settings/settings_app.cpp +++ b/server/src/settings/settings_app.cpp @@ -1,5 +1,6 @@ #include "engine/contracts/webview/validator.h" #include "config/ime_config.h" +#include "engine/core/data_path.h" #include "global/globals.h" #include "resource/resource.h" #include "settings/settings_launcher.h" @@ -257,6 +258,14 @@ void ResetTitlebarHoverAfterVisibilityChange() nullptr); } +static nlohmann::json CustomHelpcodeSchemasJson() +{ + nlohmann::json schemas = nlohmann::json::array(); + for (const auto &schema : GetCustomHelpcodeSchemas()) + schemas.push_back({{"id", schema.schema}, {"name", schema.name}, {"name_en", schema.name_en}}); + return schemas; +} + std::wstring BuildConfigMessage(bool refresh_skin_catalog) { @@ -511,6 +520,8 @@ std::wstring BuildConfigMessage(bool refresh_skin_catalog) {"statistics", {{"enabled", GetConfiguredStatisticsEnabled()}, {"retention", GetConfiguredStatisticsRetention()}}}}}}; payload["data"]["voice_input"]["polish_presets"] = std::move(polish_presets); + payload["data"]["helpcode"]["custom_schemas"] = CustomHelpcodeSchemasJson(); + payload["data"]["helpcode"]["custom_directory"] = GetCustomHelpcodeDirectory(); payload["protocolVersion"] = metasequoia::webview::Version; const std::string serialized = payload.dump(); if (!metasequoia::webview::Validate(json::parse(serialized), "server")) @@ -1132,6 +1143,14 @@ void HandleWebMessage(HWND hwnd, ICoreWebView2WebMessageReceivedEventArgs *args) if (!ec) ShellExecuteW(hwnd, L"open", skins.c_str(), nullptr, nullptr, SW_SHOWNORMAL); } + else if (type == "openHelpcodeDirectory") + { + const std::filesystem::path directory = metasequoia::path_from_utf8(GetCustomHelpcodeDirectory().c_str()); + std::error_code ec; + std::filesystem::create_directories(directory, ec); + if (!ec) + ShellExecuteW(hwnd, L"open", directory.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } else if (type == "configUpdate") { const auto data = value.at("data").as_object(); diff --git a/server/src/webview2/windows_webview2.cpp b/server/src/webview2/windows_webview2.cpp index 9d2b0f7a..9b9c5486 100644 --- a/server/src/webview2/windows_webview2.cpp +++ b/server/src/webview2/windows_webview2.cpp @@ -4,6 +4,7 @@ #include "webview2/candidate_window_template.h" #include "webview2/skin_css_policy.h" #include "config/ime_config.h" +#include "engine/core/data_path.h" #include "defines/globals.h" #include "utils/common_utils.h" #include "utils/ime_utils.h" @@ -3760,6 +3761,15 @@ HRESULT OnControllerCreatedSettingsWnd( // { PostSettingsConfig(); } + else if (type == "openHelpcodeDirectory") + { + const std::filesystem::path directory = + metasequoia::path_from_utf8(GetCustomHelpcodeDirectory().c_str()); + std::error_code ec; + std::filesystem::create_directories(directory, ec); + if (!ec) + ShellExecuteW(hwnd, L"open", directory.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } else if (type == "configUpdate") { try @@ -4664,6 +4674,14 @@ void PostSettingsWindowState(HWND hwnd) ::webviewSettingsWnd->PostWebMessageAsJson(message.c_str()); } +static nlohmann::json CustomHelpcodeSchemasJson() +{ + nlohmann::json schemas = nlohmann::json::array(); + for (const auto &schema : GetCustomHelpcodeSchemas()) + schemas.push_back({{"id", schema.schema}, {"name", schema.name}, {"name_en", schema.name_en}}); + return schemas; +} + void PostSettingsConfig() { if (!::webviewSettingsWnd) @@ -4793,6 +4811,8 @@ void PostSettingsConfig() {"show_qp_helpcode_in_candidate_window", GetConfiguredShowQuanpinHelpcodeInCandidateWindow()}}}, {"statistics", {{"enabled", GetConfiguredStatisticsEnabled()}, {"retention", GetConfiguredStatisticsRetention()}}}}}}; + payload["data"]["helpcode"]["custom_schemas"] = CustomHelpcodeSchemasJson(); + payload["data"]["helpcode"]["custom_directory"] = GetCustomHelpcodeDirectory(); const std::wstring message = string_to_wstring(payload.dump()); ::webviewSettingsWnd->PostWebMessageAsJson(message.c_str()); } diff --git a/ui-html/webview2/settings/ime-settings/src/modules/config-sync.ts b/ui-html/webview2/settings/ime-settings/src/modules/config-sync.ts index 6fc18140..6578fec5 100644 --- a/ui-html/webview2/settings/ime-settings/src/modules/config-sync.ts +++ b/ui-html/webview2/settings/ime-settings/src/modules/config-sync.ts @@ -265,6 +265,28 @@ function applyConfigData(data: Record, target?: string): void { data.helpcode.show_sp_helpcode_in_candidate_window ); } + // 自定义辅助码由 Server 扫描 helpcodes/custom 得到,追加在内置方案之后;先建好菜单项再回填选中值。 + if (Array.isArray(data?.helpcode?.custom_schemas)) { + for (const menuId of ['shuangpinHelpcodeSchemeMenu', 'quanpinHelpcodeSchemeMenu']) { + const menu = findElement(menuId); + if (!menu) continue; + menu.querySelectorAll('.dropdown-item[data-custom]').forEach((item) => item.remove()); + for (const schema of data.helpcode.custom_schemas) { + if (typeof schema?.id !== 'string' || typeof schema?.name !== 'string') continue; + const item = document.createElement('div'); + item.className = 'dropdown-item'; + item.dataset.value = schema.id; + item.dataset.custom = 'true'; + item.textContent = schema.name; + if (typeof schema.name_en === 'string' && schema.name_en !== schema.name) item.title = schema.name_en; + menu.appendChild(item); + } + } + } + if (typeof data?.helpcode?.custom_directory === 'string') { + const directory = findElement('customHelpcodeDirectory'); + if (directory) directory.textContent = `文件夹:${data.helpcode.custom_directory}`; + } if (typeof data?.helpcode?.shuangpin_helpcode === 'boolean') { applyToggleState('shuangpinHelpcodeToggleBtn', data.helpcode.shuangpin_helpcode); } diff --git a/ui-html/webview2/settings/ime-settings/src/modules/helpcode.ts b/ui-html/webview2/settings/ime-settings/src/modules/helpcode.ts index b2b42b2c..705ff47a 100644 --- a/ui-html/webview2/settings/ime-settings/src/modules/helpcode.ts +++ b/ui-html/webview2/settings/ime-settings/src/modules/helpcode.ts @@ -1,6 +1,47 @@ import { setupDropdownMenu, setupToggleButton } from './shared'; import { updateConfig } from './config-sync'; import { updateCandidatePreviewHelpcode } from './appearance'; +import { serializeHostMessage } from '../../../../shared/messages'; +import { onHostMessage } from '../utils/host-messages'; +import { hoistOverlay } from '../utils/overlay-host'; + +const REFRESH_TIMEOUT_MS = 5000; +let toastTimer: number | null = null; +let pendingRefresh: (() => void) | null = null; + +function showToast(message: string, ok: boolean, durationMs = 3200): void { + const toast = document.getElementById('helpcodeToast'); + if (!toast) return; + document.getElementById('helpcodeToastMessage')!.textContent = message; + document.getElementById('helpcodeToastIcon')!.textContent = ok ? '' : '!'; + toast.className = `dict-toast visible ${ok ? 'success' : 'error'}`; + if (toastTimer !== null) window.clearTimeout(toastTimer); + toastTimer = window.setTimeout(() => { toast.classList.remove('visible'); toastTimer = null; }, durationMs); +} + +// 等宿主推回重新扫描后的快照再提示;超时没回来就报失败。重复点击只保留最后一次。 +function refreshCustomHelpcodes(): void { + const webview = window.chrome?.webview; + if (!webview) return; + pendingRefresh?.(); + const timer = window.setTimeout(() => { + finish(); + showToast('刷新失败,请稍后重试', false); + }, REFRESH_TIMEOUT_MS); + const unsubscribe = onHostMessage('configSnapshot', (message) => { + finish(); + const schemas = (message.data as Record | undefined)?.helpcode?.custom_schemas; + const count = Array.isArray(schemas) ? schemas.length : 0; + showToast(count > 0 ? `刷新成功,找到 ${count} 个自定义方案` : '刷新成功,文件夹里还没有自定义方案', true); + }); + function finish(): void { + window.clearTimeout(timer); + unsubscribe(); + pendingRefresh = null; + } + pendingRefresh = finish; + webview.postMessage(serializeHostMessage({ type: 'configRequest' })); +} export function setupHelpcode(): void { // 双拼辅助码方案 @@ -29,6 +70,17 @@ export function setupHelpcode(): void { updateCandidatePreviewHelpcode({ show_sp_helpcode_in_candidate_window: active }); }); + // 打开自定义辅助码所在文件夹,由宿主负责创建并用资源管理器打开 + document.getElementById('openCustomHelpcodeDirectory')?.addEventListener('click', () => { + window.chrome?.webview?.postMessage(serializeHostMessage({ type: 'openHelpcodeDirectory' })); + }); + // 重新请求配置快照:宿主会重新扫描 custom 文件夹,下拉框随快照重建 + document.getElementById('refreshCustomHelpcodes')?.addEventListener('click', refreshCustomHelpcodes); + hoistOverlay(document.getElementById('helpcodeToast')); + document.getElementById('helpcodeToastClose')?.addEventListener('click', () => { + document.getElementById('helpcodeToast')?.classList.remove('visible'); + }); + // 是否在候选窗口中显示全拼辅助码 setupToggleButton('showQuanpinHelpcodeToggleBtn', (active) => { updateConfig('helpcode.show_qp_helpcode_in_candidate_window', active); diff --git a/ui-html/webview2/settings/ime-settings/src/partials/help-settings.html b/ui-html/webview2/settings/ime-settings/src/partials/help-settings.html index f97132d6..d75c23f2 100644 --- a/ui-html/webview2/settings/ime-settings/src/partials/help-settings.html +++ b/ui-html/webview2/settings/ime-settings/src/partials/help-settings.html @@ -11,7 +11,7 @@
基本功能
-

支持全拼、双拼和五笔。可以在设置窗口下的输入功能分区进行切换。全拼和双拼均支持辅助码。辅助码方案目前支持自然码辅助码、蓝天小雨点、首右2.0、首右plus和小鹤。可以在设置的辅助码分区进行辅助码的开关和切换。打字过程中,全拼使用辅助码需要用大写的字母唤起单码和双码辅助。双拼在开启辅助码的状态下默认使用单码辅助,第二个码是大写字母的情况下,则会使用完整双码进行筛选。

+

支持全拼、双拼和五笔。可以在设置窗口下的输入功能分区进行切换。全拼和双拼均支持辅助码。辅助码方案目前支持自然码辅助码、蓝天小雨点、首右2.0、首右plus、小鹤和拼音加加,也可以把自己的辅助码文件放进数据目录下的 helpcodes\custom 文件夹导入。可以在设置的辅助码分区进行辅助码的开关和切换。打字过程中,全拼使用辅助码需要用大写的字母唤起单码和双码辅助。双拼在开启辅助码的状态下默认使用单码辅助,第二个码是大写字母的情况下,则会使用完整双码进行筛选。

语音识别和 AI 联想需要自行填入 API 和 token。内测期间,将默认使用开发者个人的 api 和 token,仅供测试使用。云联想目前支持谷歌的云接口,请用户注意网络问题。

更多功能欢迎自由探索~

diff --git a/ui-html/webview2/settings/ime-settings/src/partials/helpcode.html b/ui-html/webview2/settings/ime-settings/src/partials/helpcode.html index 361aa600..9879e59e 100644 --- a/ui-html/webview2/settings/ime-settings/src/partials/helpcode.html +++ b/ui-html/webview2/settings/ime-settings/src/partials/helpcode.html @@ -23,7 +23,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -70,4 +70,28 @@ - + +
+
+
+
自定义辅助码
+
+ 把 UTF-8 编码的 .txt 辅助码文件放进下面的文件夹,点击“刷新方案”后即可在上方方案中选择。 + 每行写作 字=辅助码;设置中默认按文件名显示,可在文件开头用 + # name: 中文名 和 # name_en: English name 注释指定方案名称。修改正在使用的方案文件后,需要切换一次方案或重启输入法才会生效。 +
+
+
+
+ + +
+
+
+ +
+ +
+ +
+ \ No newline at end of file diff --git a/ui-html/webview2/settings/ime-settings/src/styles/modules/helpcode.css b/ui-html/webview2/settings/ime-settings/src/styles/modules/helpcode.css index 09158420..87b96288 100644 --- a/ui-html/webview2/settings/ime-settings/src/styles/modules/helpcode.css +++ b/ui-html/webview2/settings/ime-settings/src/styles/modules/helpcode.css @@ -24,4 +24,68 @@ .dropdown-menu-quanpin-helpcode-scheme { min-width: 110px; -} \ No newline at end of file +} + +/* 自定义方案追加在内置方案之后,方案多了就滚动 */ +.dropdown-menu-shuangpin-helpcode-scheme, +.dropdown-menu-quanpin-helpcode-scheme { + max-height: 320px; + overflow-y: auto; +} + +.dropdown-item:not([data-custom]) + .dropdown-item[data-custom] { + margin-top: 6px; + position: relative; +} + +.dropdown-item:not([data-custom]) + .dropdown-item[data-custom]::before { + content: ''; + position: absolute; + left: 6px; + right: 6px; + top: -4px; + border-top: 1px solid var(--dropdown-menu-border); +} + +/* 辅助码 -> 自定义辅助码 */ +.helpcode-description { + margin-top: 6px; + color: var(--text-muted); + font-size: 12px; + line-height: 1.5; +} + +.helpcode-custom-directory { + user-select: text; + word-break: break-all; +} + +.section-header:has(> .helpcode-custom-actions) { + align-items: flex-start; + gap: 16px; +} + +.helpcode-custom-actions { + display: flex; + flex: 0 0 auto; + gap: 8px; +} + +.helpcode-custom-button { + height: 30px; + margin-top: 2px; + padding: 0 10px; + color: var(--text-muted); + background: transparent; + border: 1px solid var(--border-color); + border-radius: 6px; + font: inherit; + font-size: 11px; + line-height: 1; + cursor: pointer; +} + +.helpcode-custom-button:hover { + color: var(--text-color); + background: var(--button-secondary-bg); +} diff --git a/ui-html/webview2/shared/messages.ts b/ui-html/webview2/shared/messages.ts index 474769a7..c7e2b7e9 100644 --- a/ui-html/webview2/shared/messages.ts +++ b/ui-html/webview2/shared/messages.ts @@ -5,6 +5,7 @@ export type ClientMessage = | { "type": "configRequest"; "protocolVersion"?: 1 } | { "type": "skinCatalogRequest"; "protocolVersion"?: 1 } | { "type": "openSkinDirectory"; "protocolVersion"?: 1 } + | { "type": "openHelpcodeDirectory"; "protocolVersion"?: 1 } | { "type": "openHandwritingPanel"; "protocolVersion"?: 1 } | { "type": "restartServer"; "protocolVersion"?: 1 } | { "type": "copyText"; "protocolVersion"?: 1; "data": string } @@ -59,6 +60,7 @@ export type SettingsMessage = | { "type": "configRequest"; "protocolVersion"?: 1 } | { "type": "skinCatalogRequest"; "protocolVersion"?: 1 } | { "type": "openSkinDirectory"; "protocolVersion"?: 1 } + | { "type": "openHelpcodeDirectory"; "protocolVersion"?: 1 } | { "type": "openHandwritingPanel"; "protocolVersion"?: 1 } | { "type": "restartServer"; "protocolVersion"?: 1 } | { "type": "copyText"; "protocolVersion"?: 1; "data": string } diff --git a/ui-html/webview2/shared/schema.js b/ui-html/webview2/shared/schema.js index e39c8897..4eb28495 100644 --- a/ui-html/webview2/shared/schema.js +++ b/ui-html/webview2/shared/schema.js @@ -1,6 +1,6 @@ // Generated by generate.py; edit messages.json. (function(root) { - const schema = {"version":1,"client":{"configRequest":{"surfaces":["settings"]},"skinCatalogRequest":{"surfaces":["settings"]},"openSkinDirectory":{"surfaces":["settings"]},"openHandwritingPanel":{"surfaces":["settings"]},"restartServer":{"surfaces":["settings"]},"copyText":{"surfaces":["settings"],"data":{"type":"string"}},"openExternalUrl":{"surfaces":["settings"],"data":{"type":"string"}},"dragStart":{"surfaces":["settings","toolbar"]},"focus":{"surfaces":["settings"]},"resizeStart":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"resizeHitTest":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"windowControl":{"surfaces":["settings"],"data":{"type":"string","enum":["minimize","maximize","restore","close"]}},"maximizeButtonRect":{"surfaces":["settings"],"data":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"dpr":{"type":"number","minimum":0.01}},"required":["x","y","width","height","dpr"],"additionalProperties":false}},"configUpdate":{"surfaces":["settings"],"data":{"type":"object","properties":{"path":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},"required":["path","value"],"additionalProperties":false}},"apiCredentialTest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"service":{"type":"string","enum":["translation.tencent","translation.niutrans","translation.custom","voice.asr","voice.polish","ai.assistant"]},"config":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"required":["requestId","service","config"],"additionalProperties":false}},"dictionaryRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"dictionary":{"type":"string","enum":["quanpin","wubi","english","quick"]},"action":{"type":"string","enum":["query","create","update","delete","import","importHans","export"]},"word":{"type":"string"},"code":{"type":"string"},"content":{"type":"string"},"weight":{"type":"integer"},"oldWord":{"type":"string"},"oldCode":{"type":"string"},"display":{"type":"string"},"oldDisplay":{"type":"string"},"offset":{"type":"integer","minimum":0,"maximum":2147483647},"limit":{"type":"integer","minimum":1,"maximum":200}},"required":["requestId","dictionary","action"],"additionalProperties":false}},"statsRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"action":{"type":"string","enum":["overview","clearAll","openDirectory"]}},"required":["requestId","action"],"additionalProperties":false}},"openKeyboardPanel":{"surfaces":["settings","toolbar"]},"candidate":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"delete":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"pin":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"clearPosition":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"candidateWheel":{"surfaces":["candidate"],"data":{"type":"integer","minimum":-32768,"maximum":32767}},"fixPosition":{"surfaces":["candidate"],"data":{"type":"object","properties":{"index":{"type":"integer","minimum":1,"maximum":10},"position":{"type":"integer","minimum":1,"maximum":5}},"required":["index","position"],"additionalProperties":false}},"contextMenuResize":{"surfaces":["candidate"],"data":{"type":"object","properties":{"width":{"type":"integer","minimum":0,"maximum":65535},"height":{"type":"integer","minimum":0,"maximum":65535},"topExpansion":{"type":"integer","minimum":0,"maximum":65535},"leftExpansion":{"type":"integer","minimum":0,"maximum":65535}},"required":["width","height"],"additionalProperties":false}},"contextMenuClosed":{"surfaces":["candidate"]},"candidatePointerArmed":{"surfaces":["candidate"]},"candidatePointerMotion":{"surfaces":["candidate"]},"candidateFrameProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"revision":{"type":"integer"},"stage":{"type":"integer"},"performanceMs":{"type":"number","minimum":0}},"required":["revision","stage","performanceMs"],"additionalProperties":false}},"candidatePointerProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"probe":{"type":"integer"},"screenX":{"type":"integer"},"screenY":{"type":"integer"},"clientX":{"type":"integer"},"clientY":{"type":"integer"},"movementX":{"type":"integer"},"movementY":{"type":"integer"},"armed":{"type":"boolean"}},"required":["probe","screenX","screenY","clientX","clientY","movementX","movementY","armed"],"additionalProperties":false}},"contentTruncated":{"surfaces":["candidate","toolbar","menu"],"data":{"type":"object","properties":{"surface":{"type":"string","enum":["candidate","toolbar","menu","ftb","candwnd","cand"]},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0}},"required":["surface","width","height"],"additionalProperties":true}},"floatingToggle":{"surfaces":["menu"],"data":{"type":"boolean"}},"settings":{"surfaces":["menu"]},"about":{"surfaces":["menu"]},"emojiSymbols":{"surfaces":["menu"]},"keyboardPanel":{"surfaces":["menu"]},"handwritingPanel":{"surfaces":["menu"]},"voiceInput":{"surfaces":["menu"]},"ready":{"surfaces":["toolbar"]},"exitEnglishInputMode":{"surfaces":["toolbar"]},"changeCharacterSet":{"surfaces":["toolbar"]},"openSettings":{"surfaces":["toolbar"]},"openEmojiPanel":{"surfaces":["toolbar"]},"changeIMEMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["cn","en"]}},"changeCharMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["fullwidth","halfwidth"]}},"changePuncMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["puncEn","puncCn"]}}},"server":{"configSnapshot":{"data":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"windowState":{"data":{"type":"object","properties":{"isMaximized":{"type":"boolean"}},"required":["isMaximized"],"additionalProperties":false}},"maxButtonEvent":{"data":{"type":"object","properties":{"event":{"type":"string","enum":["enter","leave","down","up","click"]}},"required":["event"],"additionalProperties":false}},"apiCredentialTestResult":{"properties":{"requestId":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["requestId","ok","message"],"additionalProperties":false},"dictionaryResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"dictionary":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"code":{"type":"string"},"display":{"type":"string"},"weight":{"type":"integer"}},"required":["word"],"additionalProperties":false}},"content":{"type":"string"},"filename":{"type":"string"},"offset":{"type":"integer","minimum":0},"hasMore":{"type":"boolean"}},"required":["requestId","ok","message","rows"],"additionalProperties":true},"statsResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"overview":{"type":"object","properties":{"hasData":{"type":"boolean"},"firstDayKey":{"type":"integer"},"lastDayKey":{"type":"integer"},"days":{"type":"integer"},"totalChars":{"type":"integer"},"totalActiveMs":{"type":"integer"},"todayDayKey":{"type":"integer"},"todayChars":{"type":"integer"},"todayActiveMs":{"type":"integer"},"averagePerDay":{"type":"number"},"currentStreak":{"type":"integer"},"longestStreak":{"type":"integer"},"bestDayKey":{"type":"integer"},"bestDayChars":{"type":"integer"},"todaySpeed":{"type":"number"},"averageSpeed":{"type":"number"},"fastestSpeed":{"type":"number"},"fastestDayKey":{"type":"integer"},"categories":{"type":"object","properties":{"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"}},"required":["cjk","latin","digit","punct","other"],"additionalProperties":false},"todayHourly":{"type":"array","items":{"type":"integer"}},"daily":{"type":"array","items":{"type":"object","properties":{"dayKey":{"type":"integer"},"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"},"activeMs":{"type":"integer"}},"required":["dayKey","cjk","latin","digit","punct","other","activeMs"],"additionalProperties":false}}},"required":["hasData","firstDayKey","lastDayKey","days","totalChars","totalActiveMs","todayDayKey","todayChars","todayActiveMs","averagePerDay","currentStreak","longestStreak","bestDayKey","bestDayChars","todaySpeed","averageSpeed","fastestSpeed","fastestDayKey","categories","todayHourly","daily"],"additionalProperties":false},"removedDays":{"type":"integer"}},"required":["requestId","action","ok","message"],"additionalProperties":true}}}; + const schema = {"version":1,"client":{"configRequest":{"surfaces":["settings"]},"skinCatalogRequest":{"surfaces":["settings"]},"openSkinDirectory":{"surfaces":["settings"]},"openHelpcodeDirectory":{"surfaces":["settings"]},"openHandwritingPanel":{"surfaces":["settings"]},"restartServer":{"surfaces":["settings"]},"copyText":{"surfaces":["settings"],"data":{"type":"string"}},"openExternalUrl":{"surfaces":["settings"],"data":{"type":"string"}},"dragStart":{"surfaces":["settings","toolbar"]},"focus":{"surfaces":["settings"]},"resizeStart":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"resizeHitTest":{"surfaces":["settings"],"data":{"type":"string","enum":["left","right","top","bottom","left-top","right-top","left-bottom","right-bottom","client"]}},"windowControl":{"surfaces":["settings"],"data":{"type":"string","enum":["minimize","maximize","restore","close"]}},"maximizeButtonRect":{"surfaces":["settings"],"data":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"dpr":{"type":"number","minimum":0.01}},"required":["x","y","width","height","dpr"],"additionalProperties":false}},"configUpdate":{"surfaces":["settings"],"data":{"type":"object","properties":{"path":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},"required":["path","value"],"additionalProperties":false}},"apiCredentialTest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"service":{"type":"string","enum":["translation.tencent","translation.niutrans","translation.custom","voice.asr","voice.polish","ai.assistant"]},"config":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"required":["requestId","service","config"],"additionalProperties":false}},"dictionaryRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"dictionary":{"type":"string","enum":["quanpin","wubi","english","quick"]},"action":{"type":"string","enum":["query","create","update","delete","import","importHans","export"]},"word":{"type":"string"},"code":{"type":"string"},"content":{"type":"string"},"weight":{"type":"integer"},"oldWord":{"type":"string"},"oldCode":{"type":"string"},"display":{"type":"string"},"oldDisplay":{"type":"string"},"offset":{"type":"integer","minimum":0,"maximum":2147483647},"limit":{"type":"integer","minimum":1,"maximum":200}},"required":["requestId","dictionary","action"],"additionalProperties":false}},"statsRequest":{"surfaces":["settings"],"data":{"type":"object","properties":{"requestId":{"type":"string"},"action":{"type":"string","enum":["overview","clearAll","openDirectory"]}},"required":["requestId","action"],"additionalProperties":false}},"openKeyboardPanel":{"surfaces":["settings","toolbar"]},"candidate":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"delete":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"pin":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"clearPosition":{"surfaces":["candidate"],"data":{"type":"integer","minimum":1,"maximum":10}},"candidateWheel":{"surfaces":["candidate"],"data":{"type":"integer","minimum":-32768,"maximum":32767}},"fixPosition":{"surfaces":["candidate"],"data":{"type":"object","properties":{"index":{"type":"integer","minimum":1,"maximum":10},"position":{"type":"integer","minimum":1,"maximum":5}},"required":["index","position"],"additionalProperties":false}},"contextMenuResize":{"surfaces":["candidate"],"data":{"type":"object","properties":{"width":{"type":"integer","minimum":0,"maximum":65535},"height":{"type":"integer","minimum":0,"maximum":65535},"topExpansion":{"type":"integer","minimum":0,"maximum":65535},"leftExpansion":{"type":"integer","minimum":0,"maximum":65535}},"required":["width","height"],"additionalProperties":false}},"contextMenuClosed":{"surfaces":["candidate"]},"candidatePointerArmed":{"surfaces":["candidate"]},"candidatePointerMotion":{"surfaces":["candidate"]},"candidateFrameProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"revision":{"type":"integer"},"stage":{"type":"integer"},"performanceMs":{"type":"number","minimum":0}},"required":["revision","stage","performanceMs"],"additionalProperties":false}},"candidatePointerProbe":{"surfaces":["candidate"],"data":{"type":"object","properties":{"probe":{"type":"integer"},"screenX":{"type":"integer"},"screenY":{"type":"integer"},"clientX":{"type":"integer"},"clientY":{"type":"integer"},"movementX":{"type":"integer"},"movementY":{"type":"integer"},"armed":{"type":"boolean"}},"required":["probe","screenX","screenY","clientX","clientY","movementX","movementY","armed"],"additionalProperties":false}},"contentTruncated":{"surfaces":["candidate","toolbar","menu"],"data":{"type":"object","properties":{"surface":{"type":"string","enum":["candidate","toolbar","menu","ftb","candwnd","cand"]},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0}},"required":["surface","width","height"],"additionalProperties":true}},"floatingToggle":{"surfaces":["menu"],"data":{"type":"boolean"}},"settings":{"surfaces":["menu"]},"about":{"surfaces":["menu"]},"emojiSymbols":{"surfaces":["menu"]},"keyboardPanel":{"surfaces":["menu"]},"handwritingPanel":{"surfaces":["menu"]},"voiceInput":{"surfaces":["menu"]},"ready":{"surfaces":["toolbar"]},"exitEnglishInputMode":{"surfaces":["toolbar"]},"changeCharacterSet":{"surfaces":["toolbar"]},"openSettings":{"surfaces":["toolbar"]},"openEmojiPanel":{"surfaces":["toolbar"]},"changeIMEMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["cn","en"]}},"changeCharMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["fullwidth","halfwidth"]}},"changePuncMode":{"surfaces":["toolbar"],"data":{"type":"string","enum":["puncEn","puncCn"]}}},"server":{"configSnapshot":{"data":{"type":"object","properties":{},"required":[],"additionalProperties":true}},"windowState":{"data":{"type":"object","properties":{"isMaximized":{"type":"boolean"}},"required":["isMaximized"],"additionalProperties":false}},"maxButtonEvent":{"data":{"type":"object","properties":{"event":{"type":"string","enum":["enter","leave","down","up","click"]}},"required":["event"],"additionalProperties":false}},"apiCredentialTestResult":{"properties":{"requestId":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["requestId","ok","message"],"additionalProperties":false},"dictionaryResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"dictionary":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"code":{"type":"string"},"display":{"type":"string"},"weight":{"type":"integer"}},"required":["word"],"additionalProperties":false}},"content":{"type":"string"},"filename":{"type":"string"},"offset":{"type":"integer","minimum":0},"hasMore":{"type":"boolean"}},"required":["requestId","ok","message","rows"],"additionalProperties":true},"statsResponse":{"properties":{"requestId":{"type":"string"},"action":{"type":"string"},"ok":{"type":"boolean"},"message":{"type":"string"},"overview":{"type":"object","properties":{"hasData":{"type":"boolean"},"firstDayKey":{"type":"integer"},"lastDayKey":{"type":"integer"},"days":{"type":"integer"},"totalChars":{"type":"integer"},"totalActiveMs":{"type":"integer"},"todayDayKey":{"type":"integer"},"todayChars":{"type":"integer"},"todayActiveMs":{"type":"integer"},"averagePerDay":{"type":"number"},"currentStreak":{"type":"integer"},"longestStreak":{"type":"integer"},"bestDayKey":{"type":"integer"},"bestDayChars":{"type":"integer"},"todaySpeed":{"type":"number"},"averageSpeed":{"type":"number"},"fastestSpeed":{"type":"number"},"fastestDayKey":{"type":"integer"},"categories":{"type":"object","properties":{"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"}},"required":["cjk","latin","digit","punct","other"],"additionalProperties":false},"todayHourly":{"type":"array","items":{"type":"integer"}},"daily":{"type":"array","items":{"type":"object","properties":{"dayKey":{"type":"integer"},"cjk":{"type":"integer"},"latin":{"type":"integer"},"digit":{"type":"integer"},"punct":{"type":"integer"},"other":{"type":"integer"},"activeMs":{"type":"integer"}},"required":["dayKey","cjk","latin","digit","punct","other","activeMs"],"additionalProperties":false}}},"required":["hasData","firstDayKey","lastDayKey","days","totalChars","totalActiveMs","todayDayKey","todayChars","todayActiveMs","averagePerDay","currentStreak","longestStreak","bestDayKey","bestDayChars","todaySpeed","averageSpeed","fastestSpeed","fastestDayKey","categories","todayHourly","daily"],"additionalProperties":false},"removedDays":{"type":"integer"}},"required":["requestId","action","ok","message"],"additionalProperties":true}}}; root.MsimeMessageSchema = schema; if (typeof module !== 'undefined' && module.exports) module.exports = schema; })(globalThis);