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:
Jerop Kipruto
2026-01-30 15:40:05 -05:00
parent 76387d22ae
commit c30f964fba
3 changed files with 43 additions and 2 deletions

View File

@@ -4,10 +4,11 @@
* 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 { OverflowProvider } from '../../contexts/OverflowContext.js';
import { MaxSizedBox } from './MaxSizedBox.js';
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
import { Box, Text } from 'ink';
import { describe, it, expect } from 'vitest';
@@ -226,4 +227,31 @@ describe('<MaxSizedBox />', () => {
expect(lastFrame()).toMatchSnapshot();
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();
});
});

View File

@@ -120,7 +120,12 @@ export const MaxSizedBox: React.FC<MaxSizedBoxProps> = ({
hidden ...
</Text>
)}
<Box flexDirection="column" overflow="hidden" flexGrow={1}>
<Box
flexDirection="column"
overflow="hidden"
flexGrow={1}
maxHeight={isOverflowing ? visibleContentHeight : undefined}
>
<Box
flexDirection="column"
ref={onRefChange}

View File

@@ -31,6 +31,14 @@ Line 29
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`] = `
"Line 1
Line 2"