fix(cli): switch default back to terminalBuffer=false and fix regressions introduced for that mode (#24873)

This commit is contained in:
Jacob Richman
2026-04-07 22:47:54 -07:00
committed by GitHub
parent b9f1d832c8
commit 7e1938c1bc
21 changed files with 363 additions and 244 deletions
@@ -293,8 +293,8 @@ describe('<ShellToolMessage />', () => {
await waitUntilReady();
const frame = lastFrame();
// Since it's Executing, it might still constrain to ACTIVE_SHELL_MAX_LINES (10)
// Actually let's just assert on the behaviour that happens right now (which is 10 lines)
expect(frame.match(/Line \d+/g)?.length).toBe(10);
// Actually let's just assert on the behaviour that happens right now (which is 100 lines because we removed the terminalBuffer check)
expect(frame.match(/Line \d+/g)?.length).toBe(100);
unmount();
});
@@ -444,8 +444,8 @@ describe('<ToolMessage />', () => {
constrainHeight: true,
},
width: 80,
config: makeFakeConfig({ useAlternateBuffer: false }),
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
config: makeFakeConfig({ useAlternateBuffer: true }),
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
},
);
const output = lastFrame();
@@ -5,6 +5,7 @@
*/
import { renderWithProviders } from '../../../test-utils/render.js';
import { waitFor } from '../../../test-utils/async.js';
import { createMockSettings } from '../../../test-utils/settings.js';
import { ToolResultDisplay } from './ToolResultDisplay.js';
import { describe, it, expect, vi } from 'vitest';
@@ -351,9 +352,10 @@ describe('ToolResultDisplay', () => {
expect(output).not.toContain('Line 1');
expect(output).not.toContain('Line 2');
expect(output).toContain('Line 3');
expect(output).not.toContain('Line 3');
expect(output).toContain('Line 4');
expect(output).toContain('Line 5');
expect(output).toContain('hidden');
expect(output).toMatchSnapshot();
unmount();
});
@@ -391,4 +393,86 @@ describe('ToolResultDisplay', () => {
await expect(renderResult).toMatchSvgSnapshot();
unmount();
});
it('stays scrolled to the bottom when lines are incrementally added', async () => {
const createAnsiLine = (text: string) => [
{
text,
fg: '',
bg: '',
bold: false,
italic: false,
underline: false,
dim: false,
inverse: false,
isUninitialized: false,
},
];
let currentLines: AnsiOutput = [];
// Start with 3 lines, max lines 5. It should fit without scrolling.
for (let i = 1; i <= 3; i++) {
currentLines.push(createAnsiLine(`Line ${i}`));
}
const renderResult = await renderWithProviders(
<ToolResultDisplay
resultDisplay={currentLines}
terminalWidth={80}
maxLines={5}
availableTerminalHeight={5}
overflowDirection="top"
/>,
{
config: makeFakeConfig({ useAlternateBuffer: false }),
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
uiState: { constrainHeight: true, terminalHeight: 10 },
},
);
const { waitUntilReady, rerender, lastFrame, unmount } = renderResult;
await waitUntilReady();
// Verify initial render has the first 3 lines
expect(lastFrame()).toContain('Line 1');
expect(lastFrame()).toContain('Line 3');
// Incrementally add lines up to 8. Max lines is 5.
// So by the end, it should only show lines 4-8.
for (let i = 4; i <= 8; i++) {
currentLines = [...currentLines, createAnsiLine(`Line ${i}`)];
rerender(
<ToolResultDisplay
resultDisplay={currentLines}
terminalWidth={80}
maxLines={5}
availableTerminalHeight={5}
overflowDirection="top"
/>,
);
// Wait for the new line to be rendered
await waitFor(() => {
expect(lastFrame()).toContain(`Line ${i}`);
});
}
await waitUntilReady();
const output = lastFrame();
// The component should have automatically scrolled to the bottom.
// Lines 1, 2, 3, 4 should be scrolled out of view.
expect(output).not.toContain('Line 1');
expect(output).not.toContain('Line 2');
expect(output).not.toContain('Line 3');
expect(output).not.toContain('Line 4');
// Lines 5, 6, 7, 8 should be visible along with the truncation indicator.
expect(output).toContain('hidden');
expect(output).toContain('Line 5');
expect(output).toContain('Line 8');
expect(output).toMatchSnapshot();
unmount();
});
});
@@ -10,6 +10,7 @@ import { DiffRenderer } from './DiffRenderer.js';
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
import { AnsiOutputText, AnsiLineText } from '../AnsiOutput.js';
import { SlicingMaxSizedBox } from '../shared/SlicingMaxSizedBox.js';
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
import { theme } from '../../semantic-colors.js';
import {
type AnsiOutput,
@@ -51,7 +52,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
hasFocus = false,
overflowDirection = 'top',
}) => {
const { renderMarkdown } = useUIState();
const { renderMarkdown, constrainHeight } = useUIState();
const isAlternateBuffer = useAlternateBuffer();
const availableHeight = calculateToolContentMaxLines({
@@ -209,30 +210,73 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
if (Array.isArray(resultDisplay)) {
const limit = maxLines ?? availableHeight ?? ACTIVE_SHELL_MAX_LINES;
const listHeight = Math.min(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(resultDisplay as AnsiOutput).length,
limit,
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const data = resultDisplay as AnsiOutput;
const initialScrollIndex =
overflowDirection === 'bottom' ? 0 : SCROLL_TO_ITEM_END;
// Calculate list height: if not constrained, use full data length.
// If constrained (e.g. alternate buffer), limit to available height
// to ensure virtualization works and fits within the viewport.
const listHeight = !constrainHeight
? data.length
: Math.min(data.length, limit);
return (
<Box width={childWidth} flexDirection="column" maxHeight={listHeight}>
<ScrollableList
width={childWidth}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
data={resultDisplay as AnsiOutput}
renderItem={renderVirtualizedAnsiLine}
estimatedItemHeight={() => 1}
keyExtractor={keyExtractor}
initialScrollIndex={initialScrollIndex}
hasFocus={hasFocus}
fixedItemHeight={true}
/>
</Box>
);
if (isAlternateBuffer) {
const initialScrollIndex =
overflowDirection === 'bottom' ? 0 : SCROLL_TO_ITEM_END;
return (
<Box width={childWidth} flexDirection="column" maxHeight={listHeight}>
<ScrollableList
width={childWidth}
containerHeight={listHeight}
data={data}
renderItem={renderVirtualizedAnsiLine}
estimatedItemHeight={() => 1}
fixedItemHeight={true}
keyExtractor={keyExtractor}
initialScrollIndex={initialScrollIndex}
hasFocus={hasFocus}
/>
</Box>
);
} else {
let displayData = data;
let hiddenLines = 0;
if (constrainHeight && data.length > listHeight) {
hiddenLines = data.length - listHeight;
if (overflowDirection === 'top') {
displayData = data.slice(hiddenLines);
} else {
displayData = data.slice(0, listHeight);
}
}
return (
<Box width={childWidth} flexDirection="column">
<MaxSizedBox
maxHeight={constrainHeight ? listHeight : undefined}
maxWidth={childWidth}
overflowDirection={overflowDirection}
additionalHiddenLinesCount={hiddenLines}
>
{displayData.map((item, index) => {
const actualIndex =
(overflowDirection === 'top' ? hiddenLines : 0) + index;
return (
<Box
key={keyExtractor(item, actualIndex)}
height={1}
overflow="hidden"
>
<AnsiLineText line={item} />
</Box>
);
})}
</MaxSizedBox>
</Box>
);
}
}
// ASB Mode Handling (Interactive/Fullscreen)
@@ -29,11 +29,12 @@ describe('ToolResultDisplay Overflow', () => {
await waitUntilReady();
const output = lastFrame();
expect(output).not.toContain('Line 1');
expect(output).not.toContain('Line 2');
expect(output).toContain('Line 3');
expect(output).toContain('Line 4');
expect(output).toContain('Line 5');
expect(output).toContain('Line 1');
expect(output).toContain('Line 2');
expect(output).not.toContain('Line 3');
expect(output).not.toContain('Line 4');
expect(output).not.toContain('Line 5');
expect(output).toContain('hidden');
unmount();
});
@@ -57,9 +58,10 @@ describe('ToolResultDisplay Overflow', () => {
expect(output).not.toContain('Line 1');
expect(output).not.toContain('Line 2');
expect(output).toContain('Line 3');
expect(output).not.toContain('Line 3');
expect(output).toContain('Line 4');
expect(output).toContain('Line 5');
expect(output).toContain('hidden');
unmount();
});
@@ -95,11 +97,10 @@ describe('ToolResultDisplay Overflow', () => {
expect(output).toContain('Line 1');
expect(output).toContain('Line 2');
expect(output).toContain('Line 3');
expect(output).not.toContain('Line 3');
expect(output).not.toContain('Line 4');
expect(output).not.toContain('Line 5');
// ScrollableList uses a scroll thumb rather than writing "hidden"
expect(output).toContain('█');
expect(output).toContain('hidden');
unmount();
});
});
@@ -1,18 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="37" viewBox="0 0 920 37">
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="88" viewBox="0 0 920 88">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="37" fill="#000000" />
<rect width="920" height="88" fill="#000000" />
<g transform="translate(10, 10)">
<text x="18" y="2" fill="#d7ffd7" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="45" y="2" fill="#ffffff" textLength="45" lengthAdjust="spacingAndGlyphs" font-weight="bold">edit </text>
<text x="99" y="2" fill="#afafaf" textLength="63" lengthAdjust="spacingAndGlyphs">test.ts</text>
<text x="171" y="2" fill="#d7afff" textLength="18" lengthAdjust="spacingAndGlyphs"></text>
<text x="189" y="2" fill="#d7afff" textLength="72" lengthAdjust="spacingAndGlyphs" text-decoration="underline">Accepted</text>
<text x="171" y="2" fill="#d7afff" textLength="90" lengthAdjust="spacingAndGlyphs">Accepted</text>
<text x="270" y="2" fill="#afafaf" textLength="9" lengthAdjust="spacingAndGlyphs">(</text>
<text x="279" y="2" fill="#d7ffd7" textLength="18" lengthAdjust="spacingAndGlyphs">+1</text>
<text x="297" y="2" fill="#afafaf" textLength="18" lengthAdjust="spacingAndGlyphs">, </text>
<text x="315" y="2" fill="#ff87af" textLength="18" lengthAdjust="spacingAndGlyphs">-1</text>
<text x="333" y="2" fill="#afafaf" textLength="9" lengthAdjust="spacingAndGlyphs">)</text>
<rect x="54" y="34" width="9" height="17" fill="#5f0000" />
<text x="54" y="36" fill="#afafaf" textLength="9" lengthAdjust="spacingAndGlyphs">1</text>
<rect x="63" y="34" width="9" height="17" fill="#5f0000" />
<rect x="72" y="34" width="9" height="17" fill="#5f0000" />
<text x="72" y="36" fill="#ff87af" textLength="9" lengthAdjust="spacingAndGlyphs">-</text>
<rect x="81" y="34" width="9" height="17" fill="#5f0000" />
<rect x="90" y="34" width="27" height="17" fill="#5f0000" />
<text x="90" y="36" fill="#e5e5e5" textLength="27" lengthAdjust="spacingAndGlyphs">old</text>
<rect x="54" y="51" width="9" height="17" fill="#005f00" />
<text x="54" y="53" fill="#afafaf" textLength="9" lengthAdjust="spacingAndGlyphs">1</text>
<rect x="63" y="51" width="9" height="17" fill="#005f00" />
<rect x="72" y="51" width="9" height="17" fill="#005f00" />
<text x="72" y="53" fill="#d7ffd7" textLength="9" lengthAdjust="spacingAndGlyphs">+</text>
<rect x="81" y="51" width="9" height="17" fill="#005f00" />
<rect x="90" y="51" width="27" height="17" fill="#005f00" />
<text x="90" y="53" fill="#0000ee" textLength="27" lengthAdjust="spacingAndGlyphs">new</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -7,12 +7,21 @@ exports[`DenseToolMessage > Toggleable Diff View (Alternate Buffer) > hides diff
exports[`DenseToolMessage > Toggleable Diff View (Alternate Buffer) > shows diff content by default when NOT in alternate buffer mode 1`] = `
" ✓ test-tool test.ts → Accepted
1 - old line
1 + new line
"
`;
exports[`DenseToolMessage > Visual Regression > matches SVG snapshot for a Rejected tool call 1`] = `" - read_file Reading important.txt"`;
exports[`DenseToolMessage > Visual Regression > matches SVG snapshot for an Accepted file edit with diff stats 1`] = `" ✓ edit test.ts → Accepted (+1, -1)"`;
exports[`DenseToolMessage > Visual Regression > matches SVG snapshot for an Accepted file edit with diff stats 1`] = `
" ✓ edit test.ts → Accepted (+1, -1)
1 - old
1 + new
"
`;
exports[`DenseToolMessage > does not render result arrow if resultDisplay is missing 1`] = `
" o test-tool Test description
@@ -26,11 +35,17 @@ exports[`DenseToolMessage > flattens newlines in string results 1`] = `
exports[`DenseToolMessage > renders correctly for Edit tool using confirmationDetails 1`] = `
" ? Edit styles.scss → Confirming
1 - body { color: blue; }
1 + body { color: red; }
"
`;
exports[`DenseToolMessage > renders correctly for Errored Edit tool 1`] = `
" x Edit styles.scss → Failed (+1, -1)
1 - old line
1 + new line
"
`;
@@ -45,21 +60,33 @@ exports[`DenseToolMessage > renders correctly for ReadManyFiles results 1`] = `
exports[`DenseToolMessage > renders correctly for Rejected Edit tool 1`] = `
" - Edit styles.scss → Rejected (+1, -1)
1 - old line
1 + new line
"
`;
exports[`DenseToolMessage > renders correctly for Rejected Edit tool with confirmationDetails and diffStat 1`] = `
" - Edit styles.scss → Rejected (+1, -1)
1 - body { color: blue; }
1 + body { color: red; }
"
`;
exports[`DenseToolMessage > renders correctly for Rejected WriteFile tool 1`] = `
" - WriteFile config.json → Rejected
1 - old content
1 + new content
"
`;
exports[`DenseToolMessage > renders correctly for WriteFile tool 1`] = `
" ✓ WriteFile config.json → Accepted (+1, -1)
1 - old content
1 + new content
"
`;
@@ -75,6 +102,9 @@ exports[`DenseToolMessage > renders correctly for error status with string messa
exports[`DenseToolMessage > renders correctly for file diff results with stats 1`] = `
" ✓ test-tool test.ts → Accepted (+15, -6)
1 - old line
1 + diff content
"
`;
@@ -4,7 +4,7 @@
</style>
<rect width="920" height="445" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 26 </text>
<text x="0" y="2" fill="#afafaf" textLength="234" lengthAdjust="spacingAndGlyphs">... 26 hidden (Ctrl+O) ...</text>
<text x="0" y="19" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 27 </text>
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 28 </text>
<text x="0" y="53" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 29 </text>
@@ -16,31 +16,18 @@
<text x="0" y="155" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 35 </text>
<text x="0" y="172" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 36 </text>
<text x="0" y="189" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 37 </text>
<text x="0" y="206" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 38 </text>
<text x="675" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="223" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 39 </text>
<text x="675" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="240" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 40 </text>
<text x="675" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="257" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 41 </text>
<text x="675" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="274" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 42 </text>
<text x="675" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="291" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 43 </text>
<text x="675" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="308" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 44 </text>
<text x="675" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="325" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 45 </text>
<text x="675" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="342" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 46 </text>
<text x="675" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="359" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 47 </text>
<text x="675" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="376" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 48 </text>
<text x="675" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="393" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 49 </text>
<text x="675" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="410" fill="#ffffff" textLength="675" lengthAdjust="spacingAndGlyphs">Line 50 </text>
<text x="675" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="206" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 38 </text>
<text x="0" y="223" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 39 </text>
<text x="0" y="240" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 40 </text>
<text x="0" y="257" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 41 </text>
<text x="0" y="274" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 42 </text>
<text x="0" y="291" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 43 </text>
<text x="0" y="308" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 44 </text>
<text x="0" y="325" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 45 </text>
<text x="0" y="342" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 46 </text>
<text x="0" y="359" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 47 </text>
<text x="0" y="376" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 48 </text>
<text x="0" y="393" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 49 </text>
<text x="0" y="410" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">Line 50 </text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

@@ -33,15 +33,24 @@ exports[`ToolResultDisplay > renders string result as plain text when renderOutp
"
`;
exports[`ToolResultDisplay > stays scrolled to the bottom when lines are incrementally added 1`] = `
"... 4 hidden (Ctrl+O) ...
Line 5
Line 6
Line 7
Line 8
"
`;
exports[`ToolResultDisplay > truncates ANSI output when maxLines is provided 1`] = `
"Line 3
Line 4
Line 5
"... 3 hidden (Ctrl+O) ...
Line 4
Line 5
"
`;
exports[`ToolResultDisplay > truncates ANSI output when maxLines is provided, even if availableTerminalHeight is undefined 1`] = `
"Line 26
"... 26 hidden (Ctrl+O) ...
Line 27
Line 28
Line 29
@@ -53,34 +62,36 @@ Line 34
Line 35
Line 36
Line 37
Line 38
Line 39
Line 40
Line 41
Line 42
Line 43
Line 44
Line 45
Line 46
Line 47
Line 48
Line 49
Line 50"
Line 38
Line 39
Line 40
Line 41
Line 42
Line 43
Line 44
Line 45
Line 46
Line 47
Line 48
Line 49
Line 50"
`;
exports[`ToolResultDisplay > truncates very long string results 1`] = `
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… █
"... 250 hidden (Ctrl+O) ...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaa
"
`;