ansimax 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +324 -0
- package/README.es.md +148 -47
- package/README.md +147 -46
- package/dist/index.js +39 -8
- package/dist/index.mjs +39 -8
- package/examples/01-quick-smoke.ts +121 -0
- package/examples/02-colors-gradients.ts +108 -0
- package/examples/03-ascii-banners.ts +81 -0
- package/examples/04-trees.ts +117 -0
- package/examples/05-components.ts +96 -0
- package/examples/06-pixel-art.ts +116 -0
- package/examples/07-animations.ts +68 -0
- package/examples/08-loaders.ts +98 -0
- package/examples/09-themes.ts +90 -0
- package/examples/10-everything.ts +133 -0
- package/examples/all-in-one.cjs +210 -0
- package/examples/all-in-one.mjs +203 -0
- package/examples/tsconfig.json +18 -18
- package/package.json +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **ansimax** are documented in this file.
|
|
4
|
+
This project follows [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [1.1.2] — maturity & robustness
|
|
7
|
+
|
|
8
|
+
Patch release focused on maturity: better error semantics, defensive
|
|
9
|
+
defaults, and cleaner type re-exports. No API breaking changes — every
|
|
10
|
+
1.1.1 program runs identically.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **CI: `jest.config.js` syntax error.** The config file used `export default {}`
|
|
15
|
+
(ESM syntax), which crashed in Node CommonJS context — including in
|
|
16
|
+
GitHub Actions runners. Fixed by switching to `module.exports = {}` to
|
|
17
|
+
match `useESM: false` in the ts-jest configuration. Tests now run
|
|
18
|
+
correctly across Linux, macOS, and Windows runners.
|
|
19
|
+
|
|
20
|
+
### Improved
|
|
21
|
+
|
|
22
|
+
- **`process.setMaxListeners` defensive bump.** Ansimax modules
|
|
23
|
+
(`animations`, `frames`, `loaders`, `utils/ansi`) each register
|
|
24
|
+
`SIGINT` / `SIGTERM` / `exit` handlers for crash-safe cursor
|
|
25
|
+
restoration. With Node's default cap of 10, hot-reload setups
|
|
26
|
+
(Vite HMR, nodemon, ts-node-dev) could occasionally emit
|
|
27
|
+
`MaxListenersExceededWarning`. We now bump the cap to 20 on first
|
|
28
|
+
install — silently and safely, only if the current limit is lower.
|
|
29
|
+
Production apps unaffected.
|
|
30
|
+
- **Uniform `TypeError` for theme validation.** `themes.register()`
|
|
31
|
+
now throws `TypeError` for any structural / type issue (missing
|
|
32
|
+
fields, non-string `name`, invalid hex), matching the rest of the
|
|
33
|
+
validation surface. Previously it threw a mix of `Error` and
|
|
34
|
+
`TypeError`, which made `try / catch` filtering inconsistent.
|
|
35
|
+
- **`themes.use()` throws `RangeError`** for unknown theme names
|
|
36
|
+
(was `Error`). `RangeError` better reflects "value out of allowed
|
|
37
|
+
set" semantics — same standard library convention as `Array(-1)`.
|
|
38
|
+
Error message now also says "Available themes:" instead of
|
|
39
|
+
"Available:" for clarity.
|
|
40
|
+
- **Cleaner type re-exports in the barrel.** Added a header comment
|
|
41
|
+
explaining the legacy aliases (`stripAnsiColors`, `stripAnsiCodes`)
|
|
42
|
+
and recommending `stripAnsi` for new code. Version string in the
|
|
43
|
+
barrel header updated from the stale `v1.0.0` to `v1.1.2`.
|
|
44
|
+
|
|
45
|
+
### Notes
|
|
46
|
+
|
|
47
|
+
- All 1848 tests pass; 4 new tests cover the error-type guarantees.
|
|
48
|
+
- The error-type changes are technically observable via `instanceof`
|
|
49
|
+
checks, but `RangeError` and `TypeError` both extend `Error`, so any
|
|
50
|
+
`catch (e: Error)` block keeps working. We classify this as a
|
|
51
|
+
non-breaking quality-of-life improvement.
|
|
52
|
+
- No new dependencies — still zero runtime deps.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [1.1.1] — bug fixes + improved examples
|
|
57
|
+
|
|
58
|
+
Patch release with two bug fixes from real-world testing of v1.1.0, plus
|
|
59
|
+
a cleaner set of examples covering every public API.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **`box()` no longer crashes with object padding.** Previously, calling
|
|
64
|
+
`box(text, { padding: { x: 2, y: 1 } })` threw `RangeError: Invalid
|
|
65
|
+
array length` because the code assumed `padding` was always a number.
|
|
66
|
+
Now non-numeric padding falls back to the default (`1`) gracefully.
|
|
67
|
+
The fix also covers `NaN`, `Infinity`, strings, and other malformed
|
|
68
|
+
input.
|
|
69
|
+
- **`components.menu()` cursor restoration on abrupt exit.** Previously,
|
|
70
|
+
killing the process while a menu was active (Ctrl+C, kill signal)
|
|
71
|
+
left the terminal cursor hidden because the cleanup handler only ran
|
|
72
|
+
through the normal menu lifecycle. Now `SIGINT`, `SIGTERM`, and
|
|
73
|
+
`exit` events trigger an emergency cursor restoration, so the terminal
|
|
74
|
+
is always left in a sane state.
|
|
75
|
+
|
|
76
|
+
### Changed — Examples
|
|
77
|
+
|
|
78
|
+
- Replaced all examples in `/examples` with a clean set covering every
|
|
79
|
+
public API:
|
|
80
|
+
- `01-quick-smoke.ts` — verifies major imports
|
|
81
|
+
- `02-colors-gradients.ts` — color fns, gradients, `colorPresets`
|
|
82
|
+
- `03-ascii-banners.ts` — banners + 6 box styles + dividers
|
|
83
|
+
- `04-trees.ts` — builder + plain-data + 4 styles + algorithms
|
|
84
|
+
- `05-components.ts` — tables, badges, status, timeline, etc.
|
|
85
|
+
- `06-pixel-art.ts` — sprites + canvas + transforms
|
|
86
|
+
- `07-animations.ts` — typewriter, fade, slide, pulse, wave, glitch
|
|
87
|
+
- `08-loaders.ts` — spinners + tasks + countdown
|
|
88
|
+
- `09-themes.ts` — 8 themes + listeners + isolation
|
|
89
|
+
- `10-everything.ts` — comprehensive showcase
|
|
90
|
+
- `all-in-one.mjs` — ESM (`import`) version, no TypeScript
|
|
91
|
+
- `all-in-one.cjs` — CommonJS (`require`) version, no TypeScript
|
|
92
|
+
- All examples now import from `'ansimax'` (npm registry) instead of
|
|
93
|
+
`'../src/index.js'`, making them copy-pasteable into user projects.
|
|
94
|
+
- READMEs updated with animations + loaders preview GIFs in the
|
|
95
|
+
header, and an `all-ansimax.gif` showcase near the footer.
|
|
96
|
+
|
|
97
|
+
### Notes
|
|
98
|
+
|
|
99
|
+
- No API changes — `1.1.1` is a drop-in replacement for `1.1.0`.
|
|
100
|
+
- No new dependencies — still zero runtime deps.
|
|
101
|
+
- All 1848 tests still pass.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## [1.1.0] — comprehensive hardening + new features
|
|
106
|
+
|
|
107
|
+
A massive robustness pass across every module, plus a new `trees` module,
|
|
108
|
+
new API surfaces, and broader test coverage (~1700+ tests across 16 suites).
|
|
109
|
+
|
|
110
|
+
**Backwards compatibility:** 100% preserved. All existing APIs work
|
|
111
|
+
identically — only defensive validation, new features, and bug fixes.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### Added — Trees (`trees/index.ts`) — NEW MODULE
|
|
116
|
+
|
|
117
|
+
Hierarchical text renderer inspired by Rich's `Tree`.
|
|
118
|
+
|
|
119
|
+
- **Builder API** — `tree('root').add('child').add('grandchild')`. `addLeaf()` returns the parent for fluent sibling-adds.
|
|
120
|
+
- **Plain-data API** — `renderTree({ label, children: [...] })` accepts any plain JS object.
|
|
121
|
+
- **4 visual styles** — `'normal'`, `'rounded'` (╰─), `'heavy'` (┣━), `'ascii'` (`+--`). Per-node `style` override mixes styles in one tree.
|
|
122
|
+
- **Per-node colors** — `color: ColorFn` colorizes the label.
|
|
123
|
+
- **Depth-based palette** — `palette: [color1, color2, ...]` cycles colors per depth level. Per-node `color` overrides.
|
|
124
|
+
- **Guide-line colors** — `guideColor` colorizes the `├──`/`│`/`└──` chars separately from labels.
|
|
125
|
+
- **Per-node icons** — `icon: '📁'` renders before the label.
|
|
126
|
+
- **Multi-line labels** — extra lines align with proper continuation glyph. CRLF normalized to LF.
|
|
127
|
+
- **Collapsed subtrees** — `collapse: N` hides the first N children, shows `[+N hidden]`.
|
|
128
|
+
- **Max depth truncation** — `maxDepth` truncates deep trees, shows `[+N more]` markers.
|
|
129
|
+
- **Indent option** — pad the entire tree with N leading spaces.
|
|
130
|
+
- **`renderTreeStream(root, opts)`** — generator that yields one rendered line at a time.
|
|
131
|
+
- **`measureTree(root, opts)`** — `{ width, height }` for layout decisions.
|
|
132
|
+
- **`walkTree(root, visitor)`** — depth-first traversal with cycle detection.
|
|
133
|
+
- **`findInTree(root, predicate)`** — locate first matching node.
|
|
134
|
+
- **`countNodes(root)`** — total node count.
|
|
135
|
+
- **`mapTree(root, fn)`** — transform every node, returns new tree (input untouched).
|
|
136
|
+
- **`filterTree(root, predicate, opts?)`** — keep matching nodes with optional `prune` mode.
|
|
137
|
+
- **Cycle detection** — `walkTree` / `mapTree` throw a clear error on circular references instead of stack overflow.
|
|
138
|
+
- **Strict validation** — non-string labels coerced, null/array root rejected.
|
|
139
|
+
|
|
140
|
+
### Added — Configuration (`configure.ts`)
|
|
141
|
+
|
|
142
|
+
- **`onConfigKeyChange(key, listener)`** — subscribe to changes of a specific config key only. Listener fires with `(newValue, oldValue)`.
|
|
143
|
+
- **`pauseListeners()`/`resumeListeners()`** — batch multiple updates without flooding subscribers; resume flushes a single notification.
|
|
144
|
+
- **`withConfig(overrides, fn)`** — temporarily override config for a sync or async block, restoring previous state automatically (even on throw).
|
|
145
|
+
- **`strict` mode** — `configure(opts, { strict: true })` rejects unknown keys with `RangeError`; useful for catching typos in config files.
|
|
146
|
+
- **`DEFAULTS` exported** — `Object.freeze`d, accessible to consumers as `CONFIG_DEFAULTS`.
|
|
147
|
+
- **No-op detection** — `configure({})` or setting unchanged values no longer fires listeners.
|
|
148
|
+
- **Soft theme fallback** — uses `themes.tryUse` instead of `themes.use` so configure() doesn't throw on themes registered later.
|
|
149
|
+
- **Validation hardening** — `null`/array opts rejected, empty-string `theme`/`locale`/`asciiFont` rejected.
|
|
150
|
+
|
|
151
|
+
### Added — Colors (`colors/index.ts`)
|
|
152
|
+
|
|
153
|
+
- **Adaptive escape cache** — `_fgEscCache` / `_bgEscCache` packed-RGB keyed, bounded LRU (512 entries). Gradient animations now 10–50× faster on repeated colors.
|
|
154
|
+
- **`clearColorCache()`** exported for tests and post-level-change cleanup.
|
|
155
|
+
- **`registerPreset(name, stops)`** — register custom gradient presets accessible via `color.<name>`.
|
|
156
|
+
- **`listPresets()`** — runtime list of available presets.
|
|
157
|
+
- **Reserved-preset guard** — registering presets with names like `bold`, `red`, `gradient` throws with a clear conflict message.
|
|
158
|
+
- **Text coercion** — `color.red(42)` now returns `"\x1b[31m42\x1b[0m"` (chalk/kleur compatibility).
|
|
159
|
+
- **NaN/Infinity-safe RGB** — `Infinity → 255`, `-Infinity → 0`, `NaN → 0` in `clampRgb`/`clamp256`.
|
|
160
|
+
- **`compose` filters non-functions** — `compose(red, null, bold)` works (null silently ignored).
|
|
161
|
+
- **`compose` swallows extractor errors** — user fns that throw on `extractOpen` skipped.
|
|
162
|
+
- **`gradient` single-stop colors statically** — consistent with CSS `linear-gradient` UX.
|
|
163
|
+
- **`gradient` defensive** — null/undefined/empty stops return text unchanged. Non-string text coerced. Grapheme iteration preserves emoji.
|
|
164
|
+
- **Bare `\x1b` literal in gradient** — malformed ANSI doesn't corrupt output.
|
|
165
|
+
|
|
166
|
+
### Added — Themes (`themes/index.ts`)
|
|
167
|
+
|
|
168
|
+
- **Per-instance isolation** — `createTheme()` instances have their own registry. Registering a theme on one no longer leaks into others. Critical for multi-tenant SSR.
|
|
169
|
+
- **`tryUse(name)`** — tolerant theme switch returning `boolean` instead of throwing.
|
|
170
|
+
- **`onChange(listener)`** — subscribe to theme changes, returns unsubscribe. Errors swallowed.
|
|
171
|
+
- **`unregister(name)`** — remove themes, throws if removing the active one.
|
|
172
|
+
- **Background color helpers** — `bgPrimary`, `bgSecondary`, `bgAccent`, `bgSuccess`, `bgWarning`, `bgError`, `bgInfo`, `bgMuted`, `bgSurface`.
|
|
173
|
+
- **`success` color with fallback** — built-ins define it; user themes without `success` fall back to `accent`.
|
|
174
|
+
- **`style(name)` dynamic accessor** — `theme.style('primary')(text)` for config-driven styling. Identity fn for unknown names (no throw).
|
|
175
|
+
- **HEX_RE consistent** — `#` optional, matches colors module.
|
|
176
|
+
- **Strict validation** — `register()` rejects non-string/empty names, null/array defs, non-array gradient, short gradient (< 2 stops).
|
|
177
|
+
- **`BannerOpts` interface** — explicit type instead of fragile `Omit<Parameters<...>>` derivation.
|
|
178
|
+
|
|
179
|
+
### Added — Animations (`animations/index.ts`)
|
|
180
|
+
|
|
181
|
+
- **Crash-safe cursor restore** — `exit/SIGINT/SIGTERM` handlers force-restore cursor even on uncaught exceptions.
|
|
182
|
+
- **Reference-counted `hideCursor`/`showCursor`** — concurrent animations don't reveal the cursor early.
|
|
183
|
+
- **`animate.delay(ms)` helper** — compatible with `sequence`/`chain`. Pause respecting signal.
|
|
184
|
+
- **`animate.parallel({ timeout })`** — race steps against a timeout to prevent hangs.
|
|
185
|
+
- **`animate.parallel` swallows per-step errors** — one failing step doesn't reject the whole Promise.all.
|
|
186
|
+
- **Signal propagation** — parallel steps receive the parent signal.
|
|
187
|
+
- **`wave` with single color** — renders statically with that color (better UX than skip).
|
|
188
|
+
- **`wave` with empty palette** — renders plain.
|
|
189
|
+
- **`reveal` with `steps` option** — scales with text length by default (`Math.min(60, Math.max(10, len*2))`).
|
|
190
|
+
- **`pulse`/`glitch` final write use `safeWriteAsync`** — backpressure-aware.
|
|
191
|
+
- **`safeWriteAsync`/`safeWrite`** wrappers swallow stream errors.
|
|
192
|
+
- **Hooks errors swallowed** — `onFrame`/`onDone`/`onAbort` errors don't break the loop.
|
|
193
|
+
|
|
194
|
+
### Added — ASCII (`ascii/index.ts`)
|
|
195
|
+
|
|
196
|
+
- **Strict input validation** — `ensureString()` throws `TypeError` with clear messages for non-string text.
|
|
197
|
+
- **`ensureFontMap` validates type** — rejects null/array/non-object font maps.
|
|
198
|
+
- **`hasFont(name)`** — check if a font is registered without throwing.
|
|
199
|
+
- **`measure(text, font?, letterSpacing?)`** — get `{ width, height }` without paying full render cost.
|
|
200
|
+
- **`stream(text, { signal })`** — pre-aborted yields nothing; aborted mid-stream stops at next poll.
|
|
201
|
+
- **Cache key uses `\u0001` separator** — eliminates collision risk from font names containing `|`.
|
|
202
|
+
- **Grapheme iteration** in `renderFont` — preserves surrogate pairs and emoji.
|
|
203
|
+
- **`box`/`divider`/`logo` defensive** against -Infinity from `Math.max([])`, width 0, empty text.
|
|
204
|
+
- **`colorEachVisibleChar` bare `\x1b` literal** — non-CSI escapes emitted instead of consumed.
|
|
205
|
+
|
|
206
|
+
### Added — Loaders (`loaders/index.ts`)
|
|
207
|
+
|
|
208
|
+
- **`spin` coerces non-string text/prefix/suffix**.
|
|
209
|
+
- **`spin` clamps NaN interval** to default 80.
|
|
210
|
+
- **`progress` clamps NaN/Infinity percent** to 0.
|
|
211
|
+
- **`progress` empty-char fallback**.
|
|
212
|
+
- **`countdown(NaN)` → 0**, negative → 0.
|
|
213
|
+
- **`tasks(non-array)` → `[]`** instead of crash.
|
|
214
|
+
|
|
215
|
+
### Added — Frames (`frames/index.ts`)
|
|
216
|
+
|
|
217
|
+
- **Reference-counted cursor** — concurrent `play()` + `live()` + animations safe.
|
|
218
|
+
- **`registerCrashHandlers()`** — restore cursor on exit/SIGINT/SIGTERM.
|
|
219
|
+
- **`resetFramesCursorCount()`** exported for tests.
|
|
220
|
+
- **`play(non-array)`** — no-op controller instead of crash.
|
|
221
|
+
- **`play({ repeat: 0 })`** — explicit infinite loop.
|
|
222
|
+
- **`play({ repeat: -N })`** — negative now falls back to 1 (was infinite — dangerous on bad input).
|
|
223
|
+
- **`fps` capped at 60** — prevents CPU saturation with `fps: 9999`.
|
|
224
|
+
- **`generate` swallows per-frame errors** — one bad frame doesn't poison the sequence.
|
|
225
|
+
- **`generate` coerces non-string returns**.
|
|
226
|
+
- **`morph(steps=1)` clamps to 2** — avoids division by zero.
|
|
227
|
+
- **All presets defensive** — width=0 OK, NaN fallback, empty char fallback.
|
|
228
|
+
- **`live` with stop() idempotent** — multiple stops safe via `wasRunning` flag.
|
|
229
|
+
|
|
230
|
+
### Added — Components (`components/index.ts`)
|
|
231
|
+
|
|
232
|
+
- **`progressBar(NaN/Infinity)` → 0%** — defensive numeric inputs.
|
|
233
|
+
- **`progressBar` with single-stop gradient** — colors statically (consistent with `gradient()` UX).
|
|
234
|
+
- **`badge` SGR codes validated** — NaN fg/bg falls back to defaults.
|
|
235
|
+
- **`table(non-array)` → `''`**, filters non-array rows, coerces non-string cells.
|
|
236
|
+
- **`columns(cols<1)` clamps to default** — no longer throws.
|
|
237
|
+
- **`columns(non-array)` → `''`**.
|
|
238
|
+
- **`timeline(non-array)` → `''`**, coerces event labels.
|
|
239
|
+
- **`section(NaN width)` falls back to terminal cols**.
|
|
240
|
+
- **`status({ icon: '' })` omits icon** — coherent with `icon: null`.
|
|
241
|
+
- **`menu([])` returns `MENU_CANCELLED`** instead of throwing (safer for runtime data).
|
|
242
|
+
- **`menu(non-array)` → `MENU_CANCELLED`**.
|
|
243
|
+
- **`menu` cleanup symmetric** — every path that hides cursor also restores it.
|
|
244
|
+
- **`menu` `safeResolve` prevents double-resolve** races.
|
|
245
|
+
|
|
246
|
+
### Added — Images (`images/index.ts`)
|
|
247
|
+
|
|
248
|
+
- **All numeric inputs clamped** — `MAX_DIMENSION = 10000` prevents OOM on `Infinity`.
|
|
249
|
+
- **`renderPixelArt(non-array)` → `''`** instead of crash.
|
|
250
|
+
- **`ensurePixelGrid`** filters malformed rows.
|
|
251
|
+
- **`flipHorizontal`/`flipVertical`/`rotate90` defensive** — non-array input returns `[]`.
|
|
252
|
+
- **`gradientRect` validates colors array** — clear errors for empty/all-invalid.
|
|
253
|
+
- **`gradientRect` single-stop renders solid fill** — better UX.
|
|
254
|
+
- **`gradientRect(Infinity width)` clamps** to MAX_DIMENSION.
|
|
255
|
+
- **`createCanvas(NaN/0)` → 1×1**.
|
|
256
|
+
- **`createCanvas(Infinity)`** clamps to MAX_DIMENSION.
|
|
257
|
+
- **`canvas.set/get` reject non-finite coords** as no-op.
|
|
258
|
+
- **`canvas.drawRect/Circle/Sprite` defensive** against NaN, negative dims, non-array sprites.
|
|
259
|
+
- **`canvas.pixels` getter returns deep clone** — callers can't mutate canvas state.
|
|
260
|
+
- **`canvas.print` with try/catch** — stream torn down doesn't crash.
|
|
261
|
+
- **ANSI cache LRU bounded** at 1024 entries — survives massive color counts.
|
|
262
|
+
- **`Pixel` and `PixelGrid` exported** for typed consumers.
|
|
263
|
+
|
|
264
|
+
### Added — Utils (`utils/ansi.ts`)
|
|
265
|
+
|
|
266
|
+
- **`OSC`, `ST`, `BEL` constants** exported.
|
|
267
|
+
- **`setTitle(text)`** — set terminal window title (OSC 2). Control chars stripped.
|
|
268
|
+
- **`link(text, url)`** — clickable hyperlink (OSC 8). Supported in iTerm2, Terminal.app, WezTerm, Kitty, modern xterm.
|
|
269
|
+
- **`bell()`** — terminal bell.
|
|
270
|
+
- **`cursor.position()`** — query position (CSI 6n).
|
|
271
|
+
- **`cursor.nextLine()`/`prevLine()`** — line-aware navigation.
|
|
272
|
+
- **`screen.clearAll()`** — alias for `clear()`.
|
|
273
|
+
- **`DEFAULT_TERM_COLS = 80`, `DEFAULT_TERM_ROWS = 24`** exported.
|
|
274
|
+
- **`writeAsync({ timeout })`** option — prevents infinite hangs on broken streams.
|
|
275
|
+
- **`OutputBuffer.pushIf(cond, str)`** — conditional append.
|
|
276
|
+
- **`detectColorSupport`** improved — TERM truecolor/24bit detection, 256 substring match, rxvt support, try/catch around `os.release()`.
|
|
277
|
+
- **All numeric inputs clamped** — `cursor.up(NaN)` → min=1, `fgRgb(Infinity, ...)` → 0.
|
|
278
|
+
- **`ensureString` coercion** — all writes accept any input.
|
|
279
|
+
- **`sleep(NaN/negative)` clamped** to 0.
|
|
280
|
+
|
|
281
|
+
### Added — Utils (`utils/helpers.ts`)
|
|
282
|
+
|
|
283
|
+
- **`once(fn)`** — invoke a function exactly once.
|
|
284
|
+
- **`escapeRegex(str)`** — escape regex metacharacters.
|
|
285
|
+
- **`safeJson(value, indent?)`** — JSON.stringify handling BigInt and circular references.
|
|
286
|
+
- **`padBoth(str, width, ch?)`** — pad both sides equally, Unicode-aware.
|
|
287
|
+
- **`nextTick(cb)`** — `setImmediate` fallback to `setTimeout(0)`.
|
|
288
|
+
- **`memoize` with `{ keyFn }` option** — multi-argument memoization.
|
|
289
|
+
- **`onResize` with implicit throttle (50ms default)** — coalesces rapid resize events.
|
|
290
|
+
- **`debounce` with `maxWait` option** — guarantees invocation within window.
|
|
291
|
+
- **`diffLines` with `type: 'added' | 'removed' | 'changed'`** — richer damage tracking.
|
|
292
|
+
- **`gradientColor` auto-clamps `t`** — values outside [0,1] clamped automatically. NaN → 0.
|
|
293
|
+
- **`stripAnsi`/`visibleLen` defensive** against non-string inputs.
|
|
294
|
+
- **`termSize` validates** cols/rows > 0.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### Test infrastructure
|
|
299
|
+
|
|
300
|
+
- **~1700+ tests across 16 suites**, all green.
|
|
301
|
+
- Coverage: ~98% statements, ~95% branches, ~99% functions, ~99% lines.
|
|
302
|
+
- All test files use `FORCE_COLOR=3` + `resetColorSupportCache()` in `beforeEach` for isolation.
|
|
303
|
+
- New test isolation helpers exported: `resetCursorRefCount`, `resetFramesCursorCount`, `resetLoaderCursorCount`, `clearAnsiCache`, `clearThemeColorCache`, `clearColorCache`, `clearRenderCache`, `resetConfig`.
|
|
304
|
+
|
|
305
|
+
### Examples
|
|
306
|
+
|
|
307
|
+
6 production-grade examples in `/examples`:
|
|
308
|
+
|
|
309
|
+
- `01-cli-installer.ts` — npm-create style installer (banner + hierarchical tasks + status icons + summary box).
|
|
310
|
+
- `02-live-dashboard.ts` — real-time dashboard (frames.live + service table + gradient bars + onResize + SIGINT cleanup).
|
|
311
|
+
- `03-pixel-art-game.ts` — bouncing rocket sprite (canvas + alpha blending + sunset gradient + FPS counter + drift-corrected loop).
|
|
312
|
+
- `04-interactive-deploy.ts` — interactive menu + multi-select + loader.multi + createTheme + onConfigChange.
|
|
313
|
+
- `05-tree-visualizations.ts` — filesystem + dependency + JSON + decision trees (4 scenarios, walk + measure bonus).
|
|
314
|
+
- `06-everything-together.ts` — comprehensive showcase touching every module (NEW).
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## [1.0.0] — initial release
|
|
319
|
+
|
|
320
|
+
- Core modules: `color`, `animate`, `ascii`, `loader`, `frames`, `components`, `themes`, `images`, `configure`.
|
|
321
|
+
- TypeScript types exported.
|
|
322
|
+
- Adaptive color rendering (NO_COLOR / FORCE_COLOR / TTY detection).
|
|
323
|
+
- AbortSignal support across all blocking APIs.
|
|
324
|
+
- 750+ tests, 85%+ coverage.
|