/** * SpinnerHandle — the canonical owner of `\r` writes for CLEO animations. * * @remarks * Wraps a {@link Spinner} frame set with a managed timer, cursor hiding, and * idempotent start/stop. All animation output flows through here so the * `AnimateContext` gate can be enforced at one place. * * The handle is silent when its {@link AnimateContext} has `enabled === false` * (JSON output, --quiet, non-TTY, NO_COLOR). All public methods become no-ops * in that case so callers never need to branch on output mode. * * Lint rule: any `process.stdout.write` of a string starting with `\r` outside * `@cleocode/animations` is a violation. Route through `createSpinnerHandle`. */ import type { AnimateContext } from './animate-context.js'; import { type BrailleSpinnerName, type CanonSpinnerName } from './braille.js'; /** * Imperative handle to a running spinner. * * @remarks * `start()` and `stop()` are idempotent — calling `start()` twice has the same * effect as calling it once; calling `stop()` on a handle that never started * is a no-op. `update()` changes the label without restarting the timer. */ export interface SpinnerHandle { /** Begin rendering frames. Idempotent. */ start(): void; /** * Stop rendering, clear the spinner line, and (optionally) print a final * line in its place. * * @param finalLine - Optional message to print after clearing (no `\r` needed). */ stop(finalLine?: string): void; /** Update the trailing label without restarting the frame timer. */ update(label: string): void; /** * Whether this handle will actually render. `false` when the underlying * {@link AnimateContext} disabled output (e.g. `--json`, `--quiet`). */ readonly enabled: boolean; } /** * Options controlling spinner timing. */ export interface SpinnerHandleOptions { /** * Milliseconds to wait before showing the spinner. Prevents flashing on * fast operations. * * @defaultValue `150` */ readonly delayMs?: number; } /** * Test-only: reset the process-exit listener install state and clear the * active-handles registry. * * @internal * @remarks * Vitest reuses the same Node.js process across multiple test files. * Without this hook, tests that assert listener-count invariants would * see state from earlier files. Production code MUST NOT call this. */ export declare function __resetExitListenersForTesting(): void; /** * Create a managed spinner that obeys the {@link AnimateContext} gate. * * @param context - Resolved render gate (typically from `createAnimateContext`) * @param name - Spinner name from {@link spinners} or {@link canonSpinners} * @param label - Trailing label rendered next to the frame * @param options - Optional timing controls * @returns A handle whose methods are no-ops when `context.enabled === false` * * @example * ```ts * import { resolveOutputFormat } from '@cleocode/lafs'; * import { createAnimateContext, createSpinnerHandle } from '@cleocode/animations'; * * const flags = resolveOutputFormat({ humanFlag: true }); * const ctx = createAnimateContext({ flagResolution: flags }); * const spinner = createSpinnerHandle(ctx, 'weaving', 'Loading tasks…'); * * spinner.start(); * try { * const result = await heavyWork(); * spinner.stop(); * console.log(result); * } catch (err) { * spinner.stop(); * throw err; * } * ``` */ export declare function createSpinnerHandle(context: AnimateContext, name: CanonSpinnerName | BrailleSpinnerName, label: string, options?: SpinnerHandleOptions): SpinnerHandle; //# sourceMappingURL=spinner-handle.d.ts.map