[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: add withGuide option (#409)

authored by

James Garbutt and committed by
GitHub
(Nov 21, 2025, 11:38 AM UTC) acc4c3a8 4ba2d783

+696 -177
+6
.changeset/tall-keys-allow.md
··· 1 + --- 2 + "@clack/prompts": patch 3 + "@clack/core": patch 4 + --- 5 + 6 + Add a new `withGuide` option to all prompts to disable the default clack border
+8
packages/core/src/utils/settings.ts
··· 9 9 cancel: string; 10 10 error: string; 11 11 }; 12 + withGuide: boolean; 12 13 } 13 14 14 15 export const settings: InternalClackSettings = { ··· 27 28 cancel: 'Canceled', 28 29 error: 'Something went wrong', 29 30 }, 31 + withGuide: true, 30 32 }; 31 33 32 34 export interface ClackSettings { ··· 54 56 */ 55 57 error?: string; 56 58 }; 59 + 60 + withGuide?: boolean; 57 61 } 58 62 59 63 export function updateSettings(updates: ClackSettings) { ··· 80 84 if (messages.error !== undefined) { 81 85 settings.messages.error = messages.error; 82 86 } 87 + } 88 + 89 + if (updates.withGuide !== undefined) { 90 + settings.withGuide = updates.withGuide !== false; 83 91 } 84 92 } 85 93
+1 -1
packages/core/tsconfig.json
··· 1 1 { 2 2 "extends": "../../tsconfig.json", 3 - "include": ["src"] 3 + "include": ["src", "test"] 4 4 }
+3 -3
packages/prompts/src/box.ts
··· 1 1 import type { Writable } from 'node:stream'; 2 - import { getColumns } from '@clack/core'; 2 + import { getColumns, settings } from '@clack/core'; 3 3 import stringWidth from 'fast-string-width'; 4 4 import { wrapAnsi } from 'fast-wrap-ansi'; 5 5 import { ··· 35 35 titlePadding?: number; 36 36 contentPadding?: number; 37 37 rounded?: boolean; 38 - includePrefix?: boolean; 39 38 formatBorder?: (text: string) => string; 40 39 } 41 40 ··· 68 67 const titlePadding = opts?.titlePadding ?? 1; 69 68 const contentPadding = opts?.contentPadding ?? 2; 70 69 const width = opts?.width === undefined || opts.width === 'auto' ? 1 : Math.min(1, opts.width); 71 - const linePrefix = opts?.includePrefix ? `${S_BAR} ` : ''; 70 + const hasGuide = (opts?.withGuide ?? settings.withGuide) !== false; 71 + const linePrefix = !hasGuide ? '' : `${S_BAR} `; 72 72 const formatBorder = opts?.formatBorder ?? defaultFormatBorder; 73 73 const symbols = (opts?.rounded ? roundedSymbols : squareSymbols).map(formatBorder); 74 74 const hSymbol = formatBorder(S_BAR_H);
+1
packages/prompts/src/common.ts
··· 71 71 input?: Readable; 72 72 output?: Writable; 73 73 signal?: AbortSignal; 74 + withGuide?: boolean; 74 75 }
+13 -5
packages/prompts/src/log.ts
··· 1 + import { settings } from '@clack/core'; 1 2 import color from 'picocolors'; 2 3 import { 3 4 type CommonOptions, ··· 23 24 secondarySymbol = color.gray(S_BAR), 24 25 output = process.stdout, 25 26 spacing = 1, 27 + withGuide, 26 28 }: LogMessageOptions = {} 27 29 ) => { 28 30 const parts: string[] = []; 31 + const hasGuide = (withGuide ?? settings.withGuide) !== false; 32 + const spacingString = !hasGuide ? '' : secondarySymbol; 33 + const prefix = !hasGuide ? '' : `${symbol} `; 34 + const secondaryPrefix = !hasGuide ? '' : `${secondarySymbol} `; 35 + 29 36 for (let i = 0; i < spacing; i++) { 30 - parts.push(`${secondarySymbol}`); 37 + parts.push(spacingString); 31 38 } 39 + 32 40 const messageParts = Array.isArray(message) ? message : message.split('\n'); 33 41 if (messageParts.length > 0) { 34 42 const [firstLine, ...lines] = messageParts; 35 43 if (firstLine.length > 0) { 36 - parts.push(`${symbol} ${firstLine}`); 44 + parts.push(`${prefix}${firstLine}`); 37 45 } else { 38 - parts.push(symbol); 46 + parts.push(hasGuide ? '' : symbol); 39 47 } 40 48 for (const ln of lines) { 41 49 if (ln.length > 0) { 42 - parts.push(`${secondarySymbol} ${ln}`); 50 + parts.push(`${secondaryPrefix}${ln}`); 43 51 } else { 44 - parts.push(secondarySymbol); 52 + parts.push(hasGuide ? '' : secondarySymbol); 45 53 } 46 54 } 47 55 }
+16 -9
packages/prompts/src/text.ts
··· 1 - import { TextPrompt } from '@clack/core'; 1 + import { settings, TextPrompt } from '@clack/core'; 2 2 import color from 'picocolors'; 3 3 import { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js'; 4 4 ··· 20 20 signal: opts.signal, 21 21 input: opts.input, 22 22 render() { 23 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 23 + const hasGuide = (opts?.withGuide ?? settings.withGuide) !== false; 24 + const titlePrefix = `${hasGuide ? `${color.gray(S_BAR)}\n` : ''}${symbol(this.state)} `; 25 + const title = `${titlePrefix}${opts.message}\n`; 24 26 const placeholder = opts.placeholder 25 27 ? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1)) 26 28 : color.inverse(color.hidden('_')); ··· 30 32 switch (this.state) { 31 33 case 'error': { 32 34 const errorText = this.error ? ` ${color.yellow(this.error)}` : ''; 33 - return `${title.trim()}\n${color.yellow(S_BAR)} ${userInput}\n${color.yellow( 34 - S_BAR_END 35 - )}${errorText}\n`; 35 + const errorPrefix = hasGuide ? `${color.yellow(S_BAR)} ` : ''; 36 + const errorPrefixEnd = hasGuide ? color.yellow(S_BAR_END) : ''; 37 + return `${title.trim()}\n${errorPrefix}${userInput}\n${errorPrefixEnd}${errorText}\n`; 36 38 } 37 39 case 'submit': { 38 40 const valueText = value ? ` ${color.dim(value)}` : ''; 39 - return `${title}${color.gray(S_BAR)}${valueText}`; 41 + const submitPrefix = hasGuide ? color.gray(S_BAR) : ''; 42 + return `${title}${submitPrefix}${valueText}`; 40 43 } 41 44 case 'cancel': { 42 45 const valueText = value ? ` ${color.strikethrough(color.dim(value))}` : ''; 43 - return `${title}${color.gray(S_BAR)}${valueText}${value.trim() ? `\n${color.gray(S_BAR)}` : ''}`; 46 + const cancelPrefix = hasGuide ? color.gray(S_BAR) : ''; 47 + return `${title}${cancelPrefix}${valueText}${value.trim() ? `\n${cancelPrefix}` : ''}`; 44 48 } 45 - default: 46 - return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`; 49 + default: { 50 + const defaultPrefix = hasGuide ? `${color.cyan(S_BAR)} ` : ''; 51 + const defaultPrefixEnd = hasGuide ? color.cyan(S_BAR_END) : ''; 52 + return `${title}${defaultPrefix}${userInput}\n${defaultPrefixEnd}\n`; 53 + } 47 54 } 48 55 }, 49 56 }).prompt() as Promise<string | symbol>;
+178 -156
packages/prompts/test/__snapshots__/box.test.ts.snap
··· 2 2 3 3 exports[`box (isCI = false) > cannot have width larger than 100% 1`] = ` 4 4 [ 5 - "┌─title────────────────────────────────────────────────────────────────────────┐ 5 + "│ ┌─title──────────────────────────────────────────────────────────────────────┐ 6 6 ", 7 - "│ message │ 7 + "│ │ message │ 8 8 ", 9 - "└──────────────────────────────────────────────────────────────────────────────┘ 9 + "│ └────────────────────────────────────────────────────────────────────────────┘ 10 10 ", 11 11 ] 12 12 `; 13 13 14 14 exports[`box (isCI = false) > renders as specified width 1`] = ` 15 15 [ 16 - "┌─title────────────────────────────────┐ 16 + "│ ┌─title──────────────────────────────┐ 17 17 ", 18 - "│ short │ 18 + "│ │ short │ 19 19 ", 20 - "│ somewhat questionably long line │ 20 + "│ │ somewhat questionably long line │ 21 21 ", 22 - "└──────────────────────────────────────┘ 22 + "│ └────────────────────────────────────┘ 23 23 ", 24 24 ] 25 25 `; 26 26 27 27 exports[`box (isCI = false) > renders as wide as longest line with width: auto 1`] = ` 28 28 [ 29 - "┌─title──────────────────────────────┐ 29 + "│ ┌─title──────────────────────────────┐ 30 30 ", 31 - "│ short │ 31 + "│ │ short │ 32 32 ", 33 - "│ somewhat questionably long line │ 33 + "│ │ somewhat questionably long line │ 34 34 ", 35 - "└────────────────────────────────────┘ 35 + "│ └────────────────────────────────────┘ 36 36 ", 37 37 ] 38 38 `; 39 39 40 40 exports[`box (isCI = false) > renders auto width with content longer than title 1`] = ` 41 41 [ 42 - "┌─title──────────────────────────┐ 42 + "│ ┌─title──────────────────────────┐ 43 43 ", 44 - "│ messagemessagemessagemessage │ 44 + "│ │ messagemessagemessagemessage │ 45 45 ", 46 - "└────────────────────────────────┘ 46 + "│ └────────────────────────────────┘ 47 47 ", 48 48 ] 49 49 `; 50 50 51 51 exports[`box (isCI = false) > renders auto width with title longer than content 1`] = ` 52 52 [ 53 - "┌─titletitletitletitle─┐ 53 + "│ ┌─titletitletitletitle─┐ 54 54 ", 55 - "│ message │ 55 + "│ │ message │ 56 56 ", 57 - "└──────────────────────┘ 57 + "│ └──────────────────────┘ 58 58 ", 59 59 ] 60 60 `; 61 61 62 62 exports[`box (isCI = false) > renders center aligned content 1`] = ` 63 63 [ 64 - "┌─title──────┐ 64 + "│ ┌─title──────┐ 65 65 ", 66 - "│ message │ 66 + "│ │ message │ 67 67 ", 68 - "└────────────┘ 68 + "│ └────────────┘ 69 69 ", 70 70 ] 71 71 `; 72 72 73 73 exports[`box (isCI = false) > renders center aligned title 1`] = ` 74 74 [ 75 - "┌───title────┐ 75 + "│ ┌───title────┐ 76 76 ", 77 - "│ message │ 77 + "│ │ message │ 78 78 ", 79 - "└────────────┘ 79 + "│ └────────────┘ 80 80 ", 81 81 ] 82 82 `; 83 83 84 84 exports[`box (isCI = false) > renders left aligned content 1`] = ` 85 85 [ 86 - "┌─title──────┐ 86 + "│ ┌─title──────┐ 87 87 ", 88 - "│ message │ 88 + "│ │ message │ 89 89 ", 90 - "└────────────┘ 90 + "│ └────────────┘ 91 91 ", 92 92 ] 93 93 `; 94 94 95 95 exports[`box (isCI = false) > renders left aligned title 1`] = ` 96 96 [ 97 - "┌─title──────┐ 97 + "│ ┌─title──────┐ 98 98 ", 99 - "│ message │ 99 + "│ │ message │ 100 100 ", 101 - "└────────────┘ 101 + "│ └────────────┘ 102 102 ", 103 103 ] 104 104 `; 105 105 106 106 exports[`box (isCI = false) > renders message 1`] = ` 107 107 [ 108 - "┌──────────────────────────────────────────────────────────────────────────────┐ 108 + "│ ┌────────────────────────────────────────────────────────────────────────────┐ 109 109 ", 110 - "│ message │ 110 + "│ │ message │ 111 111 ", 112 - "└──────────────────────────────────────────────────────────────────────────────┘ 112 + "│ └────────────────────────────────────────────────────────────────────────────┘ 113 113 ", 114 114 ] 115 115 `; 116 116 117 117 exports[`box (isCI = false) > renders message with title 1`] = ` 118 118 [ 119 - "┌─some title───────────────────────────────────────────────────────────────────┐ 119 + "│ ┌─some title─────────────────────────────────────────────────────────────────┐ 120 120 ", 121 - "│ message │ 121 + "│ │ message │ 122 122 ", 123 - "└──────────────────────────────────────────────────────────────────────────────┘ 123 + "│ └────────────────────────────────────────────────────────────────────────────┘ 124 124 ", 125 125 ] 126 126 `; 127 127 128 128 exports[`box (isCI = false) > renders right aligned content 1`] = ` 129 129 [ 130 - "┌─title──────┐ 130 + "│ ┌─title──────┐ 131 131 ", 132 - "│ message │ 132 + "│ │ message │ 133 133 ", 134 - "└────────────┘ 134 + "│ └────────────┘ 135 135 ", 136 136 ] 137 137 `; 138 138 139 139 exports[`box (isCI = false) > renders right aligned title 1`] = ` 140 140 [ 141 - "┌──────title─┐ 141 + "│ ┌──────title─┐ 142 142 ", 143 - "│ message │ 143 + "│ │ message │ 144 144 ", 145 - "└────────────┘ 145 + "│ └────────────┘ 146 146 ", 147 147 ] 148 148 `; 149 149 150 150 exports[`box (isCI = false) > renders rounded corners when rounded is true 1`] = ` 151 151 [ 152 - "╭─title──────╮ 152 + "│ ╭─title──────╮ 153 153 ", 154 - "│ message │ 154 + "│ │ message │ 155 155 ", 156 - "╰────────────╯ 156 + "│ ╰────────────╯ 157 157 ", 158 158 ] 159 159 `; 160 160 161 161 exports[`box (isCI = false) > renders specified contentPadding 1`] = ` 162 162 [ 163 - "┌─title──────────────┐ 163 + "│ ┌─title──────────────┐ 164 164 ", 165 - "│ message │ 165 + "│ │ message │ 166 166 ", 167 - "└────────────────────┘ 167 + "│ └────────────────────┘ 168 168 ", 169 169 ] 170 170 `; 171 171 172 172 exports[`box (isCI = false) > renders specified titlePadding 1`] = ` 173 173 [ 174 - "┌──────title───────┐ 174 + "│ ┌──────title───────┐ 175 175 ", 176 - "│ message │ 176 + "│ │ message │ 177 177 ", 178 - "└──────────────────┘ 178 + "│ └──────────────────┘ 179 179 ", 180 180 ] 181 181 `; 182 182 183 183 exports[`box (isCI = false) > renders truncated long titles 1`] = ` 184 184 [ 185 - "┌─foofoofoo...─┐ 185 + "│ ┌─foofoof...─┐ 186 186 ", 187 - "│ message │ 187 + "│ │ message │ 188 188 ", 189 - "└──────────────┘ 189 + "│ └────────────┘ 190 190 ", 191 191 ] 192 192 `; 193 193 194 194 exports[`box (isCI = false) > renders wide characters with auto width 1`] = ` 195 195 [ 196 - "┌─这是标题─────────────────┐ 196 + "│ ┌─这是标题─────────────────┐ 197 197 ", 198 - "│ 이게 첫 번째 줄이에요 │ 198 + "│ │ 이게 첫 번째 줄이에요 │ 199 199 ", 200 - "│ これは次の行です │ 200 + "│ │ これは次の行です │ 201 201 ", 202 - "└──────────────────────────┘ 202 + "│ └──────────────────────────┘ 203 203 ", 204 204 ] 205 205 `; 206 206 207 207 exports[`box (isCI = false) > renders wide characters with specified width 1`] = ` 208 208 [ 209 - "┌─这是标题─────┐ 209 + "│ ┌─这是标题───┐ 210 210 ", 211 - "│ 이게 첫 │ 211 + "│ │ 이게 첫 │ 212 212 ", 213 - "│ 번째 │ 213 + "│ │ 번째 │ 214 214 ", 215 - "│ 줄이에요 │ 215 + "│ │ 줄이에요 │ 216 216 ", 217 - "│ これは次の │ 217 + "│ │ これは次 │ 218 218 ", 219 - "│ 行です │ 219 + "│ │ の行です │ 220 220 ", 221 - "└──────────────┘ 221 + "│ └────────────┘ 222 222 ", 223 223 ] 224 224 `; 225 225 226 226 exports[`box (isCI = false) > renders with formatBorder formatting 1`] = ` 227 227 [ 228 - "┌─title──────┐ 228 + "│ ┌─title──────┐ 229 + ", 230 + "│ │ message │ 231 + ", 232 + "│ └────────────┘ 233 + ", 234 + ] 235 + `; 236 + 237 + exports[`box (isCI = false) > renders without guide when global withGuide is false 1`] = ` 238 + [ 239 + "┌─title──────┐ 229 240 ", 230 - "│ message │ 241 + "│ message │ 231 242 ", 232 - "└────────────┘ 243 + "└────────────┘ 233 244 ", 234 245 ] 235 246 `; 236 247 237 - exports[`box (isCI = false) > renders with prefix when includePrefix is true 1`] = ` 248 + exports[`box (isCI = false) > renders without guide when withGuide is false 1`] = ` 238 249 [ 239 - "│ ┌─title──────┐ 250 + "┌─title──────┐ 240 251 ", 241 - "│ │ message │ 252 + "│ message │ 242 253 ", 243 - "│ └────────────┘ 254 + "└────────────┘ 244 255 ", 245 256 ] 246 257 `; 247 258 248 259 exports[`box (isCI = false) > wraps content to fit within specified width 1`] = ` 249 260 [ 250 - "┌─title────────────────────────────────┐ 261 + "│ ┌─title──────────────────────────────┐ 251 262 ", 252 - "│ foo barfoo barfoo barfoo barfoo │ 263 + "│ │ foo barfoo barfoo barfoo barfoo │ 253 264 ", 254 - "│ barfoo barfoo barfoo barfoo barfoo │ 265 + "│ │ barfoo barfoo barfoo barfoo │ 255 266 ", 256 - "│ barfoo barfoo barfoo barfoo │ 267 + "│ │ barfoo barfoo barfoo barfoo │ 257 268 ", 258 - "│ barfoo barfoo barfoo barfoo barfoo │ 269 + "│ │ barfoo barfoo barfoo barfoo │ 259 270 ", 260 - "│ barfoo bar │ 271 + "│ │ barfoo barfoo barfoo bar │ 261 272 ", 262 - "└──────────────────────────────────────┘ 273 + "│ └────────────────────────────────────┘ 263 274 ", 264 275 ] 265 276 `; 266 277 267 278 exports[`box (isCI = true) > cannot have width larger than 100% 1`] = ` 268 279 [ 269 - "┌─title────────────────────────────────────────────────────────────────────────┐ 280 + "│ ┌─title──────────────────────────────────────────────────────────────────────┐ 270 281 ", 271 - "│ message │ 282 + "│ │ message │ 272 283 ", 273 - "└──────────────────────────────────────────────────────────────────────────────┘ 284 + "│ └────────────────────────────────────────────────────────────────────────────┘ 274 285 ", 275 286 ] 276 287 `; 277 288 278 289 exports[`box (isCI = true) > renders as specified width 1`] = ` 279 290 [ 280 - "┌─title────────────────────────────────┐ 291 + "│ ┌─title──────────────────────────────┐ 281 292 ", 282 - "│ short │ 293 + "│ │ short │ 283 294 ", 284 - "│ somewhat questionably long line │ 295 + "│ │ somewhat questionably long line │ 285 296 ", 286 - "└──────────────────────────────────────┘ 297 + "│ └────────────────────────────────────┘ 287 298 ", 288 299 ] 289 300 `; 290 301 291 302 exports[`box (isCI = true) > renders as wide as longest line with width: auto 1`] = ` 292 303 [ 293 - "┌─title──────────────────────────────┐ 304 + "│ ┌─title──────────────────────────────┐ 294 305 ", 295 - "│ short │ 306 + "│ │ short │ 296 307 ", 297 - "│ somewhat questionably long line │ 308 + "│ │ somewhat questionably long line │ 298 309 ", 299 - "└────────────────────────────────────┘ 310 + "│ └────────────────────────────────────┘ 300 311 ", 301 312 ] 302 313 `; 303 314 304 315 exports[`box (isCI = true) > renders auto width with content longer than title 1`] = ` 305 316 [ 306 - "┌─title──────────────────────────┐ 317 + "│ ┌─title──────────────────────────┐ 307 318 ", 308 - "│ messagemessagemessagemessage │ 319 + "│ │ messagemessagemessagemessage │ 309 320 ", 310 - "└────────────────────────────────┘ 321 + "│ └────────────────────────────────┘ 311 322 ", 312 323 ] 313 324 `; 314 325 315 326 exports[`box (isCI = true) > renders auto width with title longer than content 1`] = ` 316 327 [ 317 - "┌─titletitletitletitle─┐ 328 + "│ ┌─titletitletitletitle─┐ 318 329 ", 319 - "│ message │ 330 + "│ │ message │ 320 331 ", 321 - "└──────────────────────┘ 332 + "│ └──────────────────────┘ 322 333 ", 323 334 ] 324 335 `; 325 336 326 337 exports[`box (isCI = true) > renders center aligned content 1`] = ` 327 338 [ 328 - "┌─title──────┐ 339 + "│ ┌─title──────┐ 329 340 ", 330 - "│ message │ 341 + "│ │ message │ 331 342 ", 332 - "└────────────┘ 343 + "│ └────────────┘ 333 344 ", 334 345 ] 335 346 `; 336 347 337 348 exports[`box (isCI = true) > renders center aligned title 1`] = ` 338 349 [ 339 - "┌───title────┐ 350 + "│ ┌───title────┐ 340 351 ", 341 - "│ message │ 352 + "│ │ message │ 342 353 ", 343 - "└────────────┘ 354 + "│ └────────────┘ 344 355 ", 345 356 ] 346 357 `; 347 358 348 359 exports[`box (isCI = true) > renders left aligned content 1`] = ` 349 360 [ 350 - "┌─title──────┐ 361 + "│ ┌─title──────┐ 351 362 ", 352 - "│ message │ 363 + "│ │ message │ 353 364 ", 354 - "└────────────┘ 365 + "│ └────────────┘ 355 366 ", 356 367 ] 357 368 `; 358 369 359 370 exports[`box (isCI = true) > renders left aligned title 1`] = ` 360 371 [ 361 - "┌─title──────┐ 372 + "│ ┌─title──────┐ 362 373 ", 363 - "│ message │ 374 + "│ │ message │ 364 375 ", 365 - "└────────────┘ 376 + "│ └────────────┘ 366 377 ", 367 378 ] 368 379 `; 369 380 370 381 exports[`box (isCI = true) > renders message 1`] = ` 371 382 [ 372 - "┌──────────────────────────────────────────────────────────────────────────────┐ 383 + "│ ┌────────────────────────────────────────────────────────────────────────────┐ 373 384 ", 374 - "│ message │ 385 + "│ │ message │ 375 386 ", 376 - "└──────────────────────────────────────────────────────────────────────────────┘ 387 + "│ └────────────────────────────────────────────────────────────────────────────┘ 377 388 ", 378 389 ] 379 390 `; 380 391 381 392 exports[`box (isCI = true) > renders message with title 1`] = ` 382 393 [ 383 - "┌─some title───────────────────────────────────────────────────────────────────┐ 394 + "│ ┌─some title─────────────────────────────────────────────────────────────────┐ 384 395 ", 385 - "│ message │ 396 + "│ │ message │ 386 397 ", 387 - "└──────────────────────────────────────────────────────────────────────────────┘ 398 + "│ └────────────────────────────────────────────────────────────────────────────┘ 388 399 ", 389 400 ] 390 401 `; 391 402 392 403 exports[`box (isCI = true) > renders right aligned content 1`] = ` 393 404 [ 394 - "┌─title──────┐ 405 + "│ ┌─title──────┐ 395 406 ", 396 - "│ message │ 407 + "│ │ message │ 397 408 ", 398 - "└────────────┘ 409 + "│ └────────────┘ 399 410 ", 400 411 ] 401 412 `; 402 413 403 414 exports[`box (isCI = true) > renders right aligned title 1`] = ` 404 415 [ 405 - "┌──────title─┐ 416 + "│ ┌──────title─┐ 406 417 ", 407 - "│ message │ 418 + "│ │ message │ 408 419 ", 409 - "└────────────┘ 420 + "│ └────────────┘ 410 421 ", 411 422 ] 412 423 `; 413 424 414 425 exports[`box (isCI = true) > renders rounded corners when rounded is true 1`] = ` 415 426 [ 416 - "╭─title──────╮ 427 + "│ ╭─title──────╮ 417 428 ", 418 - "│ message │ 429 + "│ │ message │ 419 430 ", 420 - "╰────────────╯ 431 + "│ ╰────────────╯ 421 432 ", 422 433 ] 423 434 `; 424 435 425 436 exports[`box (isCI = true) > renders specified contentPadding 1`] = ` 426 437 [ 427 - "┌─title──────────────┐ 438 + "│ ┌─title──────────────┐ 428 439 ", 429 - "│ message │ 440 + "│ │ message │ 430 441 ", 431 - "└────────────────────┘ 442 + "│ └────────────────────┘ 432 443 ", 433 444 ] 434 445 `; 435 446 436 447 exports[`box (isCI = true) > renders specified titlePadding 1`] = ` 437 448 [ 438 - "┌──────title───────┐ 449 + "│ ┌──────title───────┐ 439 450 ", 440 - "│ message │ 451 + "│ │ message │ 441 452 ", 442 - "└──────────────────┘ 453 + "│ └──────────────────┘ 443 454 ", 444 455 ] 445 456 `; 446 457 447 458 exports[`box (isCI = true) > renders truncated long titles 1`] = ` 448 459 [ 449 - "┌─foofoofoo...─┐ 460 + "│ ┌─foofoof...─┐ 450 461 ", 451 - "│ message │ 462 + "│ │ message │ 452 463 ", 453 - "└──────────────┘ 464 + "│ └────────────┘ 454 465 ", 455 466 ] 456 467 `; 457 468 458 469 exports[`box (isCI = true) > renders wide characters with auto width 1`] = ` 459 470 [ 460 - "┌─这是标题─────────────────┐ 471 + "│ ┌─这是标题─────────────────┐ 461 472 ", 462 - "│ 이게 첫 번째 줄이에요 │ 473 + "│ │ 이게 첫 번째 줄이에요 │ 463 474 ", 464 - "│ これは次の行です │ 475 + "│ │ これは次の行です │ 465 476 ", 466 - "└──────────────────────────┘ 477 + "│ └──────────────────────────┘ 467 478 ", 468 479 ] 469 480 `; 470 481 471 482 exports[`box (isCI = true) > renders wide characters with specified width 1`] = ` 472 483 [ 473 - "┌─这是标题─────┐ 484 + "│ ┌─这是标题───┐ 474 485 ", 475 - "│ 이게 첫 │ 486 + "│ │ 이게 첫 │ 476 487 ", 477 - "│ 번째 │ 488 + "│ │ 번째 │ 478 489 ", 479 - "│ 줄이에요 │ 490 + "│ │ 줄이에요 │ 480 491 ", 481 - "│ これは次の │ 492 + "│ │ これは次 │ 482 493 ", 483 - "│ 行です │ 494 + "│ │ の行です │ 484 495 ", 485 - "└──────────────┘ 496 + "│ └────────────┘ 486 497 ", 487 498 ] 488 499 `; 489 500 490 501 exports[`box (isCI = true) > renders with formatBorder formatting 1`] = ` 491 502 [ 492 - "┌─title──────┐ 503 + "│ ┌─title──────┐ 504 + ", 505 + "│ │ message │ 506 + ", 507 + "│ └────────────┘ 508 + ", 509 + ] 510 + `; 511 + 512 + exports[`box (isCI = true) > renders without guide when global withGuide is false 1`] = ` 513 + [ 514 + "┌─title──────┐ 493 515 ", 494 - "│ message │ 516 + "│ message │ 495 517 ", 496 - "└────────────┘ 518 + "└────────────┘ 497 519 ", 498 520 ] 499 521 `; 500 522 501 - exports[`box (isCI = true) > renders with prefix when includePrefix is true 1`] = ` 523 + exports[`box (isCI = true) > renders without guide when withGuide is false 1`] = ` 502 524 [ 503 - "│ ┌─title──────┐ 525 + "┌─title──────┐ 504 526 ", 505 - "│ │ message │ 527 + "│ message │ 506 528 ", 507 - "│ └────────────┘ 529 + "└────────────┘ 508 530 ", 509 531 ] 510 532 `; 511 533 512 534 exports[`box (isCI = true) > wraps content to fit within specified width 1`] = ` 513 535 [ 514 - "┌─title────────────────────────────────┐ 536 + "│ ┌─title──────────────────────────────┐ 515 537 ", 516 - "│ foo barfoo barfoo barfoo barfoo │ 538 + "│ │ foo barfoo barfoo barfoo barfoo │ 517 539 ", 518 - "│ barfoo barfoo barfoo barfoo barfoo │ 540 + "│ │ barfoo barfoo barfoo barfoo │ 519 541 ", 520 - "│ barfoo barfoo barfoo barfoo │ 542 + "│ │ barfoo barfoo barfoo barfoo │ 521 543 ", 522 - "│ barfoo barfoo barfoo barfoo barfoo │ 544 + "│ │ barfoo barfoo barfoo barfoo │ 523 545 ", 524 - "│ barfoo bar │ 546 + "│ │ barfoo barfoo barfoo bar │ 525 547 ", 526 - "└──────────────────────────────────────┘ 548 + "│ └────────────────────────────────────┘ 527 549 ", 528 550 ] 529 551 `;
+211
packages/prompts/test/__snapshots__/log.test.ts.snap
··· 1 + // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 + 3 + exports[`log (isCI = false) > error > renders error message 1`] = ` 4 + [ 5 + "│ 6 + ■ error message 7 + ", 8 + ] 9 + `; 10 + 11 + exports[`log (isCI = false) > info > renders info message 1`] = ` 12 + [ 13 + "│ 14 + ● info message 15 + ", 16 + ] 17 + `; 18 + 19 + exports[`log (isCI = false) > message > renders message 1`] = ` 20 + [ 21 + "│ 22 + │ message 23 + ", 24 + ] 25 + `; 26 + 27 + exports[`log (isCI = false) > message > renders message from array 1`] = ` 28 + [ 29 + "│ 30 + │ line 1 31 + │ line 2 32 + │ line 3 33 + ", 34 + ] 35 + `; 36 + 37 + exports[`log (isCI = false) > message > renders message with custom spacing 1`] = ` 38 + [ 39 + "│ 40 + │ 41 + │ 42 + │ spaced message 43 + ", 44 + ] 45 + `; 46 + 47 + exports[`log (isCI = false) > message > renders message with custom symbols and spacing 1`] = ` 48 + [ 49 + "-- 50 + >> custom 51 + -- symbols 52 + ", 53 + ] 54 + `; 55 + 56 + exports[`log (isCI = false) > message > renders message with guide disabled 1`] = ` 57 + [ 58 + " 59 + standalone message 60 + ", 61 + ] 62 + `; 63 + 64 + exports[`log (isCI = false) > message > renders multiline message 1`] = ` 65 + [ 66 + "│ 67 + │ line 1 68 + │ line 2 69 + │ line 3 70 + ", 71 + ] 72 + `; 73 + 74 + exports[`log (isCI = false) > message > renders multiline message with guide disabled 1`] = ` 75 + [ 76 + " 77 + line 1 78 + line 2 79 + line 3 80 + ", 81 + ] 82 + `; 83 + 84 + exports[`log (isCI = false) > step > renders step message 1`] = ` 85 + [ 86 + "│ 87 + ◇ step message 88 + ", 89 + ] 90 + `; 91 + 92 + exports[`log (isCI = false) > success > renders success message 1`] = ` 93 + [ 94 + "│ 95 + ◆ success message 96 + ", 97 + ] 98 + `; 99 + 100 + exports[`log (isCI = false) > warn > renders warn message 1`] = ` 101 + [ 102 + "│ 103 + ▲ warn message 104 + ", 105 + ] 106 + `; 107 + 108 + exports[`log (isCI = true) > error > renders error message 1`] = ` 109 + [ 110 + "│ 111 + ■ error message 112 + ", 113 + ] 114 + `; 115 + 116 + exports[`log (isCI = true) > info > renders info message 1`] = ` 117 + [ 118 + "│ 119 + ● info message 120 + ", 121 + ] 122 + `; 123 + 124 + exports[`log (isCI = true) > message > renders message 1`] = ` 125 + [ 126 + "│ 127 + │ message 128 + ", 129 + ] 130 + `; 131 + 132 + exports[`log (isCI = true) > message > renders message from array 1`] = ` 133 + [ 134 + "│ 135 + │ line 1 136 + │ line 2 137 + │ line 3 138 + ", 139 + ] 140 + `; 141 + 142 + exports[`log (isCI = true) > message > renders message with custom spacing 1`] = ` 143 + [ 144 + "│ 145 + │ 146 + │ 147 + │ spaced message 148 + ", 149 + ] 150 + `; 151 + 152 + exports[`log (isCI = true) > message > renders message with custom symbols and spacing 1`] = ` 153 + [ 154 + "-- 155 + >> custom 156 + -- symbols 157 + ", 158 + ] 159 + `; 160 + 161 + exports[`log (isCI = true) > message > renders message with guide disabled 1`] = ` 162 + [ 163 + " 164 + standalone message 165 + ", 166 + ] 167 + `; 168 + 169 + exports[`log (isCI = true) > message > renders multiline message 1`] = ` 170 + [ 171 + "│ 172 + │ line 1 173 + │ line 2 174 + │ line 3 175 + ", 176 + ] 177 + `; 178 + 179 + exports[`log (isCI = true) > message > renders multiline message with guide disabled 1`] = ` 180 + [ 181 + " 182 + line 1 183 + line 2 184 + line 3 185 + ", 186 + ] 187 + `; 188 + 189 + exports[`log (isCI = true) > step > renders step message 1`] = ` 190 + [ 191 + "│ 192 + ◇ step message 193 + ", 194 + ] 195 + `; 196 + 197 + exports[`log (isCI = true) > success > renders success message 1`] = ` 198 + [ 199 + "│ 200 + ◆ success message 201 + ", 202 + ] 203 + `; 204 + 205 + exports[`log (isCI = true) > warn > renders warn message 1`] = ` 206 + [ 207 + "│ 208 + ▲ warn message 209 + ", 210 + ] 211 + `;
+68
packages/prompts/test/__snapshots__/text.test.ts.snap
··· 71 71 ] 72 72 `; 73 73 74 + exports[`text (isCI = false) > global withGuide: false removes guide 1`] = ` 75 + [ 76 + "<cursor.hide>", 77 + "◆ foo 78 + _ 79 + 80 + ", 81 + "<cursor.backward count=999><cursor.up count=3>", 82 + "<erase.down>", 83 + "◇ foo 84 + ", 85 + " 86 + ", 87 + "<cursor.show>", 88 + ] 89 + `; 90 + 74 91 exports[`text (isCI = false) > placeholder is not used as value when pressing enter 1`] = ` 75 92 [ 76 93 "<cursor.hide>", ··· 257 274 "<erase.down>", 258 275 "◇ foo 259 276 │ xy", 277 + " 278 + ", 279 + "<cursor.show>", 280 + ] 281 + `; 282 + 283 + exports[`text (isCI = false) > withGuide: false removes guide 1`] = ` 284 + [ 285 + "<cursor.hide>", 286 + "◆ foo 287 + _ 288 + 289 + ", 290 + "<cursor.backward count=999><cursor.up count=3>", 291 + "<erase.down>", 292 + "◇ foo 293 + ", 260 294 " 261 295 ", 262 296 "<cursor.show>", ··· 334 368 ] 335 369 `; 336 370 371 + exports[`text (isCI = true) > global withGuide: false removes guide 1`] = ` 372 + [ 373 + "<cursor.hide>", 374 + "◆ foo 375 + _ 376 + 377 + ", 378 + "<cursor.backward count=999><cursor.up count=3>", 379 + "<erase.down>", 380 + "◇ foo 381 + ", 382 + " 383 + ", 384 + "<cursor.show>", 385 + ] 386 + `; 387 + 337 388 exports[`text (isCI = true) > placeholder is not used as value when pressing enter 1`] = ` 338 389 [ 339 390 "<cursor.hide>", ··· 525 576 "<cursor.show>", 526 577 ] 527 578 `; 579 + 580 + exports[`text (isCI = true) > withGuide: false removes guide 1`] = ` 581 + [ 582 + "<cursor.hide>", 583 + "◆ foo 584 + _ 585 + 586 + ", 587 + "<cursor.backward count=999><cursor.up count=3>", 588 + "<erase.down>", 589 + "◇ foo 590 + ", 591 + " 592 + ", 593 + "<cursor.show>", 594 + ] 595 + `;
+16 -2
packages/prompts/test/box.test.ts
··· 1 + import { updateSettings } from '@clack/core'; 1 2 import colors from 'picocolors'; 2 3 import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; 3 4 import * as prompts from '../src/index.js'; ··· 24 25 25 26 afterEach(() => { 26 27 vi.restoreAllMocks(); 28 + updateSettings({ withGuide: true }); 27 29 }); 28 30 29 31 test('renders message', () => { ··· 96 98 expect(output.buffer).toMatchSnapshot(); 97 99 }); 98 100 99 - test('renders with prefix when includePrefix is true', () => { 101 + test('renders without guide when withGuide is false', () => { 100 102 prompts.box('message', 'title', { 101 103 input, 102 104 output, 103 - includePrefix: true, 105 + withGuide: false, 106 + width: 'auto', 107 + }); 108 + 109 + expect(output.buffer).toMatchSnapshot(); 110 + }); 111 + 112 + test('renders without guide when global withGuide is false', () => { 113 + updateSettings({ withGuide: false }); 114 + 115 + prompts.box('message', 'title', { 116 + input, 117 + output, 104 118 width: 'auto', 105 119 }); 106 120
+139
packages/prompts/test/log.test.ts
··· 1 + import colors from 'picocolors'; 2 + import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; 3 + import * as prompts from '../src/index.js'; 4 + import { MockWritable } from './test-utils.js'; 5 + 6 + describe.each(['true', 'false'])('log (isCI = %s)', (isCI) => { 7 + let originalCI: string | undefined; 8 + let output: MockWritable; 9 + 10 + beforeAll(() => { 11 + originalCI = process.env.CI; 12 + process.env.CI = isCI; 13 + }); 14 + 15 + afterAll(() => { 16 + process.env.CI = originalCI; 17 + }); 18 + 19 + beforeEach(() => { 20 + output = new MockWritable(); 21 + }); 22 + 23 + afterEach(() => { 24 + vi.restoreAllMocks(); 25 + }); 26 + 27 + describe('message', () => { 28 + test('renders message', () => { 29 + prompts.log.message('message', { 30 + output, 31 + }); 32 + 33 + expect(output.buffer).toMatchSnapshot(); 34 + }); 35 + 36 + test('renders multiline message', () => { 37 + prompts.log.message('line 1\nline 2\nline 3', { 38 + output, 39 + }); 40 + 41 + expect(output.buffer).toMatchSnapshot(); 42 + }); 43 + 44 + test('renders message from array', () => { 45 + prompts.log.message(['line 1', 'line 2', 'line 3'], { 46 + output, 47 + }); 48 + 49 + expect(output.buffer).toMatchSnapshot(); 50 + }); 51 + 52 + test('renders message with custom symbols and spacing', () => { 53 + prompts.log.message('custom\nsymbols', { 54 + symbol: colors.red('>>'), 55 + secondarySymbol: colors.yellow('--'), 56 + output, 57 + }); 58 + 59 + expect(output.buffer).toMatchSnapshot(); 60 + }); 61 + 62 + test('renders message with guide disabled', () => { 63 + prompts.log.message('standalone message', { 64 + withGuide: false, 65 + output, 66 + }); 67 + 68 + expect(output.buffer).toMatchSnapshot(); 69 + }); 70 + 71 + test('renders multiline message with guide disabled', () => { 72 + prompts.log.message('line 1\nline 2\nline 3', { 73 + withGuide: false, 74 + output, 75 + }); 76 + 77 + expect(output.buffer).toMatchSnapshot(); 78 + }); 79 + 80 + test('renders message with custom spacing', () => { 81 + prompts.log.message('spaced message', { 82 + spacing: 3, 83 + output, 84 + }); 85 + 86 + expect(output.buffer).toMatchSnapshot(); 87 + }); 88 + }); 89 + 90 + describe('info', () => { 91 + test('renders info message', () => { 92 + prompts.log.info('info message', { 93 + output, 94 + }); 95 + 96 + expect(output.buffer).toMatchSnapshot(); 97 + }); 98 + }); 99 + 100 + describe('success', () => { 101 + test('renders success message', () => { 102 + prompts.log.success('success message', { 103 + output, 104 + }); 105 + 106 + expect(output.buffer).toMatchSnapshot(); 107 + }); 108 + }); 109 + 110 + describe('step', () => { 111 + test('renders step message', () => { 112 + prompts.log.step('step message', { 113 + output, 114 + }); 115 + 116 + expect(output.buffer).toMatchSnapshot(); 117 + }); 118 + }); 119 + 120 + describe('warn', () => { 121 + test('renders warn message', () => { 122 + prompts.log.warn('warn message', { 123 + output, 124 + }); 125 + 126 + expect(output.buffer).toMatchSnapshot(); 127 + }); 128 + }); 129 + 130 + describe('error', () => { 131 + test('renders error message', () => { 132 + prompts.log.error('error message', { 133 + output, 134 + }); 135 + 136 + expect(output.buffer).toMatchSnapshot(); 137 + }); 138 + }); 139 + });
+33
packages/prompts/test/text.test.ts
··· 1 + import { updateSettings } from '@clack/core'; 1 2 import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; 2 3 import * as prompts from '../src/index.js'; 3 4 import { MockReadable, MockWritable } from './test-utils.js'; ··· 23 24 24 25 afterEach(() => { 25 26 vi.restoreAllMocks(); 27 + updateSettings({ withGuide: true }); 26 28 }); 27 29 28 30 test('renders message', async () => { ··· 203 205 controller.abort(); 204 206 const value = await result; 205 207 expect(prompts.isCancel(value)).toBe(true); 208 + expect(output.buffer).toMatchSnapshot(); 209 + }); 210 + 211 + test('withGuide: false removes guide', async () => { 212 + const result = prompts.text({ 213 + message: 'foo', 214 + withGuide: false, 215 + input, 216 + output, 217 + }); 218 + 219 + input.emit('keypress', '', { name: 'return' }); 220 + 221 + await result; 222 + 223 + expect(output.buffer).toMatchSnapshot(); 224 + }); 225 + 226 + test('global withGuide: false removes guide', async () => { 227 + updateSettings({ withGuide: false }); 228 + 229 + const result = prompts.text({ 230 + message: 'foo', 231 + input, 232 + output, 233 + }); 234 + 235 + input.emit('keypress', '', { name: 'return' }); 236 + 237 + await result; 238 + 206 239 expect(output.buffer).toMatchSnapshot(); 207 240 }); 208 241 });
+1 -1
packages/prompts/tsconfig.json
··· 1 1 { 2 2 "extends": "../../tsconfig.json", 3 - "include": ["src"] 3 + "include": ["src", "test"] 4 4 }
+2
tsconfig.json
··· 11 11 "skipLibCheck": true, 12 12 "isolatedModules": true, 13 13 "verbatimModuleSyntax": true, 14 + "noUnusedParameters": true, 15 + "noUnusedLocals": true, 14 16 "lib": ["ES2022"], 15 17 "paths": { 16 18 "@clack/core": ["./packages/core/src/index.ts"],