Skip to content

feat(widgets): optional truecolor gradients for progress bars, with colorblind-safe presets - #818

Open
eric-engberg wants to merge 1 commit into
sirmalloc:mainfrom
eric-engberg:feat/bar-gradients
Open

eric-engberg wants to merge 1 commit into
sirmalloc:mainfrom
eric-engberg:feat/bar-gradients

Conversation

@eric-engberg

Copy link
Copy Markdown
Contributor

What

A new (g)radient option for progress bars colors a bar's filled cells by where they sit in the bar, from calm to urgent, so a fuller bar reaches further toward the urgent end. Empty cells turn dark gray. In the line editor, g cycles through six presets and then back to off. Off is the default, so existing setups render exactly as before.

It works on every widget that draws a bar, in the block, slider and line styles: Context Bar; Context % and Context % (usable) in slider modes; Block, Weekly, Weekly Sonnet, Weekly Opus and Weekly Fable Usage, and Spend Limit Usage; Extra Usage Utilization; and the Block, Block Reset and Weekly Reset timers in their bar modes. g is offered only while a widget shows a bar, and only at the Truecolor color level.

Preset Colors (low → high)
traffic green → yellow → red
thermal blue → purple → red
viridis teal → green → yellow colorblind-safe, grayscale-safe
cividis blue → gray → yellow colorblind-safe, grayscale-safe
blue-orange blue → yellow → vermillion (Okabe-Ito colors) colorblind-safe
mono gray → white colorblind-safe, grayscale-safe

"Colorblind-safe" means readable with red-green color blindness, the most common kind. Viridis, cividis and mono get steadily brighter, so they read even in grayscale.

Why

A bar draws all its filled cells in one color, so at a glance a bar at 85% looks like one at 40% until you count cells or read the number. When the color moves from calm to urgent as the bar fills, how close a limit is shows from across the screen. A plain green-to-red ramp is the hardest pair to tell apart with red-green color blindness, so the presets include colorblind-safe ramps.

How

  • Painting. Colors are blended with the existing OKLab gradient code (sampleGradient). The gradient handling lives in widgets/shared/gradient-bar.ts. With refactor(widgets): consolidate the progress-bar widgets' duplicated code #810's shared bar code, each bar is painted in one place: formatUsageBar for the usage-style bars, the Context % base class for its slider, and the Context Bar itself. The label and numbers keep the widget's normal color, and the usage widgets' time cursor keeps the widget color. Viridis and cividis skip their darkest colors, which vanish on black terminals and dark Powerline segments.
  • Direction. When a bar fills with what's left (inverted fill, or remaining instead of used), the gradient runs from the urgent end, so the color where the fill ends still shows how urgent things are.
  • Truecolor only. At 256 colors, neighbouring cells snap to washed-out or off-hue palette colors, so most presets came out as a patchwork rather than a ramp. Hand-picked 256-color ramps were cleaner but still visibly stepped. So, as Edit Colors does with (h)ex and (g)radient, g is offered only where it can show. A gradient saved earlier, from a truecolor session or an imported config, is kept: it renders plain at lower levels, and the line editor labels it (gradient: thermal, needs truecolor) so the plain bar is explained. Widget.getEditorDisplay and getCustomKeybinds take an optional settings argument for this; the line editor passes its settings, and widgets that ignore it are unaffected.
  • Renderer. The widget colors only the bar and ends the colored run with the default-foreground code (\x1b[39m). Inside a colored widget, that code used to drop the rest of the text to the terminal's default color. The renderer now turns it back into the foreground it applied to the widget: the widget's own color in plain mode, the theme's color in a Powerline segment. This applies only to widgets that get a solid foreground from the renderer. Color-preserving widgets (Claude Status, Thinking Effort's level colors, Custom Command with preserve colors) and gradient foregrounds are unaffected. No existing widget embeds that code inside a colored segment, so nothing else renders differently.
  • Gradient widget colors. A bar widget with a saved preset tells the renderer it colors its own cells (colorsOnlyItsRuns, the flag Thinking Effort's level colors use), while it shows a bar. A gradient:<theme> widget color then sweeps only the label, brackets and numbers, and the bar keeps its preset and dark-gray track, in plain mode as in Powerline. Edit Colors reads the same flag, so it shows its existing note that the widget sets some of its own colors.
  • Overrides. With a global foreground override, the bar stays plain.
  • Previews. The TUI previews sample the bars nearly full, so presets are easy to compare while cycling: the Context Bar at 180k/200k (90%) instead of 50k/200k (25%), and the other bar widgets at 60–90% instead of 3–20%. Block Reset Timer's preview time changes to match, Weekly Reset Timer's bar now matches its existing preview time, and fix: use env override and settings for usable context ratio calculation #407's preview test expects the 90% sample.
  • Docs. docs/USAGE.md describes bar gradients once (presets, the reversed direction, the truecolor requirement) and points each widget's keys to it.

In Powerline mode, contrast depends on the segment color: light cells on a light theme segment are hard to see. The bar reads best on a dark segment, like the ones in the demo.

Demo

All values are sample data. Pressing g on the Context Bar in the line editor cycles through the presets; the preview samples the bar at 90%. The comparison shows the real status line for every preset at 30, 60 and 90%. Next come the other bar widgets, each on a different preset, using the TUI's sample values, in the block style and then the line style; Context % (remaining) and Extra Usage Utilization (inverted) run from the urgent end. The last image shows a gradient saved earlier, viewed at 256 colors.

Powerline

Cycling the Context Bar gradient presets in Powerline mode

Every gradient preset at 30, 60 and 90 percent in Powerline mode

Gradient presets on the other bar widgets in Powerline mode

Gradient presets on line bars in Powerline mode

A saved gradient at 256 colors in Powerline mode: plain bar, labeled needs truecolor

Plain

Cycling the Context Bar gradient presets in plain mode

Every gradient preset at 30, 60 and 90 percent in plain mode

Gradient presets on the other bar widgets in plain mode

Gradient presets on line bars in plain mode

A saved gradient at 256 colors in plain mode: plain bar, labeled needs truecolor

A gradient widget color on bar widgets with presets: seven bar widgets on six presets in the Main Menu preview, each with the widget color gradient:atlas. Before, plain mode swept atlas over every bar cell, empty track included. After, the bars keep their presets and atlas colors only the labels, brackets and numbers, as in Powerline.

Powerline, before and after:

Before, Powerline

After, Powerline

Plain, before and after:

Before, Plain

After, Plain

Testing

  • On main as of 2.2.34 (0c94df7): it applies as one commit, keeping feat(context-bar): add a thin line display mode #794's line-bar test. bun run lint is clean, and bun test on Linux CI gives 4500 pass, 0 fail (4403 on main). The built CLI under Bun 1.4.4 and Node 26.13.0 agrees across runtimes and matches main on the baseline line in plain and Powerline mode; on a line of bar widgets with gradient presets they agree and differ from main, as intended, and the TUI opens and exits cleanly under both. It adds no JavaScript or Node API newer than Node 14, the build's target.
  • New tests:
    • widgets/__tests__/bar-gradients.test.ts: for each of the 12 bar widgets, the bar painted in true RGB with only its colors changing, plain below truecolor, g offered only at truecolor while a bar shows, cycling and naming the preset, the reversed direction for inverted or remaining bars, and the bar keeping its colors under a gradient:atlas widget color in plain mode while atlas still sweeps the label (it fails for all 12 without the colorsOnlyItsRuns flag).
    • widgets/shared/__tests__/gradient-bar.test.ts: only colors change, colors follow position rather than fill, empty cells are gray, every preset has its own end colors, the colorblind-safe presets brighten steadily, the slider's and the line bar's cells take it too, and the line editor's truecolor rule (g hidden below truecolor, a saved preset noted as needing it).
    • widgets/__tests__/ContextBarGradient.test.ts: g cycling through the presets and back to off, the preset name in the line editor, each preset rendering its own colors, an earlier on/off setting reading as traffic, off by default, the text left unchanged, every bar style, the TUI preview, plain rendering at 256 and 16 colors with the "needs truecolor" label, g offered only at truecolor, and staying plain with colors off or a foreground override.
    • utils/__tests__/renderer-embedded-colors.test.ts: applyColors returning to the widget's color after an embedded run, and the text after a gradient bar keeping the widget color in plain mode and the theme color in Powerline mode.
    • tui/components/__tests__/ItemsEditorBarGradient.test.tsx: in the real line editor, (g) offered with the preset shown at truecolor, and hidden below it with the plain bar explained.
  • Under Node 26.13.0 with Vitest, the 19 changed test files pass, 398 of 398 (main: 301 of 301 in the 14 it has).

…olorblind-safe presets

A new (g)radient option colors a progress bar's filled cells by where
they sit in the bar, from calm to urgent, so a fuller bar reaches further
toward the urgent end; empty cells turn dark gray. g cycles through the
presets (traffic, thermal, viridis, cividis, blue-orange, mono) and back
to off, the default, so existing setups render as before.

- Every widget that draws a bar takes it: Context Bar, Context % and
  Context % (usable) in slider modes, the usage-percent widgets, Extra
  Usage Utilization and the Block, Block Reset and Weekly Reset timers in
  their bar modes, in the block, slider and line styles. g is offered only
  while a widget shows a bar.
- A bar that fills with what's left (inverted, or remaining instead of
  used) runs the gradient from the urgent end.
- Gradients need the Truecolor color level, as Edit Colors' hex and
  gradient options do: g is offered only there, and a gradient saved
  earlier renders plain below it, marked "needs truecolor" in the line
  editor.
- The renderer turns a widget's embedded default-foreground code back
  into the foreground it applied to the widget, so the numbers after a
  painted bar keep the widget or theme color.
- A bar with a preset tells the renderer it colors its own cells, so a
  gradient widget color sweeps only its label and numbers.
- The TUI previews sample the bars nearly full, so presets are easy to
  compare while cycling.
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