Files
gemini-cli/packages/cli/src/ui/utils/toolLayoutUtils.ts
T
Jarrod Whelan 09a6667c35 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.

refactor(cli): cleanup and simplify UI components

- Reduced UI refresh delay in AppContainer.tsx for a more responsive user experience.
- Reorder imports and hook definitions within AppContainer.tsx to reduce diff 'noise'.

refactor(cli): enhance compact output robustness and visual regression testing

Addressing automated review feedback to improve code maintainability and layout stability.

1. Robust File Extension Parsing:
- Introduced getFileExtension utility in packages/cli/src/ui/utils/fileUtils.ts using node:path for reliable extension extraction.
- Updated DenseToolMessage and DiffRenderer to use the new utility, replacing fragile string splitting.

2. Visual Regression Coverage:
- Added SVG snapshot tests to DenseToolMessage.test.tsx to verify semantic color rendering and layout integrity in compact mode.

fix(cli): resolve dense tool output code quality issues

- Replaced manual string truncation with Ink's `wrap="truncate-end"` to adhere to UI guidelines.
- Added `isReadManyFilesResult` type guard to `packages/core/src/tools/tools.ts` to improve typing for structured tool results.
- Fixed an incomplete test case in `DenseToolMessage.test.tsx` to properly simulate expansion via context instead of missing mouse events.
2026-03-26 10:37:26 -07:00

124 lines
3.9 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
ACTIVE_SHELL_MAX_LINES,
COMPLETED_SHELL_MAX_LINES,
} from '../constants.js';
import { CoreToolCallStatus } from '@google/gemini-cli-core';
/**
* Constants used for calculating available height for tool results.
* These MUST be kept in sync between ToolGroupMessage (for overflow detection)
* and ToolResultDisplay (for actual truncation).
*/
export const TOOL_RESULT_STATIC_HEIGHT = 1;
export const TOOL_RESULT_ASB_RESERVED_LINE_COUNT = 6;
export const TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT = 4;
export const TOOL_RESULT_MIN_LINES_SHOWN = 2;
/**
* The vertical space (in lines) consumed by the shell UI elements
* (1 line for the shell title/header and 2 lines for the top and bottom borders).
*/
export const SHELL_CONTENT_OVERHEAD =
TOOL_RESULT_STATIC_HEIGHT + TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT;
/**
* Calculates the final height available for the content of a tool result display.
*
* This accounts for:
* 1. The static height of the tool message (name, status line).
* 2. Reserved space for hints and padding (different in ASB vs Standard mode).
* 3. Enforcing a minimum number of lines shown.
*/
export function calculateToolContentMaxLines(options: {
availableTerminalHeight: number | undefined;
isAlternateBuffer: boolean;
maxLinesLimit?: number;
}): number | undefined {
const { availableTerminalHeight, isAlternateBuffer, maxLinesLimit } = options;
const reservedLines = isAlternateBuffer
? TOOL_RESULT_ASB_RESERVED_LINE_COUNT
: TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT;
let contentHeight =
availableTerminalHeight !== undefined
? Math.max(
availableTerminalHeight - TOOL_RESULT_STATIC_HEIGHT - reservedLines,
TOOL_RESULT_MIN_LINES_SHOWN + 1,
)
: undefined;
if (maxLinesLimit !== undefined) {
contentHeight =
contentHeight !== undefined
? Math.min(contentHeight, maxLinesLimit)
: maxLinesLimit;
}
return contentHeight;
}
/**
* Calculates the maximum number of lines to display for shell output.
*
* This logic distinguishes between:
* 1. Process Status: Active (Executing) vs Completed.
* 2. UI Focus: Whether the user is currently interacting with the shell.
* 3. Expansion State: Whether the user has explicitly requested to "Show More Lines" (CTRL+O).
*/
export function calculateShellMaxLines(options: {
status: CoreToolCallStatus;
isAlternateBuffer: boolean;
isThisShellFocused: boolean;
availableTerminalHeight: number | undefined;
constrainHeight: boolean;
isExpandable: boolean | undefined;
}): number | undefined {
const {
status,
isAlternateBuffer,
isThisShellFocused,
availableTerminalHeight,
constrainHeight,
isExpandable,
} = options;
// 1. If the user explicitly requested expansion (unconstrained), remove all caps.
if (!constrainHeight && isExpandable) {
return undefined;
}
// 2. Handle cases where height is unknown (Standard mode history).
if (availableTerminalHeight === undefined) {
return isAlternateBuffer
? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD
: undefined;
}
const maxLinesBasedOnHeight = Math.max(
1,
availableTerminalHeight - TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT,
);
// 3. Handle ASB mode focus expansion.
// We allow a focused shell in ASB mode to take up the full available height,
// BUT only if we aren't trying to maintain a constrained view (e.g., history items).
if (isAlternateBuffer && isThisShellFocused && !constrainHeight) {
return maxLinesBasedOnHeight;
}
// 4. Fall back to process-based constants.
const isExecuting = status === CoreToolCallStatus.Executing;
const shellMaxLinesLimit = isExecuting
? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD
: COMPLETED_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD;
return Math.min(maxLinesBasedOnHeight, shellMaxLinesLimit);
}