fix(rendering): adapt terminal output to narrow widths (swamp-club#377)#1401
Conversation
TUI SearchPicker preview panes used nested <Box> structures that Ink/Yoga laid out at natural content width then clipped, causing garbled text at narrow terminals. Flatten all 14 preview callbacks to use only <Text> elements in a single <Box flexDirection="column">, and add overflow="hidden" to ResultsList and the inline/minimal tier containers. Log-mode renderers (extension list, audit timeline, data list, summarise, method-run reports, report get) had zero terminal width awareness — hardcoded separators and unclamped column widths. Add getTerminalColumns() utility wrapping Deno.consoleSize() with 80-col fallback, and use it for terminal-width-aware separators, truncation, and markdown maxWidth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restore dropped raw type name in type_search.tsx detail preview - Fix report header separator overflow in model_method_run.ts by computing separator length as cols minus header prefix length - Remove unreachable getTerminalColumns() fallback in report_get.ts - Remove whole-line truncation before logger.info in extension_list.ts to follow the convention that only writeOutput() calls get width constraints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
CLI UX Review
Blocking
None.
Suggestions
-
audit_timeline.ts— truncation sentinel inconsistency (pre-existing, not introduced here): The audit timeline still uses"..."(3-char ASCII) with a-3offset, while the new truncations indata_list.tsandextension_list.tsuse"…"(single Unicode ellipsis) with a-1offset. This pre-dated the PR, but since several renderers were touched it would be a natural place to align them. Non-blocking. -
report_search.tsx—renderMarkdownToTerminalin detail preview has nomaxWidth: The full-detail path callsrenderMarkdownToTerminal(detail.markdown)withoutmaxWidthbefore wrapping the result in<Text wrap="truncate-end">. Log-mode renderers (report_get.ts,model_method_run.ts) do passmaxWidth. In TUI mode Ink'swrap="truncate-end"will clip each line, so this is functional — but markdown tables and code blocks may render wider than the pane before being clipped rather than reflowed. PassinginnerWidthhere would make it consistent with the log-mode approach.
Verdict
PASS — rendering-only change that improves narrow-terminal experience across all interactive search pickers and six log-mode renderers. No UX regressions, no missing JSON fields, no broken help text. Both truncation strategies (log-mode getTerminalColumns() + Ink overflow="hidden" / wrap="truncate-end") are applied consistently across the renderer surface area.
There was a problem hiding this comment.
Code Review
Blocking Issues
None.
Suggestions
-
Missing truncation in
summarise.tsbeforepadEnd()— Inextension_list.ts, names are properly truncated before padding when they exceed the clamped max:const name = ext.name.length > maxName ? ext.name.substring(0, maxName - 1) + "…" : ext.name; const paddedName = name.padEnd(maxName);
But in
summarise.ts, bothmethod.method(line 82) andgroup.workflowName(line 153) are passed directly to.padEnd()without truncation. When a name exceedsMath.floor(cols * 0.4),padEndis a no-op and the full name is printed, defeating the column clamping. Consider adding the same truncation pattern used inextension_list.ts. -
report_get.tscaptures terminal width at construction time —LogReportGetRenderercallsgetTerminalColumns()in its constructor rather than in thecompletedhandler. If a resize happens between construction and rendering, the old width is used. The other log-mode renderers all call it at render time. This is unlikely to matter in practice (these are short-lived command invocations), but it's a minor inconsistency.
Overall
Well-scoped fix. All changes are confined to the presentation layer — no domain logic touched. The getTerminalColumns() utility is placed correctly in presentation/output/, has a unit test, and the Ink changes (adding overflow="hidden", width constraints, wrap="truncate-end") consistently follow the same pattern. The flattening from nested <Box> to flat <Text> arrays is the right approach to avoid Ink/Yoga layout issues. Design doc update is a nice addition.
Summary
<Box>structures to flat<Text>arrays — nested Boxes cause Ink/Yoga layout garbling at narrow terminal widthsoverflow="hidden"toResultsListand SearchPicker's inline/minimal tier containersgetTerminalColumns()utility wrappingDeno.consoleSize()with 80-col fallback for log-mode renderersmaxWidthdesign/rendering.mdTest Plan
model type search,model search,data search,extension search,workflow search,data query🤖 Generated with Claude Code