[READ-ONLY] Mirror of https://github.com/bombshell-dev/clack. Effortlessly build beautiful command-line apps bomb.sh/docs/clack/basics/getting-started/
cli command-line command-line-app node prompt prompts
5

Configure Feed

Select the types of activity you want to include in your feed.

feat: text prompt multiline

Bruno Rocha (Aug 12, 2023, 3:11 PM -0300) b19e4b2f ef7d4142

+89 -9
+89 -9
packages/prompts/src/index.ts
··· 58 58 } 59 59 }; 60 60 61 + function formatTextWithMaxWidth( 62 + text: string, 63 + options?: Partial<{ 64 + defaultSymbol: string; 65 + initialSymbol: string; 66 + newLineSymbol: string; 67 + endSymbol: string; 68 + lineWrapper: (line: string) => string; 69 + }> 70 + ): string { 71 + const defaultSymbol = options?.defaultSymbol ?? color.cyan(S_BAR); 72 + const initialSymbol = options?.initialSymbol ?? defaultSymbol; 73 + const newLineSymbol = options?.newLineSymbol ?? defaultSymbol; 74 + const endSymbol = options?.endSymbol ?? defaultSymbol; 75 + 76 + const terminalWidth = process.stdout.columns || 80; 77 + const maxWidth = terminalWidth - 2; 78 + 79 + const formattedLines: string[] = []; 80 + const paragraphs = text.split(/\n/g); 81 + 82 + for (const paragraph of paragraphs) { 83 + let currentLine = ''; 84 + const words = paragraph.split(/\s/g); 85 + 86 + for (const word of words) { 87 + if (strLength(currentLine) + strLength(word) + 1 <= maxWidth) { 88 + currentLine += ` ${word}`; 89 + } else { 90 + formattedLines.push(currentLine); 91 + currentLine = word; 92 + } 93 + } 94 + 95 + formattedLines.push(currentLine); 96 + } 97 + 98 + return formattedLines 99 + .map((line, i, ar) => { 100 + const symbol = i === 0 ? initialSymbol : i + 1 === ar.length ? endSymbol : newLineSymbol; 101 + const fullLine = line.padEnd(maxWidth + 1, ' '); 102 + const wrappedLine = options?.lineWrapper ? options.lineWrapper(fullLine) : fullLine; 103 + return `${symbol} ${wrappedLine}`; 104 + }) 105 + .join('\n'); 106 + } 107 + 61 108 export interface TextOptions { 62 109 message: string; 63 110 placeholder?: string; ··· 72 119 defaultValue: opts.defaultValue, 73 120 initialValue: opts.initialValue, 74 121 render() { 75 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 122 + const title = [ 123 + color.gray(S_BAR), 124 + formatTextWithMaxWidth(opts.message, { 125 + initialSymbol: symbol(this.state), 126 + }), 127 + ].join('\n'); 76 128 const placeholder = opts.placeholder 77 129 ? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1)) 78 130 : color.inverse(color.hidden('_')); ··· 80 132 81 133 switch (this.state) { 82 134 case 'error': 83 - return `${title.trim()}\n${color.yellow(S_BAR)} ${value}\n${color.yellow( 84 - S_BAR_END 85 - )} ${color.yellow(this.error)}\n`; 135 + return [ 136 + title, 137 + formatTextWithMaxWidth(value, { 138 + defaultSymbol: color.yellow(S_BAR), 139 + }), 140 + formatTextWithMaxWidth(this.error, { 141 + defaultSymbol: color.yellow(S_BAR), 142 + endSymbol: color.yellow(S_BAR_END), 143 + lineWrapper: color.yellow, 144 + }), 145 + ].join('\n'); 86 146 case 'submit': 87 - return `${title}${color.gray(S_BAR)} ${color.dim(this.value || opts.placeholder)}`; 147 + return [ 148 + title, 149 + formatTextWithMaxWidth(this.value ?? opts.placeholder, { lineWrapper: color.dim }), 150 + ].join('\n'); 88 151 case 'cancel': 89 - return `${title}${color.gray(S_BAR)} ${color.strikethrough( 90 - color.dim(this.value ?? '') 91 - )}${this.value?.trim() ? '\n' + color.gray(S_BAR) : ''}`; 152 + return [ 153 + title, 154 + formatTextWithMaxWidth(this.value?.trim() ? this.value : '', { 155 + defaultSymbol: color.gray(S_BAR), 156 + endSymbol: color.gray(S_BAR_END), 157 + lineWrapper: (line) => color.strikethrough(color.dim(line)), 158 + }), 159 + ].join('\n'); 92 160 default: 93 - return `${title}${color.cyan(S_BAR)} ${value}\n${color.cyan(S_BAR_END)}\n`; 161 + return [title, formatTextWithMaxWidth(value)].join('\n'); 94 162 } 95 163 }, 96 164 }).prompt() as Promise<string | symbol>; ··· 564 632 }; 565 633 566 634 const strip = (str: string) => str.replace(ansiRegex(), ''); 635 + const strLength = (str: string) => { 636 + if (!str) return 0; 637 + 638 + const colorCodeRegex = /\x1B\[[0-9;]*[mG]/g; 639 + const arr = [...str.replace(colorCodeRegex, '')]; 640 + let len = 0; 641 + 642 + for (const char of arr) { 643 + len += char.charCodeAt(0) > 127 || char.charCodeAt(0) === 94 ? 2 : 1; 644 + } 645 + return len; 646 + }; 567 647 export const note = (message = '', title = '') => { 568 648 const lines = `\n${message}\n`.split('\n'); 569 649 const titleLen = strip(title).length;