繁體中文

新功能

斷網時的費用估算,終於不再把價格算歪

出門沒網路卻想核對用量花多少錢,螢幕上的數字本該是你的依據,這回卻曾把 Opus 4.6、4.7 算成三倍,還把 Opus 5 算成零元。程式抓不到 LiteLLM 的價格表,或快取(把查過的結果暫存起來直接用)過期時,會改查手寫的備援價格表;它長期沒和目前表格比對,舊價格便一路留著。現在已按快取的 LiteLLM 表校正:4.6、4.7 的每百萬計費文字單位從舊的 $15/$75 改為 $5/$25,Haiku 4.5 也補回少算的兩成,原本缺席的 Opus 5 也不會再回傳 $0。像離線收銀機翻出過期價目表,現在換回正確版本;斷網時更該能信帳單。

問題修復

繁中介面混入 52 處簡體字,這次修正

你選繁體中文開啟文字介面(TUI,用文字排成、在終端機操作的畫面)或報表,卻看到「检测到」「时间」這種簡體字,像一本校對到一半的書。問題出在 52 個字串直接從簡中 zh-CN 複製過來,沒有轉換。先前靠人工列簡體字清單,只抓到一部分,連「来」和「项」都漏了;這次把每個值交給 OpenCC 的 s2twp 逐一轉換,再逐項檢視差異。轉換器想把原本正確的「發送」改成「傳送」等九處,則沒有採用。實際改動的兩個 HTML 測試畫面也只補那兩句,免得連固定的時間與版本都被重生。日文、韓文各有 63 個空字串是尚未翻譯,這次沒有假裝修好它們。

新功能

選單列更新工作搬家,畫面行為照舊

你會看到選單列照常更新,只是背後那段 203 行的工作搬了家。menubar.py 已經拆過又長回來兩次,原先離檔案長度上限只差四行;與其把上限往上推,開發者把背景更新的資料處理拆到 menubar_refresh.py,分成 load_sources() 和 build_result()。畫面相關的三次 performSelectorOnMainThread 呼叫與所有 selector(靠名稱找到要執行方法的標記)留在原檔,因為 AppDelegate 的方法就是按名稱綁定,搬走會斷線。新檔透過 typing.Protocol 描述需要的 App 介面,完全不載入 PyObjC。像把塞爆的廚房分出備料間:接客的窗口不動,備料移到隔壁,送餐流程仍照原本走。六個更新計時階段、降級路徑的 15 次狀態寫入,以及失敗後避免卡死的保護,都逐行比對確認沒變。

English

FEATURE

Cost estimates stay accurate when you're offline

When you are away from the internet and checking what your usage cost, the number on screen needs to be dependable. It was not: Opus 4.6 and 4.7 were priced at three times their rate, while Opus 5 appeared to cost nothing. If LiteLLM's price table could not be downloaded, or its cache (saved lookup results kept for reuse) had expired, the app used a hand-maintained fallback table that had drifted from the current one. It is now reconciled with LiteLLM's cached table: 4.6 and 4.7 move from $15/$75 to $5/$25 per million billing units, Haiku 4.5 regains its missing twenty percent, and Opus 5 no longer returns $0. Think of an offline checkout pulling out an old price sheet; it now has the right sheet when you need it most.

BUGFIX

Traditional Chinese screens had 52 Simplified Chinese strings mixed in, now fixed

If you chose Traditional Chinese and opened the text interface (TUI, a screen operated in a terminal with text), you could meet Simplified Chinese such as 检测到 and 时间—like opening a book only half proofread. Fifty-two strings had been copied from the Simplified Chinese zh-CN locale without conversion. A hand-written list of Simplified characters had caught only some of them, missing even 来 and 项. Every value now went through OpenCC's s2twp conversion and each difference was reviewed. Nine suggested replacements, including 發送 to 傳送, were left alone because the existing text was already correct. Only two changed strings in the HTML test snapshot were patched, preserving its fixed timestamp and version. Japanese and Korean each still have sixty-three empty strings; those are missing translations, not this bug, so they remain for later work.

FEATURE

Menu-bar refresh work moves house without changing behavior

This is not a new button: your menu-bar refresh still looks the same, but its 203-line back-room job has moved. menubar.py had been split apart twice and then grown again, leaving only four lines below its size limit. Instead of raising that limit, the data work of the background refresh moved to menubar_refresh.py, divided into load_sources() and build_result(). The three performSelectorOnMainThread calls and every selector (a name used to locate a method to run) stay in the original file, because AppDelegate methods are bound by name and moving them would break that connection. The new module describes the App it needs through typing.Protocol and imports no PyObjC. It is like moving prep work from an overcrowded kitchen into a side room: the service window stays put and meals follow the same path. Six timing stages, fifteen degraded-path state assignments, and the guard against a failed refresh getting stuck were compared line by line.

原始 CHANGELOG

Original changelog斷網時的費用估算,終於不再把價格算歪
**The offline price table overcharged Opus 4.6 and 4.7 threefold, and valued Opus 5 at nothing.** `_fallback_pricing()` is what the cost estimate falls back on when the LiteLLM table cannot be downloaded or its cache has expired. It is written by hand and nothing ever reconciled it against the live table, so it drifted in silence: `claude-opus-4-6` and `claude-opus-4-7` still carried the Opus 4.1-era $15/$75 per Mtok long after Anthropic dropped Opus to $5/$25, `claude-haiku-4-5-20251001` was twenty percent low, and `claude-opus-5` was missing outright — and a missing key makes `calculate_cost()` return `0.0`, so Opus 5 usage was reported as costing nothing. The corrected figures come from reconciling against the cached LiteLLM table rather than from the changelog: Claude Code 2.1.219 announced Opus 5 at `$10/$50 per Mtok`, but that is the fast-mode price, not the standard one. What makes this class of bug worth naming is when it surfaces — only offline, which is exactly the moment nobody thinks to doubt the number on screen.
Original changelog繁中介面混入 52 處簡體字,這次修正
**Fifty-two keys in the Traditional Chinese locale contained Simplified Chinese.** The TUI and report strings had been copied across from `zh-CN` without conversion, so a Traditional Chinese user opening the TUI read `检测到`, `时间`, `来源`, `项目` and `消息`. A hand-written list of Simplified characters found only some of them — it missed `来` and `项` — so every value was instead run through OpenCC's `s2twp` and the differences reviewed one by one. Nine of those differences were the converter overreaching on text that was already correct (`發送`→`傳送`, `吹回`→`吹迴`, `連接詞`→`連線詞`) and were left alone. The golden HTML snapshot was patched for the two strings that actually changed rather than regenerated, because regenerating it would also replace the frozen timestamp and version the test asserts on. The same region of `ja` and `ko` holds sixty-three empty strings each; those are missing translations rather than wrong ones and are left for a separate change.
Original changelog選單列更新工作搬家,畫面行為照舊
**`_refresh_in_background` moved into `menubar_refresh.py`.** At 203 lines it was the largest method in `menubar.py`, which had four lines left under its ceiling — and a ceiling exists on that file precisely because it has been split apart and grown back twice. Only three lines of the method touch PyObjC, the three `performSelectorOnMainThread` calls, and those stay behind along with every selector: an AppDelegate method is bound by name, so relocating one breaks the binding. What moved is the data work between them, split into `load_sources()` and `build_result()`, with the app passed in behind a `typing.Protocol` so the new module imports no PyObjC at all — the same rule that keeps `menubar_state.py` clean. `menubar.py` goes from 2116 lines to 1934 and the ceiling was lowered to match rather than raised, since raising it to get CI green would dismantle the only thing stopping the file from growing back a third time. Behaviour is unchanged, and that was verified rather than assumed: the six `refresh_timing` stages, the fifteen state assignments on the degraded path, and the `submitted`/`finally` guard that keeps a failed refresh from wedging the app were each compared line by line against the original.