Repository navigation
feat(widgets): value colors on the usage, context, memory, remaining and cache hit widgets - #46
Draft
eric-engberg wants to merge 115 commits into
Draft
eric-engberg wants to merge 115 commits into
eric-engberg wants to merge 115 commits into
Conversation
Setting a widget's glyph meant typing it, which needs a way to type emoji or Nerd Font icons. In the glyph editor, → now opens a picker: - 17 groups of glyphs, paged with Tab/Shift+Tab or PgDn/PgUp: folders and files, dev tools, git and status, weather and time, nature, objects, faces and hearts, arrows, shapes, text symbols, then Nerd Font groups (folders, git, languages, OS and apps, status, weather) and Powerline glyphs. - The arrow keys move through a group's grid, the highlighted glyph's name shows below it, Enter picks it and ESC goes back. Each group's glyphs all draw at one width so the grid lines up: emoji that terminals show two cells wide by default, and one-cell symbols and Nerd Font icons. Names come from Unicode and the Nerd Fonts glyph names. Typing a glyph still works.
The percent widgets (Context %, Context % (usable), Session and Weekly Usage, Extra Usage Utilization) get one more style for (p): a single glyph for how high the used percent runs, 🟢 under 20%, ⚡ from 20%, 🔥 from 70% and 🚨 from 90%. It replaces the number; a second copy of the widget merged beside it shows both, e.g. `⚡ 42.0%`. The glyph always measures what's used, so (u) isn't offered in this mode. (g) sets the glyph for each level in the glyph editor, picker included, and (l) the three break points. Settings store only what differs from the defaults: `levelGlyph<Level>` and `levelFrom<Level>`.
A row's glyphs are unique within its group, so they make a stable key where the row's position doesn't.
The curated groups hold about 280 of Nerd Fonts' 10,600 icons, so many, like the pull request created, merged, closed and draft icons, weren't there. Typing in the picker now searches every glyph by name: the curated groups' and all of Nerd Fonts', matching every word of the search (`pull req draft`). The groups stay for browsing; a long list of matches scrolls. The full set comes from Nerd Fonts' glyph names (`scripts/generate-nerd-font-glyphs.ts` regenerates it), with aliases sharing a codepoint merged into one entry. The picker loads all its glyph data when it opens, from its own build chunk, so the status line's render path no longer loads even the curated groups.
The full Nerd Font set was only reachable by searching. After the 17 curated groups, the picker now has a group for each icon set: Codicons, Devicons, Font Awesome, Material Design (6,896 icons), Octicons, Weather Icons and the rest, 33 groups in all. PgUp/PgDn move a page within a group and Tab/Shift+Tab switch groups (PgUp/PgDn switched groups before).
A break point held one above its neighbor, or typed, kept its odd offset on every later step (21, 26, 31...), so it never landed on a multiple of 5 again. Steps now go to the next multiple of 5 in that direction.
The arrow and paging keys now work out where the highlight goes in one function, so the key handler only decides between picking, going back, moving and typing. The Nerd Font name format builds its alias list first, and the generator script reads glyphnames.json from the current directory instead of taking a path.
The picker's search found every Nerd Font icon but only the curated emoji and symbols, so most emoji (🤡 clown, 🤢 nauseated) couldn't be found at all. It now also searches every fully-qualified Unicode 18.0 emoji, without the skin tone variants, and every character in the Unicode symbol blocks (arrows, box drawing, math, dingbats and more), plus a few common ones from elsewhere (§ ± × ÷ π ...). scripts/generate-unicode-glyphs.ts generates the data from Unicode's emoji-test.txt and UnicodeData.txt, as the Nerd Font data is generated from glyphnames.json. It loads with the rest of the picker's data, so the status line still never loads it. A glyph is listed once, under its first name, and found by any of its names; an emoji's text and emoji forms (❤ and ❤️) stay separate glyphs.
Ctrl+T in the glyph picker cycles the emoji skin tone: the default, then light, medium-light, medium, medium-dark and dark. Every emoji that takes a tone (332 of them) shows and picks in it, in the groups and in search, with both people toned in a two-person emoji. The tone is a new optional setting, emojiSkinTone, so it's remembered next time; an unknown value falls back to the default rather than invalidating the settings. The Unicode generator records each such emoji's light tone version, worked out from Unicode's own toned versions (the emoji selector goes when a tone comes in, and both people take it), keeping only emoji that have all five tones. The picker turns that into the chosen tone. It reads and saves the tone through a small context that the line editor provides, so the widget editors in between don't change. The glyph editor now shows a multi-part emoji whole (👍🏾, 👩💻). Ink lays out each part as its own character, so the "(default: …)" text after it overwrote the tone or the rest of the emoji; the two are one Text now.
…el-glyphs # Conflicts: # src/tui/App.tsx
A new object on every app render made everything reading the context redraw with it; the value is memoized on the tone now.
…ue-colors-more # Conflicts: # docs/USAGE.md # src/widgets/ExtraUsageUtilization.ts # src/widgets/__tests__/ExtraUsageUtilization.test.ts
A scale can mark a higher value as the good one, as a cache hit rate is: its bands then default to red, yellow and green, and a gradient runs from its urgent end, so 100% is the calm color.
…h they run Session Usage, Weekly Usage, Weekly Sonnet, Weekly Opus and Weekly Fable get value colors (v) on their plain percent, with Extra Usage Utilization's break points: green below 70% used, yellow below 90%, red from 90%, following the used percent even while they show what's left. Their bar modes keep their gradients and the level glyph has no number to color, so (v) is offered only on the plain percent. Utilization now uses the same helpers for when that is.
…text is Context % and Context % (usable) get value colors (v) on their plain percent, with the usage widgets' break points, following the used percent while they show what's left; the slider and the level glyph keep their own looks, so (v) is offered only on the plain percent. Context Length gets them too, measured against the context window as Context % is: 150k of a 200k window is yellow.
…t Rate Memory Usage colors by the share of memory used, and Extra Usage Remaining by the share of the limit spent, as Extra Usage Used does, both with the usage widgets' break points. Cache Hit Rate counts the other way, since a higher rate is the good one: red below 50%, yellow below 80%, green from 80%. The docs list every widget with value colors and how each one measures.
eric-engberg
force-pushed
the
feat/value-colors-more
branch
from
October 6, 2026 06:29
8c5a372 to
59f6c8a
Compare
# Conflicts: # src/widgets/shared/symbol-override.tsx
# Conflicts: # src/widgets/ExtraUsageUtilization.ts # src/widgets/shared/context-percentage-widget-base.ts # src/widgets/shared/symbol-override.tsx
# Conflicts: # src/widgets/CacheHitRate.ts # src/widgets/ContextLength.ts # src/widgets/FreeMemory.ts # src/widgets/shared/context-percentage-widget-base.ts # src/widgets/shared/symbol-override.tsx
eric-engberg
added a commit
that referenced
this pull request
Oct 30, 2026
…colors Brings in upstream main through #46. #72 is upstream as sirmalloc#699, so the files this branch had from it take main's versions, and only Block Forecast's value colors stay on top: its widget, its tests and the docs lines for (v). Main's BlockForecast.ts matches the #72 code this branch built on, so the value colors apply to it unchanged. # Conflicts: # docs/DEVELOPMENT.md # docs/USAGE.md # src/types/RenderContext.ts # src/utils/__tests__/usage-fetch.test.ts # src/utils/usage-fetch.ts # src/utils/usage.ts # src/widgets/BlockForecast.ts # src/widgets/__tests__/BlockForecast.test.ts
eric-engberg
added a commit
that referenced
this pull request
Oct 30, 2026
Brings in upstream main through #96. The conflicts are between main and the #46 code this branch holds; those files take the resolutions #46 made when main reached it, with #96's paste handling on top where #96 changes them. # Conflicts: # docs/DEVELOPMENT.md # docs/USAGE.md # src/tui/App.tsx # src/tui/__tests__/App-dev-reload.test.ts # src/tui/components/ItemsEditor.tsx # src/tui/components/__tests__/ItemsEditor.test.ts # src/types/RenderContext.ts # src/utils/__tests__/usage-fetch.test.ts # src/utils/__tests__/usage-prefetch.test.ts # src/utils/renderer.ts # src/utils/usage-fetch.ts # src/utils/usage-prefetch.ts # src/utils/usage-types.ts # src/utils/usage.ts # src/widgets/SessionUsage.ts # src/widgets/__tests__/ThinkingEffort.test.ts # src/widgets/shared/symbol-override.tsx # src/widgets/shared/usage-display.ts # src/widgets/shared/usage-percent-widget.ts
# Conflicts: # docs/USAGE.md
# Conflicts: # docs/USAGE.md
↑ and ↓ each spelled out the wrap-around in the editor's key handler, which the picker key and the Shift+Tab toggle had already made long. One helper keeps it short; a test now covers the wrap in both directions.
# Conflicts: # docs/USAGE.md
The level glyph stays the last style (p) cycles to, now after the line: text, block, slider, line, glyph on the percent widgets, and percentage, slider, line, glyph on Context %. The timers still go from the line back to text.
# Conflicts: # docs/USAGE.md
# Conflicts: # docs/USAGE.md
|
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.



Based on #41 (value colors), and merges #43 (level glyph), whose glyph mode shares these widgets: review the four non-merge commits after those merges. It goes upstream after #41 and #43.
What
Value colors (
v) come to every widget that shows a number with a natural 100%:Each one opens the same editor as Extra Usage Utilization: break points or a gradient, colors, and the 256-color warning.
Why
Value colors arrived with the Extra Usage widgets (#39, #41). Weekly Fable Usage sitting next to them, in plain colors, showed the gap: the usage and context percents are exactly the values you want to read at a glance. I went through every widget that shows a number. This PR covers the ones measured against a whole: a limit, the context window, total memory, or a hit rate's 100%. Costs, rates and token counts, which need break points in their own units, are left for a follow-up.
How
higherIsBetter, which makes its bands default to red, yellow and green and runs a gradient from its urgent end. Its break points are 50% and 80%.v, thevalue colorsmodifier andpreservesRenderedColorsapply only to the plain percent. Two small helpers carry that rule,showsPlainUsageValueandwithValueColorsKeybind, and Utilization now uses them too.Demo
Piped through the real pipeline with a fake login, a fresh usage cache and a payload, at three levels. All amounts are made up except Memory Usage, which is this machine's. The usage, context and spend levels rise from 40% to 95%, while the cache hit rate falls from 92% to 30%.
Powerline
Plain
The editor:
von Weekly Fable Usage turns value colors on. Then Cache Hit Rate's editor shows its reversed defaults (low red, high green, 50% and 80%), and both rows end up marked(value colors).Powerline
Plain
Fix: labels and empty values under a theme or item color
With value colors on, these widgets colored their label in the widget's own color and left text with no value to color in the terminal's default color. Under Powerline with the solarized theme, Context %'s
Ctx Used:came out blue on the theme's blue, andCache Hit: n/aignored the theme's text color. In plain mode, a gradient item color on Context % showed only its first stop, andCache Hit: n/aignored its cyan item color. Now the value keeps its value color and the rest takes the theme's text color, the item color or the gradient. Piped renders at 45%, 75% and 92% context with no cache data; the payloads are sample data.Powerline (solarized theme, 256 colors)
Before:
After:
Plain (Context % with item color
gradient:atlas, Cache Hit Rate cyan)Before:
After:
Testing
usage-widget-suites.ts, run for all five usage widgets (4 new each, 20 in all): bands at 69.9/70/89.9/90%; the used percent while showing what's left, with the label in the widget color; bar and glyph modes unchanged and withoutv; and the modifier, editor andpreservesRenderedColors. The shared keybind test now expectsvon the plain percent.context-value-colors.test.ts(10):more-value-colors.test.ts(8): Memory Usage, Extra Usage Remaining at 20/70/90% spent and with a zero limit, Cache Hit Rate at 49/50/80%, and its editor's red, yellow and green defaults.value-coloring.test.ts(2 new): a higher-is-better scale's default bands and its reversed gradient.CacheWidgets.test.ts: Cache Hit Rate's keys now includev.bun testgives 3055 pass, 0 fail (Bun 1.4.2).bun run lintis clean. A 12-run loop of the editor Ink tests had no failures.runtime-check.sh -c value-colors-morewith seeded data showed Powerline output agreeing. Plain output differed once, because Memory Usage reads live memory and it changed between the Bun and Node runs. With Memory Usage left out, Bun 1.4.2 and Node 26.10.0 agree byte for byte. The TUI opens and exits under both.