Skip to content

feat(widgets): allow speed widget windows up to an hour - #769

Merged
sirmalloc merged 4 commits into
sirmalloc:mainfrom
eric-engberg:feat/speed-window-hour
Oct 30, 2026
Merged

sirmalloc merged 4 commits into
sirmalloc:mainfrom
eric-engberg:feat/speed-window-hour

Conversation

@eric-engberg

Copy link
Copy Markdown
Contributor

What

The rolling window on the Input, Output and Total Speed widgets now goes up to 3600 seconds (an hour) instead of 120. The editor keeps a typed 600 or 3600. Anything longer saves as 3600, the same way values above 120 used to save as 120. 0 still means the whole-session average.

Why

The 120-second cap came in with the speed widgets in #141, and no reason was recorded for it. Nothing in the calculation needs it:

  • Every render reads the whole transcript, whatever the window. The window only chooses which of the collected responses count.
  • The window ends at the transcript's last entry. It counts only the time from each prompt or tool result to its response, so idle time between turns doesn't thin it out.
  • A window longer than the session gives the same figure as the session average.

So the cap only rules out windows someone might want, such as the last 10 minutes or the last hour of a long session. Those smooth out the swings of a two-minute window without going all the way back to the start of the session.

Measured with two made-up transcripts, 40 minutes and 2 hours long. Each has one 20-second response a minute at 60 output t/s, then 30 t/s for the last 10 minutes and 15 t/s for the last 2. The line has five Output Speed widgets with windows of 0, 120, 600, 3600 and 36000 seconds:

Transcript main this branch
40 minutes 51.8, 15.0, 15.0, 15.0, 15.0 51.8, 15.0, 27.0, 51.8, 51.8
2 hours 57.3, 15.0, 15.0, 15.0, 15.0 57.3, 15.0, 27.0, 54.5, 54.5

The built CLI prints the same under Bun 1.4.2 and Node 26.10.0. On main, every window above 120 reads as 120. On this branch, 600 covers the last 10 minutes, and on the 40-minute session 3600 matches the session average.

Render time doesn't depend on the window. Over a 100,000-line (11.9 MB) transcript, the built CLI took 180-189 ms under Bun and 391-412 ms under Node, for windows of 0, 120 and 3600 alike (7 runs each).

Why an hour rather than no limit: once a window is longer than the session, it gives the session average, and 0 already gives that. A limit loses nothing, and it stops a typo such as 36000 from quietly meaning the whole session. In the 2-hour row, 36000 reads as the last hour (54.5) and not as the session average (57.3).

How

  • MAX_SPEED_WINDOW_SECONDS goes from 120 to 3600. Every clamp reads it: saving from the editor, reading a widget's saved window, and the list of windows the render asks the transcript scan for. The scan (normalizeSpeedWindows in jsonl-metrics.ts) has no upper limit of its own. windowedSpeedMetrics is keyed by the clamped value on both the request side and the lookup side, so nothing else changes.
  • The window editor already accepts any number of digits and clamps on Enter. So 3600 can be typed, and a longer value saves as 3600. Its prompt reads the constant and now shows (0-3600).
  • The three widget descriptions and the speed widgets' entry in docs/USAGE.md now say 0-3600.
  • Saved windows read the same as before, because the editor never saved more than 120. A value above 120 written into settings.json by hand used to read as 120; it now takes effect, up to 3600.
  • Left as is:
    • The editor row label stays in seconds ((600s window), (3600s window)). The editor takes seconds and the label repeats the number typed. Switching to minutes for round values would show 10m for a typed 600 next to 90s for a typed 90.
    • The README's v2.2.0 release notes still say 0-120, because they describe that release.

Demo

In the line editor: select Output Speed, press w, clear the 0, type 600, press Enter and save with ctrl+s. Before, the row then shows (120s window); after, it shows (600s window). The TUI preview only has sample values, the same for every window, so each GIF then quits the TUI and pipes a payload through the CLI with the saved settings.

The transcript in that payload is synthetic, written for the demo. It covers 25 minutes in five turns, with idle gaps between them. Every response runs at 30 output t/s, except the three in the last turn, which run at 80 t/s within the transcript's final 70 seconds. That gives three different figures: 35.0 t/s for the session average, 40.0 t/s for the last 600 seconds and 80.0 t/s for the last 120 seconds. The before GIFs end on 80.0 t/s, the 120-second figure, because the 600 was saved as 120. The after GIFs end on 40.0 t/s, the 600-second figure. Rendered directly with windows of 0, 120 and 600 seconds, main reads 35.0, 80.0 and 80.0, and this branch reads 35.0, 80.0 and 40.0.

Powerline

Before:

Output Speed window set to 600 in Powerline mode, saved as 120s, rendering 80.0 t/s

After:

Output Speed window set to 600 in Powerline mode, saved as 600s, rendering 40.0 t/s

Plain

Before:

Output Speed window set to 600 in plain mode, saved as 120s, rendering 80.0 t/s

After:

Output Speed window set to 600 in plain mode, saved as 600s, rendering 40.0 t/s

Testing

  • On main as of [low] perf(claude-status): hold a lock while the status page fetch is in flight #635 and [medium] perf(git-review): key the PR cache through the cached runGit #639 (62a371b): merges cleanly, bun run lint is clean, and bun test gives 3711 pass, 0 fail. The built CLI under Bun 1.4.4 and Node 26.13.0 matches main in plain and Powerline mode with the baseline configs, and the TUI opens and exits cleanly.
  • bun test: 3176 pass, 0 fail (179 files). main has 3168 pass (178 files).
  • 8 new tests and 4 changed ones. 10 of them fail on main:
    • the 4 changed tests, which expected 120;
    • 6 new tests: a one-hour window is stored, rendered and labelled; the editor prompt shows 0-3600; the editor saves 600 and 3600; the editor clamps 3601 to 3600.
  • The other 2 new tests pass on main. They check that a saved 120-second window reads and saves unchanged.
  • The new editor test file passed 15 runs in a row.
  • bun run lint is clean.
  • Under vitest on Node 26.10.0, the three changed test files pass: 35 tests. Run against main's code, the same 10 fail.
  • The built CLI renders the same under Bun 1.4.2 and Node 26.10.0, in plain and Powerline mode. With an Output Speed widget that has no window set, it also renders the same as main. The TUI opens and exits cleanly under both. Windowed renders are in the table above.

eric-engberg and others added 4 commits October 17, 2026 16:21
The Input, Output and Total Speed widgets' rolling window was capped at
120 seconds. Nothing in the calculation needs that cap: every render
reads the whole transcript whatever the window, the window ends at the
transcript's last entry and counts only the time responses were being
generated, and a window longer than the session gives the session
average. The cap only rules out longer horizons, such as the last 10
minutes or the last hour of a session.

Raise the cap to 3600 seconds. The window editor already takes any
number of digits and clamps on save, so 3600 is now kept and a longer
value saves as 3600, the way 120 did before. Past the session's length a
window equals the session average, which 0 already gives, so a bound
loses nothing, and it keeps a typo such as 36000 from quietly meaning
the whole session. Windows saved under the old cap read the same.
@sirmalloc
sirmalloc merged commit 0b08b31 into sirmalloc:main Oct 30, 2026
3 checks passed
pcvelz added a commit to pcvelz/ccstatusline-usage that referenced this pull request Nov 2, 2026
Upstream: weekly limit timers (sirmalloc#768), prompt cache miss widgets (sirmalloc#770), context window size env override (sirmalloc#352, sirmalloc#407), windowed speed fixes (sirmalloc#769, sirmalloc#771), separator/renderer fixes (sirmalloc#773), custom-command styling (sirmalloc#775), clickable cwd links (sirmalloc#744), account email from live credentials (sirmalloc#724), cache timer reported expiry + short toggle (sirmalloc#576, sirmalloc#592), claude-status lock (sirmalloc#635, sirmalloc#772), dedup token metrics (sirmalloc#622, sirmalloc#725, sirmalloc#735).
Not adopted: sirmalloc#765 terminal width from COLUMNS with fixed reserve - the fork keeps flexMode/compactThreshold and tmux pane priority.

Hand edits outside conflicts:
- README.md: kept fork Configure flex separator behavior bullet and older-updates details wrapper; dropped upstream Related Projects additions (claude-eta)
- AGENTS.md: kept fork terminal width wording (sirmalloc#765 not adopted)
- src/types/FlexMode.ts, src/tui/components/TerminalWidthMenu.tsx, src/tui/components/__tests__/TerminalWidthMenu.test.ts, src/tui/components/TerminalOptionsMenu.tsx, src/tui/components/__tests__/TerminalOptionsMenu.test.ts: restored, fork keeps terminal width settings
- src/tui/App.tsx, src/tui/components/index.ts, src/types/index.ts: re-wired terminal width menu and FlexMode export
- src/types/StatusJSON.ts: dropped fork duplicate prompt_cache key, upstream schema is a superset
- src/utils/terminal.ts: COLUMNS source gated on non-tmux so tmux pane width keeps priority
- src/widgets/WeeklyLimitTimer.ts, src/utils/usage-prefetch.ts, src/widgets/__tests__/WeeklyLimitTimer.test.ts: compat fix - fableUsage/fableResetAt -> weeklyFableUsage/weeklyFableResetAt
- src/widgets/ApiUsage.tsx: compat fix - Context Bar honors effective context window (override, clamp, 1.0M format); Weekly renders a valid value despite a session error
- src/widgets/__tests__/ApiUsage.localModelFallback.test.ts: output tokens no longer counted in context (upstream sirmalloc#730)
- src/utils/__tests__/renderer-flex-width.test.ts, src/utils/__tests__/renderer-ansi.test.ts, src/utils/__tests__/gradient.test.ts, src/utils/__tests__/renderer-separator-collapse.test.ts, src/utils/__tests__/config.test.ts, src/tui/__tests__/App.test.ts: fork deviation - fork width model (flexMode, 6-column full reserve, kept settings, Terminal Width menu entry)
- src/utils/__tests__/default-colors.test.ts: fork deviation - accept ansi256:/hex: defaults
- src/utils/__tests__/usage-prefetch.test.ts: fork deviation - weekly usage comes from the API, fork widget imports from ApiUsage
- src/widgets/__tests__/CurrentWorkingDir.test.ts, src/widgets/__tests__/CustomCommand.test.ts: Settings literal gains flexMode/compactThreshold
- src/utils/__tests__/context-window.test.ts, src/utils/__tests__/jsonl-metrics.test.ts: restored fork tests upstream dropped
- docs/test-retirements.md: ledger for the dev-reload snapshot-path and RemoteControlStatus glyph (sirmalloc#605) tests upstream replaced
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.

2 participants