Skip to content

refactor(usage): extract the usage-percent widgets onto a shared module - #545

Open
zachthedev wants to merge 1 commit into
sirmalloc:mainfrom
zachthedev:refactor-usage-percent-widgets
Open

zachthedev wants to merge 1 commit into
sirmalloc:mainfrom
zachthedev:refactor-usage-percent-widgets

Conversation

@zachthedev

@zachthedev zachthedev commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Now that #456 has merged this is a single standalone commit against main.
The extraction sits on top of that PR's formatPercent(value, format) threading
rather than colliding with it, which is why it was sequenced this way.

Five widgets carried five copies of one render body: SessionUsage.ts,
WeeklyUsage.ts, WeeklySonnetUsage.ts, WeeklyOpusUsage.ts and
FableWeeklyUsage.ts. Normalized for the per-widget names, SessionUsage and
WeeklyUsage are identical, so are WeeklySonnetUsage and WeeklyOpusUsage,
and the worst pair differs by three lines. Net -225 lines.

Each file is now delegation, following the existing shared/speed-widget.tsx and
InputSpeed.ts pair.

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 three supports* flags were
already uniform across all five, so they live in the shared module outright.
None of the five declares getHideableStates or renderEditor.

One rendered string changes. FableWeeklyUsage's label was the only one of
the five that disagreed with its own display name:

Widget Display name Label
Session Usage Session Usage Session:
Weekly Usage Weekly Usage Weekly:
Weekly Sonnet Usage Weekly Sonnet Usage Weekly Sonnet:
Weekly Opus Usage Weekly Opus Usage Weekly Opus:
Weekly Fable Usage Weekly Fable Usage Fable Weekly:

It becomes Weekly Fable: , matching the sibling rule of display name minus
Usage. Raw mode prints no label, so raw output is unchanged, as are the widget
type fable-weekly-usage, the class name and the display name. Six assertions in
FableWeeklyUsage.test.ts move with it; no other assertion in the repo changed.

Two things worth a look:

  • The resolvers dispatch at call time, not through a captured reference. All
    five test files spy their resolver on the utils/usage namespace. A
    module-level config table holding resolveWeeklyOpusUsageWindow would bind at
    module evaluation, before beforeEach installs the spy, and silently bypass
    it. SessionUsage also needs a second argument (context.blockMetrics) that
    the four weekly resolvers ignore, so the parameter takes the whole context.
  • ExtraUsageUtilization stays out. It gates on a tri-state
    extraUsageEnabled with its own n/a branch, has no window resolver, no time
    cursor, 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's format argument is optional and its default
reproduces 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 lint clean; bun test 1900 pass, plus the
global-command-resolution failure main already has on this host and one hit of
the flaky fetchUsageData case.

@zachthedev
zachthedev force-pushed the refactor-usage-percent-widgets branch from c568271 to b275047 Compare August 14, 2026 23:03
@zachthedev
zachthedev force-pushed the refactor-usage-percent-widgets branch 2 times, most recently from d7b870f to ee44c7d Compare September 3, 2026 15:21
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.
@zachthedev
zachthedev force-pushed the refactor-usage-percent-widgets branch from ee44c7d to b001d9c Compare September 4, 2026 13:58
@zachthedev
zachthedev marked this pull request as ready for review September 4, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant