Repository navigation
fix(renderer): keep separators placed next to each other - #773
Merged
Merged
Conversation
In plain mode, manual separators placed next to each other between two visible widgets rendered as one: [A, ' ', ' ', B] printed "A B", [A, '|', '|', B] printed "A | B" and [A, ' ', '•', ' ', B] printed "A•B". The TUI preview showed the same, so there was no way to stack separators for a wider gap or to pad a custom glyph with spaces. The backward scan that decides whether a separator renders treated any earlier separator as if a widget that rendered empty sat between the two. A visible earlier separator kept the boundary, so the current one was dropped; a spacing-only one was removed and replaced by the current one. That rule is for separators on either side of an empty widget, where one of them has to go, but the scan applied it whether or not a widget lay between them. Separators with no widget between them now form one run that renders whole: the scan steps over the run's earlier separators to the content before it. Across an empty widget the rule is unchanged, per run: a run with a visible separator keeps the boundary, and a spacing-only run is replaced by the run after the empty widget. Only that run's first separator removes the spacing it replaces, so the rest of the run is kept.
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
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.
What
In plain mode, manual separators placed next to each other between visible widgets all render again. Two spaces give a two-space gap,
' ' '•' ' 'gives•, and two pipes give two pipes. The status line and the TUI preview both change.Separators on either side of a widget that renders empty still collapse, now a run at a time: a run with a visible separator keeps the boundary, and a run of spaces is replaced by the separators after the empty widget. With a single separator on each side, nothing changes.
One case that looks different: deleting the widget between two separators leaves both in the line, and both now show, as they do in the editor's list. Before, the preview showed one.
Why
renderStatusLinescans back from each manual separator to decide whether it renders. Since #525, an earlier separator met on that scan either owns the boundary (a visible one: the current separator is dropped) or is replaced (a spacing one: it is popped from the output). That rule is for separators on either side of a widget that renders empty, where one of them has to go. But the scan applied it whether or not a widget lay between the two, so separators placed side by side were deduplicated too. Each #525 test with two separators puts an empty widget between them, so none covers adjacent ones with every widget visible.The TUI can build these lines: Space cycles a separator through
|,-,,and space, andkcopies the selected one.Piped render with a scratch
HOME,colorLevel: 0, custom-text widgets, Bun 1.4.2:mainef521ee9^)A ' ' ' ' BA BA BA BC '|' '|' DC | DC | | DC | | DE ' ' '•' ' ' FE•FE • FE • FCalling
renderStatusLinedirectly gives the same results for[A, ' ', '|', B](main:A | B, nowA | B) and[A, '|', ' ', B](main:A | B, nowA | B).How
[A, ' ', '•', ' ', hidden, '|', C]rendersA • C(main:A•C). A run of spaces is replaced by the run after the empty widget, and that whole run renders. Only that run's first separator removes the spaces it replaces, so the rest of its run is kept:[A, ' ', ' ', hidden, ' ', '|', C]rendersA | C(main:A | C).renderer-separator-collapse.test.ts: four cases of adjacent separators between visible widgets, plus the two cases above around an empty widget. fix(renderer): preserve separators after hidden widgets #525's tests are unchanged and pass.Left as is:
docs/USAGE.mddescribes collapsing only around widgets that render empty, which matches this behaviour, so the docs are unchanged.Demo
Line 1 is Model,
' ' '•' ' ', Context Length,|, Git Branch,|, Git Changes. The demo opens it, turns the|after Context Length into a space with Space, then copies it twice withkfor a three-space gap. Before, the preview showsClaude•Ctxand a single space, andkleaves it unchanged. After, it showsClaude • Ctxand the gap widens with each copy. Powerline mode ignores manual separators, so its two recordings look the same; they are there as a control.Powerline
Before:
After:
Plain
Before:
After:
Testing
mainas 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 lintis clean, andbun testgives 3709 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.bun test: 3174 pass, 0 fail (178 files);main: 3168 pass, 0 fail. The 6 new tests all fail onmain.bun run lintis clean.renderer-separator-collapse.test.tsunder vitest on Node 26.10.0: 25 pass (main: 19 pass).mainin plain and Powerline mode. With this demo's configs, plain differs frommainas intended, Powerline matchesmain, and both runtimes agree. The TUI opens and exits cleanly under both.