import type { ColorInput } from "bun"; import type { Accessor } from "solid-js"; import { useClock } from "../animation/clock.ts"; /** * Named glyph-frame presets for {@link Spinner}. Each is the literal sequence * of frames cycled in order; pass one to the `glyphs` prop, or supply your own * `readonly string[]`. * * - `sparkle` — the default: a sparkle that grows then shrinks for a smooth * bounce (uses `*` instead of `✳` off macOS for wider font coverage). * - `line` — the classic `-\|/` ASCII spinner. * - `dots` — a braille spinner (`⠋⠙⠹…`). * - `circle` — a rotating quarter-filled circle (`◐◓◑◒`). */ export const SPINNER_GLYPHS = { sparkle: process.platform === "darwin" ? (["·", "✢", "✳", "✶", "✻", "✽", "✻", "✶", "✳", "✢"] as const) : (["·", "✢", "*", "✶", "✻", "✽", "✻", "✶", "*", "✢"] as const), line: ["-", "\\", "|", "/"] as const, dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const, circle: ["◐", "◓", "◑", "◒"] as const, } satisfies Record; const DEFAULT_BASE = 0x58a6ff; // #58a6ff blue export type SpinnerProps = { /** * Glyph frames cycled in order. Pass a {@link SPINNER_GLYPHS} preset or your * own `readonly string[]`. Default {@link SPINNER_GLYPHS.sparkle}. */ glyphs?: readonly string[]; /** Milliseconds per glyph frame. Default 120. */ glyphInterval?: number; /** * Glyph color. Inside an `Animated`/`Shimmer`/… region the group's paint * filter recolors the glyph instead. Default blue (#58a6ff). */ color?: ColorInput; /** Expose the internal clock for external consumers (e.g. useStalled). */ clockRef?: (time: Accessor) => void; }; /** * A bare animated spinner glyph — just the bouncing glyph in `color`. It needs * no animation awareness: dropped inside an `Animated`/`Shimmer`/`Wave`/`Pulse` * region, the group's paint filter recolors the glyph along with the text * beside it, so an effect sweeps across the spinner and its label as one. * * Stateful coloring (e.g. fading toward red as a stream stalls) belongs to the * caller, not here — wrap the spinner in a `Shimmer`/`Animated` whose colors * animate (see the `stream-spinner` demo), and the paint filter recolors the * glyph along with everything else in the region. */ export function Spinner(props: SpinnerProps) { const time = useClock(50); if (props.clockRef) props.clockRef(time); const frames = () => props.glyphs ?? SPINNER_GLYPHS.sparkle; const interval = () => props.glyphInterval ?? 120; const glyph = () => { const f = frames(); return f[Math.floor(time() / interval()) % f.length] ?? f[0]; }; return ( {glyph()} ); }