Skip to content

Commit fe40d42

Browse files
authored
Fix extra newline in fullscreen mode (#769)
1 parent 9581ae1 commit fe40d42

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

src/ink.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export default class Ink {
238238

239239
if (this.lastOutputHeight >= this.options.stdout.rows) {
240240
this.options.stdout.write(
241-
ansiEscapes.clearTerminal + this.fullStaticOutput + output + '\n',
241+
ansiEscapes.clearTerminal + this.fullStaticOutput + output,
242242
);
243243
this.lastOutput = output;
244244
this.lastOutputHeight = outputHeight;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import process from 'node:process';
2+
import React, {useEffect} from 'react';
3+
import {Box, Text, render, useApp} from '../../src/index.js';
4+
5+
function Fullscreen() {
6+
const {exit} = useApp();
7+
8+
useEffect(() => {
9+
// Exit after first render to check the output
10+
const timer = setTimeout(() => {
11+
exit();
12+
}, 100);
13+
14+
return () => {
15+
clearTimeout(timer);
16+
};
17+
}, [exit]);
18+
19+
// Force the root to occupy exactly terminal rows
20+
const rows = Number(process.argv[2]) || 5;
21+
22+
return (
23+
<Box height={rows} flexDirection="column">
24+
<Box flexGrow={1}>
25+
<Text>Full-screen: top</Text>
26+
</Box>
27+
<Text>Bottom line (should be usable)</Text>
28+
</Box>
29+
);
30+
}
31+
32+
// Set terminal size from argument
33+
process.stdout.rows = Number(process.argv[2]) || 5;
34+
35+
render(<Fullscreen />);

test/render.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,32 @@ test.serial('erase screen where state changes in small viewport', async t => {
144144
}
145145
});
146146

147+
test.serial(
148+
'fullscreen mode should not add extra newline at the bottom',
149+
async t => {
150+
const ps = term('fullscreen-no-extra-newline', ['5']);
151+
await ps.waitForExit();
152+
153+
t.true(ps.output.includes('Bottom line'));
154+
155+
const lastFrame = ps.output.split(ansiEscapes.clearTerminal).at(-1) ?? '';
156+
157+
// Check that the bottom line is at the end without extra newlines
158+
// In a 5-line terminal:
159+
// Line 1: Fullscreen: top
160+
// Lines 2-4: empty (from flexGrow)
161+
// Line 5: Bottom line (should be usable)
162+
const lines = lastFrame.split('\n');
163+
164+
t.is(lines.length, 5, 'Should have exactly 5 lines for 5-row terminal');
165+
166+
t.true(
167+
lines[4]?.includes('Bottom line') ?? false,
168+
'Bottom line should be on line 5',
169+
);
170+
},
171+
);
172+
147173
test.serial('clear output', async t => {
148174
const ps = term('clear');
149175
await ps.waitForExit();

0 commit comments

Comments
 (0)