createSpinner function
Spinner
createSpinner(
- WriteStream stream,
- String text, {
- ColorTheme? color,
- Symbols? symbols,
- EnvLookup? readEnv,
- SpinnerTimerFactory? startInterval,
The optional color/symbols/readEnv/startInterval overrides are the DI
seams replacing the vitest theme mock, vi.stubEnv, and fake timers.
Implementation
Spinner createSpinner(
WriteStream stream,
String text, {
ColorTheme? color,
Symbols? symbols,
EnvLookup? readEnv,
SpinnerTimerFactory? startInterval,
}) {
final theme = color ?? _defaultColorTheme();
final syms = symbols ?? SYMBOLS;
final isCI = _resolveIsCI(readEnv ?? lookupPlatformEnv);
final frames = syms.spinner;
var frameIndex = 0;
SpinnerTimer? intervalId;
var running = true;
void clear() {
// The TS guard also checks that clearLine/cursorTo exist; the Dart
// WriteStream interface always provides them.
if (stream.isTTY) {
stream.clearLine(0);
stream.cursorTo(0);
}
}
void writeLine(String msg) {
clear();
stream.write('$msg\n');
}
void render() {
if (!running) {
return;
}
final frame = '${theme.info(frames[frameIndex])} ${theme.dim(text)}';
if (stream.isTTY) {
// Overwrite the previous frame in place instead of erasing the whole
// line first: move to column 0, redraw over the old glyph/text, then
// clear only any trailing leftover from a longer previous frame. A full
// `\x1B[2K` erase before the redraw leaves a momentary blank line that
// reads as flicker on Windows terminals; overwriting avoids it.
stream.cursorTo(0);
stream.write(frame);
stream.clearLine(1);
} else {
stream.write(frame);
}
frameIndex = (frameIndex + 1) % frames.length;
}
if (stream.isTTY && !isCI) {
intervalId = (startInterval ?? _startPeriodicTimer)(
const Duration(milliseconds: 80),
render,
);
render();
} else {
stream.write('${theme.dim(syms.bullet)} ${theme.dim(text)}\n');
}
return _SpinnerHandle(
(String msg) {
running = false;
intervalId?.cancel();
writeLine('${theme.success(syms.success)} ${theme.success(msg)}');
},
(String msg) {
running = false;
intervalId?.cancel();
writeLine('${theme.error(syms.error)} ${theme.error(msg)}');
},
(String msg) {
running = false;
intervalId?.cancel();
writeLine('${theme.warn(syms.warn)} ${theme.warn(msg)}');
},
() {
running = false;
intervalId?.cancel();
clear();
},
);
}