diff --git a/.changeset/hungry-drinks-raise.md b/.changeset/hungry-drinks-raise.md new file mode 100644 index 00000000..5e0ec138 --- /dev/null +++ b/.changeset/hungry-drinks-raise.md @@ -0,0 +1,5 @@ +--- +"@clack/prompts": patch +--- + +Fix spinner clearing by tracking the previously rendered output instead of message. diff --git a/examples/basic/package.json b/examples/basic/package.json index f8e617e7..082bf97a 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -16,6 +16,7 @@ "path": "jiti ./path.ts", "spinner-ci": "npx cross-env CI=\"true\" jiti ./spinner-ci.ts", "spinner-timer": "jiti ./spinner-timer.ts", + "spinner-wrap": "jiti ./spinner-wrap.ts", "task-log": "jiti ./task-log.ts" }, "devDependencies": { diff --git a/examples/basic/spinner-wrap.ts b/examples/basic/spinner-wrap.ts new file mode 100644 index 00000000..6939440d --- /dev/null +++ b/examples/basic/spinner-wrap.ts @@ -0,0 +1,48 @@ +import * as p from '@clack/prompts'; + +p.intro('spinner start...'); + +const barWidth = 3; +const columns = (process.stdout.columns || 80) - barWidth; + +async function testCase(subtract = 0) { + p.log.info(`Testing spinner with message width = terminal columns - ${subtract}...`); + + const spin = p.spinner(); + + spin.start(); + + // Create a message that fills the width the terminal + const length = columns - subtract; + const step = Math.floor(length / 5); + + // Start with full line + const chars = Array.from({ length }, () => '□'); + + // Replace some characters with spaces for better wrapping + for (let i = step; i < length; i += step) { + chars[i] = ' '; + } + + const message = chars.join(''); + + spin.message(message); + + await wait(3000); + + spin.stop('Done'); +} + +async function test() { + await testCase(0); // Do not subtract anything, output width = terminal columns + await testCase(2); // Subtract 2 + await testCase(3); // Subtract 3, which is the length of spinner symbols +} + +test(); + +// + +function wait(ms: number) { + return new Promise((r) => setTimeout(r, ms)); +} diff --git a/packages/prompts/src/spinner.ts b/packages/prompts/src/spinner.ts index 9b427e3a..1e8179a0 100644 --- a/packages/prompts/src/spinner.ts +++ b/packages/prompts/src/spinner.ts @@ -53,6 +53,7 @@ export const spinner = ({ let isCancelled = false; let _message = ''; let _prevMessage: string | undefined; + let _prevOutput: string | undefined; let _origin: number = performance.now(); const columns = getColumns(output); const styleFn = opts?.styleFrame ?? defaultStyleFn; @@ -102,13 +103,9 @@ export const spinner = ({ }; const clearPrevMessage = () => { - if (_prevMessage === undefined) return; + if (_prevOutput === undefined) return; if (isCI) output.write('\n'); - const wrapped = wrapAnsi(_prevMessage, columns, { - hard: true, - trim: false, - }); - const prevLines = wrapped.split('\n'); + const prevLines = _prevOutput.split('\n'); if (prevLines.length > 1) { output.write(cursor.up(prevLines.length - 1)); } @@ -164,6 +161,8 @@ export const spinner = ({ }); output.write(wrapped); + _prevOutput = wrapped; + frameIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0; // indicator increase by 1 every 8 frames indicatorTimer = indicatorTimer < 4 ? indicatorTimer + 0.125 : 0;