Skip to content

feat(ui): let sidebar agent tokens wrap with max_rows - #5

Open
nierz wants to merge 7 commits into
tvaintrob:masterfrom
nierz:feat/sidebar-title-wrap
Open

nierz wants to merge 7 commits into
tvaintrob:masterfrom
nierz:feat/sidebar-title-wrap

Conversation

@nierz

@nierz nierz commented Aug 11, 2026

Copy link
Copy Markdown

Long agent titles are hard-truncated in the sidebar, so anything past the panel width is simply unreadable. This adds an opt-in max_rows to the inline token style, letting a token continue onto indented rows instead.

[ui.sidebar.agents]
rows = [
  ["state_icon", { token = "terminal_title_stripped", fg = "#D2D2DC", bold = true, max_rows = 2 }],
  [{ token = "workspace", dim = true }, "tab"],
]

Before / after at 26 columns:

● Investigate internal ac…          ● Investigate internal
  workflows-v3  v3 Tester             activities blocks in wo…
                                      workflows-v3  v3 Tester

Design

max_rows lives on the existing inline token style (alongside fg / bold / dim) rather than as a new sidebar-level key, so it composes with what's already there and stays opt-in per token occurrence. Defaults to 1, so existing configs render byte-identically.

Two implementation notes worth reviewing:

  • Wrapping happens during row resolution. agent_entry_height_in_body() derives entry height from resolved_agent_rows(...).len(), and scroll math, visible-count and hit testing all read from that. Emitting continuation rows there means those all follow automatically instead of needing parallel bookkeeping.
  • Wrap width must equal the render budget. The budget-sharing logic is extracted from resolved_token_spans into token_layout() and used by both paths. If they disagree, text gets double-truncated or overflows.

Wraps on whitespace where possible, hard-splits a single overlong word, and uses the existing unicode-width helpers so CJK titles stay correct. The last permitted row still truncates with the normal ellipsis.

Tests

5 added: default-unchanged, wrapping onto an indented row, entry height growing by exactly the continuation count (scroll geometry), overlong single word, and config parsing/validation.

cargo test --release --bin herdr -- --test-threads=12644 passed. cargo clippy --release --all-targets clean.

Known caveats

  • Agents only. max_rows parses on space tokens too (shared struct) but is ignored — space row heights come from a separate path. Documented as agents-only.
  • One wrapped token per row. The first visible token that declares max_rows > 1 and overflows wins; others truncate as before.
  • Scrollbar column. Entry height depends on width and scrollbar visibility depends on height, so wrapping measures against the scrollbar-free width to avoid a feedback loop. When a scrollbar is shown a wrapped row can be one column too wide; the renderer elides it, so it never overflows, but that line can lose its final character.

Depends on #2

master doesn't compile under cfg(test) (NextBlockedAgent missing from a test-only match) and scripts/config_reference_check.py fails on keys.next_blocked_agent. Both are fixed in #2. Test counts above were measured with that fix applied locally; CI here stays red until #2 merges.

Unrelated pre-existing flake: pane_graphics_stream::inactive_owner_cancels_idle_stream_and_dispatches_close fails intermittently even serially and passes consistently in isolation.

🤖 Generated with Claude Code

tvaintrob and others added 7 commits July 16, 2026 13:30
Add ui.tab_bar_position, ui.show_clock, and ui.prefix_hint to the
versioned config reference so it stays in sync with the config model.
…ts (tvaintrob#1)

Adds a new configurable keybinding `next_blocked_agent` that focuses the
next agent in blocked state, wrapping around when reaching the end.
This allows quickly jumping to agents that need human input without
cycling through idle/working agents.

Configuration (in config.toml):
  [keys]
  next_blocked_agent = "prefix+shift+n"

Co-authored-by: tamir altshuler <tamir@tamirs-MacBook-Pro.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Long agent titles are hard-truncated in the sidebar, so anything past the
panel width is unreadable. Adds an opt-in `max_rows` to the inline token
style, letting a token continue onto indented continuation rows instead:

    [ui.sidebar.agents]
    rows = [
      ["state_icon", { token = "terminal_title_stripped", max_rows = 2 }],
      [{ token = "workspace", dim = true }, "tab"],
    ]

Defaults to 1, so existing configs render byte-identically.

Wrapping happens during row resolution, which is what agent entry height
is derived from, so scroll geometry, visible-count and hit testing pick
up the continuation rows without separate bookkeeping. The budget-sharing
logic is extracted into token_layout() and shared by both the wrap and
render paths so the wrap width matches the budget the renderer will use;
otherwise the two disagree and text is double-truncated.

Wraps on whitespace where possible and hard-splits a single overlong
word, using the unicode-width helpers so CJK titles stay correct. The
last permitted row still truncates with the existing ellipsis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

3 participants