Fixes for Ink 6.4.0 (#12352)

This commit is contained in:
Jacob Richman
2025-10-31 07:43:12 -07:00
committed by GitHub
parent 9da3cb7e43
commit ab8c24f5ea
12 changed files with 114 additions and 77 deletions
+23
View File
@@ -5,6 +5,7 @@
*/
import { render as inkRender } from 'ink-testing-library';
import { Box } from 'ink';
import type React from 'react';
import { act } from 'react';
import { LoadedSettings, type Settings } from '../config/settings.js';
@@ -22,6 +23,7 @@ import { type Config } from '@google/gemini-cli-core';
// Wrapper around ink-testing-library's render that ensures act() is called
export const render = (
tree: React.ReactElement,
terminalWidth?: number,
): ReturnType<typeof inkRender> => {
let renderResult: ReturnType<typeof inkRender> =
undefined as unknown as ReturnType<typeof inkRender>;
@@ -29,6 +31,19 @@ export const render = (
renderResult = inkRender(tree);
});
if (terminalWidth !== undefined && renderResult?.stdout) {
// Override the columns getter on the stdout instance provided by ink-testing-library
Object.defineProperty(renderResult.stdout, 'columns', {
get: () => terminalWidth,
configurable: true,
});
// Trigger a rerender so Ink can pick up the new terminal width
act(() => {
renderResult.rerender(tree);
});
}
const originalUnmount = renderResult.unmount;
const originalRerender = renderResult.rerender;
@@ -148,13 +163,21 @@ export const renderWithProviders = (
<VimModeProvider settings={settings}>
<ShellFocusContext.Provider value={shellFocus}>
<KeypressProvider kittyProtocolEnabled={kittyProtocolEnabled}>
<Box
width={terminalWidth}
flexShrink={0}
flexGrow={0}
flexDirection="column"
>
{component}
</Box>
</KeypressProvider>
</ShellFocusContext.Provider>
</VimModeProvider>
</UIStateContext.Provider>
</SettingsContext.Provider>
</ConfigContext.Provider>,
terminalWidth,
);
};
@@ -47,6 +47,7 @@ const renderWithContext = (
<StreamingContext.Provider value={contextValue}>
{ui}
</StreamingContext.Provider>,
width,
);
};
@@ -67,7 +67,10 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
</Text>
)}
{!isNarrow && cancelAndTimerContent && (
<>
<Box flexShrink={0} width={1} />
<Text color={theme.text.secondary}>{cancelAndTimerContent}</Text>
</>
)}
</Box>
{!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
@@ -14,6 +14,7 @@ describe('LoopDetectionConfirmation', () => {
it('renders correctly', () => {
const { lastFrame } = renderWithProviders(
<LoopDetectionConfirmation onComplete={onComplete} />,
{ width: 101 },
);
expect(lastFrame()).toMatchSnapshot();
});
@@ -21,6 +22,7 @@ describe('LoopDetectionConfirmation', () => {
it('contains the expected options', () => {
const { lastFrame } = renderWithProviders(
<LoopDetectionConfirmation onComplete={onComplete} />,
{ width: 100 },
);
const output = lastFrame()!.toString();
@@ -50,11 +50,12 @@ export function LoopDetectionConfirmation({
];
return (
<Box width="100%" flexDirection="row">
<Box
flexDirection="column"
borderStyle="round"
borderColor={theme.status.warning}
width="100%"
flexGrow={1}
marginLeft={1}
>
<Box paddingX={1} paddingY={0} flexDirection="column">
@@ -72,12 +73,12 @@ export function LoopDetectionConfirmation({
</Text>
</Box>
</Box>
<Box width="100%" marginTop={1}>
<Box marginTop={1}>
<Box flexDirection="column">
<Text color={theme.text.secondary}>
This can happen due to repetitive tool calls or other model
behavior. Do you want to keep loop detection enabled or disable it
for this session?
behavior. Do you want to keep loop detection enabled or disable
it for this session?
</Text>
<Box marginTop={1}>
<RadioButtonSelect items={OPTIONS} onSelect={onComplete} />
@@ -86,5 +87,6 @@ export function LoopDetectionConfirmation({
</Box>
</Box>
</Box>
</Box>
);
}
@@ -73,13 +73,14 @@ describe('PrepareLabel', () => {
textColor={color}
isExpanded={true}
/>,
100,
);
expect(lastFrame()).toMatchSnapshot();
unmount();
});
it('creates centered window around match when collapsed', () => {
const prefix = 'cd /very/long/path/that/keeps/going/'.repeat(3);
const prefix = 'cd_/very/long/path/that/keeps/going/'.repeat(3);
const core = 'search-here';
const suffix = '/and/then/some/more/components/'.repeat(3);
const label = prefix + core + suffix;
@@ -92,6 +93,7 @@ describe('PrepareLabel', () => {
textColor={color}
isExpanded={false}
/>,
100,
);
const out = lastFrame();
const f = flat(out);
@@ -19,6 +19,7 @@ describe('ShellConfirmationDialog', () => {
it('renders correctly', () => {
const { lastFrame } = renderWithProviders(
<ShellConfirmationDialog request={request} />,
{ width: 101 },
);
expect(lastFrame()).toMatchSnapshot();
});
@@ -45,6 +46,7 @@ describe('ShellConfirmationDialog', () => {
it('calls onConfirm with Cancel when "No (esc)" is selected', () => {
const { lastFrame } = renderWithProviders(
<ShellConfirmationDialog request={request} />,
{ width: 100 },
);
const select = lastFrame()!.toString();
// Simulate selecting the third option
@@ -68,12 +68,13 @@ export const ShellConfirmationDialog: React.FC<
];
return (
<Box flexDirection="row" width="100%">
<Box
flexDirection="column"
borderStyle="round"
borderColor={theme.status.warning}
padding={1}
width="100%"
flexGrow={1}
marginLeft={1}
>
<Box flexDirection="column" marginBottom={1}>
@@ -104,5 +105,6 @@ export const ShellConfirmationDialog: React.FC<
<RadioButtonSelect items={options} onSelect={handleSelect} isFocused />
</Box>
</Box>
);
};
@@ -112,7 +112,7 @@ export function SuggestionsDisplay({
</Box>
)}
{isActive && isLong && (
<Box>
<Box width={3} flexShrink={0}>
<Text color={Colors.Gray}>{isExpanded ? ' ← ' : ' → '}</Text>
</Box>
)}
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<LoadingIndicator /> > should truncate long primary text instead of wrapping 1`] = `
"MockResponding This is an extremely long loading phrase that should be truncated in t (esc to
Spinner cancel, 5s)"
"MockRespondin This is an extremely long loading phrase that shoul (esc to
gSpinner cancel, 5s)"
`;
@@ -1,7 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`PrepareLabel > creates centered window around match when collapsed 1`] = `
"...ry/long/path/that/keeps/going/cd /very/long/path/that/keeps/going/search-here/and/then/some/more/
"...ry/long/path/that/keeps/going/cd_/very/long/path/that/keeps/going/search-here/and/then/some/more/
components//and/then/some/more/components//and/..."
`;