Card order was fixed: provider grouping, then whatever order the backend
listed files in. With more than a handful of credentials, "which one is
usable next" meant reading every card.
The new sort ranks credentials by their nearest upcoming reset, across
windows and reset credits alike, and it runs before pagination so the
answer is global rather than per-page.
Quota is click-to-fetch, so most of the list has no instant to sort by.
Those credentials sink to the bottom keeping their provider-grouped
order — the unloaded tail still reads like the default view — and slot
into place as their data arrives. The comparator takes the instant as an
injected resolver, which keeps it store-free and directly testable.
writeQuotaUiState now merges. It previously wrote the whole object, so
adding a second preference would have made changing a tab silently reset
the sort.
The Select sits inside the existing tabs reveal node rather than beside
it: useRevealGroup staggers every [data-reveal] descendant, and a
sibling would have added a cascade step. Reordering relies on stable
card keys, so React moves nodes instead of remounting them — no FLIP and
no replayed entrance, because a re-sort triggered by a minute tick
should not move things with fanfare.
A card lists several limits at once — a 5-hour window, a weekly one, a
handful of manual reset credits — and only the one that recovers first
governs what the credential can do next. Finding it meant reading five
timestamps.
That row now carries a warning-tinted rule and a bolder countdown, with
a title attribute so the state is not conveyed by colour alone. The
emphasis is ranked across windows and credits together: a credit
expiring tonight outranks a weekly window resetting on Friday.
Only genuine capacity-return events are ranked. The Codex subscription
renewal date and xAI's monthly billing rollover are excluded — a spend
cap turning over is not a rate limit lifting — though both still render
their own countdown.
resetCreditRowId is exported and used for both the React key and the
highlight comparison, so the two cannot drift and put the emphasis on
the wrong row. Antigravity ranks against its own server-corrected clock
so the highlight and the countdown beside it always agree.
No transition on the highlight: it moves to another row the moment a
window resets, and cross-fading a jumping target reads as flicker.
Deliberately not merged with buildTimelineLane, which reads the same
five shapes to pick one window per credential — the longest that fits
the visible span. Same inputs, opposite selection rules.
Cards stated `08-13 14:30` and left the arithmetic to the reader, while
Antigravity and Kimi showed only a countdown — three readings of time on
one page. Every absolute date on a Claude, Codex or xAI card now carries
its own countdown: window resets, reset-credit expiry, and the Codex
renewal date.
The renewal date was also the one value rendered as a full toLocaleString;
it is reformatted from its instant so it reads like the rest, falling back
to the old rendering when the payload has no parseable timestamp.
Absolute labels baked at fetch time are kept rather than recomputed —
formatCodexResetLabel resolves reset_after_seconds against the fetch-time
clock and cannot be reproduced at render. A store entry cached without
resetAtMs therefore renders exactly as before.
Relative magnitudes are truncated rather than rounded up. Rounding up
crossed the unit thresholds, rendering "24 hours" just under a day and
"60 minutes" just under an hour, and overstated the time left before a
deadline.
Adds a QuotaClassMap key, so both hosts gain the class in this commit —
bindQuotaClasses throws at module init, which white-screens the app
rather than degrading one row. A test now asserts both stylesheets
define every contract key.
Reset-credit expiry was formatted in a hardcoded Asia/Shanghai while
every other timestamp on the page — window resets, the timeline — used
the browser's timezone. The same credit therefore appeared twice on one
screen in two different timezones: GMT+8 on the card, local in the
timeline tooltip.
Credits now use the same local MM-DD HH:mm shape as every other reset
label, and the "(GMT+8)" that was baked into the heading in all four
locales becomes a runtime-resolved offset, so the timezone is stated
rather than assumed.
The offset is computed arithmetically rather than via Intl's
timeZoneName: 'shortOffset' — that is ES2022-only, needs its locale
pinned to stay ASCII across the four UI languages, and disagrees at UTC
itself (GMT+0 vs GMT).
Relative time goes stale on its own — nothing re-renders when a minute
passes, so a long-lived tab silently lies. The naive fix is a timer per
component, which on the quota page means one per card body.
Adds one app-wide clock instead, started on the first subscriber and
cleared on the last, exposed through useNow(). QuotaTimeline drops its
private interval and joins it, so the chart and the cards above it tick
in lockstep.
Also extracts the timeline's private relative-time formatter into a
shared, testable one. It is now signed: the old version clamped with
Math.max(0, …), so a credit that expired last week read "in 1 minute".
Duration constants and the MM-DD HH:mm shape are deduplicated into
utils/time/durations.ts and formatInstantShort, which the existing
formatQuotaResetTime / formatUnixSeconds now delegate to.
- Introduced deriveAuthFileIdentity to manage display of account email and project ID.
- Updated search logic to include account email and project ID in search criteria.
- Improved styling for file names and account display in AuthFileCard.
- Normalized email and project ID fields in API responses.
- Added tests for new identity logic and search functionalities.
- Introduced new localization strings for quota windows in English, Russian, Simplified Chinese, and Traditional Chinese.
- Enhanced quota types to include reset timestamps and period lengths for various providers (Claude, Antigravity, Codex, Kimi, Xai).
- Implemented utility functions for parsing reset instants from different formats (ISO, Unix, and offsets).
- Developed tests for new reset instant parsing utilities and timeline functionalities.
- Updated quota builders to utilize new reset instant logic and ensure accurate timeline projections.
- Removed deprecated localization keys related to excluded models in zh-TW.json.
- Introduced a new structure for excluded models with improved UI components.
- Refactored AuthFilesOAuthExcludedEditPage to utilize ExcludedModelsPicker for better handling of model exclusions.
- Consolidated styles in AuthFilesOAuthExcludedEditPage.module.scss by removing unused classes.
- Added comprehensive tests for excluded model rules and their matching logic.
- Deleted obsolete tests for OAuth excluded rules and replaced them with more relevant tests for the new structure.
- Ensured that the disabled state of providers is correctly managed and reflected in the UI.