refactor(cli): address nuanced snapshot rendering scenarios through layout and padding refinements

- Refined height calculation logic in ToolGroupMessage to ensure consistent spacing between compact and standard tools.
- Adjusted padding and margins in StickyHeader, ToolConfirmationQueue, ShellToolMessage, and ToolMessage for visual alignment.
- Updated TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT to account for internal layout changes.
- Improved ToolResultDisplay height handling in alternate buffer mode.
- Updated test snapshots to reflect layout and spacing corrections.
This commit is contained in:
Jarrod Whelan
2026-03-25 16:07:59 -07:00
parent 1f0248d8df
commit e036fc3bc2
13 changed files with 60 additions and 66 deletions

View File

@@ -67,7 +67,6 @@ export const StickyHeader: React.FC<StickyHeaderProps> = ({
borderLeft={true}
borderRight={true}
paddingX={1}
paddingBottom={1}
paddingTop={isFirst ? 0 : 1}
>
{children}

View File

@@ -66,9 +66,9 @@ export const ToolConfirmationQueue: React.FC<ToolConfirmationQueueProps> = ({
// ToolConfirmationMessage needs to know the height available for its OWN content.
// We subtract the lines used by the Queue wrapper:
// - 2 lines for the rounded border
// - 2 lines for the rounded border (top/bottom)
// - 2 lines for the Header (text + margin)
// - 2 lines for Tool Identity (text + margin)
// - 2 lines for Tool Identity (text + margin) if shown
const availableContentHeight = constrainHeight
? Math.max(maxHeight - (hideToolIdentity ? 4 : 6), 4)
: undefined;
@@ -83,10 +83,7 @@ export const ToolConfirmationQueue: React.FC<ToolConfirmationQueueProps> = ({
>
<Box flexDirection="column" width={mainAreaWidth - 4}>
{/* Header */}
<Box
marginBottom={hideToolIdentity ? 0 : 1}
justifyContent="space-between"
>
<Box marginBottom={1} justifyContent="space-between">
<Text color={borderColor} bold>
{getConfirmationHeader(tool.confirmationDetails)}
</Text>
@@ -98,7 +95,7 @@ export const ToolConfirmationQueue: React.FC<ToolConfirmationQueueProps> = ({
</Box>
{!hideToolIdentity && (
<Box>
<Box marginBottom={1}>
<ToolStatusIndicator status={tool.status} name={tool.name} />
<ToolInfo
name={tool.name}

View File

@@ -6,12 +6,11 @@ AppHeader(full)
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command Running a long command... │
│ │
│ Line 10 │
│ Line 11 │
│ Line 12 │
│ Line 13 │
│ Line 14 │
│ Line 15
│ Line 15
│ Line 16 █ │
│ Line 17 █ │
│ Line 18 █ │
@@ -27,12 +26,11 @@ AppHeader(full)
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command Running a long command... │
│ │
│ Line 10 │
│ Line 11 │
│ Line 12 │
│ Line 13 │
│ Line 14 │
│ Line 15
│ Line 15
│ Line 16 █ │
│ Line 17 █ │
│ Line 18 █ │
@@ -47,8 +45,7 @@ exports[`MainContent > MainContent Tool Output Height Logic > 'Normal mode - Con
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command Running a long command... │
│ │
│ ... first 10 lines hidden (Ctrl+O to show) ... │
│ Line 11 │
│ ... first 11 lines hidden (Ctrl+O to show) ... │
│ Line 12 │
│ Line 13 │
│ Line 14 │

View File

@@ -190,6 +190,7 @@ export const ShellToolMessage: React.FC<ShellToolMessageProps> = ({
borderLeft={true}
borderRight={true}
paddingX={1}
paddingTop={1}
flexDirection="column"
>
<ToolResultDisplay

View File

@@ -414,7 +414,7 @@ describe('<ToolGroupMessage />', () => {
];
const item = createItem(toolCalls);
const { lastFrame, unmount } = await renderWithProviders(
<Scrollable height={10} hasFocus={true} scrollToBottom={true}>
<Scrollable height={12} hasFocus={true} scrollToBottom={true}>
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />
</Scrollable>,
{

View File

@@ -222,38 +222,41 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
!Array.isArray(prevGroup) &&
isCompactTool(prevGroup, isCompactModeEnabled);
if (Array.isArray(group)) {
const isAgentGroup = Array.isArray(group);
const isCompact =
!isAgentGroup && isCompactTool(group, isCompactModeEnabled);
if (isFirst) {
height += (borderTopOverride ?? false) ? 1 : 0;
} else if (isCompact && prevIsCompact) {
height += 0;
} else if (isCompact || prevIsCompact) {
height += 1;
} else {
// Gap is provided by StickyHeader's paddingTop=1
height += 0;
}
const isFirstProp = !!(isFirst
? (borderTopOverride ?? true)
: prevIsCompact);
if (isAgentGroup) {
// Agent group
height += 1; // Header
height += group.length; // 1 line per agent
const isFirstProp = isFirst
? (borderTopOverride ?? true)
: prevIsCompact;
if (isFirstProp) height += 1; // Top border
// Spacing logic
if (isFirst) {
height += (borderTopOverride ?? true) ? 1 : 0;
} else {
height += 1; // marginTop
}
} else {
const isCompact = isCompactTool(group, isCompactModeEnabled);
if (isCompact) {
height += 1; // Base height for compact tool
// Spacing logic (matching marginTop)
if (isFirst) {
height += (borderTopOverride ?? true) ? 1 : 0;
} else if (!prevIsCompact) {
height += 1;
}
} else {
height += 3; // Static overhead for standard tool
if (isFirst) {
height += (borderTopOverride ?? true) ? 1 : 0;
} else {
height += 1; // marginTop is always 1 for non-compact tools (not first)
}
// Static overhead for standard tool header:
// 1 line for header text
// 1 line for dark separator
// 1 line for tool body internal paddingTop=1
// 1 line for top border (if isFirstProp) OR StickyHeader paddingTop=1 (if !isFirstProp)
height += 3;
height += isFirstProp ? 1 : 1; // Either top border or paddingTop
}
}
}
@@ -351,12 +354,14 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
let marginTop = 0;
if (isFirst) {
// marginTop = (borderTopOverride ?? true) ? 1 : 0;
marginTop = (borderTopOverride ?? false) ? 1 : 0;
} else if (isCompact && prevIsCompact) {
marginTop = 0;
} else {
} else if (isCompact || prevIsCompact) {
marginTop = 1;
} else {
// Subsequent standard tools: StickyHeader's paddingTop=1 provides the gap.
marginTop = 0;
}
const isFirstProp = !!(isFirst

View File

@@ -119,6 +119,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
borderLeft={true}
borderRight={true}
paddingX={1}
paddingTop={1}
flexDirection="column"
>
{status === CoreToolCallStatus.Executing && progress !== undefined && (

View File

@@ -179,10 +179,16 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
// Final render based on session mode
if (isAlternateBuffer) {
// If availableTerminalHeight is undefined, we don't have a fixed budget,
// so if maxLines is also undefined, we shouldn't cap the height at all.
const effectiveMaxHeight =
maxLines ??
(availableTerminalHeight !== undefined ? availableHeight : undefined);
return (
<Scrollable
width={childWidth}
maxHeight={maxLines ?? availableHeight}
maxHeight={effectiveMaxHeight}
hasFocus={hasFocus} // Allow scrolling via keyboard (Shift+Up/Down)
scrollToBottom={true}
reportOverflow={true}

View File

@@ -4,7 +4,6 @@ exports[`<ShellToolMessage /> > Height Constraints > defaults to ACTIVE_SHELL_MA
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command A shell command │
│ │
│ Line 90 │
│ Line 91 │
│ Line 92 │
│ Line 93 │
@@ -129,7 +128,6 @@ exports[`<ShellToolMessage /> > Height Constraints > respects availableTerminalH
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command A shell command │
│ │
│ Line 94 │
│ Line 95 │
│ Line 96 │
│ Line 97 │
@@ -143,7 +141,6 @@ exports[`<ShellToolMessage /> > Height Constraints > stays constrained in altern
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ✓ Shell Command A shell command │
│ │
│ Line 90 │
│ Line 91 │
│ Line 92 │
│ Line 93 │
@@ -161,7 +158,6 @@ exports[`<ShellToolMessage /> > Height Constraints > uses ACTIVE_SHELL_MAX_LINES
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command A shell command │
│ │
│ Line 90 │
│ Line 91 │
│ Line 92 │
│ Line 93 │
@@ -179,11 +175,10 @@ exports[`<ShellToolMessage /> > Height Constraints > uses full availableTerminal
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ⊶ Shell Command A shell command (Shift+Tab to unfocus) │
│ │
│ Line 4 │
│ Line 5 │
│ Line 6 │
│ Line 7
│ Line 8
│ Line 7
│ Line 8
│ Line 9 █ │
│ Line 10 █ │
│ Line 11 █ │

View File

@@ -32,7 +32,6 @@ exports[`<ToolGroupMessage /> > Border Color Logic > uses gray border when all t
│ ✓ test-tool A tool for testing │
│ │
│ Test result │
│ │
│ ✓ another-tool A tool for testing │
│ │
@@ -62,10 +61,12 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders canceled tool calls >
exports[`<ToolGroupMessage /> > Golden Snapshots > renders empty tool calls array 1`] = `""`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders header when scrolled 1`] = `
"│ ✓ tool-1 Description 1. This is a long description that will need to b… │
──────────────────────────────────────────────────────────────────────────
"╭──────────────────────────────────────────────────────────────────────────╮
✓ tool-1 Description 1. This is a long description that will need to b…
│──────────────────────────────────────────────────────────────────────────│ ▄
line4
│ line5 │ █
│ │ █
│ ✓ tool-2 Description 2 │ █
│ │ █
│ line1 │ █
@@ -80,12 +81,10 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders mixed tool calls incl
│ ✓ read_file Read a file │
│ │
│ Test result │
│ │
│ ⊶ run_shell_command Run command │
│ │
│ Test result │
│ │
│ o write_file Write to file │
│ │
@@ -99,12 +98,10 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders multiple tool calls w
│ ✓ successful-tool This tool succeeded │
│ │
│ Test result │
│ │
│ o pending-tool This tool is pending │
│ │
│ Test result │
│ │
│ x error-tool This tool failed │
│ │
@@ -147,7 +144,6 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders with limited terminal
│ ✓ tool-with-result Tool with output │
│ │
│ This is a long result that might need height constraints │
│ │
│ ✓ another-tool Another tool │
│ │
@@ -170,12 +166,10 @@ exports[`<ToolGroupMessage /> > Height Calculation > calculates available height
│ ✓ test-tool A tool for testing │
│ │
│ Result 1 │
│ │
│ ✓ test-tool A tool for testing │
│ │
│ Result 2 │
│ │
│ ✓ test-tool A tool for testing │
│ │

View File

@@ -37,8 +37,7 @@ exports[`ToolResultDisplay > renders string result as plain text when renderOutp
`;
exports[`ToolResultDisplay > truncates very long string results 1`] = `
"... 249 hidden (Ctrl+O) ...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
"... 250 hidden (Ctrl+O) ...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

View File

@@ -40,7 +40,7 @@ exports[`ToolMessage Sticky Header Regression > verifies that multiple ToolMessa
"│ │
│ ✓ tool-2 Description for tool-2 │
│────────────────────────────────────────────────────────────────────────│
│ c2-09
│ c2-10 │
│ c2-10 │
╰────────────────────────────────────────────────────────────────────────╯
"
`;

View File

@@ -17,7 +17,7 @@ import { CoreToolCallStatus } from '@google/gemini-cli-core';
*/
export const TOOL_RESULT_STATIC_HEIGHT = 1;
export const TOOL_RESULT_ASB_RESERVED_LINE_COUNT = 6;
export const TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT = 3;
export const TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT = 4;
export const TOOL_RESULT_MIN_LINES_SHOWN = 2;
/**