# 插件作者指南 插件是只含数据的文件夹:一份 `plugin.toml` 清单,加上清单用到的音频或数据文件和可选的说明文字。插件不能带任何可执行内容,也不能申请权限;输入法按固定规则校验每个包,不符合规则的包整包拒绝,不会只装一半。 这份指南覆盖八种类型:音效包(`sound`)、音乐包(`music`)、指令表(`command_table`)、特效包(`effect`)、短语表(`phrase_table`)、辅助码表(`helpcode`)、单词本(`wordbook`)和符号集(`symbol_set`)。下面的限制都取自 `crates/client-core/src/plugins/` 里的代码,两者不一致时以代码为准。`crates/client-core/tests/fixtures/plugin-packs/` 里有每种新类型能通过和会被拒绝的示例包,社区后端用同一批包校验。 - 想直接开始:复制 [plugin-template](plugin-template/),这是一个能通过校验的最小按键音效包。 - 社区插件收集在 [metasequoiaime/msime-plugins](https://github.com/metasequoiaime/msime-plugins),欢迎把自己的包提交到那里。 ## 文件夹结构 ```text my-keys/ 文件夹名随意;安装后的文件夹按 id 命名 plugin.toml 清单,必需 key.wav 清单里提到的音频(辅助码表和单词本则是清单点名的数据文件) LICENSE.txt 可选的说明文字(.txt 或 .md) ``` - 文件夹里只能有普通文件:不能有子文件夹、符号链接或设备文件。 - 以 `.` 开头的文件(例如 `.DS_Store`、`.git`)会被忽略,不算进包里。 - 除了 `plugin.toml`,每个文件要么是清单里用到的音频或数据文件,要么是 `.txt` / `.md` 说明文件。其他文件都会让整个包被拒绝,包括多余的音频,以及特效包、指令表、短语表、辅助码表、单词本和符号集里的任何音频。 - 清单点名的数据文件(辅助码表的 `.txt`、单词本的 `.tsv`)不按说明文件处理:它必须存在、不能是空文件、不超过该类型的上限,扩展名必须符合(大小写不限)。 - 文件名只能用 ASCII 字母、数字、`_`、`-` 和 `.`,以字母或数字开头,不超过 64 字节。 - 一个包最多 16 个文件(含清单和说明文件),`plugin.toml` 不超过 256 KiB,每个说明文件不超过 64 KiB。 ## 通用字段 每种类型的清单都以这些字段开头: ```toml schema_version = 1 kind = "sound" # sound | music | command_table | effect | phrase_table | helpcode | wordbook | symbol_set id = "my-keys" name = "我的按键音" version = "1.0.0" license = "CC0-1.0" author = "你的名字" # 可选 description = "一句话介绍" # 可选 permissions = [] # 可选,只能是空数组 ``` | 字段 | 规则 | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `schema_version` | 必须是整数 `1` | | `kind` | `sound`、`music`、`command_table`、`effect`、`phrase_table`、`helpcode`、`wordbook` 或 `symbol_set` 之一 | | `id` | 1 到 64 字节,只含小写字母、数字、`.`、`-`、`_`,以字母或数字开头;安装后的文件夹按 id 命名,源文件夹或压缩包里外层文件夹的名字不要求与它相同 | | `name` | 必填,不超过 80 字节,不能全是空白,不能含控制字符 | | `version` | 必填,不超过 32 字节,同上 | | `license` | 必填,不超过 64 字节,SPDX 许可证表达式(只含字母、数字、`.`、`-`、`+`、`(`、`)` 和空格),例如 `CC0-1.0`、`CC-BY-4.0`、`MIT OR Apache-2.0` | | `author` | 可选,不超过 120 字节 | | `description` | 可选,不超过 500 字节 | | `permissions` | 可选,只能是 `[]`:插件不能申请任何权限 | - 清单里出现上表和该类型专属键以外的任何键,都会拒绝整个包。专属键之内的未知键同样会被拒绝。 - 以下 id 属于内置包,安装的包不能使用: - 音效包:`default`、`twinkle`、`msime-typewriter`、`msime-bubble`、`msime-8bit`、`msime-woodblock`、`msime-pentatonic`、`msime-canon`、`msime-ode-to-joy` - 音乐包:`msime-music-lofi`、`msime-music-ambient` - 导入与已安装的包同类型、同 id 的包,会整包替换旧包。 ## 音效包(`kind = "sound"`) 音效包有两种模式。 - `keys` 模式:每类按键一个样本,另有上屏音和里程碑音效。 - `sequence` 模式:只有一个样本,按音符序列变调播放,形成按键旋律。 ```toml mode = "keys" # keys | sequence,省略时为 keys [sounds] default = "key.wav" # keys 模式必填:其他按键都用它 space = "space.wav" # 以下可选 enter = "enter.wav" backspace = "backspace.wav" commit = "commit.wav" # 上屏音,连击升级的提示音也用它(每升一级高 3 个半音) achievement = "achievement.wav" # 累计上屏数达到里程碑时播放 ``` ```toml mode = "sequence" [sequence] sample = "tone.wav" semitones = [0, 0, 7, 7, 9, 9, 7] # 每个音相对样本原音高的半音数 advance = "key" # key | commit,省略时为 key ``` - `keys` 模式必须有 `sounds.default`,不能有 `[sequence]`。 - `sequence` 模式必须有 `[sequence]`。 - `semitones`:1 到 128 个音符,每个是 -24 到 24 的整数,即上下各两个八度。 - `advance`:`key` 表示每按一个键走一个音,`commit` 表示每次上屏走一个音。 - 停止打字 3 秒后,旋律从第一个音重新开始。 ### 只接受 WAV 音效包的样本只能是 `.wav` 文件(RIFF/WAVE,文件头必须与扩展名相符),不接受 Ogg。原因有三个: - 宿主播放按键音前要把样本整个解码进内存。 - 只有 WAV 能在解码前从文件头读出帧数,从而确认时长不超限;Ogg 要解码完才知道多长。 - HarmonyOS 的播放器本来就只播 WAV。如果接受 Ogg,同一个包在桌面有声、在鸿蒙静音。 音乐包是边读边播的,所以仍然接受 WAV 和 Ogg,见下一节。 ### 样本限制 | 限制 | 值 | | -------------- | ------------------------------------------- | | 不同样本文件数 | 最多 8 个(同一个文件被多个键引用只算一次) | | 单个样本 | 1 字节到 512 KiB | | 全部样本合计 | 4 MiB | | 单个样本时长 | 不超过 1.5 秒 | | 采样率 | 8000 到 192000 Hz | 时长和采样率不在导入时检查,而是宿主在解码前按文件头检查。超出的样本会被跳过,那个键不出声,所以请自己确认每个样本都不超过 1.5 秒。建议用 16 位 PCM、单声道或双声道、22050 Hz 或 44100 Hz。 ## 音乐包(`kind = "music"`) 背景音乐只在输入法处于活动状态时播放;焦点在密码框里时暂停。 ```toml [music] tracks = ["rain.ogg", "piano.wav"] # 按顺序播放,播完最后一首回到第一首 ``` | 限制 | 值 | | ------------ | -------------------------------------------------- | | 曲目数 | 1 到 8 首,不能重复 | | 格式 | `.wav` 或 `.ogg`(文件头必须与扩展名相符) | | 单首大小 | 1 字节到 16 MiB | | 全部曲目合计 | 64 MiB | | 单首时长 | 不超过 15 分钟,宿主播放时检查,超出的曲目会被跳过 | | 采样率 | 8000 到 192000 Hz | ## 指令表(`kind = "command_table"`) 指令表给 `/` 模式添加指令:输入 `/` 和触发词,候选里会出现模板展开后的文字。 ```toml [[commands]] trigger = "sig" title = "签名" template = "{date:%Y-%m-%d} 张三" ``` | 限制 | 值 | | ------------ | ----------------------------------------------------------------- | | 指令条数 | 1 到 256 条;同时启用的所有指令表合计也只用前 256 条 | | `trigger` | 1 到 32 个小写 ASCII 字母,同一个包里不能重复 | | `title` | 不能为空,不超过 48 字节 | | `template` | 不能为空,不超过 199 个 UTF-16 码元,不能含换行、制表符等控制字符 | | 展开后的文字 | 同样不超过 199 个 UTF-16 码元,也不能含控制字符 | - 模板是普通文字加三种占位符: - `{date}` 或 `{date:格式}`,默认格式是 `%Y-%m-%d` - `{time}` 或 `{time:格式}`,默认格式是 `%H:%M` - `{weekday}`,展开成「星期一」到「星期日」 - 格式是 strftime 写法。 - 没有其他占位符,也不能嵌套。模板里单独出现的 `{` 或 `}` 会被视为错误。 - 多个指令表启用时,按设置页里的顺序取第一个定义了该触发词的包。 - 并非所有设备都能导入指令表。不能导入的设备上,内置的 `rq`、`sj`、`xq` 指令照常可用。 ## 特效包(`kind = "effect"`) 特效包不带任何文件,只选用宿主内置的一种打字特效并调整参数。 ```toml [effect] style = "sparks" # 必填:flash | sparks | power_mode intensity = 70 # 可选,0 到 100,默认 50 colors = ["#FFB000", "#FF4060"] # 可选,1 到 4 个颜色 duration_ms = 400 # 可选,60 到 1500 particles = 24 # 可选,0 到 64 ``` | 键 | 规则 | | ------------- | -------------------------------------------------------------------------------------------------------------- | | `style` | 必填,`flash`(一闪)、`sparks`(火花)或 `power_mode`(火花、震动,并随连击增强)之一。特效包不能选 `off` | | `intensity` | 可选,0 到 100 的整数,默认 50:特效的大小和持续程度 | | `colors` | 可选,1 到 4 个颜色,每个都必须写成 `#RRGGBB`(`#` 加 6 位十六进制数,大小写均可);不支持简写、透明度和颜色名 | | `duration_ms` | 可选,60 到 1500 的整数:一次特效持续的毫秒数 | | `particles` | 可选,0 到 64 的整数:每次按键迸出的火花数 | - `[effect]` 里只能有这五个键。 - 特效包里除了 `plugin.toml` 只能放 `.txt` / `.md` 说明文件,不能有图片或音频。 - 省略的参数使用宿主对该特效的默认值。 - `colors`、`duration_ms`、`particles` 只是提示,宿主绘制的特效用不上时会忽略。例如 `flash` 没有火花,`particles` 对它无效。 选用和显示: - 选用一个特效包后,它的 `style` 和参数替代设置里的「打字特效」样式和强度。 - 选中的包被删除或无法载入时不绘制特效,不会退回其他样式。 - Linux 只显示连击计数,不绘制任何特效,所以特效包在 Linux 上没有可见效果。 - 在设置的「插件 → 我的插件」里打开已导入的特效包,选「使用此特效包」。各平台按自己能画的部分取用参数:macOS 用全部参数;Windows 和鸿蒙电脑只闪烁候选卡片,取 `intensity`、`duration_ms` 和第一个颜色,忽略 `particles`。 ## 数据文件的共同规则 辅助码表和单词本的数据文件按同一套规则读: - 整个文件必须是 UTF-8,开头可以有 BOM。 - 按 `\n` 分行,每行去掉一个行尾的 `\r`,所以 LF 和 CRLF 都可以;行内其他位置的 `\r` 算控制字符,整包拒绝。 - 只有完全为空的行才跳过;只有空格的行不算空行。以 `#` 开头的行是注释。 - 不支持引号转义,引号是普通字符。任何一行不合规,整个包被拒绝,不会跳过坏行。 ## 短语表(`kind = "phrase_table"`) 短语表给 K 模式(中文模式下按 Shift+K)添加短语,全部写在清单里,没有数据文件。 ```toml [[phrases]] key = "dh" text = "电话" [[phrases]] key = "dh" text = "电话号码" ``` | 限制 | 值 | | ------ | --------------------------------------------------------------------------- | | 条数 | 1 到 2000 条,受清单 256 KiB 的上限约束 | | `key` | 1 到 32 个小写 ASCII 字母 | | `text` | 不能为空白,1 到 199 个 UTF-16 码元,不能含任何控制字符(包括换行和制表符) | - 每条只有 `key` 和 `text` 两个键。同一个 `key` 可以对应多段文字,但同一对 `key` 和 `text` 不能重复。 - 在「插件 → 我的插件」里打开短语表,打开「启用」。最多同时启用 16 个,按启用顺序排列。 - K 模式先列词库里的短语(内置的和自己加的),再列编码以输入开头的插件短语;同一编码下靠前的表先列出,已经出现过的文字不再列出,总数最多 100 条。所有启用的表合计最多取 8192 条。 - 设置的「输入 → 快捷模式」里关闭了快捷短语时,短语表不起作用。 - 短语从不写进词库,删除短语表后直接消失。 ## 辅助码表(`kind = "helpcode"`) 辅助码表替换全拼或双拼的辅助码方案。 ```toml [helpcode] table = "table.txt" ``` `[helpcode]` 只有 `table` 一个键,点名包里的一个 `.txt` 文件: ```text # 注释 啊=a 吧=ba ``` | 限制 | 值 | | -------- | ---------------------------------------------------------------------- | | 文件大小 | 1 字节到 1 MiB | | 条数 | 1 到 30000 条,同一个字不能出现两次 | | 字 | 恰好一个 Unicode 字符,不能是 ASCII、空白或控制字符 | | 码 | 1 到 2 个小写 ASCII 字母;更长的码会拒绝整个包,而不是像内置表那样截断 | - 每行是空行、`#` 注释,或者 `字=码`,等号两边不能有空格。 - 在设置的「输入 → 辅助码」里,辅助码方案的下拉框会列出已安装的辅助码表,显示为「名称(插件)」;也可以在插件详情里打开「用于全拼」或「用于双拼」。每个方案最多用一个辅助码表。 - 选中的辅助码表被删除或无法载入时,退回该方案原来的辅助码方案,并在「我的插件」里显示为未找到。 - 成熟的辅助码方案可能有版权。投稿到社区前请确认自己有权以所写的许可证发布这张表。 ## 单词本(`kind = "wordbook"`) 单词本是背单词里的一本词书。 ```toml [wordbook] file = "words.tsv" ``` `[wordbook]` 只有 `file` 一个键,点名包里的一个 `.tsv` 文件,每行用制表符分成两列或三列: ```text # 单词 音标 释义 algorithm /ˈælɡərɪðəm/ n. 算法 compiler n. 编译器 cache n. 高速缓存 ``` | 限制 | 值 | | -------- | ------------------------------------------------------------------------------------- | | 文件大小 | 1 字节到 4 MiB | | 单词数 | 1 到 20000 个,同一个单词不能出现两次 | | 每行 | `单词释义` 或 `单词音标释义`;各列不去空白,音标可以留空,释义不能为空 | | 单词 | 1 到 64 个字符;音标不超过 64 个字符;释义不超过 256 个字符;都不能含控制字符 | | `id` | 除了通用规则,还只能用小写字母、数字和 `-`,首尾不能是 `-`,不超过 59 个字符 | | `name` | 除了通用规则,还不能超过 64 个字符 | - 安装后,这本书出现在背单词的词书列表里,排在内置词书之后、导入的词表之前,标着「插件」。插件详情里的「去背单词」会选中它。 - 插件词书不能在背单词里删除,要在插件页卸载。卸载后复习进度仍然保留,重新安装或升级后可以接着复习;卸载期间背单词会让你重新选一本书。 - 投稿到社区的词表请声明释义的来源和许可证。 ## 符号集(`kind = "symbol_set"`) 符号集给符号面板追加符号组或颜文字组,全部写在清单里,没有数据文件。 ```toml [[groups]] tab = "symbols" # symbols | kaomoji title = "箭头" keywords = "jiantou arrow" # 可选,用于搜索 items = ["→", "←", "⇒"] ``` | 限制 | 值 | | ---------- | ------------------------------------------------------------------------------------------------------- | | 组数 | 1 到 32 组,合计最多 2048 项 | | `tab` | `symbols` 或 `kaomoji` | | `title` | 必填,不能为空白,不超过 48 字节,不能含控制字符;同一个 `tab` 内不能重复,不同 `tab` 可以同名 | | `keywords` | 可选;写了就不能为空白,不超过 256 字节,不能含控制字符 | | `items` | 只能是字符串,每组 1 到 512 个;每个 1 到 64 个 UTF-16 码元,不能为空白、不能含控制字符,同组内不能重复 | - 每组只有这四个键。 - 不需要选用:安装后就出现在符号面板里,卸载后消失。`symbols` 组以插件名作为一个分类,排在内置符号之后;`kaomoji` 组排在颜文字的「All」之后。不与内置符号去重。 - 目前显示插件符号集的是 Windows 和 Linux 的符号面板、macOS 的表情与符号面板,以及鸿蒙电脑的键盘选择器;不支持的设备会在插件详情里说明。 ## 校验 用 `msime-pack` 检查插件。它调用的就是输入法导入时用的校验代码:它接受的包,设置页一定能导入;它拒绝的包,设置页也会拒绝。 ```sh cargo build -p msime-pack-tool --bin msime-pack target/debug/msime-pack validate path/to/my-keys path/to/other-pack.zip ``` - 每个包输出一行,顺序与参数相同: ```text ok my-keys sound 1.0.0 error path/to/other-pack.zip: plugin_invalid: key.ogg 不是 .wav 文件名,音效包只接受 WAV 音频 ``` - 错误行里冒号后面是失败代码: - `plugin_invalid`:违反了某条规则,后面写着是哪条 - `plugin_archive`:压缩包有问题 - `plugin_unsupported_source`:不是文件夹或 `.zip` - `plugin_reserved`:用了内置包的 id - 退出码:全部通过为 0;有任何一个不通过为 1;命令行写错为 2。 - 只检查,不安装,不会改动本机的插件目录。 ## 导入 在设置的「插件」页点「导入文件夹」选择包的文件夹,或点「导入 .zip」选择压缩包。 压缩包的要求: - 不超过 80 MiB,最多 64 个条目。 - 包的文件要么全部在压缩包顶层,要么全部在同一个文件夹里;直接压缩包的文件夹就是后一种。 - 压缩包里不能有更深的子文件夹或符号链接。 - macOS 生成的 `__MACOSX` 文件夹和以 `.` 开头的条目会被忽略。 导入成功后: - 包装进数据目录下的 `plugins///`。 - 在「插件 → 我的插件」里打开这个包:音效包和音乐包选「设为当前」,指令表和短语表打开「启用」,辅助码表打开「用于全拼」或「用于双拼」。单词本和符号集装上就能用。按键音、背景音乐的开关和音量在同一页的「声音与效果」里。 - 删除一个正在使用的包后,设置会回到默认选择。