fix(tui): cap rendered request/response body lines - #2480
Merged
Merged
Conversation
The TUI rendered an HTTP body as one Ink <Box> per pretty-printed line, with no bound, in four copies (App.tsx details and RequestsTab, request and response each). A large tool result became thousands of components in one render pass. Add a pure layoutBody() in utils/bodyLines.ts that pretty-prints JSON, caps the body at 500 lines and each line at 2000 characters, and reports what it cut. Render it through one BodyLines component used by all four sites; the note under a truncated body says how many lines were hidden. The per-line character cap is needed because an embedded resource pretty-prints as a single enormous base64 line that a line cap alone leaves untouched. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
Closed
2 tasks
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The bounded shared renderer addresses the reported component explosion and has appropriate focused coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Bounds TUI request/response body rendering to prevent large payloads from overwhelming Ink.
Changes:
- Adds shared formatting with line-count and per-line limits.
- Replaces four duplicated render paths with
BodyLines. - Adds focused utility and component tests.
| File | Description |
|---|---|
clients/tui/src/utils/bodyLines.ts |
Formats and bounds body content. |
clients/tui/src/components/BodyLines.tsx |
Renders bounded lines and truncation notices. |
clients/tui/src/components/RequestsTab.tsx |
Uses the shared body renderer. |
clients/tui/src/App.tsx |
Uses the shared renderer in request details. |
clients/tui/__tests__/BodyLines.test.tsx |
Tests formatting, limits, and rendering. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Member
Author
|
Copilot review closed after round 1: the round was clean. No inline comments, the headline reports "Findings: None", and there's no Suppressed comments block. Per pr-flow step 7c, one clean round ends the loop. |
Closed
2 tasks
2 tasks
2 tasks
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.
Closes #2407
What changed
The TUI rendered an HTTP request/response body as one Ink
<Box>per pretty-printed line, with no limit on the number of lines. The same code existed in four places: the App details view (App.tsx) and the Requests tab (RequestsTab.tsx), each for the request body and the response body. A large tool result became thousands of components in one render pass.clients/tui/src/utils/bodyLines.ts(new, pure):layoutBody()pretty-prints JSON (anything else passes through as is), keeps the first 500 lines, clips each line to 2,000 characters, and reports how much it cut.clients/tui/src/components/BodyLines.tsx(new): renders that layout, followed by… N more lines not shown (M total)when lines were dropped. All four call sites now use it, replacing four copies of the same inline code.Why there's also a per-line cap: an embedded resource, one of the payloads the issue names, pretty-prints as a single enormous base64 line. A line-count cap alone does nothing for that, so the line is cut and marked
… (+N chars).The issue's "e.g." also mentions a "show more" affordance. I left that out: the cap with an explicit note is what bounds the render, and "show more" would be a new interaction (a keybinding plus paging state) beyond what the bug needs. The one other change in behavior is that a non-JSON body now renders line by line through the same capped path, where before it was a single uncapped
<Text>.Evidence
A frame from the real
RequestsTab, rendered with a 20,000-character argument in the request and a 5,000-file listing (20,008 pretty-printed lines) in the response. It's the Ink frame as text, which is the TUI's actual output. The… (…) …marker lines were added when excerpting.Before this change, the same response rendered all 20,008 lines as separate
<Box>components.Tests
clients/tui/__tests__/BodyLines.test.tsxcovers the util (JSON vs raw, line cap, per-line clip, caller-supplied caps) and the component (no note under the cap; exactly the cap plus an accurate note over it). Both new files are at 100% on all four coverage dimensions. The existingRequestsTabandApptests cover the swapped call sites, including the non-JSON fallback.npm run local:gatepasses.🤖 Generated with Claude Code