+3
-1
@@ -112,4 +112,6 @@ import {type SpinnerName} from 'cli-spinners'; | ||
| This has no effect on Windows as there's no good way to implement discarding stdin properly there. | ||
| This has no effect on Windows as there is no good way to implement discarding stdin properly there. | ||
| Note: `discardStdin` puts stdin into raw mode. In raw mode, `Ctrl+C` no longer generates `SIGINT` from the terminal. Ora re-emits `Ctrl+C` from stdin input, but if your code blocks the event loop with synchronous work, `Ctrl+C` handling is delayed until the blocking work ends. Use async APIs, a worker thread, or a child process to keep `Ctrl+C` responsive, or set `discardStdin` to `false`. | ||
| @default true | ||
@@ -116,0 +118,0 @@ */ |
+37
-21
@@ -14,2 +14,4 @@ import process from 'node:process'; | ||
| const RENDER_DEFERRAL_TIMEOUT = 200; // Milliseconds to wait before re-rendering after partial chunk write | ||
| const SYNCHRONIZED_OUTPUT_ENABLE = '\u001B[?2026h'; | ||
| const SYNCHRONIZED_OUTPUT_DISABLE = '\u001B[?2026l'; | ||
@@ -446,30 +448,44 @@ // Global state for concurrent spinner detection | ||
| this.clear(); | ||
| const useSynchronizedOutput = this.#stream.isTTY; | ||
| let shouldDisableSynchronizedOutput = false; | ||
| let frameContent = this.frame(); | ||
| const columns = this.#stream.columns ?? 80; | ||
| const actualLineCount = this.#computeLineCountFrom(frameContent, columns); | ||
| try { | ||
| if (useSynchronizedOutput) { | ||
| this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_ENABLE)); | ||
| shouldDisableSynchronizedOutput = true; | ||
| } | ||
| // If content would exceed viewport height, truncate it to prevent garbage | ||
| const consoleHeight = this.#stream.rows; | ||
| if (consoleHeight && consoleHeight > 1 && actualLineCount > consoleHeight) { | ||
| const lines = frameContent.split('\n'); | ||
| const maxLines = consoleHeight - 1; // Reserve one line for truncation message | ||
| frameContent = [...lines.slice(0, maxLines), '... (content truncated to fit terminal)'].join('\n'); | ||
| } | ||
| this.clear(); | ||
| const canContinue = this.#internalWrite(() => this.#stream.write(frameContent)); | ||
| let frameContent = this.frame(); | ||
| const columns = this.#stream.columns ?? 80; | ||
| const actualLineCount = this.#computeLineCountFrom(frameContent, columns); | ||
| // Handle backpressure - pause rendering if stream buffer is full | ||
| if (canContinue === false && this.#stream.isTTY) { | ||
| this.#drainHandler = () => { | ||
| this.#drainHandler = undefined; | ||
| this.#tryRender(); | ||
| }; | ||
| // If content would exceed viewport height, truncate it to prevent garbage | ||
| const consoleHeight = this.#stream.rows; | ||
| if (consoleHeight && consoleHeight > 1 && actualLineCount > consoleHeight) { | ||
| const lines = frameContent.split('\n'); | ||
| const maxLines = consoleHeight - 1; // Reserve one line for truncation message | ||
| frameContent = [...lines.slice(0, maxLines), '... (content truncated to fit terminal)'].join('\n'); | ||
| } | ||
| this.#stream.once('drain', this.#drainHandler); | ||
| const canContinue = this.#internalWrite(() => this.#stream.write(frameContent)); | ||
| // Handle backpressure - pause rendering if stream buffer is full | ||
| if (canContinue === false && this.#stream.isTTY) { | ||
| this.#drainHandler = () => { | ||
| this.#drainHandler = undefined; | ||
| this.#tryRender(); | ||
| }; | ||
| this.#stream.once('drain', this.#drainHandler); | ||
| } | ||
| this.#linesToClear = this.#computeLineCountFrom(frameContent, columns); | ||
| } finally { | ||
| if (shouldDisableSynchronizedOutput) { | ||
| this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_DISABLE)); | ||
| } | ||
| } | ||
| this.#linesToClear = this.#computeLineCountFrom(frameContent, columns); | ||
| return this; | ||
@@ -476,0 +492,0 @@ } |
+1
-1
| { | ||
| "name": "ora", | ||
| "version": "9.2.0", | ||
| "version": "9.3.0", | ||
| "description": "Elegant terminal spinner", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+6
-0
@@ -141,2 +141,4 @@ # ora | ||
| Note: `discardStdin` puts stdin into raw mode. In raw mode, `Ctrl+C` no longer generates `SIGINT` from the terminal. Ora re-emits `Ctrl+C` from stdin input, but if your code blocks the event loop with synchronous work, `Ctrl+C` handling is delayed until the blocking work ends. Use async APIs, a worker thread, or a child process to keep `Ctrl+C` responsive, or set `discardStdin` to `false`. | ||
| ### Instance | ||
@@ -316,2 +318,6 @@ | ||
| ### Why do I get the line spinner on Windows Terminal or WSL? | ||
| Windows Terminal does [not expose](https://github.com/microsoft/terminal/issues/1040) a reliable, stable way to detect itself or Unicode support from Node, and `WT_SESSION` is explicitly informative, not a detection API and can be inherited by other terminals. That makes environment-based detection best-effort. If you are in a Unicode-capable terminal, set `spinner` explicitly, for example `ora({spinner: 'dots'})`. | ||
| ### Can I log messages while the spinner is running? | ||
@@ -318,0 +324,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
38143
4.99%753
2.17%400
1.52%3
-25%