Skip to content

feat(tui): support content reflow on terminal resize#43

Merged
cnjack merged 2 commits intomainfrom
feat/tui-contentline-reflow
Apr 25, 2026
Merged

feat(tui): support content reflow on terminal resize#43
cnjack merged 2 commits intomainfrom
feat/tui-contentline-reflow

Conversation

@cnjack
Copy link
Copy Markdown
Owner

@cnjack cnjack commented Apr 25, 2026

Summary

Introduce a contentLine abstraction to decouple data from rendering, enabling automatic content reflow when the terminal window is resized.

Previously, tool results and Markdown content were rendered at a fixed width at first paint. Resizing the terminal would leave content misaligned and broken.

Changes

  • New types: contentLine (with text and toolResultData variants) and toolResultData struct to hold raw tool result data
  • Model.lines type change: []string[]contentLine — content is now stored as structured data, rendered lazily via render(width)
  • recreateMDRenderer(): rebuilds the glamour Markdown renderer on WindowSizeMsg with updated WordWrap width
  • Bug fix: renderSubagentBox() now uses m.contentWidth() instead of m.width, correctly accounting for sidebar width
  • 70+ call sites updated to use textLine() wrapper for string→contentLine conversion

Files Changed

File Description
internal/tui/tui.go Core: new types, contentLine methods, renderer recreation, Model.lines type change
internal/tui/channel_panel.go Adapt to []contentLine
internal/tui/input_views.go Adapt to []contentLine
internal/tui/pickers.go Adapt to []contentLine
internal/tui/ssh_handlers.go Adapt to []contentLine
internal/tui/team_view.go Adapt to []contentLine

Testing

  • Built successfully with make build
  • Manually verified content reflows correctly on terminal resize

Summary by CodeRabbit

  • Refactor
    • Restructured internal content rendering to make status messages and tool results responsive to terminal window resizing, improving visual consistency across the UI.

Introduce contentLine abstraction to decouple data from rendering,
enabling automatic content reflow when terminal window is resized.

- Add contentLine type with text and toolResultData variants
- Change Model.lines from []string to []contentLine
- Add recreateMDRenderer() to rebuild glamour renderer on resize
- Fix renderSubagentBox() to use contentWidth() instead of m.width
- Update all 70+ call sites to use textLine() wrapper
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@cnjack has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 21 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 52 minutes and 21 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d8762b5f-0512-482f-a203-69aa820900ef

📥 Commits

Reviewing files that changed from the base of the PR and between dada83b and 79c87b8.

📒 Files selected for processing (1)
  • internal/tui/tui.go
📝 Walkthrough

Walkthrough

This PR refactors TUI content representation from raw strings to structured contentLine types. Tool results are now stored as structured toolResultData and re-rendered on-demand based on terminal width, with rendering computed dynamically during display and window resize events.

Changes

Cohort / File(s) Summary
Status Message Formatting
internal/tui/channel_panel.go, internal/tui/input_views.go, internal/tui/pickers.go, internal/tui/ssh_handlers.go
Status and error messages appended to m.lines now consistently wrapped via textLine(...) instead of directly appending raw or styled strings. No control flow changes.
Viewport Content Management
internal/tui/team_view.go
Viewport snapshot and teammate population refactored to treat m.lines as []contentLine instead of []string, with content appended via textLine(...) and converted via toContentLines(...).
Dynamic Content Rendering
internal/tui/tui.go
Conversation history refactored from []string to []contentLine, with tool results stored as structured toolResultData. Rendering now computes contentWidth() dynamically and re-renders content on window resize. Markdown renderer made width-aware via WindowSizeMsg handler.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Strings once rigid, now they flex and bend,
Content lines dance when window's width won't end,
Tool results bloom in structured array,
Resize, reflow—responsive all the way! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(tui): support content reflow on terminal resize' accurately captures the primary objective of the PR: introducing a contentLine abstraction to enable content reflow on terminal resize.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tui-contentline-reflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/tui/tui.go (1)

2654-2685: ⚠️ Potential issue | 🟠 Major

Reflow only covers tool results — assistant Markdown remains frozen at the width it was first rendered with.

flushText (Lines 2660–2667) renders streamed text through m.mdRenderer and stores the resulting string into m.lines via textLine(rendered). On WindowSizeMsg, recreateMDRenderer rebuilds the renderer for future output, but every previously-flushed assistant message keeps its stale wrap width. The same applies to the assistant entries appended in SessionResumedMsg (Lines 1764–1771) and team assistant messages (Lines 2244–2251).

The PR objective is "content reflows automatically when the terminal is resized," yet the most visible block of content in a normal session — the assistant's markdown — won't actually reflow. To fully achieve the goal, consider storing the raw markdown source (e.g., a third contentLine variant markdownData{source string}) and rendering it dynamically in contentLine.render(width), the same way tool results are handled.

If reflowing markdown is intentionally out of scope for this PR, please call that out in the PR description / a TODO so it's clear which content is actually width-adaptive.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/tui/tui.go` around lines 2654 - 2685, flushText currently stores
pre-rendered assistant Markdown into m.lines so it never reflows after
recreateMDRenderer changes; update flushText (and the spots that append
assistant entries in SessionResumedMsg and team assistant handling) to store the
raw markdown instead of the rendered string by adding a new contentLine variant
(e.g., markdownData{source string}) and implementing contentLine.render(width)
to call m.mdRenderer.Render with the current width (or a renderer built by
recreateMDRenderer) at draw time; ensure textLine(rendered) usage is replaced
with the markdownData variant and that contentWidth/recreateMDRenderer are used
when rendering so assistant Markdown reflows on WindowSizeMsg.
🧹 Nitpick comments (2)
internal/tui/tui.go (2)

1545-1582: Optional: skip recreateMDRenderer when width didn't change.

WindowSizeMsg fires for any size change (including height-only changes from things like the terminal tab bar appearing/disappearing). Glamour renderer creation parses styles each call, so guarding on width keeps idle resizes cheap.

♻️ Suggested guard
-		m.recreateMDRenderer()
+		if m.viewport.Width() != mainWidth || m.mdRenderer == nil {
+			m.recreateMDRenderer()
+		}

(Or capture the previous content width before reassigning m.width and compare against the new one.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/tui/tui.go` around lines 1545 - 1582, The WindowSizeMsg handler
currently always calls m.recreateMDRenderer even for height-only changes; before
assigning m.width (or immediately after computing mainWidth) capture the
previous main/content width (e.g., prevMainWidth := previous m.width or compute
prevMainWidth based on previous showSidebar) and only call
m.recreateMDRenderer() when the newly computed mainWidth differs from
prevMainWidth; this ensures recreateMDRenderer is skipped for height-only
resizes while leaving the rest of the resize logic (textarea, viewport sizing,
SetContent) intact.

230-234: Remove the contentLine.contains method — it's unused and has incorrect logic for tool results.

This method is not called anywhere in the codebase (all substring checks use strings.Contains directly on line.text). Additionally, for toolResultContentLine with an empty text field, it would always return false even if the rendered output contains the substring.

♻️ Removal
-// contains reports whether the rendered content contains the given substring.
-// For tool result lines, this checks the last-rendered text.
-func (cl contentLine) contains(sub string) bool {
-	return strings.Contains(cl.text, sub)
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/tui/tui.go` around lines 230 - 234, Remove the unused and incorrect
method contentLine.contains: delete the contains receiver method on type
contentLine so callers use strings.Contains(line.text, ...) directly; ensure
there are no references to contentLine.contains (search for "contains(" and
"contentLine.contains") and remove or update them if found, and be mindful of
toolResultContentLine where rendered output may differ from the empty text
field—rely on existing uses of strings.Contains(line.text, ...) or other
rendering-aware checks instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/tui/tui.go`:
- Around line 2670-2685: The comment for contentWidth was left orphaned above
recreateMDRenderer; move the doc comment "// contentWidth returns the width
available for the main content area, accounting for the sidebar when visible."
so it directly precedes the contentWidth function definition (instead of sitting
above recreateMDRenderer), or delete that orphaned line; also ensure
recreateMDRenderer has an appropriate doc comment (e.g., about rebuilding the
glamour renderer) so comments correctly associate with the contentWidth and
recreateMDRenderer methods on type Model.

---

Outside diff comments:
In `@internal/tui/tui.go`:
- Around line 2654-2685: flushText currently stores pre-rendered assistant
Markdown into m.lines so it never reflows after recreateMDRenderer changes;
update flushText (and the spots that append assistant entries in
SessionResumedMsg and team assistant handling) to store the raw markdown instead
of the rendered string by adding a new contentLine variant (e.g.,
markdownData{source string}) and implementing contentLine.render(width) to call
m.mdRenderer.Render with the current width (or a renderer built by
recreateMDRenderer) at draw time; ensure textLine(rendered) usage is replaced
with the markdownData variant and that contentWidth/recreateMDRenderer are used
when rendering so assistant Markdown reflows on WindowSizeMsg.

---

Nitpick comments:
In `@internal/tui/tui.go`:
- Around line 1545-1582: The WindowSizeMsg handler currently always calls
m.recreateMDRenderer even for height-only changes; before assigning m.width (or
immediately after computing mainWidth) capture the previous main/content width
(e.g., prevMainWidth := previous m.width or compute prevMainWidth based on
previous showSidebar) and only call m.recreateMDRenderer() when the newly
computed mainWidth differs from prevMainWidth; this ensures recreateMDRenderer
is skipped for height-only resizes while leaving the rest of the resize logic
(textarea, viewport sizing, SetContent) intact.
- Around line 230-234: Remove the unused and incorrect method
contentLine.contains: delete the contains receiver method on type contentLine so
callers use strings.Contains(line.text, ...) directly; ensure there are no
references to contentLine.contains (search for "contains(" and
"contentLine.contains") and remove or update them if found, and be mindful of
toolResultContentLine where rendered output may differ from the empty text
field—rely on existing uses of strings.Contains(line.text, ...) or other
rendering-aware checks instead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d89f5fe-8dc9-4917-81bb-fca75df71902

📥 Commits

Reviewing files that changed from the base of the PR and between 4053590 and dada83b.

📒 Files selected for processing (6)
  • internal/tui/channel_panel.go
  • internal/tui/input_views.go
  • internal/tui/pickers.go
  • internal/tui/ssh_handlers.go
  • internal/tui/team_view.go
  • internal/tui/tui.go

Comment thread internal/tui/tui.go Outdated
The doc comment for contentWidth was placed above recreateMDRenderer,
causing godoc to associate it with the wrong function.
@cnjack cnjack merged commit 4b449d7 into main Apr 25, 2026
1 check passed
@cnjack cnjack deleted the feat/tui-contentline-reflow branch April 25, 2026 05:56
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