refactor(usage): extract the usage-percent widgets onto a shared module - #545
Draft
zachthedev wants to merge 3 commits into
Draft
refactor(usage): extract the usage-percent widgets onto a shared module#545zachthedev wants to merge 3 commits into
zachthedev wants to merge 3 commits into
Conversation
Add a per-widget `numberFormat` ({ style: precise|compact|whole, decimals })
and an optional per-kind global (Settings.numberFormat), resolved by
resolveNumberFormat(kind, item, settings) with a set global winning over the
per-widget value (matching overrideForegroundColor / globalBold). Every numeric
formatter routes through renderMagnitude with a type-appropriate baseline, so
default output is unchanged: tokens/speed/percent stay 1 decimal, cost 2,
memory G1/M0/K0.
Styles: precise keeps trailing zeros (1.0M, today's default); compact trims
them (1M, 1.1M); whole drops decimals (1M).
Threaded through every numeric widget (tokens, context length/window/bar,
compaction reclaimed, cache read/write/hit, free memory, session cost, speeds,
context %, usage sliders, block/weekly timers). TUI: ColorMenu (n) cycles a
widget's style, Global Overrides has a per-type submenu, and decimals is set in
settings.json. Optional schema fields, so no settings migration.
Co-Authored-By: Claude <noreply@anthropic.com>
Widgets declare supportsNumberFormat(), matching the existing supportsRawValue / supportsColors convention, and the items editor injects a shared precision keybind for any widget that returns true. The cycle itself moves to utils/number-format.ts so all number-format logic lives in one module. This keeps every non-color override on the line editor screen, where the rest of them already live, and keeps precision reachable while a powerline theme is active. The color menu goes back to color, bold and dim only. The keybind is '.', reading as the decimal point, because every letter is already taken by a widget bind or an editor-level one. The items-editor dispatcher no longer bails early for widgets that expose no keybinds of their own, so plain numeric widgets receive the injected bind. Fable Weekly Usage, which landed since this branch opened, is threaded through formatPercent too.
SessionUsage, WeeklyUsage, WeeklySonnetUsage, WeeklyOpusUsage and FableWeeklyUsage carried five copies of one render body. Normalized for the per-widget names, the worst pair differed by three lines and two pairs were identical. Each file is now delegation, following the speed-widget.tsx and InputSpeed.ts pair. Six axes vary and are parameterized: label, preview percent, usage field, window resolver, display name and description. Default color, category, editor display, editor actions, keybinds and the three supports* flags were already uniform. The resolvers dispatch at call time rather than through a captured reference, because the widget tests spy them on the utils/usage namespace and a module-level table would bind before the spy is installed. FableWeeklyUsage's label was the one of the five that disagreed with its own display name, so 'Fable Weekly: ' becomes 'Weekly Fable: ', matching the sibling rule of display name minus ' Usage'. Raw mode prints no label and is unaffected. The widget type, class name and display name are untouched. ExtraUsageUtilization stays out: it gates on a tri-state enabled flag, has no window resolver, no time cursor and a different default color. The shared suite gains a whole-percent case. formatPercent's format argument is optional and its default reproduces the baseline, so a render path that stopped passing the resolved format rendered identically under default settings and no test could see it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #456, so this targets
mainand the diff below includes that PRuntil it merges. The commit to review here is just the last one. I will retarget
once #456 lands.
Stacked deliberately rather than sequenced after: #456 threads
formatPercent(value, format)through exactly these five render bodies, soextracting on top of it absorbs that threading instead of colliding with it.
Five widgets carried five copies of one render body:
SessionUsage.ts,WeeklyUsage.ts,WeeklySonnetUsage.ts,WeeklyOpusUsage.tsandFableWeeklyUsage.ts. Normalized for the per-widget names,SessionUsageandWeeklyUsageare identical, so areWeeklySonnetUsageandWeeklyOpusUsage,and the worst pair differs by three lines. Net -225 lines.
Each file is now delegation, following the existing
shared/speed-widget.tsxandInputSpeed.tspair.Six axes vary and are parameterized: label, preview percent, usage data field,
window resolver, display name, description. Default color, category, editor
display,
handleEditorAction, keybinds and the threesupports*flags werealready uniform across all five, so they live in the shared module outright.
None of the five declares
getHideableStatesorrenderEditor.One rendered string changes.
FableWeeklyUsage's label was the only one ofthe five that disagreed with its own display name:
Session UsageSession:Weekly UsageWeekly:Weekly Sonnet UsageWeekly Sonnet:Weekly Opus UsageWeekly Opus:Weekly Fable UsageFable Weekly:It becomes
Weekly Fable:, matching the sibling rule of display name minusUsage. Raw mode prints no label, so raw output is unchanged, as are the widgettype
fable-weekly-usage, the class name and the display name. Six assertions inFableWeeklyUsage.test.tsmove with it; no other assertion in the repo changed.Two things worth a look:
five test files spy their resolver on the
utils/usagenamespace. Amodule-level config table holding
resolveWeeklyOpusUsageWindowwould bind atmodule evaluation, before
beforeEachinstalls the spy, and silently bypassit.
SessionUsagealso needs a second argument (context.blockMetrics) thatthe four weekly resolvers ignore, so the parameter takes the whole context.
ExtraUsageUtilizationstays out. It gates on a tri-stateextraUsageEnabledwith its ownn/abranch, has no window resolver, no timecursor, an extra keybind and a different default color. Folding it in would mean
three optional flags that are uniformly absent from the five. It is also the one
usage widget that does not use
runUsagePercentWidgetSuite.The shared suite gains a whole-percent case, which is the one test addition
beyond the label.
formatPercent'sformatargument is optional and its defaultreproduces the baseline output, so a render path that stopped passing the
resolved format rendered identically under default settings and nothing could
catch it. Dropping the argument at all three call sites now fails all five
widgets.
Tested:
bun run lintclean;bun test1900 pass, plus theglobal-command-resolutionfailuremainalready has on this host and one hit ofthe flaky
fetchUsageDatacase.