fix(ui): Correct footer measurement and prevent negative terminal height (#8362)

This commit is contained in:
Gal Zahavi
2025-09-12 22:42:17 -07:00
committed by GitHub
parent 5a05fb0dd0
commit 087c192e51
2 changed files with 74 additions and 15 deletions

View File

@@ -40,6 +40,14 @@ vi.mock('./App.js', () => ({
App: TestContextConsumer,
}));
vi.mock('ink', async (importOriginal) => {
const original = await importOriginal<typeof import('ink')>();
return {
...original,
measureElement: vi.fn(),
};
});
vi.mock('./hooks/useQuotaAndFallback.js');
vi.mock('./hooks/useHistoryManager.js');
vi.mock('./hooks/useThemeCommand.js');
@@ -92,6 +100,9 @@ import { useSessionStats } from './contexts/SessionContext.js';
import { useTextBuffer } from './components/shared/text-buffer.js';
import { useLogger } from './hooks/useLogger.js';
import { useLoadingIndicator } from './hooks/useLoadingIndicator.js';
import { measureElement } from 'ink';
import { useTerminalSize } from './hooks/useTerminalSize.js';
import { ShellExecutionService } from '@google/gemini-cli-core';
describe('AppContainer State Management', () => {
let mockConfig: Config;
@@ -556,4 +567,42 @@ describe('AppContainer State Management', () => {
expect(mockHandler).toHaveBeenCalledWith('auth');
});
});
describe('Terminal Height Calculation', () => {
const mockedMeasureElement = measureElement as Mock;
const mockedUseTerminalSize = useTerminalSize as Mock;
it('should prevent terminal height from being less than 1', () => {
const resizePtySpy = vi.spyOn(ShellExecutionService, 'resizePty');
// Arrange: Simulate a small terminal and a large footer
mockedUseTerminalSize.mockReturnValue({ columns: 80, rows: 5 });
mockedMeasureElement.mockReturnValue({ width: 80, height: 10 }); // Footer is taller than the screen
mockedUseGeminiStream.mockReturnValue({
streamingState: 'idle',
submitQuery: vi.fn(),
initError: null,
pendingHistoryItems: [],
thought: null,
cancelOngoingRequest: vi.fn(),
activePtyId: 'some-id',
});
render(
<AppContainer
config={mockConfig}
settings={mockSettings}
version="1.0.0"
initializationResult={mockInitResult}
/>,
);
// Assert: The shell should be resized to a minimum height of 1, not a negative number.
// The old code would have tried to set a negative height.
expect(resizePtySpy).toHaveBeenCalled();
const lastCall =
resizePtySpy.mock.calls[resizePtySpy.mock.calls.length - 1];
// Check the height argument specifically
expect(lastCall[2]).toBe(1);
});
});
});

View File

@@ -4,7 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { useMemo, useState, useCallback, useEffect, useRef } from 'react';
import {
useMemo,
useState,
useCallback,
useEffect,
useRef,
useLayoutEffect,
} from 'react';
import { type DOMElement, measureElement } from 'ink';
import { App } from './App.js';
import { AppContext } from './contexts/AppContext.js';
@@ -622,18 +629,27 @@ Logging in with Google... Please restart Gemini CLI to continue.
streamingState === StreamingState.Responding) &&
!proQuotaRequest;
// Compute available terminal height based on controls measurement
const availableTerminalHeight = useMemo(() => {
const [controlsHeight, setControlsHeight] = useState(0);
useLayoutEffect(() => {
if (mainControlsRef.current) {
const fullFooterMeasurement = measureElement(mainControlsRef.current);
return terminalHeight - fullFooterMeasurement.height - staticExtraHeight;
if (fullFooterMeasurement.height > 0) {
setControlsHeight(fullFooterMeasurement.height);
}
}
return terminalHeight - staticExtraHeight;
}, [terminalHeight]);
}, [buffer, terminalWidth, terminalHeight]);
// Compute available terminal height based on controls measurement
const availableTerminalHeight =
terminalHeight - controlsHeight - staticExtraHeight;
config.setShellExecutionConfig({
terminalWidth: Math.floor(terminalWidth * SHELL_WIDTH_FRACTION),
terminalHeight: Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING),
terminalHeight: Math.max(
Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING),
1,
),
pager: settings.merged.tools?.shell?.pager,
showColor: settings.merged.tools?.shell?.showColor,
});
@@ -660,16 +676,10 @@ Logging in with Google... Please restart Gemini CLI to continue.
ShellExecutionService.resizePty(
activePtyId,
Math.floor(terminalWidth * SHELL_WIDTH_FRACTION),
Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING),
Math.max(Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING), 1),
);
}
}, [
terminalHeight,
terminalWidth,
availableTerminalHeight,
activePtyId,
geminiClient,
]);
}, [terminalWidth, availableTerminalHeight, activePtyId]);
useEffect(() => {
if (