[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 `clear` to spinner (#433)

authored by

James Garbutt and committed by
GitHub
(Dec 21, 2025, 2:41 AM -0600) 372b526c 4d50be6d

+59 -5
+5
.changeset/loose-days-hug.md
··· 1 + --- 2 + "@clack/prompts": patch 3 + --- 4 + 5 + Add `clear` method to spinner for stopping and clearing.
+1
packages/prompts/src/progress-bar.ts
··· 65 65 stop: spin.stop, 66 66 cancel: spin.cancel, 67 67 error: spin.error, 68 + clear: spin.clear, 68 69 advance, 69 70 isCancelled: spin.isCancelled, 70 71 message: (msg: string) => advance(0, msg),
+13 -5
packages/prompts/src/spinner.ts
··· 28 28 cancel(msg?: string): void; 29 29 error(msg?: string): void; 30 30 message(msg?: string): void; 31 + clear(): void; 31 32 readonly isCancelled: boolean; 32 33 } 33 34 ··· 165 166 }, delay); 166 167 }; 167 168 168 - const _stop = (msg = '', code = 0): void => { 169 + const _stop = (msg = '', code = 0, silent: boolean = false): void => { 169 170 if (!isSpinnerActive) return; 170 171 isSpinnerActive = false; 171 172 clearInterval(loop); ··· 177 178 ? color.red(S_STEP_CANCEL) 178 179 : color.red(S_STEP_ERROR); 179 180 _message = msg ?? _message; 180 - if (indicator === 'timer') { 181 - output.write(`${step} ${_message} ${formatTimer(_origin)}\n`); 182 - } else { 183 - output.write(`${step} ${_message}\n`); 181 + if (!silent) { 182 + if (indicator === 'timer') { 183 + output.write(`${step} ${_message} ${formatTimer(_origin)}\n`); 184 + } else { 185 + output.write(`${step} ${_message}\n`); 186 + } 184 187 } 185 188 clearHooks(); 186 189 unblock(); ··· 189 192 const stop = (msg = ''): void => _stop(msg, 0); 190 193 const cancel = (msg = ''): void => _stop(msg, 1); 191 194 const error = (msg = ''): void => _stop(msg, 2); 195 + // TODO (43081j): this will leave the initial S_BAR since we purposely 196 + // don't erase that in `clearPrevMessage`. In future, we may want to treat 197 + // `clear` as a special case and remove the bar too. 198 + const clear = (): void => _stop('', 0, true); 192 199 193 200 const message = (msg = ''): void => { 194 201 _message = removeTrailingDots(msg ?? _message); ··· 200 207 message, 201 208 cancel, 202 209 error, 210 + clear, 203 211 get isCancelled() { 204 212 return isCancelled; 205 213 },
+26
packages/prompts/test/__snapshots__/spinner.test.ts.snap
··· 11 11 ] 12 12 `; 13 13 14 + exports[`spinner (isCI = false) > clear > stops and clears the spinner from the output 1`] = ` 15 + [ 16 + "<cursor.hide>", 17 + "│ 18 + ", 19 + "◒ Loading", 20 + "<cursor.left count=1>", 21 + "<erase.down>", 22 + "<cursor.show>", 23 + ] 24 + `; 25 + 14 26 exports[`spinner (isCI = false) > indicator customization > custom delay 1`] = ` 15 27 [ 16 28 "<cursor.hide>", ··· 566 578 ", 567 579 "■ Canceled 568 580 ", 581 + "<cursor.show>", 582 + ] 583 + `; 584 + 585 + exports[`spinner (isCI = true) > clear > stops and clears the spinner from the output 1`] = ` 586 + [ 587 + "<cursor.hide>", 588 + "│ 589 + ", 590 + "◒ Loading...", 591 + " 592 + ", 593 + "<cursor.left count=1>", 594 + "<erase.down>", 569 595 "<cursor.show>", 570 596 ] 571 597 `;
+14
packages/prompts/test/spinner.test.ts
··· 425 425 426 426 expect(output.buffer).toMatchSnapshot(); 427 427 }); 428 + 429 + describe('clear', () => { 430 + test('stops and clears the spinner from the output', () => { 431 + const result = prompts.spinner({ output }); 432 + 433 + result.start('Loading'); 434 + 435 + vi.advanceTimersByTime(80); 436 + 437 + result.clear(); 438 + 439 + expect(output.buffer).toMatchSnapshot(); 440 + }); 441 + }); 428 442 });