Skip to content
This repository was archived by the owner on Jul 31, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
- name: Run unit tests
shell: pwsh
run: python -m unittest discover -s tests -v
- name: Test native UI preference persistence
shell: powershell
run: .\tests\native_ui_preference_smoke.ps1
- name: Build PIME overlay
shell: powershell
run: .\build_pime_overlay.ps1
Expand All @@ -43,5 +46,5 @@ jobs:
uses: softprops/action-gh-release@v2
with:
files: |
release/Smart-Priority-Bopomofo-Setup-0.4.2.exe
release/Smart-Priority-Bopomofo-Setup-0.6.0.exe
release/SHA256SUMS.txt
91 changes: 81 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,86 @@ When PIME is installed, also run:

```powershell
& 'C:\Program Files (x86)\PIME\python\python3\python.exe' ./tests/pime_adapter_smoke.py
& 'C:\Program Files (x86)\PIME\python\python3\python.exe' ./tests/pime_all_readings_audit.py
```

## Protected interaction contracts

- Tapping Shift toggles persistent Chinese/English mode.
- Holding Shift and pressing A-Z emits one temporary uppercase English letter
without changing modes. Pending Chinese must be committed before the letter.
- Shift punctuation, the five-item candidate menu, candidate expansion, and
right-side character editing are core behaviors. Do not replace or remove
them without an explicit user request.
- Shift punctuation, ten-item two-column candidate pages, Right/Down pagination,
and right-side character editing are core behaviors. The left column must
contain 1-5 from top to bottom and the right column 6-0; never bind A-J as
candidate labels. Do not replace or
remove these behaviors without an explicit user request.
- Candidate editing must offer and atomically apply common/personal phrases
spanning 2-12 syllables while keeping single-character choices available in
the compact five-row menu.
spanning 2-12 syllables while keeping high-frequency single-character and
literal-Zhuyin choices available on the first ten-item page. At the end of
the composition, phrase/sentence choices lead. After the caret moves inside
the composition, the single character to its right must lead, so candidate
1 locks only that syllable and advances instead of confirming the sentence.
- Automatic composition and the candidate editor share
`_ranked_phrase_options()`. Conservative fuzzy-reading and exact typo
corrections must enter that function as visible whole-sentence candidates;
Enter must never perform a hidden final rewrite. Before opening Down or
committing through any path, `_apply_phrase_ranking()` must synchronize the
editable buffer. A better whole-buffer candidate must never be hidden behind
Down; arrows are for overriding the default, not obtaining a default the
engine already knew.
- Ranking order is a core contract: an explicit user candidate selection is
first. Bundled candidates compare exact reading-span coverage before source
weight, so a shorter suffix cannot overwrite a longer complete conversion;
equal spans prefer Taiwan character/word frequencies, then engine/Rime.
A stored single-character pin stays candidate zero for isolated input but
is not a context lock; a reliable word or whole-buffer conversion may
override it. Only a choice made explicitly in the current composition or a
learned personal phrase locks its covered segments.
Automatic commits must not silently teach the personal stores.
- Activation, a Windows keyboard-close status, and forced focus termination
restore the profile to keyboard-open Bopomofo mode. Password fields and apps
that explicitly disable IMEs remain under Windows control.
- Numpad 0-9 and VK_DECIMAL always emit digits/dot and never Bopomofo or
candidate numbers.
- Single-character ranking preserves libchewing's reading-aware dictionary
default, then uses global Taiwan frequency for the remaining tail. Global
frequency has no pronunciation information and must never promote a common
alternate-reading character such as 員 over 運 for ㄩㄣˋ.
- Text/weight-only frequency indexes are search aids, not pronunciation
evidence. Before a corpus phrase or fuzzy-reading correction changes live
text, validate the complete span through `phrase_candidates(readings)`.
Character-column membership alone must never let an alternate reading
borrow an unrelated word (for example 貝殼 for ㄅㄟˋ ㄑㄩㄝˋ).
- Autocorrection is conservative, offline, and visible before commit. Apply
only exact, same-length high-confidence rules from `data/common_typos.json`
as whole-sentence candidates. A currently selected candidate or a learned
personal phrase is protected and must outrank every autocorrection rule; a
stored single-character pin alone is not a context lock. Do not add
context-dependent pairs such as 的/得/地 or
在/再 as unconditional replacements, and never upload text for correction.
- Unlocked spans are re-ranked live from their retained Bopomofo readings
through `phonetic_corrector.py` and the bundled phrase indexes. Reviewed
fuzzy phonetic-slot confusions and fallback typo rules must become visible
whole-sentence candidates before Enter. Surface variants such as 音該/英該
must not be enumerated as separate rules, and a valid exact-reading phrase
must be preserved as an alternate candidate.
- Activation and forced focus termination reset only the profile's internal
Shift toggle. Respect the TSF keyboard-open state supplied by the host;
never reopen a compartment after an app closes it. This prevents games and
secure/custom controls from entering an open/close feedback loop.
- Numpad 0-9, decimal, divide, multiply, subtract, and add always emit their
literal ASCII characters and never Bopomofo or candidate numbers.
Shift+A-Z and Shift punctuation replace only an unfinished active syllable,
preserve completed Chinese, and emit at the active caret.
- Literal Bopomofo is a core behavior. For any lone initial, medial, or rime,
Space first asks the dictionary for the corresponding first-tone Chinese
syllable; do not classify every initial as invalid because ㄙ and ㄓ,
and similar syllabic initials are real readings. When candidate zero is
Chinese, it is auto-selected and raw Zhuyin stays within the first four
candidates. When candidate zero is literal Zhuyin, open the literal-first
menu even if libchewing appended obscure Han characters to the tail.
Completed readings also offer their literal spelling within the
first four candidates, including sentence editing. With no active syllable,
bare DaQian tone keys emit their
tone marks (3=ˇ, 6=ˊ, 4=ˋ, 7=˙), while Shift+Space emits ˉ. Numpad digits
and operators remain literal text. When completed segments already exist,
raw Zhuyin and bare tone marks stay as protected segments in the editable
composition; they must not commit the surrounding text like Enter.
- Feedback collection records only explicit conversion corrections. Never add
surrounding text, application identity, automatic upload, or a network call
to the IME runtime. The user must review records before opening a report.
Expand All @@ -57,8 +112,24 @@ When PIME is installed, also run:
- `bopomofo_core/data/taiwan_frequency.json` is generated from the Ministry of
Education's official open-data character and word tables. Never hand-edit
it; preserve attribution, source hashes, generator, and data notice.
- `bopomofo_core/data/common_typos.json` is a reviewed source-attributed rule
list. Keep source identifiers and URLs, require equal-length replacements,
and add regression tests for every policy change.
- Add a regression assertion to `tests/pime_adapter_smoke.py` whenever a core
interaction is fixed.
- Single-character ranking changes must also pass
`tests/pime_all_readings_audit.py`, which enumerates every Bopomofo slot and
tone combination accepted by libchewing. Never replace this with a handful
of reported syllable examples.
- Candidate font, grid width, labels, and navigation can be changed through
PIME's Python protocol. The authorized native style lives under `native_ui/`
and is built from the exact bundled PIME `v1.3.0-stable` source. Install it
only through the explicit `-EnableUnsignedNativeUi` opt-in, only when PIME
has no unrelated modules, preserve the original DLLs, and restore them only
when the installed hashes still match our build. Fresh installs keep the
signed DLLs for game compatibility. Once opted in, persist that preference
across normal and EXE updates; never silently restore the signed UI during
a Python-layer update. `-DisableUnsignedNativeUi` is the explicit rollback.

Run `./build_release.ps1` for a distributable installer. Update all version
locations together when cutting a new version.
Expand Down
44 changes: 43 additions & 1 deletion AI_MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,55 @@
- `bopomofo_core/pinned_store.py`:單一讀音的個人優先字。
- `bopomofo_core/phrase_store.py`:使用者確認過的 2–12 字詞語。
- `bopomofo_core/frequency_lexicon.py`:離線高頻詞索引查詢;資料位於 `bopomofo_core/data/`。
- `bopomofo_core/autocorrect.py`:產生可見完整句候選的離線高可信錯字修正;規則位於 `bopomofo_core/data/common_typos.json`。
- `bopomofo_core/phonetic_corrector.py`:以每個字保留的注音、候選欄與常用詞庫重新解碼;同一讀音或保守的注音槽位混淆不應展開成大量表面錯字規則。
- `tools/build_frequency_lexicon.py`:從固定版本 Rime Essay 重建臺灣正體高頻詞索引。生成的 JSON 不應手工修改。
- `tests/`:核心與 PIME 整合測試。
- `installer/`:正式安裝與解除安裝流程。
- `native_ui/`:候選框的完整 LGPL 原始碼、可重現建置腳本及 x86/x64 產物。

候選視窗的字型、每列數量、選擇標籤與方向鍵行為由 Python 模組控制;圓角、顏色、
邊框與選取樣式位於 `native_ui/src/CandidateWindow.cpp`。原生元件固定從隨附的
PIME `v1.3.0-stable` 重建,但它沒有正式程式碼簽章,可能與遊戲反作弊衝突。
全新安裝與尚未選擇介面的使用者保留 PIME 原廠簽章 DLL;只有使用者明確傳入
`-EnableUnsignedNativeUi` 且沒有其他 PIME 模組時才首次套用自訂 DLL。這項選擇必須
跨一般更新、EXE 與 AI 維護持久保存,除非明確傳入 `-DisableUnsignedNativeUi`,不得在
更新 Python 層時恢復舊簽章介面。安裝器須備份與還原原始 DLL;被 TSF 鎖定時以
`MoveFileEx` 排程至重開機,不能把這些限制拿掉。

候選窗每頁固定 10 個、分成兩個直欄:左欄由上而下 `1–5`,右欄由上而下 `6–0`;`→` 直接翻到下一頁,`↓` 依數字順序逐項移動並在
本頁末端翻頁。完整讀音的原始注音須留在前四個候選。單獨注音按空白時一律先向
字典查詢補上一聲後的候選,不可單靠 initial/medial/rime 分類判定,因為 `ㄙ`、
`ㄓ` 等聲母本身也是完整音節。字典候選零是中文字時直接採用;候選零仍是原注音
時必須開啟原符號第一的選單,不可讓尾端生僻字自動勝出。原注音仍須保留在前四項。
沒有作用中的音節時,大千聲調鍵直接輸出符號(3=ˇ、6=ˊ、4=ˋ、7=˙);已有未提交
文字時,聲調與候選中的原始注音須以受保護片段插入同一組字區,不得提交其他片段。
右側數字鍵盤的數字、小數點與 `/ * - +` 必須直接輸出原字元,不可進入注音映射。

精確注音、保守模糊讀音與補充錯字規則都必須先產生可見的完整句候選,並即時更新
未鎖定的組字內容;Enter 不得在送出瞬間暗中改字。原始精確讀音句要保留為後續候選。
規則必須等長、精確、高可信且附來源。當次親自選字與個人詞彙所涵蓋的字元須設為保護範圍;
已儲存的單字優先只控制單一讀音排序,不得鎖死整句脈絡。`的/得/地`、`在/再`、合法異形詞等需要語境的
項目不可加入無條件規則。執行期間不得傳送文字到網路,也不得把自動修正當成個人學習。

完整句預設與候選視窗必須共用 `_ranked_phrase_options()`;開啟候選及任何送出動作前
都要先執行 `_apply_phrase_ranking()`。禁止新增只在候選視窗可見、但不會同步到組字區
的另一套第一候選邏輯,也禁止在 Enter 路徑另外做不可見修正。方向鍵是改選功能,
不是取得系統已知正解的必要步驟。

Rime Essay 與台灣字頻只有文字/權重,沒有完整詞語讀音;其逐字候選交集只能用來搜尋,
不可直接更改組字。任何高頻詞或模糊音修正都必須再經 `phrase_candidates(readings)`
驗證整段讀音。游標在句中時,候選零必須是游標右側的單字候選,選擇後只鎖定涵蓋的
音節並前進;詞語與完整句候選保留在同一頁。游標在句尾時才可把完整句/詞語排在前面。

## 修改後必須驗證

```powershell
python -m unittest discover -s tests -v
.\build_pime_overlay.ps1
& 'C:\Program Files (x86)\PIME\python\python3\python.exe' .\tests\pime_adapter_smoke.py
& 'C:\Program Files (x86)\PIME\python\python3\python.exe' .\tests\pime_all_readings_audit.py
.\native_ui\build_native_ui.ps1
.\build_release.ps1
```

Expand Down Expand Up @@ -63,7 +102,10 @@ Git Bash 使用:

- `tools/build_taiwan_frequency.py` 從教育部字頻/詞頻 CSV 重建台灣預設排序;
`bopomofo_core/data/taiwan_frequency.json` 是產物,不可手改。
- 使用者明確選擇永遠最高;內建候選先比較詞彙涵蓋的音節數,較短後綴不可
- 單字候選必須保留 libchewing 讀音字典的第一項,再用全域台灣字頻整理其餘候選;
全域字頻不含讀音資訊,不得讓多音字(例如 `員`)覆蓋 `ㄩㄣˋ→運` 等讀音首選。
- 當次使用者明確選擇永遠最高;儲存的單字優先在孤立讀音中列為候選零,但可靠的
整詞/整句脈絡可覆蓋它。內建候選先比較詞彙涵蓋的音節數,較短後綴不可
覆蓋較長完整轉換,涵蓋相同時再依台灣官方字詞頻、其他內建詞庫排序。
一般送出文字不可自動強化個人權重。
- `bopomofo_core/feedback_store.py` 與 `feedback-report.ps1` 只保存明確改選
Expand Down
Loading