Commit graph

4 commits

Author SHA1 Message Date
ozymandiashh
a208c25f86 fix(menubar): pin multi-argument catalog keys to positional specifiers
Some checks are pending
CI / semgrep (push) Waiting to run
macOS Menubar CI / test (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
The catalog comparison matched specifier lists, so a key whose
placeholders are all %@ passed even when the Chinese value bound the
arguments into the wrong slots — the drift that shipped in three
early-reset keys and was only caught by rendering real data bilingually
(#1331).

All 42 multi-argument keys in both tables now use positional specifiers
(%1$@, %2$lld, ...); the parity check compares slot to verb so Chinese
word order stays free while each slot keeps its type; a new test fails
on any multi-argument key that is not positional.

The source scanner closes its three expression gaps: ternary branches,
concatenated literals, and let-bound literals passed by name.
Text(verbatim:) stays the explicit opt-out; the four provider-name
fallbacks the new rules surfaced in HeatmapSection route through the
catalog as verbatim entries.

Closes #1331
2026-09-15 01:33:28 +03:00
iamtoruk
d4f3d6eb7a feat(menubar): translate the Codex banked-reset copy and the cache-read tooltip
The notification, the Plan-tab detail and the hover-card line #725 added were
composed in a presentation type and shipped English to a zh-Hans user. Each is
one key now. reset_type is vendor data with no fixed vocabulary, so the four
cadence words OpenAI sends are translated and anything else rides through
verbatim inside a translated frame. compactAge stays character-identical with
the TypeScript side in English, which is what the contract was about: en is an
identity table. The dock's cache-read tooltip was bound to a let before its
call site, which is why the scan never saw it.
2026-09-12 16:12:11 -07:00
iamtoruk
f995593f2a fix(menubar): carry the localization catalog onto current main
Rebase resolution for #1289 onto main (#1306, #1313, #1314, #1315, #1328,
#1329). Conflicts kept both intents: main's second-row title composition,
glance-window accessibility actions, cache-read line, host field and merged
Notifications section keep their behaviour and now resolve through L(…).

Adds 14 keys to both tables, drops 4 that no longer have a call site, and
retranslates the two Copilot explanations main rewrote.
2026-09-12 16:12:11 -07:00
ozymandiashh
79d1482951 feat(codex): notify once when OpenAI banks a limit reset
A banked reset is the one capacity signal a user can act on before it is spent,
and it arrives silently. CodeBurn already sees it: the reset-credit inventory
rides every successful Codex quota refresh. Comparing that inventory against the
previous reading is all it takes to notice a grant, so there is no new endpoint,
no new cadence and no new data source here.

The detection is a pure function over an observation and the state it is judged
against, which is what makes the awkward cases testable rather than hopeful:

- The first-ever observation is a baseline. Everything the account already holds
  is history, not a burst of notifications.
- A credit that disappears was spent or expired, and is never an event. Its id
  is kept for the retention window so a payload that lists it again — a flicker,
  not a grant — cannot re-announce it.
- An absent or malformed credits payload is *no opinion*: no event, and no state
  change at all. Treating it as an empty account would re-baseline on every
  failed fetch and re-announce the whole inventory on reconnect.

Seen ids persist to `codex-banked-resets.json` in the CodeBurn cache directory,
written through SafeFile behind an actor exactly the way SubscriptionSnapshotStore
writes its snapshots — the same mechanism, not a second one — so a relaunch does
not repeat a notice. State is written before the post, not after: a notice we
drop is a smaller failure than one that repeats forever. Disconnecting clears the
file, so a reconnect under another account baselines again.

The notification goes out on the existing UpdateNotifier path, one per new
credit, naming what was granted, when, and how many are usable. Never the credit
id, never a token. Settings gains a toggle, default on; with it off the state is
still recorded, so turning it back on does not release a backlog.

Nothing here ever spends a credit. It is a notice.
2026-09-12 14:37:24 -07:00