New:Socket for Asana Is Now Available.Learn more
Get Started

@cleocode/animations

Package Overview
Dependencies
Maintainers
1
Versions
119
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cleocode/animations

CLEO terminal animations — braille spinners, progress bars, and sparks for the cleo CLI and CleoOS. Forked from gunnargray-dev/unicode-animations (MIT).

latest
Source
npmnpm
Version
2026.8.9
Version published
Weekly downloads
855
385.8%
Maintainers
1
Weekly downloads
 
Created
Source

@cleocode/animations

Unicode terminal animations for the cleo CLI and CleoOS — woven on the LOOM, gated by LAFS.

Provides four primitive surfaces, each gated by a single AnimateContext so the LAFS protocol invariant ("JSON output is the default; human rendering requires explicit opt-in") holds uniformly:

  • 18 generic braille spinners — frame-cycled loaders ported from unicode-animations (MIT, Gunnar Gray)
  • 9 canon spinner aliases — workshop vocabulary (looming, weaving, heartbeat, …) on the same frame data
  • 3 progress bar stylestapestry, cascade, refinery (canon-themed segmented gauges)
  • 4 sparks — one-shot accents (awaken, sweep, cascade, weave)

npm

Install

pnpm add @cleocode/animations
# or
npm install @cleocode/animations

ESM-only. Requires Node ≥ 22.

Quick start

Spinner during async work — createSpinnerHandle

createSpinnerHandle is the canonical owner of \r writes for this package. It manages the timer, hides/restores the cursor, installs an exit handler so Ctrl-C doesn't leave a hidden cursor, and routes everything through the LAFS render gate. Calling process.stdout.write of a string starting with \r outside this package is a contract violation — always go through the handle.

import { resolveOutputFormat } from '@cleocode/lafs';
import { createAnimateContext, createSpinnerHandle } from '@cleocode/animations';

const ctx = createAnimateContext({
  flagResolution: resolveOutputFormat({ humanFlag: true }),
});

const spinner = createSpinnerHandle(ctx, 'looming', 'Weaving tasks…');
spinner.start();
try {
  await doWork();
  spinner.stop('✔ Tapestry complete.');
} catch (err) {
  spinner.stop();
  throw err;
}

Under --json / --quiet / non-TTY / NO_COLOR the handle is a frozen no-op and emits zero output — call sites stay branch-free.

Progress bar with a known ratio

import { renderProgressBar } from '@cleocode/animations';

function tick(done: number, total: number) {
  const ratio = done / total;
  const bar = renderProgressBar('refinery', ratio, 36);
  process.stdout.write(`\r\x1B[2K  ${bar}  ${done}/${total}`);
}

One-shot spark on success

import { sparks } from '@cleocode/animations';

async function playSpark(name: 'awaken' | 'sweep' | 'cascade' | 'weave') {
  const { frames, interval } = sparks[name];
  for (const f of frames) {
    process.stdout.write(`\r\x1B[2K  ${f}`);
    await new Promise(r => setTimeout(r, interval));
  }
  process.stdout.write('\n');
}

await shipRelease();
await playSpark('cascade');

LAFS-aware rendering — AnimateContext

Every primitive routes through an AnimateContext so the package obeys the LAFS protocol uniformly. The context is pure data — no I/O, no timers — derived from the LAFS FlagResolution plus environment signals.

import { resolveOutputFormat } from '@cleocode/lafs';
import { createAnimateContext, createSpinnerHandle } from '@cleocode/animations';

const flags = resolveOutputFormat({ humanFlag: true });
const ctx = createAnimateContext({ flagResolution: flags });

// Hand `ctx` to any primitive — they all become no-ops when `ctx.enabled === false`.
const spinner = createSpinnerHandle(ctx, 'looming', 'Loading…');

if (!ctx.enabled) {
  // ctx.reason ∈ 'format-json' | 'quiet' | 'no-tty' | 'no-color' | 'enabled'
  console.log(`silent: ${ctx.reason}`);
}
SignalSourceEffectreason
format !== 'human'LAFS flagsDisable (machine output)format-json
quiet === trueLAFS flagsDisable (script-friendly)quiet
!isTTYprocess.stdout.isTTYDisable (piped/redirected)no-tty
NO_COLOR setprocess.env.NO_COLORDisable (no-color.org)no-color

SILENT_CONTEXT is exported as a frozen always-disabled context for tests and libraries that want to opt out without constructing a full LAFS resolution.

Spinner registry

Generic — 18 braille loaders

NameFramesIntervalNameFramesInterval
braille1080mscascade1460ms
braillewave8100mscolumns2660ms
dna1280msorbit8100ms
scan1070msbreathe17100ms
rain12100mswaverows1690ms
scanline6120mscheckerboard4250ms
pulse5180mshelix1680ms
snake1680msfillsweep11100ms
sparkle6150msdiagswipe1660ms

Canon — 9 workshop aliases

Canon name→ GenericCleo lore role
loominghelixTwin strands weaving — task on the LOOM
weavingbraillewavePattern threading across columns
heartbeatbreatheOrganic in-out pulse — Hearth presence
awakeningpulseRadial bloom — first dream / cleo init
sweepingscanLeft→right beam — BRAIN integrity Sweep
watchingorbitCircular sentinel — sentient daemon tick
cascadecascadeDiagonal fall — command-success accent
tapestrywaverowsMulti-row sinusoidal — wave of tasks shipping
refinerycolumnsFilling stages — memory promotion pipeline

Canon entries are aliases, not copies — they reference the same Spinner objects as the generic registry. Renaming a generic spinner automatically updates the canon view. The mapping is exposed as CANON_TO_GENERIC and resolveSpinner(name) accepts either form.

Progress bars

renderProgressBar(style, ratio, width) returns a fixed-width string. Three canon styles:

StyleCharactersFeel
tapestry░ ▒ ▓ █Coarse blocks — woven cloth filling cell-by-cell
cascade▏ ▎ ▍ ▌ ▋ ▊ ▉ █1/8 gradient steps — smooth waterfall edge
refinery⠀ ⡀ ⡄ ⡆ ⡇ ⣇ ⣧ ⣷ ⣿Braille block stages — BRAIN memory promotion pipeline

Inputs outside [0, 1] are clamped. width ≤ 0 returns ''.

Sparks — one-shot accents

import { sparks, sparkDurationMs } from '@cleocode/animations';

sparkDurationMs('cascade'); // → ~980ms (frames * interval)
SparkFramesDurationPlayed on
awaken13 × 90ms~1.17scleo init · first dream · sentient wake
sweep7 × 80ms~560msBRAIN integrity sweep complete
cascade14 × 70ms~980msRelease shipped · task complete
weave18 × 70ms~1.26sPlaybook stage transition · CANT directive accepted

Browser demo

A self-contained vitrine page ships at scripts/demo.html. Open it in any browser to preview every spinner, canon alias, progress style, and spark animating live, with API reference tables and code samples.

# From an npm install
open node_modules/@cleocode/animations/scripts/demo.html        # macOS
xdg-open node_modules/@cleocode/animations/scripts/demo.html    # Linux

# From the cleo monorepo checkout
open packages/animations/scripts/demo.html                       # macOS
xdg-open packages/animations/scripts/demo.html                   # Linux

The page is fully self-contained (no build step, no fetch, no node_modules runtime requirement) so it can be emailed, dropped into a slide deck, or hosted as a static asset for design reviews.

Terminal demo

npx cleocode-animations                  # cycle through generic + canon spinners
npx cleocode-animations looming          # preview one spinner (generic OR canon)
npx cleocode-animations spark cascade    # play one spark and exit
npx cleocode-animations progress         # loop through all 3 progress styles

npx cleocode-animations --list           # full listing — spinners + sparks + progress
npx cleocode-animations --list-canon     # canon aliases only
npx cleocode-animations --list-sparks    # sparks only
npx cleocode-animations --list-progress  # progress styles only

API surface

Spinners

ExportType
spinnersRecord<BrailleSpinnerName, Spinner>
canonSpinnersRecord<CanonSpinnerName, Spinner>
CANON_TO_GENERICRecord<CanonSpinnerName, BrailleSpinnerName>
resolveSpinner(name)(string) => Spinner | undefined
gridToBraille(grid)(boolean[][]) => string
makeGrid(rows, cols)(number, number) => boolean[][]
Spinner{ frames: readonly string[]; interval: number }
BrailleSpinnerName · CanonSpinnerNameTS string-literal unions

SpinnerHandle (canonical \r owner)

ExportType
createSpinnerHandle(ctx, name, label, options?)(AnimateContext, name, string, SpinnerHandleOptions?) => SpinnerHandle
SpinnerHandle{ start(); stop(finalLine?); update(label); enabled: boolean }
SpinnerHandleOptions{ delayMs?: number } — defaults to 150

AnimateContext

ExportType
createAnimateContext(input)(AnimateContextInput) => AnimateContext
SILENT_CONTEXTFrozen AnimateContext — always disabled
AnimateContext{ enabled, reason, inputs }
AnimateContextInput{ flagResolution, isTTY?, noColor? }
FlagResolutionLike{ format: 'json' | 'human'; quiet: boolean }

Progress + Sparks

ExportType
progressBarsRecord<ProgressBarStyle, ProgressBarRenderer>
renderProgressBar(style, ratio, width)(style, number, number) => string
ProgressBarStyle'tapestry' | 'cascade' | 'refinery'
sparksRecord<SparkName, Spark>
sparkDurationMs(name)(SparkName) => number
SparkName'awaken' | 'sweep' | 'cascade' | 'weave'

Custom spinners

Every animation here is built from two primitives — compose your own:

import { gridToBraille, makeGrid } from '@cleocode/animations';

const grid = makeGrid(4, 4);
grid[0][0] = true;
grid[1][1] = true;
grid[2][2] = true;
grid[3][3] = true;

console.log(gridToBraille(grid)); // diagonal braille pattern

makeGrid(rows, cols) returns a boolean[][]. Set cells to true to raise braille dots. gridToBraille(grid) packs them into a braille string (2 dot columns per character, U+2800 base).

License

MIT — dual copyright:

  • © 2024 Gunnar Gray (original unicode-animations project)
  • © 2026 CLEO Code (@cleocode/animations fork)

See LICENSE.

Keywords

cleo

FAQs

Package last updated on 20 Aug 2026

Related posts