Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
3070541
migrate to nodejs phase 1
flier268 Aug 14, 2026
8a14718
feat(浮動球): 還原舊版左右鍵與右鍵選單
flier268 Aug 14, 2026
33b5009
feat(托盤): 與浮動球共用舊版動作選單
flier268 Aug 14, 2026
d2eed5b
docs(驗收): 記錄 Linux X11 本機驗收結果
flier268 Aug 14, 2026
7a0152c
test(音訊): 改用 ffmpeg-static 與內建測試樣本
flier268 Aug 14, 2026
3541be4
docs(驗收): 確認問題回報與更新檢查
flier268 Aug 14, 2026
1ff217f
feat(設定): 預設隱藏主視窗並提供啟動選項
flier268 Aug 14, 2026
f11a696
feat(設定): 快捷鍵改為捕捉組合鍵
flier268 Aug 14, 2026
430a57a
feat(導覽): 加入首次啟動教學並詢問匯入舊設定
flier268 Aug 14, 2026
913d7a9
feat(設定): 未儲存變更改為底部固定提示列
flier268 Aug 14, 2026
6828ff0
fix(視窗): 避免啟動閃爍並改用滑鼠附近的轉換提示
flier268 Aug 14, 2026
da10bb5
test(驗收): 補強剩餘項目的本機測試證據
flier268 Aug 14, 2026
e3e95d7
test(qemu): 加入 Ubuntu 22.04 乾淨環境驗收
flier268 Aug 14, 2026
23ffd7d
docs(驗收): 記錄 QEMU Ubuntu 22.04 乾淨環境結果
flier268 Aug 14, 2026
f970c0f
fix(浮動球): 拿掉灰底並禁止選取文字
flier268 Aug 14, 2026
51cedcd
feat(圖示): 重畫 Z² 並讓懸浮球改用 SVG
flier268 Aug 14, 2026
2a278fb
chore(格式): 加入 Rust、Vue 與 Node.js 自動格式化
flier268 Aug 14, 2026
3bb0d29
fix(測試): 用 find 檢查浮動球 SVG 子元素
flier268 Aug 14, 2026
03cbfdb
chore(deps): 升級 pnpm 11、TypeScript 6 與 Rust lockfile
flier268 Aug 14, 2026
20f3e04
docs: 加入代理工作指引
flier268 Aug 14, 2026
c3a85a0
ci: 改由 packageManager 決定 pnpm 版本
flier268 Aug 14, 2026
efe9463
fix(驗收): 補齊 ID3 版本寫回與設定欄位行為
flier268 Aug 14, 2026
76238a5
docs(驗收): 補上 Linux X11 畫面證據並鎖定契約測試
flier268 Aug 14, 2026
565557f
chore(tauri): 將 bundle identifier 改為 dev.flier268.convertzz
flier268 Aug 14, 2026
a57fe93
feat(更新): 加入簽署的應用程式內自動更新
flier268 Aug 14, 2026
915a4ae
fix(導覽): 匯入舊設定後將略過按鈕改為下一步
flier268 Aug 15, 2026
2f611c0
docs(更新): 加入自動更新金鑰與發行說明
flier268 Aug 15, 2026
1981207
fix(設定): 匯入舊 JSON 只讀取並另存,不再備份來源
flier268 Aug 15, 2026
c2a1d7d
fix(設定): 載入不回寫,浮動球只修補已存座標
flier268 Aug 15, 2026
f8cccce
fix(快捷鍵): 錄製後自動啟用,並在按下時觸發
flier268 Aug 15, 2026
b5bbbeb
feat(選取): 以平台選取讀寫取代模擬 Ctrl+C/V
flier268 Aug 15, 2026
1b60be6
feat(設定): 改成分頁並避免每次切換都重掛
flier268 Aug 15, 2026
7af7522
feat(更新): 可略過指定版本並在啟動時不再詢問
flier268 Aug 15, 2026
7452c98
test(e2e): 加入 Playwright 覆蓋啟動與跨頁確認流程
flier268 Aug 15, 2026
354e20d
fix(e2e): 讓 Vite 在 CI 聽 127.0.0.1 並從倉庫根目錄啟動
flier268 Aug 15, 2026
2d7a280
ci: 升級 GitHub Actions 至目前的 major
flier268 Aug 15, 2026
73a06f8
fix(windows): 發行版改用 GUI subsystem,避免多餘主控台
flier268 Aug 16, 2026
1c1aa9d
fix(sidecar): 預先註冊狀態並在啟動失敗時顯示主視窗
flier268 Aug 16, 2026
a88b336
fix(選取): Windows 改以快捷鍵複製貼上並限制剪貼簿等待
flier268 Aug 16, 2026
d0bcc25
fix(設定): 首次啟動缺檔時仍可載入預設設定
flier268 Aug 16, 2026
98c35e8
fix(sidecar): 避免選項缺少參數時誤寫路徑
flier268 Aug 17, 2026
267d3ba
fix(音訊): 資料夾選取同時接受字串與陣列回傳
flier268 Aug 17, 2026
1dc8ed9
feat(windows): 加入 MSI 繁體中文安裝字串
flier268 Aug 17, 2026
dea8398
test(桌面): 鎖定啟動順序、無主控台與發行契約
flier268 Aug 17, 2026
89c359c
fix(windows): 讓 MSI 使用既有的繁體中文 WiX 語系
flier268 Aug 17, 2026
8f0e6ca
docs(遷移): 拆分已完成與未完成驗收清單
flier268 Aug 17, 2026
76a1220
fix(dev): 對齊 Tauri sidecar 建置流程,避免 binaries 監看重啟
flier268 Aug 17, 2026
cda17d7
test(驗收): 補自動化護欄並精簡完成封存
flier268 Aug 17, 2026
fda75d2
fix(設定): 設定頁恢復匯入 ConvertZZ.json 並在取代後重綁表單
flier268 Aug 17, 2026
2ed37fc
feat(備份): 檔案、檔名與音訊轉換前預設建立 .bak
flier268 Aug 17, 2026
48ae2ca
feat(核心): 將 sidecar 轉換邏輯遷入 Rust 並移除 Node sidecar
flier268 Aug 18, 2026
40e320f
docs(遷移): 更新 Linux 驗收狀態並新增桌面驗收 skill
flier268 Aug 18, 2026
0164271
chore: 移除空的 Linux 外部二進位設定並改寫同程序核心敘述
flier268 Aug 18, 2026
1989010
refactor(rust): 將單元測試拆至同模組 tests.rs
flier268 Aug 18, 2026
84d76bc
test(檔案/音訊): 強化轉換與標籤回歸並支援 .bak 衝突策略
flier268 Aug 18, 2026
ff52d61
fix(核心): 以 inode 與檔案長度使字典快取在同秒寫入後失效
flier268 Aug 18, 2026
83dd01e
feat(更新): 支援預發佈版本檢查並將版本升為 2.0.0-beta1
flier268 Aug 18, 2026
4455487
fix(ci): 強制 LF 換行以免 Windows 發行 Prettier 失敗
flier268 Aug 18, 2026
f02d4e5
fix(windows): 以穩定 creation_time 取代 nightly file_index
flier268 Aug 18, 2026
1299edd
fix(發行): 預發行版只產 Windows NSIS,略過 MSI
flier268 Aug 18, 2026
0209f70
docs(發行): 移除作業系統程式碼簽章相關敘述
flier268 Aug 18, 2026
1032c0f
docs(遷移): 封存 G-16/J-06/J-12 並更新 G-15 進度
flier268 Aug 18, 2026
8ef38b9
fix(核心): 修正 Linux 套件分詞字典路徑解析
flier268 Aug 18, 2026
521d609
chore(發行): 將版本升為 2.0.0-beta2
flier268 Aug 18, 2026
695830e
fix(快捷鍵): 發行包先註冊 global-shortcut 再建立視窗
flier268 Aug 18, 2026
dbeae7d
chore(發行): 將版本升為 2.0.0-beta3
flier268 Aug 18, 2026
b35d0f4
feat(檔案): 選檔對話框預設使用支援的檔案篩選
flier268 Aug 19, 2026
132b099
docs(遷移): 封存 G-15/I-06/I-07/J-08/J-11 驗收結果
flier268 Aug 19, 2026
2d84e82
fix(啟動): 修正 Windows 首次啟動因 AppData 路徑不存在而失敗
flier268 Aug 19, 2026
d38799b
fix(啟動): 首次導覽時強制顯示主視窗並避免 hide/show 競態
flier268 Aug 19, 2026
275de2e
chore(發行): 將版本升為 2.0.0-beta4
flier268 Aug 19, 2026
d71dd5b
feat(發行): 新增 Windows 免安裝 zip 與可攜設定
flier268 Aug 20, 2026
b369699
feat(懸浮球): 支援拖入檔案詢問動作並記住選項
flier268 Aug 20, 2026
a9f8525
fix(視窗): 從選單叫出主視窗時還原已最小化狀態
flier268 Aug 20, 2026
47d21d3
feat(預覽): 加入並排 diff 與同步捲動並優化大量差異效能
flier268 Aug 20, 2026
3e903f9
chore(發行): 將版本升為 2.0.0-beta5
flier268 Aug 20, 2026
9a947f0
ci: 為 CI 與發行工作流程加入 Rust 編譯快取
flier268 Aug 20, 2026
411ff68
fix(發行): 避免 Windows tar 把磁碟代號當成遠端主機
flier268 Aug 20, 2026
f451647
chore(發行): Release 標題改為版本號並為下載檔加上直連
flier268 Aug 20, 2026
b85aa1c
feat(更新): 以 alpha/beta/rc 通道檢查預發佈並支援自動安裝
flier268 Aug 20, 2026
49bedf0
fix(發行): 免安裝版改以 Node 寫入真正的 PKZIP
flier268 Aug 20, 2026
93cd59e
feat(設定): 非正式版預設檢查開發/預發佈更新
flier268 Aug 20, 2026
9425ab8
chore(發行): 將版本升為 2.0.0-beta6
flier268 Aug 20, 2026
b5e3383
docs(遷移): 封存 Wayland/K 簽核並對齊平台差異表
flier268 Aug 24, 2026
959ba20
chore(遷移): 移除舊 WPF 並改由 resources 提供字典(K-07)
flier268 Aug 24, 2026
e2e4572
feat(檔案): 延遲載入內容預覽並改為可調整並排介面
flier268 Aug 26, 2026
7fba8f9
fix(轉換): 簡轉繁字形改為 cn2tw_min 再接 cjk2zht
flier268 Aug 27, 2026
950c6e3
feat: 新增語料回環額外修正詞典
flier268 Aug 28, 2026
1d0d2fa
feat(回環詞典): 支援語料頂層目錄篩選並對齊字形轉換
flier268 Aug 28, 2026
26895ee
chore(wiki): 更新 wiki submodule 指標
flier268 Aug 28, 2026
e8b2c3d
feat(roundtrip-dict): 加入檔級檢查點與動態記憶體控管
flier268 Aug 28, 2026
6b50ed2
fix(roundtrip-dict): 長句切分處理單元並升級 ws-segment-rs 至 0.1.3
flier268 Aug 29, 2026
8e22578
feat(roundtrip-dict): 加入 --extra-correction 探針旗標
flier268 Aug 29, 2026
6de2bff
feat(extra-correction): 套用 dcard 回環詞典並加入簡體轉繁腳本
flier268 Aug 29, 2026
93bd82b
chore(wiki): 更新 wiki submodule 指標
flier268 Sep 1, 2026
9dd0ad4
chore(cargo): 指定 convertzz 為預設執行檔
flier268 Sep 1, 2026
1669e95
chore(deps): 升級 ws-segment-rs 至 0.1.4
flier268 Sep 1, 2026
7f2cafb
feat(核心): 依詞性套用 extra 同義詞,並支援並行、取消與逐檔寫入
flier268 Sep 1, 2026
d0374b8
feat(extra-correction): 套用帶詞性與簡繁詞形的回環詞典
flier268 Sep 1, 2026
5564b59
build: 將 roundtrip-dict 拆成獨立 crate
flier268 Sep 1, 2026
ed555b8
feat(roundtrip-dict): 產出後自動檢查疑似左簡右繁同義詞
flier268 Sep 1, 2026
d8f3848
feat: 修正extra-correction
flier268 Sep 1, 2026
0068c58
feat(檔案預覽): 全視窗 diff、長文翻頁與上下差異導航
flier268 Sep 1, 2026
817a704
feat(roundtrip-dict): 以檔級 shard 增量重跑並避免互代正詞
flier268 Sep 2, 2026
2e89e2e
feat(核心): extra 依前後詞性套用,字形特例改為檔案協議
flier268 Sep 2, 2026
04fa55e
feat(extra-correction): 依新回環規則更新分詞表與同義詞
flier268 Sep 2, 2026
272c57c
chore(skills): 加入 conversion-specials 字形特例撰寫指引
flier268 Sep 2, 2026
44ad4ce
fix(ui): 各工作頁改為鎖視窗高度並填滿剩餘空間
flier268 Sep 2, 2026
6cd7ed0
feat(conversion-specials): 讓 rules 分詞釘詞並作為固定 extra-correction
flier268 Sep 3, 2026
dc89790
feat(conversion-specials): 釘入台灣完整 xx鄉/xx里 名稱
flier268 Sep 3, 2026
e7a7811
chore(wiki): 更新 wiki submodule 指標
flier268 Sep 3, 2026
2ad305f
feat(conversion-specials): 釘入台灣完整 xx縣/xx市/xx鎮 名稱
flier268 Sep 3, 2026
a5dae1a
fix: 單元測試分詞案例錯誤
flier268 Sep 3, 2026
4e1161a
feat(data): 用更多資料修正簡轉繁問題
flier268 Sep 3, 2026
ab3e828
fix: 修正黃金測試案例
flier268 Sep 3, 2026
ae36ff6
chore(發行): 將版本升為 2.0.0-beta7
flier268 Sep 3, 2026
a789ef8
feat(cli): 加入無頭 CLI,並以 2.0 --flag 為準
flier268 Sep 4, 2026
2591501
chore(wiki): 更新 wiki submodule 指標
flier268 Sep 4, 2026
fc5139c
feat(ui): 重設設定浮動球與關於頁版面
flier268 Sep 4, 2026
53fb366
Bump to beta8
flier268 Sep 4, 2026
8c72117
fix(test): 修正無頭 CLI 契約與效能測試門檻
flier268 Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
77 changes: 77 additions & 0 deletions .agents/skills/conversion-specials/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
name: conversion-specials
description: >
只在使用者要新增或修改 ConvertZZ 字形特例時使用:改
conversion-specials/rules.txt 或 place-names.txt、加入 skip/variant/s2t-multi/t2s-multi/pin
規則。觸發:加入特例、修改特例、改 rules.txt、台灣縣市鄉鎮里名稱、/conversion-specials。
不要在一般轉換、extra-correction、roundtrip、除錯既有行為時套用。
---

# 撰寫 conversion-specials

只在本次任務要**新增或修改**字形特例時跟這份 skill。討論轉換結果、修 extra-correction、跑 roundtrip、或只讀現有規則,不要套用。

字形特例只改 `src-tauri/resources/conversion-specials/rules.txt`。台灣完整「xx縣/xx市/xx鄉/xx鎮/xx里」放 `place-names.txt`(用 `fetch-place-names.py` 更新),不要把幾千個地名寫進 `rules.txt`。欄位、`when` 語法與現況案例以同目錄 [README.md](../../../src-tauri/resources/conversion-specials/README.md) 為準;改規則前先讀完。不要把字寫進 `conversion.rs`,也不要寫進 `segment-dict`。

## 放哪一層

| 情況 | 放哪 |
| --- | --- |
| 單字、有上下文的一簡多繁/一繁多簡、異體、引擎錯映射 | `rules.txt` |
| 固定整詞保護(和牛、胜肽、里長) | `rules.txt` 的 `pin` 或 `word=`/`word^=`(分詞釘入、轉換再套用) |
| 台灣完整縣/市/鄉/鎮/里名 | `place-names.txt`(分詞釘入、簡轉繁拉回、roundtrip-dict 寫回 extra) |
| 語料產生的整詞同義(手錶、範圍、機制)+詞性 | `extra-correction` 同義詞/分詞表 |
| 語意取代清單或 `skip: "…"` 字串 | 不准;改 `rules.txt` |

extra 已有整詞+詞性的(只/隻、制/製),不要再在這裡做無條件單字取代。需要單字量詞時用 `s2t-multi`+`pos=`。

## 選 kind

| 目標 | kind | 注意 |
| --- | --- | --- |
| 引擎會改、我們要維持原字(璇不要變璿) | `skip` | `to` 留空。不要再 `variant` 把目標改回來(璿/璇是不同人名) |
| 無條件單字正規化(皰→疱) | `variant`/`s2t`/`t2s` | 一對一單字走字形表,不靠分詞 |
| 同一簡體依上下文選不同繁體 | `s2t-multi` | 有 `when` 的例外必須寫在無條件預設**上面** |
| 同一繁體依上下文選不同簡體 | `t2s-multi` | 若引擎會先改掉來源字,先對該字 `skip` 該 `dir` |
| 只要整詞不被切開、不必改字 | `pin` | `to` 留空。2 字以上才釘入 |

`dir`:`s2t`/`t2s`/`both`。省略時 `skip`/`s2t`/`variant`/`s2t-multi` 預設 `s2t`。

## 寫一行

UTF-8,**欄位用 Tab,不是空白**。`from` 多個來源用 `|`。`when` 多條件用 `;`(AND),同一條件內 `|` 為 OR。`#`/`//` 為註解。

```
kind<TAB>from<TAB>to<TAB>dir<TAB>when
```

同一方向、能對上 `from` 的規則由上而下第一條命中即停。出現在帶 `when` 的 `from` 裡的字不會進字形 override 表(避免「老么」在分詞前變成「老麼」)。

`when` 只准 README 列出的鍵:`word=`、`word^=`、`next=`、`next^=`、`ch0=`、`ch1=`、`pos=`、`prev-pos=`。不要發明新欄位;`pos` 名稱與 extra-correction 相同(`D_MQ`、`A_Q`、`D_MQ+A_Q`)。

寫完用真實 Tab 檢查,例如:

```bash
python3 -c "
from pathlib import Path
p = Path('src-tauri/resources/conversion-specials/rules.txt')
for i, line in enumerate(p.read_text().splitlines(), 1):
s = line.strip()
if not s or s.startswith('#') or s.startswith('//'):
continue
print(i, line.split('\t'))
"
```

## 驗證

1. 在 `src-tauri/src/core/conversion/specials.rs` 的 `bundled_rules_parse`(或新測試)鎖定新行為。
2. 需要整句/分詞時補 `src-tauri/src/core/conversion/tests.rs` 黃金案例。
3. 跑:

```bash
cargo test --manifest-path src-tauri/Cargo.toml --lib core::conversion::specials
cargo test --manifest-path src-tauri/Cargo.toml --lib glyph_s2t
```

規則已載入後,`OnceLock` 不會重讀;改 `rules.txt` 後相關測試要新進程。發行包路徑與 `CONVERTZZ_CONVERSION_SPECIALS` 覆寫見 README,一般改倉庫檔即可。
125 changes: 125 additions & 0 deletions .agents/skills/linux-desktop-acceptance/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
name: linux-desktop-acceptance
description: >
Linux X11 桌面人工驗收:避開 IME/檔案選擇器/原生對話/剪貼簿坑,用 AT-SPI、CLI
路徑注入與 gpaste 歷史取證。觸發:驗收、人工證據、ConvertZZ D/E/G/I、Tauri Linux UI
驗證、/linux-desktop-acceptance。
---

# Linux 桌面人工驗收

在 Linux 上為 ConvertZZ(或同類 Tauri 桌面程式)補齊遷移驗收證據。先讀環境閘門,再依正確操作取證;細節反模式見 [references/pitfalls.md](references/pitfalls.md)。

## 何時使用

- 使用者要求驗證 `docs/MIGRATION_PLAN.md` 的 D/E/G/I 待人工項
- 需要截圖、錄影、程序清單等桌面證據
- 先前自動化卡在檔案對話、輸入法、Element Plus 下拉或原生確認框

## 開始前(環境閘門)

記錄並依結果裁切範圍,不可硬測:

```bash
echo "SESSION=$XDG_SESSION_TYPE DESKTOP=$XDG_CURRENT_DESKTOP"
echo "WAYLAND=${WAYLAND_DISPLAY:-unset} DISPLAY=${DISPLAY:-unset}"
busctl --user list 2>/dev/null | grep -iE 'StatusNotifier|secrets|keyring' || true
systemctl --user is-active gnome-keyring-daemon.service 2>/dev/null || true
```

| 條件 | 可測 | 不可測(標環境受限) |
| --- | --- | --- |
| `XDG_SESSION_TYPE=x11` | X11 項、多數 D/E/G | I-03~I-05(需 Wayland) |
| 無 Wayland session | — | Wayland 簽核 |
| 有 StatusNotifierWatcher | 托盤正常路徑 | I-06(缺 AppIndicator) |
| Secret Service 啟用 | 金鑰可持久 | I-07 完整「關閉後重啟兩次」(勿貿然殺 keyring) |
| GitHub latest < 本機版 | G-16 後備查詢 | G-16「開啟下載頁」、G-15 簽署安裝 |

二進位優先用今日 `src-tauri/target/release/convertzz`;資料目錄為 `~/.local/share/dev.flier268.convertzz/`(見 `tauri.conf.json` 的 `identifier`)。

**禁止** `convertzz --version`/對 GUI 二進位跑會掛起的「版本」指令(會開視窗且不退出)。
**禁止** `pkill -f convertzz`(會誤殺含該字串的 shell wrapper);改 `pgrep -x convertzz` 後依 PID `kill`。

## 證據目錄

證據**不要**寫進倉庫的 `docs/`(錄影/截圖體積大)。預設:

```text
~/Desktop/ConvertZZ-acceptance/YYYY-MM-DD/
META.txt # git SHA、二進位、平台、日期
README.md # 項目→檔案對照與環境限制
<ID>-*.png|mp4|txt
```

驗收前備份設定目錄到同一證據夾。只有必要證據齊全才把項目改為 `已通過` 並移入 `docs/MIGRATION_COMPLETED.md`(見 `AGENTS.md`);遷移文件只記摘要路徑,不提交媒體檔。

## 工具選擇

| 目的 | 用法 |
| --- | --- |
| 點選主視窗控件 | **pyatspi**:依 `name`/`role` 找節點,`queryAction().doAction(0)`;失敗再用 `getExtents` + `xdotool` |
| 視窗截圖 | `xdotool getwindowgeometry --shell` → `ffmpeg -f x11grab -video_size WxH -i :0.0+X,Y -frames:v 1 -update 1 out.png` |
| 操作錄影 | `ffmpeg -f x11grab -video_size 螢幕 -framerate 8 -i :0.0 -vf scale=1600:-2 ... out.mp4` |
| 寫入剪貼簿 | `gpaste-client add '文字'` |
| 讀取剪貼簿 | `gpaste-client history \| head -1 \| sed 's/^[^:]*: //'`(**不要** `gpaste-client get 0`) |
| 注入檔案路徑 | Python `subprocess.Popen([bin,'/file','/f:t','/b:f', unicode_path])` 交給既有實例;**不要**用 `xdotool type` 打路徑 |
| Element Plus 下拉 | 點 combo → `xdotool key Down`(次數依選項)→ `Return`;驗證欄位顯示已變 |
| 原生確認(rfd/plugin-dialog) | AT-SPI 找 `alert`;按鈕常為 `確定(O)`/`取消(C)`;`Tab` 後 `space` 可確認 |

切換輸入法至英文(例如 `fcitx5-remote -s keyboard-us`)仍不足以保證 `xdotool type` 路徑正確;路徑一律走剪貼簿貼上或 CLI argv。

## 項目流程(ConvertZZ)

### D-07 首次匯入前詢問

1. 備份並移走資料目錄,建立空目錄。
2. 將舊 `ConvertZZ.json` 放在啟動 cwd(`legacy_settings_path` 查 exe 旁與 cwd)。
3. 從該 cwd 啟動 release 二進位;導覽至「匯入舊版設定」。
4. 證據:畫面須有「匯入找到的設定/選擇檔案/略過匯入」,且未自動寫入。

### D-11 儲存字典前詢問

1. 設定可寫 `dictionaryPath`(可寫入 `settings-v2.json` 後重啟,或成功選檔後)。
2. 「新增」一筆即可讓 `changeCount>0`(單元格未填也可觸發儲存確認)。
3. 按「儲存變更」→ 擷取「確認字典備份」。

### E-02 檔名預覽顯示來源與輸出

1. 用 CLI 注入簡體檔名樣本路徑(避免檔案選擇器)。
2. 取消自動「內容」計畫 → 作業改「轉換檔名」、方向「簡轉繁」→「建立預覽」。
3. 證據:表格同時有「來源」「輸出」欄且檔名已轉換。

### E-05 覆寫額外確認

1. 同目錄備好 `电脑.txt` 與已存在的 `電腦.txt`。
2. CLI 注入來源 → 作業「轉換檔名」、衝突策略「覆寫」(鍵盤選取並確認 UI 顯示「覆寫」與警告「輸出路徑已存在。」)。
3. 「確認執行」→「確認檔案轉換」按 `確定(O)` → 擷取「確認覆寫」。不要真的覆寫時按取消。

### G-12 快捷鍵/托盤/浮動球同路由

1. 寫入設定:`quickActions.leftClickCtrl=a3`;啟用一組快捷鍵指向 `a3`;重啟以套用。
2. 同一輸入文字,依序:浮動球 Ctrl+左鍵、快捷鍵、右鍵選單「Unicode 簡 → Unicode 繁」(與托盤選單同一組 legacy action id)。
3. 用 gpaste history 比對三次輸出;寫入 `G-12-compare.txt`。

### G-13 第二實例交棒

1. `pgrep -ax convertzz` 存 before。
2. 再啟動 `convertzz /audio`(或 `/file …`)。
3. after:只剩一個 PID;第二程序應已退出;UI 導向對應頁。

### G-15/G-16 更新

- **G-15**:需要已發佈且已簽署、版本新於本機的 AppImage/Windows 安裝包與有效 `latest.json`。
- **G-16**:DEB/無簽署通道時應走 GitHub 後備。若遠端 latest 舊於本機,只會「已是最新」——記錄 DEB、`latest.json` HTTP 狀態與截圖,標部分完成;勿偽造成「已開啟下載頁」。

### I 平台項

- I-03~I-05:僅 Wayland;可先截 X11 關於頁能力表作對照,但不算通過。
- I-06/I-07:需缺托盤或可安全關閉 Secret Service 的受控環境。
- I-08:三平台矩陣簽核後才結案。

## 結束

1. 寫 `README.md`/`META.txt`。
2. 通過項移入 `docs/MIGRATION_COMPLETED.md`,更新 `docs/MIGRATION_PLAN.md` 摘要與待補備註。
3. 告知使用者設定備份路徑;非經要求不要默默覆寫其設定。
68 changes: 68 additions & 0 deletions .agents/skills/linux-desktop-acceptance/references/pitfalls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 坑與正確作法

從 2026-08-18 ConvertZZ Linux X11 驗收整理。SKILL 正文只保留流程;此處為反模式對照。

## 程序與啟動

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| `convertzz --version` 或對 GUI 二進位跑「印版本就退」 | 開主視窗,指令不退出 | 用 `dpkg -l`、`stat` 二進位、或設定/關於頁看版本 |
| `pkill -f convertzz` | 誤殺含該字串的 agent shell | `pgrep -x convertzz` → 對 PID `kill`/`kill -9` |
| 假設已安裝 DEB 與 repo release 同建置 | 驗到舊行為 | 優先 `src-tauri/target/release/convertzz`;在 `META.txt` 記 mtime 與 git SHA |

## 輸入法與路徑

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| `xdotool type /tmp/.../中文或長路徑` | fcitx/Rime 把按鍵組成錯字,設定寫入垃圾路徑 | `gpaste-client add "$path"` → 對話框 `Ctrl+L` → `Ctrl+V`;或完全避開對話框 |
| 依賴檔案/資料夾選擇器完成 E/D | 對話殘留、位置列彈層、路徑未進 app | 第二實例 CLI:`[bin,'/file','/f:t','/b:f', unicode_path]`(Python argv,勿經 shell 弄壞編碼) |
| 選完資料夾未確認「尚未選取路徑」是否消失 | 後續預覽空白仍繼續點 | 每次選路徑後截圖確認 path summary |

## UI 自動化

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| 用視窗百分比盲點「下一步」 | Tour 不動或誤關 | pyatspi 找 `push button`「下一步」/「結束導覽」 |
| AT-SPI `doAction` 點 Element Plus `list item` 就當選取成功 | 下拉仍顯示舊值(例如衝突策略一直「略過」) | 點 combo 後 `Down`×N + `Return`;截圖確認欄位文字已變 |
| 原生確認找 `Yes`/`No`/`是`/`否` | 找不到按鈕 | 找 `alert`;按鈕多為 `確定(O)`/`取消(C)`;或 `Tab` + `space` |
| `Escape` 以為一定關掉 rfd 確認 | 對話殘留,擋住後續操作 | 明確點取消;重開流程前先確認無 `alert` |
| 像素找 Element Plus primary 藍鈕 | 深色主題/進度點誤判 | 以 AT-SPI name 為準;像素僅輔助 |

## 剪貼簿(G-12)

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| `gpaste-client get 0` | UUID API 失敗,誤判轉換沒跑 | `gpaste-client history` 取第一筆內容 |
| 未先寫入 `quickActions`/`hotkeys` 就測 | 全是 `"0"`/未啟用,無輸出 | 改 `settings-v2.json` 後**重啟**再測 |
| 只測浮動球就宣稱托盤通過 | 證據不足 | 快捷鍵 + 浮動球手勢 + 與托盤共用 action id 的選單項;在 compare 檔註明托盤與右鍵選單同源 |

## 檔案/覆寫(E)

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| CLI 注入後不改作業模式 | 一直是「轉換內容」預覽 | 取消計畫 → 改「轉換檔名」→ 再建立預覽 |
| 衝突策略 UI 未變成「覆寫」就按執行 | 不會出現第二次「確認覆寫」 | 確認警告「輸出路徑已存在。」且策略顯示「覆寫」 |
| 覆寫測完不還原樣本 | 後續重跑缺衝突 | 測前重建 `电脑.txt`/`電腦.txt`;確認框用取消離開 |

## 更新(G-15/G-16)

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| 本機 2.0.0 + GitHub latest 1.0.0.8 卻期待「開啟下載頁」 | 只會「已是最新版本」 | 記 DEB、`latest.json` 狀態、截圖;標部分完成 |
| 無簽署 `latest.json` 卻標 G-15 通過 | 違反證據規則 | 等正式簽署發行通道 |

## 平台(I)

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| 在 X11 截關於頁就標 I-03~I-05 通過 | 證據平台不符 | 明確寫「需 Wayland」 |
| 為 I-07 直接殺 gnome-keyring | 可能鎖死桌面金鑰/登入 | 受控環境或標受限;程式契約另用單元/程式碼核對 |

## 證據與文件

| 錯誤作法 | 後果 | 正確作法 |
| --- | --- | --- |
| 單元測試通過就改 `已通過` | 違反 AGENTS.md | 必要證據入倉庫外目錄(預設 `~/Desktop/ConvertZZ-acceptance/YYYY-MM-DD/`) |
| 把錄影/截圖提交進 `docs/` | 倉庫膨脹 | 證據留本機;遷移文件只寫摘要與路徑 |
| 不寫環境限制 | 下次重複踩坑 | README 固定「環境限制」表 |
| 驗收中覆寫使用者設定不備份 | 難以還原 | 先 `cp -a` 資料目錄到證據夾 |
5 changes: 5 additions & 0 deletions .codegraph/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
5 changes: 3 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
# Normalize text to LF in the working tree too. Windows runners otherwise
# check out CRLF, and Prettier --check (endOfLine: lf) fails in release CI.
###############################################################################
* text=auto
* text=auto eol=lf

###############################################################################
# Set default behavior for command prompt diff.
Expand Down
39 changes: 39 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/sh
set -eu

staged="$(git diff --cached --name-only --diff-filter=ACM || true)"
[ -n "$staged" ] || exit 0

js_files="$(printf '%s\n' "$staged" | grep -E '\.(cjs|css|html|js|json|mjs|ts|tsx|vue|ya?ml)$' || true)"
rs_files="$(printf '%s\n' "$staged" | grep '\.rs$' || true)"

if [ -n "$js_files" ]; then
if ! command -v pnpm >/dev/null 2>&1; then
printf '%s\n' "pnpm 不存在,無法自動格式化 Vue 與 Node.js。" >&2
exit 1
fi
set --
while IFS= read -r file; do
[ -f "$file" ] || continue
set -- "$@" "$file"
done <<EOF
$js_files
EOF
if [ "$#" -gt 0 ]; then
pnpm exec prettier --write --ignore-unknown -- "$@"
git add -- "$@"
fi
fi

if [ -n "$rs_files" ]; then
if ! command -v cargo >/dev/null 2>&1; then
printf '%s\n' "cargo 不存在,無法自動格式化 Rust。" >&2
exit 1
fi
cargo fmt --manifest-path src-tauri/Cargo.toml
printf '%s\n' "$rs_files" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add -- "$file"
fi
done
fi
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CI

on:
push:
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
with:
run_install: false
- uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
- name: 安裝相依套件
run: pnpm install --frozen-lockfile
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
shared-key: ci
save-if: ${{ github.event_name != 'pull_request' }}
- name: 安裝 Linux 建置套件
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential ffmpeg libayatana-appindicator3-dev libdbus-1-dev \
librsvg2-dev libwebkit2gtk-4.1-dev pkg-config
- name: 檢查前端
run: pnpm run check
- name: 執行 Rust 核心測試
run: cargo test --manifest-path src-tauri/Cargo.toml --workspace -- --test-threads=1

e2e:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
with:
run_install: false
- uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
- name: 安裝相依套件
run: pnpm install --frozen-lockfile
- name: 安裝 Playwright Chromium
run: pnpm exec playwright install --with-deps chromium
- name: 執行前端 e2e
run: pnpm run test:e2e

rust-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: 檢查 Rust 格式
run: cargo fmt --manifest-path src-tauri/Cargo.toml -- --check
Loading
Loading