Repository navigation
Releases: 173787247/pingzhu
Release list
v0.10.6 — 七种键盘布局,六个前端都接得上
v0.10.6 — 七种键盘布局,六个前端都接得上
一句话
引擎从两种键盘布局扩到七种(新增大千式以外的许氏、倚天 26 键、IBM、精业、神通),
并且每一个前端都能选:Windows 与 Linux 读设定档、macOS 用菜单、Android 用设置页、
HarmonyOS 长按「繁/简」、网页试打版用选单。
一、七种布局,表不是手抄的
路线图 M8 的第 4 项原本写着「许氏、倚天 26 键未支援,这两种排列需要有状态的编辑器」——
这个判断是错的。多成分键(许氏的 l 同时是 ㄌ/ㄥ/ㄦ)不需要状态机:分词搜索本来就会
把每个键展开成所有成分组合,再交给库存与语法检查。
表格来自参考实作(McBopomofo,MIT),用生成器读出来,而不是凭记忆敲:
node research/tools/import-mcbopomofo-layouts.mjs --ts # 吐 TypeScript
node research/tools/import-mcbopomofo-layouts.mjs --rust # 吐 Rust手抄一张 25 键的布局只会在一个格子上错,而且不会出声。生成器的输出再由
core-rs/tests/fixture-layouts.tsv(7 布局 56 条)把 TS 与 Rust 逐句对照。
文书里原来举的例子也不成立:它说许氏把 ㄑ 实作为 v(ㄔ) + ㄧ/ㄩ 的自动转换 ——
其实 ㄑ 就在 v 上,vuj 直接得到 ㄑㄩˋ → 去。
二、每个前端怎么选
| 前端 | 怎么选 | 改了何时生效 |
|---|---|---|
| Windows(可携 + 文字服务) | pingzhu.ini 的 layout = hsu |
重新执行/切换输入法 |
| Linux(fcitx5) | ~/.local/share/fcitx5/pingzhu/layout(一个名字) |
重新启动 fcitx5 |
| macOS | 输入法菜单的「键盘布局」子菜单,七项打勾 | 立刻(重建引擎) |
| Android | 设置页「键盘布局」下拉 | 重新启用输入法 |
| HarmonyOS | 长按「繁/简」键 | 立刻(重建引擎) |
| 网页试打版 | 键盘上方的选单 | 立刻 |
布局在建立引擎时定下(解码器围绕它构建),所以没有「切换」这回事,只有「用哪个名字建」。
Windows 会校验名字:清单外的值被忽略并留在 standard,而不是传给引擎让它静默退回 ——
否则拼错一个字母的结果只是「布局没生效」,没有任何线索。
三、顺带修掉的:键面是「另一份」布局
Android 与 HarmonyOS 画键面的地方各自写死了布局(JNI 里 Layout::Standard、ArkTS 里
keyboardRows('standard'))。于是选了许氏之后,引擎按许氏解码、画出来的还是大千 ——
每一个键显示的都不是它实际的意思。
这是同一个形状的错误第三次出现(第一次是网页试打版那张手写的标准表)。现在布局名从设定
一路传到画键面的地方,键面与解码器用同一份表。
四、其他
- HarmonyOS 打不出任何拉丁字母 —— 网址、邮箱、密码在真机上无处可打。现在功能列上的
「英/中」切到 QWERTY 键面,英文直接送进编辑器 - HarmonyOS 组字时显示整句,引擎没把握时变淡、倾斜(
bestSentence()从第一天就在
napi 表里,而 ArkTS 里没有一行调用它) - Linux 的繁简热键
Ctrl+Alt+S,与 Windows 一致 - 网页试打版记得住教过的字(存在这个浏览器里),并说出「这句是猜的」
- 两个 Android 诊断修复:一行一直印「0 keys」的报告(它读的字段在视窗建立前必为空),
以及它数的到底是格子还是画出来的键
五、验证到哪
| 平台 | 验证 |
|---|---|
| Windows | 本地构建 + 四项测试(含新的 12 项配置检查) |
| Linux | 构建 + engine_smoke/userdict/script 三项 |
| Android | 模拟器实测:设置页选许氏 → 键面变成许氏 → 按 nefhwf 得到「你好」 |
| 网页试打版 | headless 浏览器实测:选单切到许氏 → nefhwf → 你好 |
| macOS | 只到 CI 编译 —— 本机没有 Mac,也没有 swiftc |
| HarmonyOS | 只到 CI 编译 —— 本机没有 SDK,也没有真机 |
最后两行是诚实的边界:它们能编译、类型正确,但没有在真机上打字验证过。
六、下载
- Windows:
pingzhu-0.10.6-setup.exe或pingzhu-0.10.6-win-x64.zip - Android:
pingzhu-0.10.6-android.apk(versionCode8,可直接覆盖安装 0.10.5) - Linux:
fcitx5-pingzhu_0.10.6_amd64.deb或fcitx5-pingzhu-0.10.6-1.x86_64.rpm - HarmonyOS:
pingzhu-0.10.6-harmonyos-project.zip(在 DevEco 打开、签名、Run) - 引擎:
pingzhu-engine-0.10.6.tgz
macOS 这一版仍然没有产物:输入法要向系统登记,签章必须是 Apple 签发的 Developer ID,
而入会仍在等。
Windows 升级注意:这一版 TSF DLL 换了新档名(pingzhu-tsf-0.10.6.dll)。Windows 会
锁住已载入的 DLL 整个行程生命周期,换档名等于换一个新档案 —— 这是升级能成功覆盖的原因。
v0.10.5 — 选的字要能上屏,教过的字要记得住
v0.10.5 — 选的字要能上屏,教过的字要记得住
一句话
两个修复,都是「功能在,但结果是空的」:Android 与 macOS 上选字之后一个字也没送进应用,
Linux 与 HarmonyOS 上使用者教过的字重启就忘。两个都没有报错,也没有任何画面提示。
一、选字之后什么都没上屏
engine.select(n) 选字并在同一次调用里提交,返回的就是选中的文字。而三个地方
又去调了一次 commit() —— 那一次拿到的已经是空字串:
if (engine.select(oneBased).isNotEmpty()) commit() // ← commit() 里 engine.commit() 已空于是选中的字没有被送进应用,组字却被 reset() 清掉了。用户按了候选,屏幕上什么都没有,
只能以为自己没点准。
| 平台 | 选字路径 | 修前 |
|---|---|---|
| Windows(可携版与文字服务) | 读 select 的返回值 |
✓ 一直是对的 |
| Linux | 读返回值 | ✓ |
| HarmonyOS | 读返回值 | ✓ |
| 网页试打版 | 读返回值 | ✓ |
| Android(触控选字、数字键选字) | 又调 commit() |
✗ 本版修复 |
| macOS(数字键选字) | 又调 commit() |
✗ 本版修复 |
二、教过的字重启就没了
引擎从使用者选的字里学习,那是这个输入法唯一的训练信号。Windows、macOS、Android 都在
启动时载入、学习后保存;Linux 的 fcitx5 附加组件与 HarmonyOS 键盘一处都没有 ——
学到的词只活在内存里,重启即失。
本版让它们和另外三个外壳一样:
- Linux:
~/.local/share/fcitx5/pingzhu/userdict.tsv(尊重$XDG_DATA_HOME),
建好引擎后载入,选字与送出之后写回;写不进去会记一条FCITX_WARN,不再安静地丢掉 - HarmonyOS:
${filesDir}/userdict.tsv(放在 model 目录外——那个目录每次启动都会被
重写),启动时载入,选字与送出之后保存
验证
| 部分 | 怎么验的 |
|---|---|
| Android 选字(触控与数字键) | 模拟器上装 release APK 实测:选中的字进应用、候选列收起 |
| Linux 词典往返 | 新增 tests/userdict.c:教一次 → 存 → 建第二个引擎 → 载入 → 那个词还在 ✓ |
| Linux 引擎与路由 | engine_smoke ✓、共享向量 27 条 0 失败 ✓ |
| HarmonyOS 词典 | 只到 CI 编译验证 ✗(没有装置) |
| macOS 选字 | 只到 CI 编译验证 ✗(没有 Mac) |
给下一个版本留的基线
文字服务的 DLL 档名本版不动(仍是 pingzhu-tsf-0.10.4.dll):档名的规则是
「windows/ 自上一个 tag 起有改动才改」,而 v0.10.4..v0.10.5 之间 windows/ 零改动 ——
这条规则第一次在发版里实际被用上,写起来只是 git log <tag>..HEAD -- windows/ 一行。
v0.10.4 — 每个外壳都修好一个「够不到」的功能
v0.10.4 — 每个外壳都修好一个「够不到」的功能
一句话
五个平台里有四个,各有一个功能存在但到不了:Android 的候选列从没显示过、
HarmonyOS 的 ⌫ 会把整个组字丢掉、Linux 翻不到第二页候选、Windows 的候选视窗
切掉了最下面一行。这一版把它们都修好,并给 windows/ 与 android/ 补上了 CI ——
在那之前,这两个外壳一次都没有在 CI 里建过。
四个「够不到」
Android:候选列从来没有显示过,候选单也打不开
候选列(CandidateBar)建好了、点选回调也接了,但全仓没有一处调用
setCandidatesViewShown(true),而 Android 的 candidates view 预设是隐藏的;就算叫了
也没用 —— 视窗被钉成整屏,框架那块容器拿不到位置(实测 isShown=false、
vis=VISIBLE、1080x551:量得好好的,就是不上屏)。
修法是让候选列成为输入视图自己的一部分(候选列在上、键盘在下、整体贴底),
不经过框架那块容器。同时 hasCandidates 原本算成 windowOpen && …,而路由开单的规则
正是 !windowOpen && hasCandidates —— 那一格永远走不到,空白每次都把句子送出。
底排另加了 ▼ 浏览键:手机键盘没有 ↓,所以翻页本来无处可去。
HarmonyOS:⌫ 丢掉整个组字,又删掉编辑区一个字
一次按键做两件错事:pingzhu.reset()(丢掉整份组字)+ Session.deleteBackward()
(删掉已经送进编辑器的一个字)。根因是 napi 从来没导出 backspace() ——
当初只能拿 reset 顶上。
Linux:翻不到第二页,而且高亮是装饰性的
空白键先开单再看状态,于是「已经开着」和「刚开好」长得一模一样,第二次按只刷新、
既不送出也不翻页;Engine::nextPage() 没有任何呼叫点;↑↓ 移动的那个高亮
没有任何键会采用 —— 把第三个候选照亮,按空白送出的还是第一个。
按键规则搬进了 src/router.cpp(不含任何 fcitx5 标头),从此和 Windows/macOS/Android
一起被 tools/routing-vectors.tsv 钉住。候选列每页也从 9 改成 10 —— 原本每页第十个
候选被挤到 fcitx5 自己开的第二页上,而选它的数字键(0)不在画面上。
Windows:候选视窗矮一行
视窗画四行(组字、候选、→ 预览、页码),高度却只算到第三行 —— 多页时候选单最下面
那条状态列被切掉下半截。行距现在只有一份(candidate_window.h 的 layout::),
高度由「最后一行画在哪」推出来。
也给这个仓库自己补了两处
windows/与android/的 CI:四个旧 workflow 都按目录过滤,这两个外壳的改动
因此不触发任何东西 —— 它们的 bug 只在开发者自己的机器上被发现。现在 Windows 会建
外壳与文字服务并跑四组测试,Android 会交叉编译两个 ABI、跑 JVM 测试并产出 APK。- 发版检查单进仓库(
docs/RELEASING.md):流程原本只写在本机的交接档里,
于是「TSF DLL 档名什么时候该改」这条规则漏过一次。
升级注意
- Windows 的文字服务 DLL 换了档名:
pingzhu-tsf-0.10.2.dll→pingzhu-tsf-0.10.4.dll。
这是故意的:Windows 会锁住已载入的 DLL,换名才升得动。安装包会自动登记新的那个。 - Linux addon 的版本号:
fcitx5-diagnose以前一直显示0.9.0——linux/fcitx5/pingzhu.conf
的Version=从 0.9.0 起就没动过(改CMakeLists.txt的project(... VERSION)没有用,
那是死值)。现在跟着发版号走。
验证
| 平台 | 怎么验的 |
|---|---|
| Windows | 候选视窗几何由 pingzhu-candidate-test.exe 真开窗、读回矩形核对;路由对共享向量 27 条 |
| Android | 模拟器实测:候选列显示 1 你 2 妳 3 擬…、空白开单、2 选字送出、▼ 翻页;JVM 18 项 |
| HarmonyOS | 引擎语义由 TS 参考实现生成的 225 条场景钉住;外壳只到编译验证(本机没有装置) |
| Linux | ctest 两项:引擎冒烟与路由对共享向量;编译零警告 |
| macOS | 未变;CI 仍在真的 macOS runner 上建置与安装验证 |
没验的:Android 的实机行为(只在模拟器上)、HarmonyOS 的真机按键行为 ——
这两项在各自的 README 里都写着。
v0.10.3 — Linux 有安装包了:deb 与 RPM
v0.10.3 — Linux 有安装包了:deb 与 RPM
一句话
Linux 的 fcitx5 附加组件以前只能自己编译;现在仓库里就带着打包定义,
dpkg-buildpackage 与 rpmbuild 各产出一种包,可以直接装。
问题
引擎和五个外壳一直在,Linux 的 addon 也一直能构建 —— 但没有任何打包定义:
cmake -DCMAKE_INSTALL_PREFIX=/usr → 装得到这台机器上 ✓
把 addon 交给别人的机器 → 没有东西可以给 ✗
UOS、Kylin、deepin、openEuler 的使用者拿到仓库也只能自己编译,而这条路在国产
发行版上并不总是通的:Rust 版本、fcitx5 开发包、cmake 宏,各家都不一样。
三种装法
| 方式 | 给谁用 | 怎么做 |
|---|---|---|
.deb |
统信 UOS / 麒麟 Kylin / deepin / Ubuntu | sudo dpkg -i fcitx5-pingzhu_0.10.3_amd64.deb |
.rpm |
openEuler / 麒麟部分版本 / Fedora | sudo rpm -ivh fcitx5-pingzhu-0.10.3-1.x86_64.rpm |
| 从原始码 | 想自己控制前缀的人 | cmake -S linux/fcitx5 -B build -DCMAKE_INSTALL_PREFIX=/usr(原有方式,未变) |
加了什么
| 档案 | 指令 | 产出 |
|---|---|---|
debian/(仓库根目录) |
dpkg-buildpackage -b -us -uc |
fcitx5-pingzhu_0.10.3_amd64.deb |
fcitx5-pingzhu.spec |
rpmbuild -bb fcitx5-pingzhu.spec |
fcitx5-pingzhu-0.10.3-1.x86_64.rpm |
两种包装的是同一组东西:
/usr/lib/*/fcitx5/libpingzhu.so RPM 侧是 /usr/lib64/fcitx5/
/usr/share/fcitx5/addon/pingzhu.conf 附加组件定义
/usr/share/fcitx5/inputmethod/pingzhu.conf 输入法定义
/usr/share/pingzhu/*.tsv 引擎资料
打包时暴露出来的三件事
① 宣告的 Rust 下限根本达不到。 core-rs/Cargo.toml 写着
rust-version = "1.75",但 crate 里用了 C 字串字面值(c"..."),那需要 1.77。
用 1.75 构建会停在 E0658 —— 也就是说宣告的下限是一个构建不起来的版本。
两个包的依赖栏位也跟着写成 1.77。
② RPM 的并行参数展开成 -j-j8。 %{_smp_mflags} 自带 -j,再补一个就成了
make -j-j8,make 印出用法就退出。改用 --parallel。
③ 安装路径原本让 CMake 自己挑。 在 Debian 上它挑 lib/x86_64-linux-gnu,
而 RPM 的档案清单要的是 %{_libdir}(openEuler 上是 /usr/lib64)。现在两边都由
各自的宏决定:-DCMAKE_INSTALL_LIBDIR=%{_libdir} 与
-DCMAKE_INSTALL_DATADIR=%{_datadir}。档案清单原先还指着
%{_datadir}/fcitx5/pingzhu,而资料实际装在 %{_datadir}/pingzhu。
安装说明也补上了
linux/README.md 多了一节「安装」:两种包的命令、从原始码构建、以及装完怎么确认
东西真的在 fcitx5 会去找的位置(libpingzhu.so 在不在、ldd 有没有 not found)。
验证
.deb 实际构建出来,解开确认含 addon、输入法定义与引擎资料
.rpm 实际构建完成并确认内容:/usr/lib64/fcitx5/libpingzhu.so、两个 .conf、
引擎资料与授权档案
TypeScript 68 项 · typecheck
Rust 29 项(26 单测 + 差异 1529 例 + 交互 207 例 + 文件测试)
engine_smoke 那三个词,与其他四个平台一致(下面是它实际印出来的样子):
ok su3cl3: 你好
ok ji394su3: 我愛你
ok w96j0: 台灣
仍有的缺口
RPM 的 BuildRequires 还没在 openEuler 上解析过一次。 本机是 Ubuntu(rpm 4.18),
rpmbuild 不认 apt 的套件名,所以是 --nodeps 加现有的 cmake 宏建起来的。
规格档已经刻意写成直接调用 cmake(不依赖 Fedora 那套 cmake 宏),但
在 openEuler 上跑一次 dnf builddep fcitx5-pingzhu.spec 才算真的验过。
CI 不建这两种包。 .github/workflows/linux.yml 仍然是从原始码构建并安装 addon;
两种包是本地实测出来的,不是每次推送都重新验一遍。
升级方式同前:Windows 用 pingzhu-0.10.3-setup.exe、Android 装
pingzhu-0.10.3-android.apk、HarmonyOS 用对应的 project zip、引擎用 .tgz;
Linux 这次多了 .deb 与 .rpm 两种包。
macOS 仍无产物(签章待办,见 v0.10.0 的说明)。
v0.10.2 — 修掉退格键与下箭头在没打字时被吃掉
v0.10.2 — 修掉退格键与下箭头在没打字时被吃掉
一句话
没在打字的时候,退格键现在会正常删字;下箭头也不会被无谓地吃掉了。
问题
macOS 与 Android 这两个外壳在任何时候都接管退格键与下箭头。于是:
没在打字,按退格 → 输入法吃掉了,应用程式收不到,字删不掉
没在打字,按下箭头 → 同上
同类的输入法不会这样:没在组字的时候,这两个键属于使用者正在编辑的东西。
Windows 与 Linux(fcitx5) 本来就是对的,所以这是两个平台上的既有缺陷。
怎么发现的
不是有人报 bug,而是把 Windows 与 Android 接上共享的路由测试向量之后
自己红的。
tools/routing-vectors.tsv 里写着一条规则:
\b composing=0 → Pass (没在组字,退格交给应用程式)
\v composing=0 → Pass (没有东西可以浏览)
这两条从写下来就没被执行过 —— 因为只有 macOS 的测试真的读那份向量。
把另外两边接上去的第一个动作,就把它们变成了红的。
(向量本身也有两条写错了 —— 把「没组字」的退格写成 Backspace、把「组字中
但没有候选」的下箭头写成 OpenCandidates。一并更正。)
修了什么
| 之前 | 现在 | |
|---|---|---|
| 退格 | 一律接管 | 组字中才接管,否则交给应用程式 |
| 下箭头 | 一律接管 | 有候选可看才接管,否则交给应用程式 |
四个外壳现在行为一致。
测试保护也补上了
原本「改一条规则,四个外壳一起红」这句话只有 macOS 兑现。现在三边都读
同一份向量:
macos/Tests/Router/main.swift (本来就读)
windows/tests/test_router.cpp (本版起)
android/.../RouterTest.kt (本版起)
验证
Windows 24 条共享向量 · 0 失败 + 原有断言全过
Android 16 项(含新增的闸门断言)
macOS 由 CI 在 macOS runner 上编译
TypeScript 68 项 · Rust 29 项 · 连结检查 657 个 URL 0 损坏
仍有的缺口
仓库没有 Windows CI,所以那一份向量只在本机 windows/build.sh --run
时执行;Android 的 workflow 只建 APK、不跑单元测试。也就是说这两个平台的
向量保护目前靠人记得跑 —— 保护是有的,自动化还没有。
升级方式同前。macOS 仍无产物(签章待办,见 v0.10.0 的说明)。
v0.10.1 — 空白改为先开候选单再接受
v0.10.1 — 空白改为「先开候选单、再接受」
一句话
打完字按空白,现在会先列出候选;再按一次空白才送出。想选第二个的人这就能选到了。
为什么改
5j4u4(ㄓㄨˋ-ㄧˋ)有两种正确的读法:
ㄓㄨˋ-ㄧˋ → 注意 ← 原本按空白直接送出的
ㄓㄨ-ㄧˋ → 助益 ← 在同一张候选单上排第 2
两个都对,只是切分不同。但原本按空白只会得到「注意」 —— 想选「助益」的人
必须事先知道要按 ↓ 才看得到那张单子。而这正是空白要解决的问题:它是大家
拿来「接受」的键,所以它必须能以接受收尾。
翻页因此移到 ↓(它本来就是「开单/翻页」一键)。原本「开着就翻页」的规则有
两个毛病:第二个候选到不了,以及最常按的键去卷一张没人要求卷的清单。
空白的三态
| 状态 | 按空白 |
|---|---|
| 组字中、没有候选 | 送出(打了一半,没有东西可挑) |
| 组字中、有候选、候单未开 | 开单,不送出 |
| 候单开着 | 送出目前那一个 |
| 没在组字 | 一般空白 |
操作对照
5j4u4 只打字 首选 注意
5j4u4 ␣ 开单 注意 助益 註譯 …
5j4u4 ␣ 2 选第 2 个 助益
5j4u4 ␣ 1 选第 1 个 注意
5j4u4 ␣ ␣ 送出首选 注意
5j4u4 ␣ ↓ … 翻页(↓ 可重复按)
su3cl3 ␣ ␣ 你好
2l3 ␣ ␣ 導
★ 升级后你会立刻感觉到的一件事
最高频的动作——打完就送出——从一次空白变成两次。
这是刻意的取舍,不是疏漏:要让「第二个候选到得了」与「空白以接受收尾」同时
成立,第一次按下就不能送出。如果你更想要「一次空白直接送出」,那第二个候选
就必须继续靠 ↓,也就是回到 v0.10.0 的行为。
另外修掉一个既有缺陷
Windows 的 ↓ 从不翻页 —— 候选单开着时它仍回传「开单」,而 macOS、Android
与文件都写着「↓ 一键开单与翻页」。现在四个外壳一致。
四个外壳都改了
Windows windows/src/router.cpp Android .../ime/Router.kt
macOS macos/Sources/Router.swift Linux linux/fcitx5/...engine.cpp
引擎侧(Rust 核心与 TypeScript 规格)同步更新,交互测试重录为 207 行,
新增 ↓、↓ ↓、↓ 选第 1 个、空白 ↓ 覆盖翻页。
验证
TypeScript 68 项 + typecheck + 打包
Rust 29 项(26 单测 + 差异 1529 例 + 交互 207 例 + 冒烟)
Windows 路由测试全过
Android 14 项
macOS 由 CI 在 macOS runner 上编译
已知的覆盖缺口(记录在案)
tools/routing-vectors.tsv 的用意是「改这里,四边一起红」,但目前只有 macOS
的测试真的读它。Windows 与 Android 是各自写死的断言,所以这次改规则时它们
没有自己红,两边的断言是手动跟着改的。修法是让那两个测试也读同一份向量 ——
尚未做。
升级方式同前:Windows 用 pingzhu-0.10.1-setup.exe、Android 装
pingzhu-0.10.1-android.apk、HarmonyOS 用对应 project zip、引擎用 .tgz。
macOS 仍无产物(签章待办,见 v0.10.0 的说明)。
v0.10.0 — 尚未成音节的按键显示为注音
v0.10.0 — 尚未成音节的按键显示为注音
一句话
打 gj2 时组字行从 ㄕㄨ [2] 变成 ㄕㄨ [ㄉ] —— 括号里显示注音,而不是你按下的那个键。
为什么改
括号里原本原样回显按键。于是 gj2 显示 ㄕㄨ [2]:一个使用者按下的数字
坐在音节旁边,对没读过解码器的人没有任何意义。su3c 更糟,泄漏的是内部
诊断符号 [c]。
现在渲染注音:ㄕㄨ [ㄉ]、ㄋㄧˇ [ㄏ]。数字键 4 显示为 ˋ(大千式上 4 就是
四声)。该布局上没有注音的按键仍然显示自己,所以不会有东西从画面上消失。
这不只是美观问题:ㄕㄨ [ㄉ] 让「数字键到底在选字还是在组字」变得可见,
使用者不必猜。
受影响的地方
| 平台 | 是否受影响 |
|---|---|
| Windows(可携版/TSF) | ✅ 经由共用 Rust 核心 |
| Android | ✅ 同上 |
| HarmonyOS | ✅ 同上 |
引擎包(npm/.tgz) |
✅ |
| macOS |
验证
TypeScript 68 项全过(新增 2 项钉住基准工具的抽样性质)
Rust 29 项全过(26 单测 + 差异测试 1529 例 + 交互测试 171 例 + 冒烟)
差异测试钉住「Rust 核心」与「TypeScript 规格」行为一致 —— 本次两边同时
改,1529 例逐例比对;交互测试新增一个形状:词由第二个音节造出。
ru04 → ㄐㄧㄢˋ 首选「建」(未选字)
ru04gk4 → ㄐㄧㄢˋ ㄕㄜˋ 首选「建設」
空白键 → 送出「建設」
它防的不是选错字,而是过早定案:若第一个音节完成时就被固定,第二个音节
再也无法回头改它,那么不论候选表多好,建設 都打不出来。
★ 关于 macOS
本版不附 macOS 产物。 原因不是这次没改 macOS,而是 macOS 那条线卡在一个
外部依赖上:输入法要出现在系统清单里,签章必须是 Apple 签发的 Developer ID,
而申请该凭证需要付费入会 + 扫脸验证。这一点已由六个变量逐个实测确认:
| 组合 | 结果 |
|---|---|
| ad-hoc,不呼叫注册 API | ✗ 不出现 |
| 自签 + 系统信任,不呼叫 API | ✗ 不出现 |
| 自签,呼叫 API | ✗ noErr 但看不到 |
| ad-hoc,呼叫 API(从 app 内部) | ✗ noErr,系统列出 0 个 |
| Apple 签发 + 公证 + 呼叫 API | ← 待办,需先取得凭证 |
macOS 使用者请继续用 v0.9.1,或在入会完成后自行从原始码构建 + 签章。
相关步骤见 macos/README.md。
其他修正
- macOS
install.sh在spctl那一步就退出 —— 它从来没真正装过东西。
同一个 bug 让每次执行都下载 0.9.1、印出REJECTED然后结束,把 0.9.0
留在原地。已修,并把结论指向notarize.sh。 - macOS 安装文档修正:原本教人走「免费 Apple ID 就能做」那条路,
而该路径已被实测推翻(免费帐号在 portal 看不到 Certificates;Xcode Personal
Team 发的 Apple Development 凭证spctl -t exec也不接受)。三份文件同步更正。 - 补齐许可全文:
LICENSES/Apache-2.0.txt(data/ts-conversion.tsv衍生自
OpenCC,Apache-2.0 第 4(a) 条要求散布时附上副本,原先只给了连结)、
LICENSES/MIT.txt。并更正 McBopomofo 的版权行为其LICENSE.txt原文。 - 授权记载修正:
libchewing-data是逐目录多授权(dict/chewing/为
LGPL-2.1-or-later、dict/moe/为 CC BY-ND 3.0 TW、只有dict/chewing_v4/
是 CC BY 4.0);g0v/moedict-data是分层授权而非「无授权」。
升级方式
| 平台 | 方式 |
|---|---|
| Windows | 下载 pingzhu-0.10.0-setup.exe,或解压缩 pingzhu-0.10.0-win-x64.zip |
| Android | 安装 pingzhu-0.10.0-android.apk |
| HarmonyOS | 下载 pingzhu-0.10.0-harmonyos-project.zip,在 DevEco 打开、签名、Run |
| 引擎 | pingzhu-engine-0.10.0.tgz |
已知限制:macOS 产物待签章(见上);符号表、联想词、快捷输入尚未实作;
许氏/倚天 26 键盘尚未实作(许氏需要新增有状态的按键层,架构上是一次改动
而非加一张表)。
v0.9.1 — macOS 加上 TIS 注册
macOS 端加上了一直缺的那一半:app 启动时呼叫 TISRegisterInputSource
和 TISEnableInputSource。
查 vChewing 的原始码查出来的——注册不是自动的,每个能用的输入法
都自己呼叫它(或靠 installer 呼叫)。
这个版本还没有 Developer ID 签章。测试目的是看「呼叫注册 API」
能不能让系统列出它,也就是把签章这个变数和注册这个变数分开。
v0.8.0 — Android 版
下载
| 平台 | 档案 |
|---|---|
| Windows 10/11 | pingzhu-0.7.6-setup.exe · 可携版 ZIP |
| Android 8+ | pingzhu-0.8.0-android.apk |
| macOS 12+ |
pingzhu-0.9.0-macos-UNVERIFIED.zip |
(Windows 那两个是 0.7.6 的建置,这次没有改动——版本号没有跟著专案走,
因为那是同一份已经验证过的档案,重新编号只会让人以为它变了。)
Android:真的能用了
su3cl3 → ㄋㄧˇ ㄏㄠˇ → 你好 → 空白送出
零权限——没有网路、没有储存空间、没有通讯录。语言模型在 APK 里,
使用者词库在应用自己的目录里。
| 键 | 行为 |
|---|---|
| 空白 | 有选字单 → 下一页;组字中 → 送出整句;否则 → 空白 |
| 数字 | 选字单开著 → 选字;否则 → 照打(1 是 ㄅ、2 是 ㄉ) |
| 繁/简 | 切换输出字形(显示的是当前字形) |
键盘是自绘的,键面来自引擎——由解码用的同一张表推导。手写的键盘图是会一个键
一个键走样的东西,而且只有「已经把大千式背下来的人」才会发现,而那正是不需要
看图的人。两个 Rust 测试把它钉住。
按键路由是可测的纯逻辑:12 项测试在 JVM 上跑,不需要模拟器、APK 或装置。
这一版背后:一个追了很久的 bug
键盘建置、安装、可选取;引擎载入;JNI 回传正确的键面;View 量测到 1080x514;
系统说视窗 isVisible=true mHasSurface=true mDrawState=HAS_DRAWN。
但画面上什么都没有,而且 onDraw 从来没被呼叫过。
一路排除掉的错误方向:
- 自绘 View 有问题 → 换成普通
TextView加绿色背景 → 仍然 0 个绿色像素 - 宿主是自己的 Activity → 换成系统设定 → 一样
- 模拟器不合成输入法视窗 → Gboard 在同一个模拟器上正常显示
真正的原因在把两个视窗倾印并排看之后才出现,差别只有一行:
ours: mAttrs={(0,0)(fillxwrap) ← 包住內容
Gboard: mAttrs={(0,0)(fillxfill) ← 填滿
API 34 的输入法视窗是填满萤幕、键盘画在底部的。我们的视窗「包住内容」,
而内容在引擎还没产生任何按键之前就被量测,于是包成 0——
之后每一次量测的上限都是 AT_MOST 0,稍后到达的键盘就再也没有地方可以画。
那个限制正是正在被计算的东西,所以它不能同时是答案。
两处修正:给输入法一个 windowIsFloating=false 的主题(在程式码里呼叫
setLayout 会被框架覆盖,主题才是对的地方),以及让根容器在遇到 0 上限时
退回萤幕高度。
顺带修掉三个「安静地失败」
worker.execute { loadEngine() }里的例外会被 Executor 吞掉——加上 try/catch 与记录- Rust 的
guarded()把 panic 吞成空字串,和合法的空结果无法区分——现在回传PANIC IN NATIVE CODE: ... --indicator模式的托盘气泡在讲一颗不存在的热键
同一个形状:失败是安静的。 这是这个专案一路在修的东西。
也发布了 npm 套件
pingzhu-engine 现在可以 npm install:零执行期依赖,浏览器安全的入口
(pingzhu-engine)与 Node 入口(pingzhu-engine/node)分开,语言模型随套件附上。
macOS:先放上来,但名字里就写了未验证
pingzhu-0.9.0-macos-UNVERIFIED.zip
^^^^^^^^^^
为什么叫 UNVERIFIED:它在真的 macOS 上编译、打包、签名,
引擎也在 CI 上真的解码过(su3cl3 → 你好 ✓),但是——
系统从来没有接受过它。 在 CI runner 上试了四件事都失败:
- 等 20 秒 ✗
lsregister向 LaunchServices 注册 ✗(安装程式就是这样做的)- 重启输入源代理 ✗
open启动它一次 ✗
而原因不明 ✗。我先前在文件里写「macOS 只在登入时扫描那个目录」——
那是我把猜测写成了原因,而探测结果否定了它(runner 报告的
launchctl managername 是 Aqua,本来就是已登入的 GUI 工作阶段)。
所以诚实的说法是观察到的事实:同一个 session 里新装的输入法
不会变成可见的输入来源。
要做的事(十分钟,需要一台真的 Mac):
cp -R PingZhu.app ~/Library/Input\ Methods/
# 登出,再登入
# 系統設定 → 鍵盤 → 輸入方式 → 加入「平注」
# 打 su3cl3 按空白清单见 macos/TESTING.md。
通过了我就把它改名去掉 UNVERIFIED;没通过就照日志修。
v0.7.6 — 记录档写出 DLL 版本
下载
| 档案 |
|---|
| pingzhu-0.7.6-setup.exe |
| pingzhu-0.7.6-win-x64.zip |
记录档现在会写出 DLL 档名
--- activated in chrome.exe [pingzhu-tsf-0.7.6.dll] ---
这是唯一能可靠判断「那个行程跑的是哪一版」的方法。
Windows 会把已载入的 DLL 留到行程结束,COM 又不会重读注册表——一个开了很久的程式
跑的是它启动时注册的那一版,而画面上没有任何东西说明这件事。
这个缺口今天骗了两个人
使用者回报「点了按钮但输出没变」。查记录档:主机名、资料目录、引擎就绪全都正常,
只是没有「settings file changed」那一行。
真相是那个行程载入的是 pingzhu-tsf-0.5.1.dll——两小时前的版本,
根本没有即时同步的程式码。
而之所以会测到那么旧的版本:Windows 11 的记事本是单一行程加分页,
所以「开启记事本」只是在那个旧行程里开一个新分页,它从来没有重新载入过。
记录档从头到尾都在说 activated in Notepad.exe——
说了主机,没说版本,而版本才是当时唯一重要的资讯。
这属于整个专案一直在遇到的那一类问题
先前的六次失败,形状都是「使用者说不能用,而程式码看起来完全正确」。
这次多了一个变体:程式码是对的,跑的东西是旧的,而没有任何东西指出这件事。
升级机制(DLL 档名带版本)解决了「新版本能不能载入」,
但没解决「怎么知道现在跑的是哪一版」。这是那个机制的缺口。
使用建议
要拿到新版本,必须让那个程式重新启动。 具体来说:
| 程式 | 要怎么做 |
|---|---|
| Chrome、Edge、VS Code、Cursor | 完全关闭后重开 |
| Windows 11 记事本 | 关掉所有记事本视窗(它是单一行程,开新分页不算) |
| 档案总管 | 工作管理员 → 重新启动 explorer.exe |
或者登出再登入,一次解决全部。