ansimax
v1.7.1
Published
Zero-dependency CLI rendering library: colors, gradients, animations, ASCII art, pixel art, components, and themes — all in TypeScript.
Maintainers
Keywords
Readme
The ultimate CLI rendering library for Node.js
Colors • Gradients • Animations • ASCII Art • Pixel Art • Trees • Components • Themes
English · Español
🎬 Preview
🌟 What is Ansimax?
Ansimax is a batteries-included rendering library for building beautiful terminal UIs in Node.js. One package replaces a stack of 8+ dependencies — colors, gradients, ASCII art, spinners, progress bars, tables, menus, trees, themes, pixel art — combined into a single coherent TypeScript API with zero runtime dependencies.
npm install ansimaximport { color, gradient, ascii, loader, sleep } from 'ansimax';
console.log(ascii.banner('hello', {
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9', '#8be9fd']),
}));
const stop = loader.spin('Building project', { color: '#bd93f9' });
await sleep(1500);
stop('Build complete', true);💡 Why Ansimax?
| Without Ansimax | With Ansimax |
|---|---|
| Install 8+ packages: chalk, gradient-string, figlet, ora, cli-progress, cli-table3, boxen, inquirer | One install: ansimax |
| Mix incompatible APIs, different paradigms, conflicting types | Consistent functional API, single source of truth |
| No coherent theme system across packages | Built-in themes (Dracula, Nord, Matrix, Cyberpunk, +5) |
| Manual cursor cleanup, no crash safety | Reference-counted cursor + crash handlers built in |
| No AbortSignal support in most CLI libs | Every animation, loader, and prompt is abortable |
| Each lib brings its own runtime fallback logic | Unified NO_COLOR / FORCE_COLOR / TTY detection |
| No memory bounds on color caches | Bounded LRU caches everywhere (no leaks under load) |
🆚 Comparison with the Node.js ecosystem
Ansimax replaces a stack of popular Node.js libraries with one coherent, typed, zero-dependency package:
| Feature | chalk | gradient-string | ora | cli-progress | figlet | boxen | inquirer | cli-table3 | Ansimax |
|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| Basic + 256 colors | ✅ | — | — | — | — | — | — | — | ✅ |
| Truecolor with adaptive fallback | ✅ | ✅ | — | — | — | — | — | — | ✅ |
| Multi-stop gradients | — | ✅ | — | — | — | — | — | — | ✅ |
| Animated gradients | — | — | — | — | — | — | — | — | ✅ |
| Easing curves (5 presets + custom) | — | — | — | — | — | — | — | — | ✅ |
| Conic gradients (radial sweep) | — | — | — | — | — | — | — | — | ✅ |
| ASCII banners | — | — | — | — | ✅ | — | — | — | ✅ |
| Image → ASCII converter | — | — | — | — | — | — | — | — | ✅ |
| Figlet .flf font parser | — | — | — | — | ✅ (own) | — | — | — | ✅ (250+ fonts) |
| Custom font registry | — | — | — | — | partial | — | — | — | ✅ |
| Boxes with multiple styles | — | — | — | — | — | ✅ | — | — | ✅ (6 styles) |
| Spinners (multiple styles) | — | — | ✅ | — | — | — | — | — | ✅ (11 styles) |
| Animated progress bars | — | — | — | ✅ | — | — | — | — | ✅ |
| Hierarchical/parallel tasks | — | — | — | — | — | — | — | — | ✅ |
| Tables (multi-line, ANSI-aware) | — | — | — | — | — | — | — | ✅ | ✅ |
| Interactive menus + multi-select | — | — | — | — | — | — | ✅ | — | ✅ |
| Trees with cycle detection | — | — | — | — | — | — | — | — | ✅ |
| Split layouts (vsplit/hsplit) | — | — | — | — | — | — | — | — | ✅ (v1.3.0) |
| JSON colored pretty-printer | — | — | — | — | — | — | — | — | ✅ (v1.3.0) |
| CSS Grid (colSpan/rowSpan/areas) | — | — | — | — | — | — | — | — | ✅ (v1.4.1–v1.4.4) |
| Markdown → terminal renderer | — | — | — | — | — | — | — | — | ✅ (v1.4.0–v1.4.4) |
| Syntax highlighting (js/ts/json/bash) | — | — | — | — | — | — | — | — | ✅ (v1.4.5) |
| Pixel art + canvas + sprites | — | — | — | — | — | — | — | — | ✅ |
| Theme system + per-instance isolation | — | — | — | — | — | — | — | — | ✅ |
| AbortSignal everywhere | — | — | partial | — | — | — | partial | — | ✅ |
| NO_COLOR env support | ✅ | partial | partial | — | — | — | — | — | ✅ |
| Stable error codes (ANSIMAX_*) | — | — | — | — | — | — | — | — | ✅ |
| TypeScript-first (strict mode) | partial | partial | ✅ | partial | partial | ✅ | partial | partial | ✅ |
| Zero runtime dependencies | ✅ | — | — | — | — | — | — | — | ✅ |
| ESM + CJS dual export | partial | partial | ✅ | ✅ | partial | ✅ | partial | partial | ✅ |
| Test coverage | ~95% | partial | partial | partial | partial | partial | partial | partial | ~98% (3000+ tests) |
Comparison reflects what each library officially supports at time of writing. Some libraries can be combined to approach ansimax's feature set, but at the cost of bundle size, version-skew bugs, and inconsistent APIs.
📦 Installation
npm install ansimax
# or
pnpm add ansimax
# or
yarn add ansimaxRequirements: Node.js ≥ 18. ESM and CJS both supported. Examples published with the package — see /examples.
⚡ 30-second example
import { color, gradient, loader, ascii, sleep } from 'ansimax';
console.log(ascii.banner('deploy', {
colorFn: (t) => gradient(t, ['#ff6b6b', '#feca57', '#48dbfb']),
}));
const stop = loader.spin('Building project', { color: '#bd93f9' });
await sleep(1500); // simulate async work
stop('Build complete', true); // ✓ + success color
console.log(color.green('✓') + ' Ready in ' + color.bold('1.4s'));🚀 Quick Start
import { configure, color, themes, gradient } from 'ansimax';
// Global configuration
configure({ theme: 'dracula', animationSpeed: 'normal' });
// Basic styling
console.log(color.red('error'));
console.log(color.bold(color.cyan('important')));
// Multi-stop gradient
console.log(gradient('rainbow text', [
'#ff5555', '#ffaa00', '#ffff00',
'#00ff00', '#0099ff', '#cc44ff',
]));
// Switch theme — fires subscribers
themes.use('cyberpunk');
console.log(themes.primary('cyberpunk primary'));✨ Features
- 🎨 Colors — Truecolor / 256 / basic with adaptive fallback. NO_COLOR / FORCE_COLOR / TTY detection
- 🌈 Gradients — Multi-stop linear, radial, diagonal, arbitrary-angle. Custom presets via
registerPreset - 🔠 ASCII Art — Banners (
big/smallfonts), boxes (6 styles), dividers, logos. Stream API + custom font registry - 🖼️ Pixel Art — Sprites, alpha blending, dithered gradients, canvas with dirty-rect rendering, braille mode (2×4 sub-pixel)
- 🌳 Trees — Builder + plain-data API, 4 styles, per-node colors/icons, max-depth, cycle detection, walk/find/map/filter algorithms
- 🎞️ Animations — Typewriter, fade, slide, pulse, wave, glitch, reveal. AbortSignal-aware, reducedMotion mode
- ⏳ Loaders — 11 spinner styles, animated bars, hierarchical/parallel tasks, countdowns, multi-spinner manager
- 🎬 Frames — Sequenced playback with pause/resume/seek, live push-based renderer, drift-corrected timing, morph
- 🧱 Components — Tables (ANSI-aware, multi-line cells), badges, status lines, sections, columns, timelines, interactive menus
- 🎨 Themes — 8 built-ins (Dracula, Nord, Monokai, Cyberpunk, Pastel, Matrix, Ocean, Sunset). Per-instance isolation,
onChangelisteners,bg*helpers - ⚙️ Configure — Centralized config with subscribers, batched updates,
withConfig()temporary overrides, strict mode - 🛠️ Utils — ANSI primitives, cursor control, terminal hyperlinks (OSC 8),
setTitle,safeJson,onResize, debounce/throttle/memoize
📸 Showcase
Colors & Gradients
import { color, gradient, rainbow } from 'ansimax';
// Basic colors
console.log(color.red('red'), color.green('green'), color.blue('blue'));
// Style modifiers
console.log(color.bold('bold'), color.italic('italic'), color.underline('underlined'));
// Multi-stop gradient
console.log(gradient('fire to ocean', ['#ff6b6b', '#feca57', '#48dbfb']));
// Built-in rainbow preset
console.log(rainbow('built-in rainbow preset'));Animated Gradients (v1.2.0)
import { animateGradient, sleep } from 'ansimax';
// Color flow animation — runs until you call stop()
const ctrl = animateGradient('Loading...', ['#ff79c6', '#bd93f9', '#8be9fd'], {
duration: 2000, // ms per cycle
fps: 30,
direction: 'forward', // or 'reverse'
});
await sleep(3000);
ctrl.stop();
// v1.2.2: await directly (no .done needed)
await animateGradient('Done!', ['#50fa7b', '#bd93f9'], {
infinite: false, cycles: 2, duration: 800,
});Easing Curves (v1.2.0)
import { gradient } from 'ansimax';
const stops = ['#ff79c6', '#bd93f9', '#8be9fd'];
// Five built-in easings + custom function support
console.log(gradient('hello world', stops, { easing: 'linear' }));
console.log(gradient('hello world', stops, { easing: 'ease-in' }));
console.log(gradient('hello world', stops, { easing: 'ease-out' }));
console.log(gradient('hello world', stops, { easing: 'ease-in-out' }));
console.log(gradient('hello world', stops, { easing: 'cubic-bezier' }));
// Or pass your own easing function (t → eased t, both in [0,1])
console.log(gradient('hello world', stops, { easing: (t) => t * t * t }));Conic Gradients (v1.2.0)
import { gradientRect } from 'ansimax';
// Radial sweep around center — rainbow wheel
console.log(gradientRect({
width: 30, height: 15,
colors: ['#ff0000', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#ff00ff', '#ff0000'],
style: 'conic',
startAngle: 0, // rotation angle in degrees
dither: 'bayer',
}));Reusable Gradients (v1.2.3)
import { createGradient, reverseGradient, ascii } from 'ansimax';
// Pre-resolve hex stops once — significantly faster for repeated use
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c']);
console.log(fire('First line'));
console.log(fire('Second line'));
console.log(fire('Third line'));
// Use as a colorFn for banners — same ColorFn signature
console.log(ascii.banner('FIRE', { colorFn: fire }));
// v1.2.4: inspect metadata
console.log('Stops:', fire.stops); // → ['#ff5555', '#ffb86c', '#f1fa8c']
console.log('Resolved:', fire.resolvedStops); // → [{r:255,g:85,b:85}, ...]
// v1.2.4: reverse a gradient (preserves default options)
const ice = reverseGradient(fire);
console.log(ice('Cool side'));
// Per-call options still work — perfect for animation
for (let p = 0; p < 1; p += 0.05) {
process.stdout.write('\r' + fire('flowing', { phase: p }));
await new Promise((r) => setTimeout(r, 50));
}ASCII Art
import { ascii, gradient } from 'ansimax';
console.log(ascii.banner('HELLO', {
font: 'big',
align: 'center',
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9']),
}));
console.log(ascii.box('Rainbow box!', { padding: 1, borderStyle: 'rounded' }));Image → ASCII (v1.2.5)
import { ascii } from 'ansimax';
import sharp from 'sharp';
// Get raw RGB pixels from any image library — example using `sharp`.
// You can use jimp, pngjs, canvas, or any decoder. Ansimax stays zero-deps.
const { data, info } = await sharp('./photo.png')
.raw()
.toBuffer({ resolveWithObject: true });
// Convert raw RGB buffer → PixelGrid (a 2D array of { r, g, b } objects)
const pixels = [];
for (let y = 0; y < info.height; y++) {
const row = [];
for (let x = 0; x < info.width; x++) {
const i = (y * info.width + x) * info.channels;
row.push({ r: data[i], g: data[i + 1], b: data[i + 2] });
}
pixels.push(row);
}
// Now use ansimax — multiple ways:
// 1. Monochrome
console.log(ascii.fromImage(pixels, { width: 80 }));
// 2. Color + Floyd-Steinberg dithering + detailed ramp
console.log(ascii.fromImage(pixels, {
width: 100,
color: true,
dither: 'floyd-steinberg',
ramp: 'detailed',
}));
// 3. Edge-detection mode (line art)
console.log(ascii.fromImage(pixels, {
width: 80,
edgeDetect: 'sobel',
edgeThreshold: 50,
ramp: 'blocks',
}));
// 4. Face mode for portraits (boosts midtone contrast)
console.log(ascii.fromImage(pixels, {
width: 60,
ramp: 'detailed',
faceMode: true,
}));Figlet Fonts (v1.2.5)
import { readFileSync } from 'node:fs';
import { parseFiglet, ascii, gradient } from 'ansimax';
// Download fonts from http://www.figlet.org/fontdb.cgi
const font = parseFiglet(readFileSync('./standard.flf', 'utf8'));
console.log(ascii.figletText('Hello!', font));
// With color
console.log(ascii.figletText('STYLE', font, {
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9', '#8be9fd']),
}));Trees
import { tree, color } from 'ansimax';
const project = tree({ label: 'my-app', icon: '📦', color: color.bold });
const src = project.add({ label: 'src', icon: '📁' });
src.addLeaf({ label: 'index.ts', icon: '📄' });
src.addLeaf({ label: 'app.ts', icon: '📄' });
console.log(project.render({
style: 'rounded',
palette: [color.cyan, color.green, color.magenta],
guideColor: color.dim,
}));Pixel Art & Canvas
import { images, createCanvas, gradientRect, SPRITES } from 'ansimax';
// Built-in sprite
console.log(images.sprite('heart'));
// Smooth gradient with Bayer dither
console.log(gradientRect({
width: 50, height: 4,
colors: ['#ff6b6b', '#feca57', '#48dbfb'],
dither: 'bayer',
}));
// Custom canvas
const c = createCanvas(40, 10);
c.fill({ r: 18, g: 18, b: 38 });
c.drawCircle(20, 5, 4, { r: 255, g: 200, b: 0 }, true);
const starSprite = SPRITES.star;
if (starSprite) c.drawSprite(2, 2, starSprite.pixels);
c.print();Components
import { components, color } from 'ansimax';
console.log(components.table([
['Module', 'Status', 'Coverage'],
['colors', color.green('● ready'), '100%'],
['animations', color.green('● ready'), '100%'],
['loaders', color.green('● ready'), '100%'],
], { borderStyle: 'rounded' }));
console.log(components.badge('VERSION', 'v1.7.0'));
console.log(components.badge('BUILD', 'passing'));Timeline
import { components } from 'ansimax';
console.log(components.timeline([
{ label: 'Project init', done: true, time: '10:00' },
{ label: 'Build pipeline', done: true, time: '10:15' },
{ label: 'Run tests', done: false, time: '10:32' },
{ label: 'Deploy to npm', done: false },
]));Loaders & Progress
import { loader, sleep } from 'ansimax';
// Spinner with success/failure
const stop = loader.spin('Loading...', { color: '#bd93f9' });
await sleep(1500);
stop('Done!', true); // ✓ green icon
// Animated progress bar
await loader.progressAnimate(100, 'Downloading', {
color: '#50fa7b', delay: 25,
});
// Hierarchical tasks with parallel execution
await loader.tasks([
{
text: 'Build',
fn: async () => await sleep(500),
subtasks: [
{ text: 'TypeScript', fn: async () => await sleep(800) },
{ text: 'Bundle', fn: async () => await sleep(600) },
],
},
{ text: 'Test', fn: async () => await sleep(700) },
], { parallel: true });Animations
import { animate, gradient, sleep } from 'ansimax';
await animate.typewriter('Welcome to the deployment wizard...', {
speed: 30,
colorFn: (t) => gradient(t, ['#bd93f9', '#ff79c6']),
});
await animate.fadeIn('Loading complete', { duration: 600 });
// Race steps against a timeout — never hang
await animate.parallel([
async () => await sleep(500), // simulated network check
async () => await sleep(700), // simulated database check
async () => await sleep(400), // simulated auth check
], { timeout: 5000 });Themes
import { themes, createTheme } from 'ansimax';
// Built-in themes
themes.use('dracula');
console.log(themes.primary('hello'));
// Listen for changes
const off = themes.onChange((newTheme, oldTheme) => {
console.log(`Theme: ${oldTheme.name} → ${newTheme.name}`);
});
// Multi-tenant: each instance fully isolated
const tenantA = createTheme('nord');
const tenantB = createTheme('matrix');
// Define a custom theme and register it ONLY in tenantA
tenantA.register('custom', {
name: 'Custom',
primary: '#ff5e5e',
secondary: '#5e5eff',
accent: '#5eff5e',
success: '#10b981',
warning: '#fbbf24',
error: '#ef4444',
info: '#06b6d4',
muted: '#6b7280',
bg: '#1e293b',
surface: '#334155',
text: '#f1f5f9',
gradient: ['#ff5e5e', '#5eff5e', '#5e5eff'],
});
console.log('tenantA themes include custom?', tenantA.list().includes('custom'));
console.log('tenantB themes include custom?', tenantB.list().includes('custom'));
// ↑ false — full isolationPanels — Split Layouts (v1.3.0)
import { panels, ascii } from 'ansimax';
// Side-by-side columns
const left = ascii.box('Sidebar', { borderStyle: 'rounded' });
const right = ascii.box('Main view', { borderStyle: 'rounded' });
console.log(panels.vsplit([left, right], { gap: 2, align: 'center' }));
// Vertical stacking
console.log(panels.hsplit([
'── Application ──',
ascii.box('Body content'),
'── Footer ──',
], { gap: 1, align: 'center' }));
// Nested — sidebar + main inside an app shell
console.log(panels.hsplit([
'── My App ──',
panels.vsplit([
ascii.box('Sidebar', { width: 20 }),
ascii.box('Main', { width: 40 }),
], { gap: 2 }),
'── End ──',
]));JSON Pretty-print (v1.3.0)
import { json } from 'ansimax';
// Colored, depth-aware pretty-printing
console.log(json.pretty({
name: 'ansimax',
version: '1.3.0',
features: ['colors', 'gradients', 'panels'],
stats: { tests: 2000, coverage: 0.98 },
active: true,
}));
// Depth limit — collapses deep objects to {...}
const deeplyNested = { a: { b: { c: { d: { e: 'too deep' } } } } };
console.log(json.pretty(deeplyNested, { maxDepth: 2 }));
// Item limit — huge arrays show "... (N more)"
const largeArray = Array.from({ length: 50 }, (_, i) => `item_${i}`);
console.log(json.pretty(largeArray, { maxItems: 5 }));
// Circular references handled gracefully
const obj = { name: 'foo' };
obj.self = obj;
console.log(json.pretty(obj)); // → "self": [Circular]📖 Documentation
The docs/ folder contains comprehensive examples for every module:
| Document | Description |
|---|---|
| docs/README.md | Documentation index — start here |
| docs/examples-ts.md | 33 TypeScript examples (3 per module × 11 modules) |
| docs/examples-mjs.md | 33 JavaScript ESM examples |
| docs/examples-cjs.md | 33 JavaScript CommonJS examples |
| docs/showcase.md | Complete demo app combining every module |
Every example is copy-paste runnable with realistic, mid-complexity scenarios — not just one-liners.
📚 Examples
Eleven production-grade examples ship in the npm package and are runnable directly. Find them in /examples once you install:
| File | What it demonstrates |
|---|---|
| 01-quick-smoke.ts | Quick smoke test — verifies every major import works |
| 02-colors-gradients.ts | Every color fn, gradient types, presets, compose, chain API |
| 03-ascii-banners.ts | Banners (big/small), 6 box styles, dividers, logo composer |
| 04-trees.ts | Tree builder + plain-data API, 4 styles, palettes, algorithms (walk/find/map/filter) |
| 05-components.ts | Tables, badges, status, sections, columns, timelines, progress bars |
| 06-pixel-art.ts | Sprites, custom canvas, gradient rects with dither, transforms (flip/rotate) |
| 07-animations.ts | typewriter, fadeIn/Out, slide, pulse, wave, glitch, reveal |
| 08-loaders.ts | spinner styles, animated progress, hierarchical tasks, countdown |
| 09-themes.ts | All 8 built-in themes, listeners, custom theme registration, per-instance isolation |
| 10-everything.ts | Comprehensive showcase — every module exercised in one cohesive demo |
| all-in-one.mjs | Full demo in ESM (plain JS with import) — no TypeScript needed |
| all-in-one.cjs | Full demo in CommonJS (plain JS with require) — no TypeScript needed |
Run any example with:
# TypeScript examples
npx tsx examples/10-everything.ts
# Plain JS — ESM
node examples/all-in-one.mjs
# Plain JS — CommonJS
node examples/all-in-one.cjs🎯 Use Cases
- CLI installers & scaffolders — beautiful first-run experience (create-react-app, create-next-app style)
- DevOps tools — deployment dashboards, build pipelines, health monitors
- Dev experience — better test runners, lint output, error formatting
- Interactive prompts — menus, confirmations, multi-select wizards
- Data exploration — tables, trees, charts for terminal-first workflows
- Status reporters — real-time progress, multi-task orchestration
- ASCII intros — game launchers, demo splash screens, login banners
⚙️ Configuration
Global config affects every module that respects it (colors, themes, animation speed, etc.):
import { configure, getConfig, withConfig, onConfigKeyChange } from 'ansimax';
configure({
colorMode: 'auto', // 'none' | 'basic' | '256' | 'truecolor' | 'auto'
animationSpeed: 'normal', // 'slow' | 'normal' | 'fast' | 'instant'
theme: 'dracula', // any registered theme
reducedMotion: false,
});
// Listen for changes (per-key — avoids over-firing)
const off = onConfigKeyChange('theme', (newTheme, oldTheme) => {
console.log(`Theme: ${oldTheme} → ${newTheme}`);
});
// Temporary override + auto-restore on completion or throw
await withConfig({ animationSpeed: 'fast' }, async () => {
// ...your fast-mode code here...
});
// Strict mode catches config typos
// configure({ unknwnKey: 'x' }, { strict: true }); // throws RangeError⚠️ Error codes
Several ansimax functions throw Error / TypeError / RangeError for invalid input.
Catching by error code is the stable, recommended way to handle them programmatically — message text may evolve, but .code values are guaranteed semver-stable.
import { themes, ascii, parseFiglet } from 'ansimax';
try {
themes.use('inexistent-theme');
} catch (e) {
if (e.code === 'ANSIMAX_UNKNOWN_THEME') {
themes.use('dracula'); // fallback
} else {
throw e; // re-throw unexpected errors
}
}All error codes
| Code | Thrown by | Type | When |
|---|---|---|---|
| ANSIMAX_INVALID_THEME | themes.register | TypeError | Theme value is not a plain object |
| ANSIMAX_INVALID_THEME_NAME | themes.register | TypeError | Theme has missing/empty name |
| ANSIMAX_UNKNOWN_THEME | themes.use | RangeError | Requested theme name not registered |
| ANSIMAX_INVALID_FONT_NAME | ascii.registerFont | TypeError | Empty or non-string font name |
| ANSIMAX_RESERVED_FONT_NAME | ascii.registerFont | Error | Overwriting built-in font without { force: true } |
| ANSIMAX_INVALID_FIGLET_INPUT | parseFiglet | TypeError | Non-string or empty .flf content |
| ANSIMAX_INVALID_FIGLET_HEADER | parseFiglet | TypeError | First line is not a valid FIGfont header |
| ANSIMAX_INVALID_FIGLET_HEIGHT | parseFiglet | TypeError | Header declared zero/negative height |
🧩 Ecosystem packages
The ansimax ecosystem is structured in two tiers — companion packages that extend the core, and independent evolutions that target different platforms.
@ansimax/* — Companion packages
Scoped packages that extend ansimax without breaking its zero-dependency promise. Each is published independently but shares ansimax's philosophy and naming.
| Package | Status | Description |
|---|:-:|---|
| ansimax | ✅ stable | Terminal-rendering core. Zero dependencies. |
| @ansimax/image | 🟡 planned | Image-to-ASCII loader — PNG/JPEG/WebP from file/buffer/URL |
| @ansimax/cli | 🟡 planned | Standalone binary — npx @ansimax/cli demo, font browser, image converter |
| @ansimax/fonts | 🟡 planned | 250+ figlet .flf fonts pre-bundled, ready to use |
| @ansimax/sprites | 🔴 future | Curated sprite library (animals, UI icons, technical diagrams) |
| @ansimax/video | 🔴 future | Video frame extraction → ASCII playback |
| @ansimax/themes-extra | 🔴 future | Community-contributed themes pack |
How they connect:
┌─────────────────────────────┐
│ ansimax (zero deps) │ ← core, you always install
│ • colors, ASCII, panels │
│ • types: PixelGrid, etc. │
└────────────┬────────────────┘
│ peer dependency
┌────────────────────┼────────────────────┐
▼ ▼ ▼
@ansimax/image @ansimax/cli @ansimax/fonts
(deps: jimp) (binary) (data only)Each companion declares "ansimax": "^X.Y.Z" as peerDependency — semver-coordinated, never duplicated, never out of sync.
ansimax-* — Independent evolutions
Standalone projects that build alongside ansimax for different platforms. Not companions — these are separate identities with their own scope and release cycle.
| Package | Status | Description |
|---|:-:|---|
| ansimax-native | 🔴 future | Rust + TS rewrite of the rendering hot path. Native performance via napi-rs. Same API surface as ansimax. |
| ansimax-web | 🔴 future | Browser rendering layer. ANSI → HTML/CSS conversion + canvas rendering. For demos, docs sites, web terminals. |
Sub-ecosystems: each of these can have their own scoped sub-packages (@ansimax-native/image, @ansimax-web/canvas, etc.) over time.
Why two naming conventions?
Industry convention used by many mature ecosystems (Babel, Vue, Webpack, etc.):
@scope/*= "same project family, coordinated release, same team"name-*= "inspired by / works alongside, independent identity"
By using both, ansimax signals:
- The core (
ansimax) stays small, zero-dep, focused on terminal rendering - The ecosystem (
@ansimax/*) grows around it as opt-in extensions - Evolutions (
ansimax-native,ansimax-web) explore different platforms without compromising the core
💡 Coming soon: When
@ansimax/imageor similar packages are released, this section will link to them. Want one of these built sooner? Open an issue to vote.
🛣️ Roadmap
Ansimax is being built toward a full terminal rendering platform — a Node-native answer to what Python developers get from rich + textual combined, with Node-specific improvements where it matters.
The roadmap intentionally targets — and aims to surpass — gaps that even mature Python TUI libraries haven't fully solved: live-diff renderers, animated gradients, terminal image protocols, and a true reactive layer.
✅ Phase 1 — Core foundation
- [x] Styling engine — ANSI 16 / 256 / truecolor with adaptive fallback
- [x] Hex + RGB helpers with clamping and validation
- [x]
NO_COLOR/FORCE_COLORenv support + non-TTY auto-detection - [x]
AbortSignalintegration across animations and loaders - [x]
compose()style stacking with single-reset emission - [x] Bounded LRU escape cache (512 entries, packed-RGB keyed)
- [x] Custom preset registry (
registerPreset,listPresets)
✅ Phase 2 — Gradient engine
- [x] Linear gradients (multi-stop)
- [x] Rainbow + 6 built-in presets
- [x] Radial gradients (in
gradientRect) - [x] Diagonal gradients
- [x] Arbitrary-angle gradients
- [x] Bayer 4×4 dithering for smooth tonal transitions
- [x] Single-stop UX (CSS-style behavior)
- [x] Animated gradients — color flow over time with
animateGradient()(v1.2.0) - [x] Gradient interpolation curves —
linear/ease-in/ease-out/ease-in-out/cubic-bezier/ custom (v1.2.0) - [x] Conic gradients — radial sweep with
style: 'conic'(v1.2.0) - [x] Mirror gradients — symmetric
A→B→C→B→Afill viamirror(v1.4.13) - [x] Interpolation space —
rgb/hsl/oklabcolor-space blending (v1.4.13) - [x] 16 named gradient presets —
viridis,plasma,pastel,cyberpunk, and more (v1.6.0) - [x]
presetStops()+gradientRect({ preset })— reuse named preset colors anywhere (v1.6.1) - [x]
gradientScale()— sample a gradient into N discrete palette colors (v1.6.3)
✅ Phase 3 — ASCII engine
- [x] Block fonts (
big,small) - [x] Banner with gradient + alignment + per-char coloring
- [x] Box drawing (6 border styles)
- [x] Divider with style variants
- [x] Logo composer (gradient + box wrapping)
- [x] Custom font registry (
registerFont,hasFont,listFonts) - [x] Stream API (
ascii.stream()with AbortSignal) - [x] Image → ASCII converter —
ascii.fromImage()with luminance mapping (v1.2.5) - [x] Color ASCII rendering — preserve image colors via
color: true(v1.2.5) - [x] Image dithering — Floyd-Steinberg error diffusion (v1.2.5)
- [x] 4 dithering algorithms — Floyd-Steinberg, Atkinson, JJN, Sierra (v1.6.2)
- [x] Face-optimized ASCII — histogram stretching for portraits (v1.2.5)
- [x] Figlet font support —
.flfparser + renderer (parseFiglet+ascii.figletText) (v1.2.5) - [x] Edge detection — Sobel operator integrated in
fromImage(v1.2.5, bonus) - [x] ASCII ramp registry —
registerAsciiRamp+ named presets (v1.4.13)
✅ Phase 4 — Terminal UI primitives
- [x] Tables (irregular rows, multi-line cells, ANSI-aware)
- [x] Boxes with multiple styles
- [x] Status messages + badges (with border option)
- [x] Timelines with done/pending states
- [x] Interactive menus (single + multi-select)
- [x] Columns layout (truncate/wrap overflow)
- [x] Sections (gradient headers with auto-width)
- [x] Trees (collapsible, max-depth, cycle-safe)
- [x] Panels — split layouts:
hsplit,vsplitwith alignment + nesting (v1.3.0) - [x] Balanced table wrap — minimum-raggedness word wrap for even cell lines (v1.6.2)
- [x] JSON/YAML pretty-printing — colored, depth-limit, circular-safe (v1.3.0)
- [x] Grid system — CSS Grid-inspired:
colSpan,rowSpan(mark-and-pack),flow,cellWidth/cellHeight, andgridAreastemplate areas (v1.4.1–v1.4.4) - [x] Markdown rendering — headings (ATX + setext), lists (nested + task lists), code blocks, tables, blockquotes, inline styles, CommonMark escapes, autolinks, reference links, footnotes, HTML blocks (v1.4.0–v1.4.11)
- [x] Markdown theme registry — custom palettes via
registerMarkdownTheme(v1.4.11) - [x] Syntax highlighting — built-in grammars for JS/TS/JSON/Bash with aliases (v1.4.5)
- [x] Layouts — CSS Grid (
grid/gridAreas) + flexbox-style flow (flexwith justify + grow, v1.4.7) - [x] Logging integration — createLogger with levels, fields, child loggers, transports + console/pino/winston shims (v1.4.12)
✅ Phase 5 — Cursor & screen control
- [x] Cursor visibility, save/restore, positioning, line navigation
- [x] Screen clearing (line, area, full)
- [x] Reference-counted cursor (overlapping calls safe)
- [x] Crash-safe restore (exit/SIGINT/SIGTERM handlers)
- [x] Terminal hyperlinks (OSC 8)
- [x] Window title (OSC 2)
- [x] Bell (BEL)
- [x] Managed alternate screen —
createScreen(alt buffer, guaranteed restore on crash) — TUI foundation (v1.7.0)
✅ Phase 6 — Animation engine
- [x] Typewriter, fadeIn, fadeOut, slide, pulse, wave, glitch, reveal
- [x] All
AbortSignal-aware - [x]
reducedMotionmode for accessibility - [x] Frame morph (text → text interpolation, cinematic decryption)
- [x]
parallel()with timeout - [x] Signal propagation to nested animations
- [x] Easing functions library (31 standard easings: cubic, elastic, bounce, back) (v1.3.5)
- [x] Animation composition (
parallel + sequence + delayDSL) (v1.5.0) - [x] Spring physics animations (
react-springstyle) (v1.5.0) - [x] Tween engine (interpolate any value type) (v1.5.0)
✅ Phase 7 — Progress ecosystem
- [x] Spinners (11 styles) with color + AbortSignal
- [x] Animated progress bars
- [x] Multi-task runners (sequential + parallel)
- [x] Countdown timers
- [x] Multi-spinner manager (stacked concurrent spinners)
- [x] Hierarchical tasks (parent + subtasks rollup)
- [x] Live ETA estimation (rolling average) (v1.6.0)
- [x] Live refresh diff renderer (no flicker, only redraw changed lines) (v1.6.0)
- [x] Progress groups (named groups with shared theme) (v1.6.1)
- [x] Throughput meters (bytes/sec, ops/sec with auto-scaling units) (v1.6.0)
🟡 Phase 8 — Capability detection
- [x] TTY detection (auto-disable in pipes/CI)
- [x]
NO_COLOR/FORCE_COLORenv support - [x] Color depth detection (16 / 256 / truecolor)
- [x] CI provider detection (GitHub Actions, CircleCI, GitLab, Buildkite, Drone, Travis)
- [x] Terminal program detection (iTerm, vscode, WezTerm, Hyper, Apple_Terminal)
- [x] Windows Terminal detection (
WT_SESSION) - [x] Unicode width detection (CJK halfwidth/fullwidth, emoji clusters, ZWJ sequences) (v1.6.5)
- [x] Image protocol detection — detection only, not an encoder: reports which protocol the terminal advertises so callers can plug in their own; ansimax itself renders via the universal path (v1.6.4)
- [ ] Terminal capability database (full xterm capability flags + version probes)
- [x] Font metrics detection — cell aspect ratio (
cellAspectRatio,aspectScale) for pixel-accurate sub-cell layouts; overridable, defaults to the near-universal 0.5 (v1.7.1)
🟡 Phase 9 — Advanced rendering
- [x] Dirty-rectangle canvas (only redraw changed pixels)
- [x] Bounded LRU caches (escape sequences, render cache, ANSI cache)
- [x] Drift-corrected timing (animations stay locked to wall-clock)
- [ ] Diff renderer (line-level damage tracking for full UIs)
- [ ] Virtual buffer (compose UI without writing to stdout)
- [ ] Z-index / layering (overlap panels with priority)
- [ ] Mouse event support (click, hover, drag, scroll wheel)
- [ ] Keyboard event abstraction (arrow keys, modifiers, key sequences, dead keys)
- [ ] Full TUI framework (reactive components — Textual-equivalent for Node)
🔴 Phase 10 — Terminal charts
- [~] Bar charts (horizontal + vertical, grouped, stacked) — horizontal bar + histogram (v1.6.6)
- [x] Line charts (with braille for sub-character resolution) —
lineChart, 8× sub-pixel, multi-series, Wu-style per-cell coverage color (v1.7.1) - [x] Sparklines (inline mini-charts for status bars) (v1.6.6)
- [ ] Area charts (filled with gradients)
- [ ] Heatmaps (color-mapped 2D grids)
- [ ] Pie / donut charts (with percentage labels)
- [ ] Scatter plots
- [ ] Box plots / candlestick charts
- [ ] Real-time streaming charts (live data feed with rolling window)
- [ ] Plot composer (multi-chart dashboards with shared axes)
🔴 Phase 11 — Forms & Input
- [ ] Text input prompts (with autocomplete + history)
- [ ] Password prompts (masked, strength meter)
- [ ] Confirm dialogs (yes/no with default highlight)
- [ ] Numeric input (with min/max validation)
- [ ] Date/time pickers (calendar widget)
- [ ] File picker (filesystem navigator)
- [ ] Form composer (multi-field with validation + error display)
- [ ] Wizard flows (multi-step forms with back/forward, progress indicator)
🔴 Phase 12 — Image & media
Philosophy: ansimax renders images through universal, self-generated
methods that work in any terminal — never by depending on a proprietary
protocol. Kitty/iTerm/Sixel are treated as detection (Phase 8); emitting
them is optional and left to the caller. renderImageAuto already picks the
best portable method automatically.
- [x] Universal auto-render —
renderImageAutopicks half-blocks (color) or ASCII (no color) by capability (v1.6.7) - [x] Half-block rendering —
▀/▄with fg/bg color, double vertical resolution, any truecolor terminal - [ ] Sixel encoder (open DEC format — pure algorithm, portable to any Sixel terminal)
- [x] Perceptual quantization —
rgbTo256Perceptual/nearestPerceptualvia Oklab ΔE (kills banding vs RGB L2) (v1.7.0) - [ ] PNG/JPEG decode → pixel grid (feed into the universal renderers)
- [ ] QR code generation (with size + ECC level options)
- [ ] Bar code generation (Code 128, EAN-13)
- [ ] (detection-only, opt-in) iTerm2 / Kitty encoders — caller-supplied, never the default
🔴 Phase 13 — Plugin system
- [ ] Plugin API for custom components
- [ ] Theme marketplace
- [ ] Custom font registration via npm packages
- [ ] Community animations registry
- [ ] Capability provider interface (plug in custom detectors)
- [ ] Renderer plugins (swap stdout for any writable stream)
🔴 Phase 14 — Reactivity layer (TUI framework)
- [ ] Component lifecycle (mount/unmount/update hooks)
- [ ] Reactive state (auto re-render on data change, signals or hooks)
- [ ] Virtual DOM diffing (line-level updates)
- [ ] Event bus (component communication)
- [ ] Application loop (single render tree with full lifecycle)
- [ ] Routing (multi-screen apps with history)
- [ ] DevTools integration (inspect component tree, mark changed nodes)
- [ ] CSS-in-TS styling (scoped styles per component)
🔵 Phase 15 — AXSS / AXB styling pipeline (ecosystem)
A dedicated stylesheet language for terminal UIs (AXSS — Ansi eXtended
Style Sheets) that compiles to a portable binary format (AXB — Ansi
Binary) consumed by ansimax-native. The design is frozen;
implementation waits on ansimax-native (there is no engine to consume AXB
yet, so this is deliberately not rushed).
- [ ] AXSS language — high-level terminal stylesheets (
Button { color: yellow; padding: 1 2; }) with type / id / multi-class / state selectors - [ ] AXB binary format — portable, versioned, little-endian bytecode; magic + format version + minimum engine version + CRC; extensible sections
- [ ] Declarative model — AXB describes state per selector, not imperative instructions; the engine decides when each state applies
- [ ] Compile-time cascade & specificity — resolved by the compiler (
id > class > type, ties → last rule wins); the runtime receives flattenedStyleStates - [ ] Stable ID tables —
PropertyId/StateId/ColorKindare part of the spec with explicit values, never reordered or recycled - [ ] Color encoding — named / ANSI-256 / RGB tri-form with a discriminant byte
- [ ]
ansimax axb inspect— a disassembler/debugger born alongside the compiler (with optional debug metadata:background: BLUE ← inherited from Button) - [ ] Multiple frontends (later) — CSS / TOML / YAML / JSON / XML all producing the same AST → the same AXB, making AXB the engine's universal format
Note: AXSS is the human-readable frontend; AXB is the compact binary the engine consumes.
.axbis bytecode — low-level relative to AXSS, but high-level relative to the hardware (closer to WASM/SPIR-V than to machine code). It is designed to be shipped with your app, like a compiled shader.
Legend: ✅ Complete · 🟡 Partial · 🔴 Planned · 🔵 Designed (awaiting ansimax-native)
🧪 Testing
npm test # Run all 3000+ tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportCoverage (as of v1.3.0):
| Metric | Score | |---|:-:| | Statements | ~98% | | Branches | ~95% | | Functions | ~99% | | Lines | ~99% | | Total tests | 2,000+ | | Test suites | 27 | | CI matrix | Node 18, 20, 22, latest | | Platforms tested | Linux, macOS, Windows |
🛠️ Requirements
- Node.js ≥ 18
- TypeScript ≥ 5.0 (for typed consumption — optional)
- Terminal with truecolor support recommended (Windows Terminal, iTerm2, WezTerm, Kitty, modern xterm). Gracefully degrades to 256 / 16 / no-color.
🏗️ Project Structure
ansimax/
├── src/
│ ├── colors/ Color rendering + gradient engine
│ ├── themes/ Theme system + 8 built-ins
│ ├── ascii/ Banners, boxes, fonts
│ ├── animations/ Typewriter, fade, slide, pulse, wave, glitch, reveal
│ ├── loaders/ Spinners, progress, tasks, multi-loader
│ ├── frames/ Sequenced playback + live renderer + morph
│ ├── components/ Tables, badges, status, timelines, menus
│ ├── images/ Sprites, canvas, dithered gradients
│ ├── trees/ Tree builder + algorithms
│ ├── utils/ ANSI primitives + helpers
│ └── configure.ts Global config + subscribers
├── examples/ 10 examples (TS) + 2 (JS — ESM & CJS) — all features covered
└── __tests__/ 27 test suites, 3000+ tests📝 Changelog
v1.7.0 — Perceptual color, managed screen, spline gradients, lap timing
A larger minor. Highlights:
- 🎨 Perceptual quantization —
rgbTo256Perceptual,nearestPerceptual,oklabDistance(Oklab ΔE — kills banding vs RGB L2) - 🖥️ Managed alternate screen —
createScreen(vim/htop-style full-screen with guaranteed restore) — TUI foundation - 🌈 Spline gradients —
gradientColorSpline(Catmull-Rom C¹, smooth through multi-stops) - ⏱️ Lap stopwatch —
createStopwatch(labelled splits,report(),slowest()) - 🧪 +60 tests
import { createScreen, rgbTo256Perceptual, gradientColorSpline, createStopwatch } from 'ansimax';
await createScreen().run((s) => { s.moveTo(1,1); s.write('Full-screen app'); });
rgbTo256Perceptual(128, 64, 32); // nearest xterm-256 by perceptual ΔE
gradientColorSpline(stops, 0.25); // smooth multi-stop sampling
const sw = createStopwatch(); sw.lap('load'); // profile stagesDrop-in replacement for 1.6.7.
v1.6.7 — Universal image auto-render + cubic-bezier easing + event counter
- 🖼️
renderImageAuto— picks half-blocks (color) or ASCII (no color) by capability; portable, no proprietary encoders - 📐
cubicBezier(x1,y1,x2,y2)— CSS-style easing factory (Newton–Raphson solve; supports overshoot) - 🔢
createCounter— event counter with EMA rate, total, and lifetime average - 🧪 +50 tests
import { renderImageAuto, cubicBezier, createCounter } from 'ansimax';
const { output, method } = renderImageAuto(pixels); // 'halfblock' | 'ascii'
const ease = cubicBezier(0.25, 0.1, 0.25, 1); // CSS "ease"
const c = createCounter(); c.tick(); c.formatRate(); // "1.2K/s"On image protocols: ansimax renders via universal, self-generated methods (half-blocks / ASCII) that work in any terminal. Sixel/Kitty/iTerm are detection only — surfaced so callers can plug in their own encoder, never emitted by default.
Drop-in replacement for 1.6.6.
v1.6.6 — Inline charts (Phase 10 begins) + stepped easings
- 📊 Inline charts —
sparkline,bar,histogram(also thechartnamespace) - 🪜 Stepped easings —
steps(n, position),stepStart,stepEnd(CSSsteps()style) - 〰️ Smoothstep easings —
smoothStep,smootherStep(shader S-curves) - 🧪 +45 tests
import { sparkline, bar, histogram, steps } from 'ansimax';
sparkline([1, 5, 2, 8, 3, 7, 9, 4]); // '▁▅▂▇▃▆█▄'
bar(0.66, { width: 12 }); // eighth-cell precision
histogram([{ label: 'GET', value: 1240 }, { label: 'POST', value: 430 }]);
steps(4); // staircase easingDrop-in replacement for 1.6.5.
v1.6.5 — Unicode width detection + ETA smoothing + numeric table alignment
- 📏 Unicode width detection —
stringWidth,isFullWidth,isEmoji,isCombining - 📊 Rate formatters —
formatPercent,formatRate("1.5 MB/s","1.2K req/s") - 📈 ETA EMA smoothing —
createETA({ smoothing: 'ema' })reacts faster to speed changes - 🔢 Auto-aligned numeric columns —
ascii.table({ autoAlignNumbers: true }) - 🧪 +40 tests
import { stringWidth, formatRate, createETA, ascii } from 'ansimax';
stringWidth('中文'); // 4
formatRate(1572864); // "1.5 MB/s"
createETA({ total: 1000, smoothing: 'ema' }); // faster reaction
ascii.table(rows, { autoAlignNumbers: true }); // numbers right-alignedDrop-in replacement for 1.6.4.
v1.6.4 — Image protocol detection + elapsed timer + colors refactor
- 🖼️ Image protocol detection —
detectImageProtocol()→'kitty'/'iterm'/'sixel'/'none' - ⏱️
createTimer— elapsed-time stopwatch (start/stop/reset, pause-aware) - 🧹 Colors refactor — shared core moved to
colors/internal.ts; public API byte-for-byte identical - 🧪 +40 tests
import { detectImageProtocol, supportsInlineImages, createTimer } from 'ansimax';
detectImageProtocol(); // 'kitty' | 'iterm' | 'sixel' | 'none'
supportsInlineImages(); // boolean
const t = createTimer();
t.start(); /* ...work... */ t.stop();
t.formatted(); // "1.5s"Drop-in replacement for 1.6.3.
v1.6.3 — Refactor: split animations + contrast/a11y + gradient scale
- 🧹 Animations split — the ~1,100-line module is now 5 focused files; public API byte-for-byte identical
- ♿ WCAG contrast helpers —
relativeLuminance,contrastRatio,readableTextColor,meetsContrast - 🎨
gradientScale()— sample a gradient into N discrete palette colors - 🧪 +30 tests
import { contrastRatio, readableTextColor, gradientScale } from 'ansimax';
contrastRatio({ r: 0, g: 0, b: 0 }, { r: 255, g: 255, b: 255 }); // 21 (max)
readableTextColor({ r: 255, g: 235, b: 59 }); // black on yellow
gradientScale(['#ff0000', '#0000ff'], 3); // ['#ff0000','#800080','#0000ff']Drop-in replacement for 1.6.2.
v1.6.2 — Advanced algorithms: dithering, balanced wrap, statistics
- 🖼️ 4 dithering algorithms —
floyd-steinberg,atkinson,jjn,sierra(error-diffusion kernels) - 📐 Balanced table wrap — minimum-raggedness word wrap (
balancedWrap: true) for even cell lines - 🧮 7 math helpers —
median,variance,stddev,percentile,quantize,catmullRom,gaussian - 🧪 +45 tests
import { ascii, balancedWrap, catmullRom, percentile } from 'ansimax';
ascii.fromImage(pixels, { dither: 'atkinson' }); // crisp, high contrast
ascii.table(rows, { wrap: true, balancedWrap: true }); // even wrapped lines
balancedWrap('I am a very long sentence here', 11); // minimum raggedness
catmullRom(0, 10, 20, 30, 0.5); // → 15 (smooth spline)
percentile([1, 2, 3, 4, 5], 50); // → 3Drop-in replacement for 1.6.1.
v1.6.1 — Phase 7 complete (progress groups) + gradient preset reuse
- 📊
createProgressGroup— several named bars under one title/theme, flicker-free (completes Phase 7) - 🎨
presetStops(name)+hasPreset(name)— reuse a preset's colors anywhere - 🖼️
gradientRect({ preset })— fill a rectangle from a named preset - 🧪 +45 tests
import { createProgressGroup, presetStops, gradientRect } from 'ansimax';
const group = createProgressGroup({ title: 'Deploying' });
group.add('api', 'API').add('web', 'Web');
group.update('api', 0.4, '12 MB/s');
group.complete('web');
group.render();
gradient('text', presetStops('viridis'), { mirror: true });
gradientRect({ width: 40, height: 10, preset: 'plasma' });Drop-in replacement for 1.6.0.
v1.6.0 — Phase 7 progress meters + more gradient presets
- ⏱️
createETA— rolling-average time-remaining estimator (eta(),rate(),progress()) - 📊
createThroughput— bytes/sec or ops/sec with auto-scaling units ("1.5 MB/s") - 🖥️
createLiveRegion— flicker-free multi-line region: redraws only changed lines - 🎨 8 new gradient presets —
viridis,plasma,pastel,cyberpunk,mono,mint,dusk,cotton(16 total) - 🧪 +35 tests
import { createETA, createThroughput, createLiveRegion } from 'ansimax';
const eta = createETA({ total: 1000 });
eta.update(downloaded);
console.log(eta.eta()); // "3.2s"
const tp = createThroughput({ unit: 'bytes' });
tp.update(bytesSoFar);
console.log(tp.format()); // "1.5 MB/s"
const region = createLiveRegion();
region.render(['A: 10%', 'B: 0%']);
region.render(['A: 100%', 'B: 0%']); // only line A rewrittenDrop-in replacement for 1.5.2.
v1.5.2 — Documentation audit: every example verified copy-paste-runnable
- ✅ All 52 README examples executed against the real package and fixed (missing imports, self-contained snippets)
- ✅ docs/examples-{mjs,cjs,ts}.md — 33/33 blocks each pass (ran ESM/CJS, type-checked TS); fixed 8 real TS API/type bugs
- 🎬 showcase.md rewritten as one cohesive enterprise app ("Stardust Deploy") combining every module — not isolated snippets
- 📝 No library code changed — documentation-only quality release
Drop-in replacement for 1.5.1.
v1.5.1 — Tween/spring quality-of-life: repeat, yoyo, callbacks, stagger
- 🔁
repeat+yoyo— loop a tween N times (orInfinity), alternating direction - 📣
onStart/onComplete— lifecycle callbacks ontweenandspring(not fired on abort) - 🎞️
stagger()— cascade a list of animations, each offset bygap × index - 🧪 +22 tests
- 🔵 Roadmap: declared the frozen AXSS → AXB styling pipeline (Phase 15, awaiting
ansimax-native)
import { tween, stagger } from 'ansimax';
const draw = (v) => process.stdout.write(`\r${v.toFixed(2)}`);
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 1500);
await tween({ from: 0, to: 1, duration: 200, repeat: Infinity, yoyo: true, onUpdate: draw, signal: ctrl.signal });
// stagger a list — each row's animation starts 80ms after the previous
const rows = [{ setOpacity(v) {} }, { setOpacity(v) {} }, { setOpacity(v) {} }];
await stagger(rows.map((row) => (s) =>
tween({ from: 0, to: 1, duration: 200, onUpdate: (v) => row.setOpacity(v), signal: s })
), 80);Drop-in replacement for 1.5.0.
v1.5.0 — Phase 6 closure: tween engine, spring physics, composition DSL
- 🎬 Tween engine —
tween()interpolates numbers, arrays, and objects over time with easing - 🌱 Spring physics —
spring()react-spring-style (stiffness/damping/mass) - 🔗 Composition DSL —
sequence,parallel,delay,tweenStep,springStep - 🧪 +40 tests · Phase 6 complete
import { tween, spring, sequence, delay } from 'ansimax';
// `draw` is your render callback — here it just prints the current value
const draw = (v) => process.stdout.write(`\r${Math.round(v)} `);
await tween({ from: 0, to: 100, duration: 1000, easing: 'easeOutCubic', onUpdate: draw });
await spring({ from: 0, to: 100, config: { stiffness: 210, damping: 20 }, onUpdate: draw });
await sequence([
(s) => tween({ from: 0, to: 100, duration: 300, onUpdate: draw, signal: s }),
delay(200),
(s) => tween({ from: 100, to: 0, duration: 300, onUpdate: draw, signal: s }),
]);All AbortSignal- and reducedMotion-aware. Drop-in replacement for 1.4.13.
v1.4.13 — Phase 2 & 3 improvements: mirror gradients, HSL interpolation, ramp registry
- 🔁 Mirror gradients —
mirror: truereflects the palette (A→B→C→B→A) ongradient,createGradient,gradientRect - 🎨 Interpolation space —
interpolation: 'rgb' | 'hsl' | 'oklab'for vivid or perceptually-smooth blends - 🖼️ ASCII ramp registry —
registerAsciiRamp+ 3 new presets (minimal,thermal,hearts) - 🧪 +27 tests
import { gradient, registerAsciiRamp, ascii } from 'ansimax';
gradient('symmetric', ['#ff0000', '#00ff00', '#0000ff'], { mirror: true });
gradient('vivid', ['#ff0000', '#0000ff'], { interpolation: 'hsl' });
registerAsciiRamp('retro', ' .oO0');
// `pixels` is a 2D array of { r, g, b } — here a tiny 2×2 sample
const pixels = [
[{ r: 0, g: 0, b: 0 }, { r: 255, g: 255, b: 255 }],
[{ r: 128, g: 128, b: 128 }, { r: 0, g: 0, b: 0 }],
];
console.log(ascii.fromImage(pixels, { ramp: 'retro' }));Drop-in replacement for 1.4.12.
v1.4.12 — Logging integration + Phase 4/2 improvements
- 🪵 Logging —
createLogger()with 7 levels, structured fields, child loggers, pluggable transports - 🔌 Drop-in shims —
asConsole,pinoShim,winstonTransport - 🖊️ Markdown
==highlight==— GFM/Obsidian mark support - 📋
ascii.tablecaption — dimmed, centered note below the table - 🧪 +40 tests
import { createLogger } from 'ansimax';
const log = createLogger({ level: 'debug', name: 'api' });
log.info('server started', { port: 3000 });
log.child({ reqId: 'abc' }).debug('handling request');Phase 4 is now complete (logging was the final item). Drop-in replacement for 1.4.11.
v1.4.11 — Phase 4 closure: theme registry, footnotes, HTML blocks
- 🎨 Custom markdown themes —
registerMarkdownTheme(name, palette), validated at registration - 📌 Footnotes —
[^label]+[^label]: text, numbered by first reference (GFM behavior) - 🏷️ HTML blocks —
htmlMode: 'strip' | 'raw' | 'hide'(CommonMark §4.6) - 🧪 +38 tests
import { registerMarkdownTheme, markdown } from 'ansimax';
registerMarkdownTheme('solarized', {
h1: ['#b58900', '#cb4b16'], h2: '#cb4b16', h3: '#d33682',
h4: '#6c71c4', h5: '#268bd2', h6: '#2aa198',
code: '#b58900', codeBlockBorder: '#586e75', link: '#268bd2',
blockquote: '#586e75', hr: '#586e75', tableHeader: '#cb4b16',
});
markdown.render('# Title\n\nCited[^a].\n\n[^a]: The note.', { theme: 'solarized' });Phase 4 is now complete. Drop-in replacement for 1.4.10.
v1.4.10 — ascii module split + table minColWidth
- 🧩
ascii/module split — 1575-lineindex.ts→ 9 focused modules (types/fonts/render/shapes/image/figlet/stream/table/index) - 📏
ascii.tableminColWidth— floor on column shrinking so narrow columns stay legible
import { ascii } from 'ansimax';
const rows = [
['Name', 'Role', 'Location'],
['Ada Lovelace', 'Engineer', 'London'],
['Alan Turing', 'Researcher', 'Manchester'],
];
console.log(ascii.table(rows, { maxWidth: 30, minColWidth: 4 }));
// No column shrinks below 4 visible charsThe module split is pure rearrangement — the ascii namespace and all exports are 100% backward-compatible. The dependency graph is strictly layered and acyclic. Drop-in replacement for 1.4.9.
v1.4.9 — Table cell wrapping + coverage hardening
- 📝
ascii.tablecell wrapping —wrap: trueword-wraps long cells to multiple lines instead of truncating - 🧪 Coverage hardening — closed remaining v1.4.8 branch gaps (reachable → tests, unreachable → documented
istanbul ignore)
import { ascii } from 'ansimax';
ascii.table([
['id', 'description'],
['1', 'a fairly long description that wraps neatly across several lines'],
], { maxWidth: 40, wrap: true });Column sizing runs first (water-filling), then cells wrap within the resulting widths. Rows grow to fit their tallest cell. wrap: false (default) keeps v1.4.8 ellipsis behavior.
Drop-in replacement for 1.4.8.
v1.4.8 — Grids, tables, wrapping + scroll regions
Four additive features, zero breaking changes:
- 📊
ascii.table— auto-layout tables with 6 border styles + water-filling column sizing - 🔲
panels.wrap— flex-wrap-style block flow (greedy bin-packing) - 📐
gridper-cell align —cellAlign: ['start', 'center', 'end'] - 🖥️
cursor.scrollRegion+cursor.batch— DECSTBM scroll regions + atomic writes (Phase 5) - 🧪 +36 tests
import { ascii, panels, cursor } from 'ansimax';
// Auto-sized table (widest column shrinks first when over budget)
console.log(ascii.table([
['Name', 'Role', 'Commits'],
['Ada', 'Author', '1200'],
], { align: ['left', 'left', 'right'], maxWidth: 40 }));
// Wrap cards to fit the terminal width
const cards = ['A', 'B', 'C', 'D'].map((c) => ascii.box(c, { padding: 1 }));
console.log(panels.wrap(cards, { maxWidth: 60, gapX: 2, gapY: 1 }));
// Pinned header/footer with a scrolling body
process.stdout.write(cursor.scrollRegion(2, 23));Drop-in replacement for 1.4.7.
v1.4.7 — Reference links + flexbox layout
Two roadmap items, zero breaking changes:
- 🔗 Markdown reference links —
[text][ref],[text][],[shortcut]+[ref]: url "title"definitions - 📐
panels.flex— flexbox-style layout withjustify(start/end/center/between/around/evenly) +growweights - 🧪 +30 tests
import { panels, ascii, markdown } from 'ansimax';
const [boxA, boxB, boxC] = ['A', 'B', 'C'].map((c) => ascii.box(c, { padding: 1 }));
// Flexbox layout
console.log(panels.flex([boxA, boxB, boxC], { width: 60, justify: 'between' }));
console.log(panels.flex([boxA, boxB], { width: 40, grow: [3, 1] })); // A grows 3× as much
// Reference links
markdown.render(`
See [the docs][docs] and [shortcut].
[docs]: https://docs.example.com "Documentation"
[shortcut]: https://example.com
`);Both build on v1.4.6 foundations (link placeholders + distribute). Every flex justify strategy conserves total width exactly.
Drop-in replacement for 1.4.6.
v1.4.6 — Consolidation v4 + math toolkit + autolinks
Maintenance + feature release. Zero breaking changes:
- 🧹
HEX_REconsolidated — 5 duplicate copies → 1 (isHexColor) - 🔢 New
utils/mathtoolkit —lerp,smoothstep,mod,gcd,distribute, and more (15 pure functions) - 🔗 Markdown autolinks —
<https://…>and bare URLs render as terminal hyperlinks - 🧪 +44 tests
import { smoothstep, distribute, mod } from 'ansimax';
smoothstep(0, 1, 0.25); // → 0.15625 (Hermite easing)
distribute(10, 3); // → [4, 3, 3] (sums exactly to 10)
mod(-1, 4); // → 3 (true modulo)
import { markdown } from 'ansimax';
markdown.render('Docs at https://example.com'); // clickable linkDrop-in replacement for 1.4.5.
v1.4.5 — Panels refactor + syntax highlighting
Two big improvements, zero breaking changes:
- 🎨 Syntax highlighting for code blocks (js/ts/json/bash with aliases)
- 📁 Panels module refactored from 1116-line monolith → 7 focused submodules
- 🧩
griddecomposed into 3 pure phases (resolve → pack → render) - ➕ 3 new exports:
highlightCode,tokenizeCode,isHighlightSupported - 🧪 +50 tests
import { markdown } from 'ansimax';
console.log(markdown.render(`
\`\`\`js
const greet = (name) => \`Hello, \${name}!\`;
\`\`\`
`));
// Renders with keyword highlighting (const), string coloring, etc.Supported languages: js/javascript/jsx, ts/typescript/tsx, json, bash/sh/shell/zsh.
Direct API:
import { highlightCode, tokenizeCode, isHighlightSupported } from 'ansimax';
highlightCode('const x = 42;', 'js'); // ANSI-colored string
tokenizeCode('const x = 42;', 'js'); // [{ kind, text }, ...]
isHighlightSupported('rust'); // falseDrop-in replacement for 1.4.4.
v1.4.4 — Grid areas + task lists + setext headings
Patch release finishing the Phase 4 roadmap:
- 🎨
panels.gridAreas— CSS Grid-style template areas with rectangle validation - ✅ Task lists in markdown:
- [ ]and- [x](GFM syntax) - 📄 Setext headings in markdown:
text\n===(h1) andtext\n---(h2) - 🧪 +27 tests for all new features
import { panels, markdown, ascii } from 'ansimax';
// CSS Grid template areas
panels.gridAreas(
{ header: 'HEAD', sidebar: 'SIDE', main: 'MAIN', footer: 'FOOT' },
{
areas: [
['header', 'header', 'header'],
['sidebar', 'main', 'main' ],
['footer', 'footer', 'footer'],
],
}
);
// Task lists render as [ ] / [✓]
markdown.render(`
- [ ] pending
- [x] done
- regular item
`);
// Setext headings work identically to # H1 / ## H2
markdown.render(`
Title
=====
Subtitle
--------
`);Rectangle validation catches non-contiguous areas at compile time:
Error: areas: "foo" is not a rectangle — cells at [0,0]..[1,1]
have 3 occurrences, expected 4Drop-in replacement for 1.4.3.
v1.4.3 — Grid rowSpan + markdown escapes + nested lists
Patch release with 3 substantial new features:
- 🎯
panels.gridrowSpan — CSS Grid-style multi-row layouts using mark-and-pack algorithm - ⛓️ CommonMark escapes in markdown:
\*,\_,\,\\,\[,\],\~ - 🌳 Nested lists in markdown — recursive indentation-based sublists with depth-aware bullets
- 🧪 +27 tests for all new features
import { panels, ascii, markdown } from 'ansimax';
const [sidebar, top, bottom, side2] = ['nav', 'top', 'bottom', 'aside']
.map((c) => ascii.box(c, { padding: 1 }));
// CSS Grid-style layout
console.log(panels.grid([sidebar, top, bottom, side2], {
columns: 3,
colSpan: [1, 2, 2, 1],
rowSpan: [2, 1, 1, 1],
cellHeight: 4,
}));
// Escape literal markdown characters
markdown.render('Show \\*literal asterisks\\*');
// Nested lists
markdown.render(`
- Outer
- Nested
- Deep
`);Note: Block.type === 'list' items are now ListItem[] (was string[]).
Only affects external code using parseBlocks directly; markdown.render users are unaffected.
v1.4.2 — Internal consolidation v3
Patch release continuing v1.3.7's DRY work. Zero behavior changes:
- ➕
ensureString(value)— coerce to string (null/undefined → '') - ➕
clampNonNeg(n, fallback?)— non-negative integer with safe fallback - ➕
clampPositiveInt(n, fallback?)— positive integer (≥ 1) with safe fallback - 🧹 Removed 10 duplicate inline implementations across components, frames, loaders, trees
- 🧪 +18 tests for the consolidated helpers
import { ensureString, clampNonNeg, clampPositiveInt } from 'ansimax';
ensureString(null) // → ''
clampNonNeg(-3) // → 0
clampPositiveInt(0) // → 1 (clamped up)
clampPositiveInt(NaN, 10) // → 10 (fallback)Drop-in replacement for 1.4.1.
v1.4.1 — Grid v2 + markdown refactor
Patch release. Zero breaking changes:
- 🎯
panels.grid— colSpan: per-block column span (CSS Grid-style auto-flow) - 📏
panels.grid— cellHeight: uniform row heights (complementscellWidth) - 🔀
panels.grid— flow:'row'(default) or'column'auto-flow direction - 📁
markdownrefactored from 522-line monolith → 5 focused submodules (API unchanged) - 🌳 Submodule imports enabled:
import { parseBlocks } from 'ansimax/markdown/block-parser' - 🧪 +32 tests
import { panels, ascii } from 'ansimax';
const [header, sidebar, con