feat(cli): improve CTRL+O experience for both standard and alternate screen buffer (ASB) modes (#19010)

Co-authored-by: jacob314 <jacob314@gmail.com>
This commit is contained in:
Jarrod Whelan
2026-02-20 16:26:11 -08:00
committed by GitHub
parent 547f5d45f5
commit 727f9b67b1
39 changed files with 1622 additions and 428 deletions
@@ -19,10 +19,7 @@ import { Scrollable } from '../shared/Scrollable.js';
import { ScrollableList } from '../shared/ScrollableList.js';
import { SCROLL_TO_ITEM_END } from '../shared/VirtualizedList.js';
import { ACTIVE_SHELL_MAX_LINES } from '../../constants.js';
const STATIC_HEIGHT = 1;
const RESERVED_LINE_COUNT = 6; // for tool name, status, padding, and 'ShowMoreLines' hint
const MIN_LINES_SHOWN = 2; // show at least this many lines
import { calculateToolContentMaxLines } from '../../utils/toolLayoutUtils.js';
// Large threshold to ensure we don't cause performance issues for very large
// outputs that will get truncated further MaxSizedBox anyway.
@@ -53,16 +50,11 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
const { renderMarkdown } = useUIState();
const isAlternateBuffer = useAlternateBuffer();
let availableHeight = availableTerminalHeight
? Math.max(
availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT,
MIN_LINES_SHOWN + 1, // enforce minimum lines shown
)
: undefined;
if (maxLines && availableHeight) {
availableHeight = Math.min(availableHeight, maxLines);
}
const availableHeight = calculateToolContentMaxLines({
availableTerminalHeight,
isAlternateBuffer,
maxLinesLimit: maxLines,
});
const combinedPaddingAndBorderWidth = 4;
const childWidth = terminalWidth - combinedPaddingAndBorderWidth;
@@ -81,7 +73,8 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
[],
);
const truncatedResultDisplay = React.useMemo(() => {
const { truncatedResultDisplay, hiddenLinesCount } = React.useMemo(() => {
let hiddenLines = 0;
// Only truncate string output if not in alternate buffer mode to ensure
// we can scroll through the full output.
if (typeof resultDisplay === 'string' && !isAlternateBuffer) {
@@ -94,14 +87,29 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
const contentText = hasTrailingNewline ? text.slice(0, -1) : text;
const lines = contentText.split('\n');
if (lines.length > maxLines) {
// We will have a label from MaxSizedBox. Reserve space for it.
const targetLines = Math.max(1, maxLines - 1);
hiddenLines = lines.length - targetLines;
text =
lines.slice(-maxLines).join('\n') +
lines.slice(-targetLines).join('\n') +
(hasTrailingNewline ? '\n' : '');
}
}
return text;
return { truncatedResultDisplay: text, hiddenLinesCount: hiddenLines };
}
return resultDisplay;
if (Array.isArray(resultDisplay) && !isAlternateBuffer && maxLines) {
if (resultDisplay.length > maxLines) {
// We will have a label from MaxSizedBox. Reserve space for it.
const targetLines = Math.max(1, maxLines - 1);
return {
truncatedResultDisplay: resultDisplay.slice(-targetLines),
hiddenLinesCount: resultDisplay.length - targetLines,
};
}
}
return { truncatedResultDisplay: resultDisplay, hiddenLinesCount: 0 };
}, [resultDisplay, isAlternateBuffer, maxLines]);
if (!truncatedResultDisplay) return null;
@@ -229,7 +237,11 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
return (
<Box width={childWidth} flexDirection="column">
<MaxSizedBox maxHeight={availableHeight} maxWidth={childWidth}>
<MaxSizedBox
maxHeight={availableHeight}
maxWidth={childWidth}
additionalHiddenLinesCount={hiddenLinesCount}
>
{content}
</MaxSizedBox>
</Box>