Skip to content

Commit e66ab12

Browse files
authored
fix: respect the prompt output stream width (#630)
1 parent 34976a7 commit e66ab12

5 files changed

Lines changed: 92 additions & 47 deletions

File tree

‎.changeset/quiet-outputs-wrap.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clack/core': patch
3+
---
4+
5+
Use the configured output stream's width when wrapping and clearing prompt frames, including prompts written to stderr while stdout is redirected.

‎packages/core/src/prompts/prompt.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { Action } from '../utils/index.js';
88
import {
99
CANCEL_SYMBOL,
1010
diffLines,
11+
getColumns,
1112
getRows,
1213
isAccessible,
1314
isActionKey,
@@ -303,13 +304,13 @@ export default class Prompt<TValue> {
303304

304305
private restoreCursor() {
305306
const lines =
306-
wrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split('\n')
307+
wrapAnsi(this._prevFrame, getColumns(this.output), { hard: true, trim: false }).split('\n')
307308
.length - 1;
308309
this.output.write(cursor.move(-999, lines * -1));
309310
}
310311

311312
private render() {
312-
const frame = wrapAnsi(this._render(this) ?? '', process.stdout.columns, {
313+
const frame = wrapAnsi(this._render(this) ?? '', getColumns(this.output), {
313314
hard: true,
314315
trim: false,
315316
});

‎packages/core/test/prompts/prompt.test.ts‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type } from 'arktype';
2-
import { cursor } from 'sisteransi';
2+
import { cursor, erase } from 'sisteransi';
33
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
44
import { default as Prompt } from '../../src/prompts/prompt.js';
55
import { isCancel, settings } from '../../src/utils/index.js';
@@ -91,6 +91,33 @@ describe('Prompt', () => {
9191
expect(renderFn).toHaveBeenCalledTimes(2);
9292
});
9393

94+
test.each([
95+
{ columns: 4, text: 'abcdefgh', wrapped: 'abcd\nefgh' },
96+
{ columns: 7, text: 'abcdefghijklmnop', wrapped: 'abcdefg\nhijklmn\nop' },
97+
])(
98+
'wraps and clears frames using the output width ($columns columns)',
99+
async ({ columns, text, wrapped }) => {
100+
Object.assign(output, { columns });
101+
const render = vi.fn(() => text);
102+
const instance = new Prompt({ input, output, render });
103+
const result = instance.prompt();
104+
105+
expect(output.buffer).to.deep.equal([cursor.hide, wrapped]);
106+
107+
output.buffer = [];
108+
render.mockReturnValue('x');
109+
output.emit('resize');
110+
111+
expect(output.buffer).to.deep.equal([
112+
cursor.move(-999, -(wrapped.split('\n').length - 1)),
113+
erase.down(),
114+
'x',
115+
]);
116+
input.emit('keypress', '\x03', { name: 'c' });
117+
await result;
118+
}
119+
);
120+
94121
test('state is active after first render', async () => {
95122
const instance = new Prompt({
96123
input,

‎packages/prompts/test/__snapshots__/multi-select.test.ts.snap‎

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -871,15 +871,16 @@ exports[`multiselect (isCI = false) > wraps cancelled state with long options 1`
871871
│ 1 Option 1 Option 1 Option 
872872
│ 1 Option 1 Option 1 Option 
873873
│ 1 Option 1
874-
│ ↑/↓ to navigate • Space: select • Enter: confirm
874+
│ ↑/↓ to navigate • Space: select •
875+
Enter: confirm
875876
└
876877
",
877-
"<cursor.backward count=999><cursor.up count=12>",
878+
"<cursor.backward count=999><cursor.up count=13>",
878879
"<cursor.down count=2>",
879880
"<erase.line><cursor.left count=1>",
880881
"│ ◼ Option 0 Option 0 Option ",
881-
"<cursor.down count=10>",
882-
"<cursor.backward count=999><cursor.up count=12>",
882+
"<cursor.down count=11>",
883+
"<cursor.backward count=999><cursor.up count=13>",
883884
"<cursor.down count=1>",
884885
"<erase.down>",
885886
"■ foo
@@ -903,15 +904,16 @@ exports[`multiselect (isCI = false) > wraps long messages 1`] = `
903904
│ foo foo foo foo foo foo foo
904905
│ ◻ opt0
905906
│ ◻ opt1
906-
│ ↑/↓ to navigate • Space: select • Enter: confirm
907+
│ ↑/↓ to navigate • Space: select •
908+
Enter: confirm
907909
└
908910
",
909-
"<cursor.backward count=999><cursor.up count=8>",
911+
"<cursor.backward count=999><cursor.up count=9>",
910912
"<cursor.down count=4>",
911913
"<erase.line><cursor.left count=1>",
912914
"│ ◼ opt0",
913-
"<cursor.down count=4>",
914-
"<cursor.backward count=999><cursor.up count=8>",
915+
"<cursor.down count=5>",
916+
"<cursor.backward count=999><cursor.up count=9>",
915917
"<cursor.down count=1>",
916918
"<erase.down>",
917919
"◇ foo foo foo foo foo foo foo
@@ -937,15 +939,16 @@ exports[`multiselect (isCI = false) > wraps success state with long options 1`]
937939
│ 1 Option 1 Option 1 Option 
938940
│ 1 Option 1 Option 1 Option 
939941
│ 1 Option 1
940-
│ ↑/↓ to navigate • Space: select • Enter: confirm
942+
│ ↑/↓ to navigate • Space: select •
943+
Enter: confirm
941944
└
942945
",
943-
"<cursor.backward count=999><cursor.up count=12>",
946+
"<cursor.backward count=999><cursor.up count=13>",
944947
"<cursor.down count=2>",
945948
"<erase.line><cursor.left count=1>",
946949
"│ ◼ Option 0 Option 0 Option ",
947-
"<cursor.down count=10>",
948-
"<cursor.backward count=999><cursor.up count=12>",
950+
"<cursor.down count=11>",
951+
"<cursor.backward count=999><cursor.up count=13>",
949952
"<cursor.down count=1>",
950953
"<erase.down>",
951954
"◇ foo
@@ -1830,15 +1833,16 @@ exports[`multiselect (isCI = true) > wraps cancelled state with long options 1`]
18301833
│ 1 Option 1 Option 1 Option 
18311834
│ 1 Option 1 Option 1 Option 
18321835
│ 1 Option 1
1833-
│ ↑/↓ to navigate • Space: select • Enter: confirm
1836+
│ ↑/↓ to navigate • Space: select •
1837+
Enter: confirm
18341838
└
18351839
",
1836-
"<cursor.backward count=999><cursor.up count=12>",
1840+
"<cursor.backward count=999><cursor.up count=13>",
18371841
"<cursor.down count=2>",
18381842
"<erase.line><cursor.left count=1>",
18391843
"│ ◼ Option 0 Option 0 Option ",
1840-
"<cursor.down count=10>",
1841-
"<cursor.backward count=999><cursor.up count=12>",
1844+
"<cursor.down count=11>",
1845+
"<cursor.backward count=999><cursor.up count=13>",
18421846
"<cursor.down count=1>",
18431847
"<erase.down>",
18441848
"■ foo
@@ -1862,15 +1866,16 @@ exports[`multiselect (isCI = true) > wraps long messages 1`] = `
18621866
│ foo foo foo foo foo foo foo
18631867
│ ◻ opt0
18641868
│ ◻ opt1
1865-
│ ↑/↓ to navigate • Space: select • Enter: confirm
1869+
│ ↑/↓ to navigate • Space: select •
1870+
Enter: confirm
18661871
└
18671872
",
1868-
"<cursor.backward count=999><cursor.up count=8>",
1873+
"<cursor.backward count=999><cursor.up count=9>",
18691874
"<cursor.down count=4>",
18701875
"<erase.line><cursor.left count=1>",
18711876
"│ ◼ opt0",
1872-
"<cursor.down count=4>",
1873-
"<cursor.backward count=999><cursor.up count=8>",
1877+
"<cursor.down count=5>",
1878+
"<cursor.backward count=999><cursor.up count=9>",
18741879
"<cursor.down count=1>",
18751880
"<erase.down>",
18761881
"◇ foo foo foo foo foo foo foo
@@ -1896,15 +1901,16 @@ exports[`multiselect (isCI = true) > wraps success state with long options 1`] =
18961901
│ 1 Option 1 Option 1 Option 
18971902
│ 1 Option 1 Option 1 Option 
18981903
│ 1 Option 1
1899-
│ ↑/↓ to navigate • Space: select • Enter: confirm
1904+
│ ↑/↓ to navigate • Space: select •
1905+
Enter: confirm
19001906
└
19011907
",
1902-
"<cursor.backward count=999><cursor.up count=12>",
1908+
"<cursor.backward count=999><cursor.up count=13>",
19031909
"<cursor.down count=2>",
19041910
"<erase.line><cursor.left count=1>",
19051911
"│ ◼ Option 0 Option 0 Option ",
1906-
"<cursor.down count=10>",
1907-
"<cursor.backward count=999><cursor.up count=12>",
1912+
"<cursor.down count=11>",
1913+
"<cursor.backward count=999><cursor.up count=13>",
19081914
"<cursor.down count=1>",
19091915
"<erase.down>",
19101916
"◇ foo

‎packages/prompts/test/__snapshots__/select.test.ts.snap‎

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,38 +49,41 @@ exports[`select (isCI = false) > correctly limits options when message wraps to
4949
│ ● Option 0
5050
│ ○ Option 1
5151
│ ...
52-
│ ↑/↓ to navigate • Enter: confirm
52+
│ ↑/↓ to navigate • Enter:
53+
confirm
5354
└
5455
",
55-
"<cursor.backward count=999><cursor.up count=10>",
56+
"<cursor.backward count=999><cursor.up count=11>",
5657
"<cursor.down count=5>",
5758
"<erase.down>",
5859
"│ ○ Option 0
5960
│ ● Option 1
6061
│ ...
61-
│ ↑/↓ to navigate • Enter: confirm
62+
│ ↑/↓ to navigate • Enter:
63+
confirm
6264
└
6365
",
64-
"<cursor.backward count=999><cursor.up count=10>",
66+
"<cursor.backward count=999><cursor.up count=11>",
6567
"<cursor.down count=5>",
6668
"<erase.down>",
6769
"│ ...
6870
│ ● Option 2
6971
│ ...
70-
│ ↑/↓ to navigate • Enter: confirm
72+
│ ↑/↓ to navigate • Enter:
73+
confirm
7174
└
7275
",
73-
"<cursor.backward count=999><cursor.up count=10>",
76+
"<cursor.backward count=999><cursor.up count=11>",
7477
"<cursor.down count=6>",
7578
"<erase.line><cursor.left count=1>",
7679
"│ ● Option 3",
77-
"<cursor.down count=4>",
78-
"<cursor.backward count=999><cursor.up count=10>",
80+
"<cursor.down count=5>",
81+
"<cursor.backward count=999><cursor.up count=11>",
7982
"<cursor.down count=6>",
8083
"<erase.line><cursor.left count=1>",
8184
"│ ● Option 4",
82-
"<cursor.down count=4>",
83-
"<cursor.backward count=999><cursor.up count=10>",
85+
"<cursor.down count=5>",
86+
"<cursor.backward count=999><cursor.up count=11>",
8487
"<cursor.down count=1>",
8588
"<erase.down>",
8689
"◇ This is a very
@@ -669,38 +672,41 @@ exports[`select (isCI = true) > correctly limits options when message wraps to m
669672
│ ● Option 0
670673
│ ○ Option 1
671674
│ ...
672-
│ ↑/↓ to navigate • Enter: confirm
675+
│ ↑/↓ to navigate • Enter:
676+
confirm
673677
└
674678
",
675-
"<cursor.backward count=999><cursor.up count=10>",
679+
"<cursor.backward count=999><cursor.up count=11>",
676680
"<cursor.down count=5>",
677681
"<erase.down>",
678682
"│ ○ Option 0
679683
│ ● Option 1
680684
│ ...
681-
│ ↑/↓ to navigate • Enter: confirm
685+
│ ↑/↓ to navigate • Enter:
686+
confirm
682687
└
683688
",
684-
"<cursor.backward count=999><cursor.up count=10>",
689+
"<cursor.backward count=999><cursor.up count=11>",
685690
"<cursor.down count=5>",
686691
"<erase.down>",
687692
"│ ...
688693
│ ● Option 2
689694
│ ...
690-
│ ↑/↓ to navigate • Enter: confirm
695+
│ ↑/↓ to navigate • Enter:
696+
confirm
691697
└
692698
",
693-
"<cursor.backward count=999><cursor.up count=10>",
699+
"<cursor.backward count=999><cursor.up count=11>",
694700
"<cursor.down count=6>",
695701
"<erase.line><cursor.left count=1>",
696702
"│ ● Option 3",
697-
"<cursor.down count=4>",
698-
"<cursor.backward count=999><cursor.up count=10>",
703+
"<cursor.down count=5>",
704+
"<cursor.backward count=999><cursor.up count=11>",
699705
"<cursor.down count=6>",
700706
"<erase.line><cursor.left count=1>",
701707
"│ ● Option 4",
702-
"<cursor.down count=4>",
703-
"<cursor.backward count=999><cursor.up count=10>",
708+
"<cursor.down count=5>",
709+
"<cursor.backward count=999><cursor.up count=11>",
704710
"<cursor.down count=1>",
705711
"<erase.down>",
706712
"◇ This is a very

0 commit comments

Comments
 (0)