Repository navigation
feat(thinking-effort): optional brackets and per-level colors - #777
Merged
sirmalloc merged 3 commits intoNov 6, 2026
Merged
Conversation
Thinking Effort gains two options, both off by default. (b)rackets wraps
the rendered value in (), [], {} or <>. (l)evel colors opens an editor that
colors the value by effort level, with the brackets either matching the
level or keeping the widget color. With level colors on, the widget paints
only the level, and the renderer colors the rest as it colors any widget,
through a new optional Widget.colorsOnlyItsRuns().
At the 256 Color level, hex colors now render as the nearest 256-palette
color instead of a 24-bit code those terminals can't draw.
…olors test The line and color editor tests no longer sleep a fixed 25ms after each render and key. They wait for the key's visible effect, and the sleep helper flushInk is gone. The color menu test that checks the note for a widget that sets some of its own colors still called flushInk after the first render, so it no longer compiled. It now waits until the frame shows the Thinking Effort row, then reads that frame as before.
This was referenced Nov 6, 2026
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.
Demo
Model is merged with Thinking Effort, so the preview reads
Claudehigh(the TUI's sample data). Pressing b adds brackets, givingClaude(high). Pressing l opens the level colors editor: Space turns level colors on, and moving through the rows shows each level's color in the sample. Enter saves, and the preview colors the level.Powerline
Plain
Fix: the label's color with level colors on
With level colors on, the
Thinking:label kept the widget's own color whatever the line's colors were: magenta on the theme's teal under Powerline with the nord theme, and only the first stop of a gradient item color. It now takes the theme's text color, or the gradient sweeps across it, and the level keeps its level color. Piped renders at high, xhigh and max; the payloads are sample data.Powerline (nord theme, 256 colors)
Before:
After:
Plain (item color
gradient:atlas)Before:
After:
Update
Status line rendered from a fake payload at the 256 Color level, with Thinking Effort's level colors set to the hex colors #f5d020, #ff8800 and #e8343c. Under each line is the escape code the real output uses for the level. Before the fix these are 24-bit codes (ESC[38;2;255;136;0m), which 256-color terminals can't show. After it they are the nearest 256-palette codes (ESC[38;5;220m, 208, 167). VHS's terminal supports truecolor, so the rendered colors only shift by that rounding, and max's red shifts the most.
Powerline, before and after:
Plain, before and after:
Summary
Showing the effort next to the model, e.g.
Opus 5.5(xhigh)with the level color-coded, used to take four merged widgets (Model,(, Thinking Effort,)) and still couldn't color by level.Thinking Effort gains two options. Both are off by default, so existing setups render exactly as before.
()→[]→{}→<>and wraps whatever the widget renders: raw gives(xhigh), labeled gives(Thinking: xhigh).#RRGGBBor0-255; custom colors show as#RRGGBBorANSI n, as in Edit Colors. (d) restores the defaults.Color responsibilities
Thinking:label, unknown levels likesuper-max?, widget-colored brackets, background, and bold/dim. It now shows a one-line note when the selected widget sets some of its own colors.Widget.colorsOnlyItsRuns()marks such widgets; the renderer returns to its color after each run the widget colored. Claude Status and Custom Command, which own all of their foreground, are unchanged.xhighorange (ansi256:208) falls back to bright magenta on 16-color terminals. Colors you pick yourself are left as-is, the same as the color menu.''("Default" in the color menu) means no color.Implementation
widgets/shared/effort-style.ts: pure helpers for bracket cycling, level color get/set/reset, custom color parsing, andformatThinkingEffort(). Options are stored in widgetmetadata, and an option at its default stores nothing.widgets/shared/effort-colors-editor.tsx: the editor screen.ThinkingEffort.ts: keybinds, editor display ((brackets (), level colors)), and rendering through the formatter.jsonl-metadata.ts: exports the existingKNOWN_THINKING_EFFORTSlist so the levels have a single source.ColorMenu.tsx: the note, shown for any listed widget that sets some of its own colors.types/Widget.ts,utils/renderer.ts,utils/colors.ts,utils/gradient.ts:colorsOnlyItsRuns(), and coloring around the runs in the Powerline and plain paths (restoreForegroundAfterRuns, and a gradient that skips the runs).Update: Map hex colors to the 256-color palette at the 256 Color level
hex:color now renders as the nearest 256-palette color (ESC[38;5;Nm/ESC[48;5;Nm). Before, it rendered as a 24-bitESC[38;2;r;g;bm/ESC[48;2;r;g;bmcode, while named,ansi256:and gradient colors already came out as palette codes. 256 Color is the default level, and Thinking Effort's level colors accept#RRGGBBat any level, so a hex color the user typed reached 256-color terminals (Terminal.app before macOS 26, for example) as an escape they can't draw. A separator that keeps its hex color after a switch from truecolor down to 256 goes through the same code, so it is fixed too.rgbToAnsi256, which treats the cube levels as evenly spaced and can land well off. For#ff8800it gives 214 (#ffaf00), where the search gives 208 (#ff8700, 1 unit away). For#5f5f5f, which is palette entry 59, it gives the gray 240.Testing
mainas of fix: use env override and settings for usable context ratio calculation #407 (71e8b42): merges cleanly,bun run lintis clean, andbun testgives 4006 pass, 0 fail. The built CLI under Bun 1.4.4 and Node 26.13.0 matchesmainin plain and Powerline mode with the baseline configs, and the TUI opens and exits cleanly. It adds no JavaScript or Node API newer than Node 14, the build's target.bun test: 2429 pass.bun run lint: clean.(high)in yellowUpdate:
bun test: 3283 pass, 0 fail (9375expect()calls across 184 files).colors-gradient.test.tscover hex at 256 Color: the nearest-entry mapping for foreground and background (#FF8800-> 208,#4e9a06-> 64,#808080-> 244), exact palette entries (#5f5f5f-> 59,#FF0000-> 196), no truecolor escape throughapplyColors, and truecolor unchanged. The first three fail on the old code.node node_modules/vitest/vitest.mjs run src/utils/__tests__/colors-gradient.test.ts): 18 of 18 pass.bun run lint(type check and ESLint, no warnings allowed) is clean.#f5d020,#ff8800and#e8343ccome out asESC[38;5;220m,ESC[38;5;208mandESC[38;5;167m(before:ESC[38;2;…m). A separator coloredhex:FF8800comes out asESC[38;5;208m.