Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/eight-lights-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@clack/prompts": patch
"@clack/core": patch
---

Account for escape sequences in prefixes during line wrapping. Very narrow outputs will now render correctly.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"test": "vitest run"
},
"dependencies": {
"fast-string-width": "^3.0.2",
"fast-wrap-ansi": "^0.2.0",
"sisteransi": "^2.0.0"
},
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Key } from 'node:readline';
import * as readline from 'node:readline';
import type { Readable, Writable } from 'node:stream';
import { ReadStream } from 'node:tty';
import stringWidth from 'fast-string-width';
import { wrapAnsi } from 'fast-wrap-ansi';
import { cursor } from 'sisteransi';
import { isActionKey } from './settings.js';
Expand Down Expand Up @@ -107,7 +108,8 @@ export function wrapTextWithPrefix(
lineFormatter?: (line: string, index: number) => string
): string {
const columns = getColumns(output ?? stdout);
const wrapped = wrapAnsi(text, columns - prefix.length, {
const prefixWidth = stringWidth(prefix);
const wrapped = wrapAnsi(text, columns - prefixWidth, {
hard: true,
trim: false,
});
Expand Down
9 changes: 7 additions & 2 deletions packages/prompts/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Readable, Writable } from 'node:stream';
import { styleText } from 'node:util';
import type { State } from '@clack/core';
import { wrapTextWithPrefix } from '@clack/core';
import isUnicodeSupported from 'is-unicode-supported';

export const unicode = isUnicodeSupported();
Expand Down Expand Up @@ -77,9 +78,13 @@ export interface CommonOptions {
accessible?: boolean;
}

export function formatInstructionFooter(instructions: string[], hasGuide: boolean): string[] {
export function formatInstructionFooter(
output: Writable | undefined,
instructions: string[],
hasGuide: boolean
): string[] {
const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const footerLines = [`${guidePrefix}${instructions.join(' • ')}`];
const footerLines = wrapTextWithPrefix(output, instructions.join(' • '), guidePrefix).split('\n');
if (hasGuide) {
footerLines.push(styleText('cyan', S_BAR_END));
}
Expand Down
2 changes: 1 addition & 1 deletion packages/prompts/src/group-multi-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const titleLineCount = title.split('\n').length;
const footerLines = showInstructions
? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide)
? formatInstructionFooter(opts.output, MULTISELECT_INSTRUCTIONS, hasGuide)
: hasGuide
? [styleText('cyan', S_BAR_END)]
: [];
Expand Down
2 changes: 1 addition & 1 deletion packages/prompts/src/multi-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const titleLineCount = title.split('\n').length;
const footerLines = showInstructions
? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide)
? formatInstructionFooter(opts.output, MULTISELECT_INSTRUCTIONS, hasGuide)
: hasGuide
? [styleText('cyan', S_BAR_END)]
: [];
Expand Down
2 changes: 1 addition & 1 deletion packages/prompts/src/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const titleLineCount = title.split('\n').length;
const footerLines = showInstructions
? formatInstructionFooter(SELECT_INSTRUCTIONS, hasGuide)
? formatInstructionFooter(opts.output, SELECT_INSTRUCTIONS, hasGuide)
: hasGuide
? [styleText('cyan', S_BAR_END)]
: [];
Expand Down
64 changes: 30 additions & 34 deletions packages/prompts/test/__snapshots__/multi-select.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ exports[`multiselect (isCI = false) > wraps cancelled state with long options 1`
│ 1 Option 1 Option 1 Option 
│ 1 Option 1
│ ↑/↓ to navigate • Space: select •
Enter: confirm
│ Enter: confirm
└
",
"<cursor.backward count=999><cursor.up count=13>",
Expand All @@ -884,10 +884,9 @@ exports[`multiselect (isCI = false) > wraps cancelled state with long options 1`
"<cursor.down count=1>",
"<erase.down>",
"■ foo
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0
│",
"
",
Expand All @@ -899,13 +898,13 @@ exports[`multiselect (isCI = false) > wraps long messages 1`] = `
[
"<cursor.hide>",
"│
◆ foo foo foo foo foo foo foo
│ foo foo foo foo foo foo
│ foo foo foo foo foo foo foo
◆ foo foo foo foo foo foo foo foo foo
│ foo foo foo foo foo foo foo foo foo
│ foo foo
│ ◻ opt0
│ ◻ opt1
│ ↑/↓ to navigate • Space: select •
Enter: confirm
│ Enter: confirm
└
",
"<cursor.backward count=999><cursor.up count=9>",
Expand All @@ -916,9 +915,9 @@ exports[`multiselect (isCI = false) > wraps long messages 1`] = `
"<cursor.backward count=999><cursor.up count=9>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo foo foo foo foo foo foo
│ foo foo foo foo foo foo
│ foo foo foo foo foo foo foo
"◇ foo foo foo foo foo foo foo foo foo
│ foo foo foo foo foo foo foo foo foo
│ foo foo
│ opt0",
"
",
Expand All @@ -940,7 +939,7 @@ exports[`multiselect (isCI = false) > wraps success state with long options 1`]
│ 1 Option 1 Option 1 Option 
│ 1 Option 1
│ ↑/↓ to navigate • Space: select •
Enter: confirm
│ Enter: confirm
└
",
"<cursor.backward count=999><cursor.up count=13>",
Expand All @@ -952,10 +951,9 @@ exports[`multiselect (isCI = false) > wraps success state with long options 1`]
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0",
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0",
"
",
"<cursor.show>",
Expand Down Expand Up @@ -1834,7 +1832,7 @@ exports[`multiselect (isCI = true) > wraps cancelled state with long options 1`]
│ 1 Option 1 Option 1 Option 
│ 1 Option 1
│ ↑/↓ to navigate • Space: select •
Enter: confirm
│ Enter: confirm
└
",
"<cursor.backward count=999><cursor.up count=13>",
Expand All @@ -1846,10 +1844,9 @@ exports[`multiselect (isCI = true) > wraps cancelled state with long options 1`]
"<cursor.down count=1>",
"<erase.down>",
"■ foo
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0
│",
"
",
Expand All @@ -1861,13 +1858,13 @@ exports[`multiselect (isCI = true) > wraps long messages 1`] = `
[
"<cursor.hide>",
"│
◆ foo foo foo foo foo foo foo
│ foo foo foo foo foo foo
│ foo foo foo foo foo foo foo
◆ foo foo foo foo foo foo foo foo foo
│ foo foo foo foo foo foo foo foo foo
│ foo foo
│ ◻ opt0
│ ◻ opt1
│ ↑/↓ to navigate • Space: select •
Enter: confirm
│ Enter: confirm
└
",
"<cursor.backward count=999><cursor.up count=9>",
Expand All @@ -1878,9 +1875,9 @@ exports[`multiselect (isCI = true) > wraps long messages 1`] = `
"<cursor.backward count=999><cursor.up count=9>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo foo foo foo foo foo foo
│ foo foo foo foo foo foo
│ foo foo foo foo foo foo foo
"◇ foo foo foo foo foo foo foo foo foo
│ foo foo foo foo foo foo foo foo foo
│ foo foo
│ opt0",
"
",
Expand All @@ -1902,7 +1899,7 @@ exports[`multiselect (isCI = true) > wraps success state with long options 1`] =
│ 1 Option 1 Option 1 Option 
│ 1 Option 1
│ ↑/↓ to navigate • Space: select •
Enter: confirm
│ Enter: confirm
└
",
"<cursor.backward count=999><cursor.up count=13>",
Expand All @@ -1914,10 +1911,9 @@ exports[`multiselect (isCI = true) > wraps success state with long options 1`] =
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 
│ Option 0",
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0 Option 0 Option 0 
│ Option 0 Option 0",
"
",
"<cursor.show>",
Expand Down
Loading
Loading