mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-02-01 22:48:03 +00:00
fix(ui): prevent content leak in MaxSizedBox bottom overflow
When using overflowDirection="bottom", content was bleeding through after the "... last N lines hidden ..." indicator. This was caused by the inner overflow Box not having a constrained height. Add maxHeight constraint to inner overflow Box when overflowing.
This commit is contained in:
@@ -4,10 +4,11 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render } from '../../../test-utils/render.js';
|
import { render, renderWithProviders } from '../../../test-utils/render.js';
|
||||||
import { waitFor } from '../../../test-utils/async.js';
|
import { waitFor } from '../../../test-utils/async.js';
|
||||||
import { OverflowProvider } from '../../contexts/OverflowContext.js';
|
import { OverflowProvider } from '../../contexts/OverflowContext.js';
|
||||||
import { MaxSizedBox } from './MaxSizedBox.js';
|
import { MaxSizedBox } from './MaxSizedBox.js';
|
||||||
|
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
@@ -226,4 +227,31 @@ describe('<MaxSizedBox />', () => {
|
|||||||
expect(lastFrame()).toMatchSnapshot();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not leak content after hidden indicator with bottom overflow', async () => {
|
||||||
|
const markdownContent = Array.from(
|
||||||
|
{ length: 20 },
|
||||||
|
(_, i) => `- Step ${i + 1}: Do something important`,
|
||||||
|
).join('\n');
|
||||||
|
const { lastFrame } = renderWithProviders(
|
||||||
|
<MaxSizedBox maxWidth={80} maxHeight={5} overflowDirection="bottom">
|
||||||
|
<MarkdownDisplay
|
||||||
|
text={`## Plan\n\n${markdownContent}`}
|
||||||
|
isPending={false}
|
||||||
|
terminalWidth={76}
|
||||||
|
/>
|
||||||
|
</MaxSizedBox>,
|
||||||
|
{ width: 80 },
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => expect(lastFrame()).toContain('... last'));
|
||||||
|
|
||||||
|
const frame = lastFrame()!;
|
||||||
|
const lines = frame.split('\n');
|
||||||
|
const lastLine = lines[lines.length - 1];
|
||||||
|
|
||||||
|
// The last line should only contain the hidden indicator, no leaked content
|
||||||
|
expect(lastLine).toMatch(/^\.\.\. last \d+ lines? hidden \.\.\.$/);
|
||||||
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -120,7 +120,12 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
|
|||||||
hidden ...
|
hidden ...
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Box flexDirection="column" overflow="hidden" flexGrow={1}>
|
<Box
|
||||||
|
flexDirection="column"
|
||||||
|
overflow="hidden"
|
||||||
|
flexGrow={1}
|
||||||
|
maxHeight={isOverflowing ? visibleContentHeight : undefined}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
ref={onRefChange}
|
ref={onRefChange}
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ Line 29
|
|||||||
Line 30"
|
Line 30"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`<MaxSizedBox /> > does not leak content after hidden indicator with bottom overflow 1`] = `
|
||||||
|
"Plan
|
||||||
|
|
||||||
|
- Step 1: Do something important
|
||||||
|
- Step 2: Do something important
|
||||||
|
... last 18 lines hidden ..."
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`<MaxSizedBox /> > does not truncate when maxHeight is undefined 1`] = `
|
exports[`<MaxSizedBox /> > does not truncate when maxHeight is undefined 1`] = `
|
||||||
"Line 1
|
"Line 1
|
||||||
Line 2"
|
Line 2"
|
||||||
|
|||||||
Reference in New Issue
Block a user