Skip to content

Commit 1a1828f

Browse files
fix(prompts): properly calculate multiselect row padding on narrow terminals
This corrects a terminal rendering bug in the MultiSelectPrompt (Issue #116). Previously, terminal row padding calculations contained two offsetting errors: 1. \ itleLineCount\ was overestimating by 1 due to trailing newlines. 2. \ ooterLineCount\ was underestimating on narrow terminals because the footer string wasn't properly wrapped. When the terminal filled up, this offset math caused the \limitOptions\ slicing window to miscalculate available space by 1 row, causing the prompt to overflow the terminal and visually overwrite itself on redraw. This fix correctly evaluates both bounds using \wrapTextWithPrefix\ and ensures small terminals gracefully constrain multiselect options. Signed-off-by: Harshal Patel <hp842484@gmail.com>
1 parent 8f1c380 commit 1a1828f

4 files changed

Lines changed: 990 additions & 896 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clack/prompts': patch
3+
---
4+
5+
fix: correctly calculate multiselect layout row padding on narrow terminals to prevent option text overflow and visual overwriting

packages/prompts/src/multi-select.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,9 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
170170
)
171171
.join('\n');
172172
// Calculate rowPadding: title lines + footer lines (error message + trailing newline)
173-
const titleLineCount = title.split('\n').length;
174-
const footerLineCount = footer.split('\n').length + 1; // footer + trailing newline
173+
const titleLineCount = title.split('\n').length - 1;
174+
const wrappedFooter = wrapTextWithPrefix(opts.output, footer, '');
175+
const footerLineCount = wrappedFooter.split('\n').length + 1; // footer + trailing newline
175176
return `${title}${prefix}${limitOptions({
176177
output: opts.output,
177178
options: this.options,
@@ -184,14 +185,15 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
184185
}
185186
default: {
186187
const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
187-
const titleLineCount = title.split('\n').length;
188+
const titleLineCount = title.split('\n').length - 1;
188189
const footerLines = showInstructions
189190
? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide)
190191
: hasGuide
191192
? [styleText('cyan', S_BAR_END)]
192193
: [];
193194
const footerText = footerLines.join('\n');
194-
const footerLineCount = footerLines.length + 1;
195+
const wrappedFooter = wrapTextWithPrefix(opts.output, footerText, '');
196+
const footerLineCount = wrappedFooter.split('\n').length + 1;
195197
return `${title}${prefix}${limitOptions({
196198
output: opts.output,
197199
options: this.options,

0 commit comments

Comments
 (0)