ansimax 1.1.0 → 1.1.1
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 +274 -0
- package/README.es.md +69 -13
- package/README.md +68 -12
- package/dist/index.js +27 -1
- package/dist/index.mjs +27 -1
- 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,274 @@
|
|
|
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.1] — bug fixes + improved examples
|
|
7
|
+
|
|
8
|
+
Patch release with two bug fixes from real-world testing of v1.1.0, plus
|
|
9
|
+
a cleaner set of examples covering every public API.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`box()` no longer crashes with object padding.** Previously, calling
|
|
14
|
+
`box(text, { padding: { x: 2, y: 1 } })` threw `RangeError: Invalid
|
|
15
|
+
array length` because the code assumed `padding` was always a number.
|
|
16
|
+
Now non-numeric padding falls back to the default (`1`) gracefully.
|
|
17
|
+
The fix also covers `NaN`, `Infinity`, strings, and other malformed
|
|
18
|
+
input.
|
|
19
|
+
- **`components.menu()` cursor restoration on abrupt exit.** Previously,
|
|
20
|
+
killing the process while a menu was active (Ctrl+C, kill signal)
|
|
21
|
+
left the terminal cursor hidden because the cleanup handler only ran
|
|
22
|
+
through the normal menu lifecycle. Now `SIGINT`, `SIGTERM`, and
|
|
23
|
+
`exit` events trigger an emergency cursor restoration, so the terminal
|
|
24
|
+
is always left in a sane state.
|
|
25
|
+
|
|
26
|
+
### Changed — Examples
|
|
27
|
+
|
|
28
|
+
- Replaced all examples in `/examples` with a clean set covering every
|
|
29
|
+
public API:
|
|
30
|
+
- `01-quick-smoke.ts` — verifies major imports
|
|
31
|
+
- `02-colors-gradients.ts` — color fns, gradients, `colorPresets`
|
|
32
|
+
- `03-ascii-banners.ts` — banners + 6 box styles + dividers
|
|
33
|
+
- `04-trees.ts` — builder + plain-data + 4 styles + algorithms
|
|
34
|
+
- `05-components.ts` — tables, badges, status, timeline, etc.
|
|
35
|
+
- `06-pixel-art.ts` — sprites + canvas + transforms
|
|
36
|
+
- `07-animations.ts` — typewriter, fade, slide, pulse, wave, glitch
|
|
37
|
+
- `08-loaders.ts` — spinners + tasks + countdown
|
|
38
|
+
- `09-themes.ts` — 8 themes + listeners + isolation
|
|
39
|
+
- `10-everything.ts` — comprehensive showcase
|
|
40
|
+
- `all-in-one.mjs` — ESM (`import`) version, no TypeScript
|
|
41
|
+
- `all-in-one.cjs` — CommonJS (`require`) version, no TypeScript
|
|
42
|
+
- All examples now import from `'ansimax'` (npm registry) instead of
|
|
43
|
+
`'../src/index.js'`, making them copy-pasteable into user projects.
|
|
44
|
+
- READMEs updated with animations + loaders preview GIFs in the
|
|
45
|
+
header, and an `all-ansimax.gif` showcase near the footer.
|
|
46
|
+
|
|
47
|
+
### Notes
|
|
48
|
+
|
|
49
|
+
- No API changes — `1.1.1` is a drop-in replacement for `1.1.0`.
|
|
50
|
+
- No new dependencies — still zero runtime deps.
|
|
51
|
+
- All 1848 tests still pass.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## [1.1.0] — comprehensive hardening + new features
|
|
56
|
+
|
|
57
|
+
A massive robustness pass across every module, plus a new `trees` module,
|
|
58
|
+
new API surfaces, and broader test coverage (~1700+ tests across 16 suites).
|
|
59
|
+
|
|
60
|
+
**Backwards compatibility:** 100% preserved. All existing APIs work
|
|
61
|
+
identically — only defensive validation, new features, and bug fixes.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### Added — Trees (`trees/index.ts`) — NEW MODULE
|
|
66
|
+
|
|
67
|
+
Hierarchical text renderer inspired by Rich's `Tree`.
|
|
68
|
+
|
|
69
|
+
- **Builder API** — `tree('root').add('child').add('grandchild')`. `addLeaf()` returns the parent for fluent sibling-adds.
|
|
70
|
+
- **Plain-data API** — `renderTree({ label, children: [...] })` accepts any plain JS object.
|
|
71
|
+
- **4 visual styles** — `'normal'`, `'rounded'` (╰─), `'heavy'` (┣━), `'ascii'` (`+--`). Per-node `style` override mixes styles in one tree.
|
|
72
|
+
- **Per-node colors** — `color: ColorFn` colorizes the label.
|
|
73
|
+
- **Depth-based palette** — `palette: [color1, color2, ...]` cycles colors per depth level. Per-node `color` overrides.
|
|
74
|
+
- **Guide-line colors** — `guideColor` colorizes the `├──`/`│`/`└──` chars separately from labels.
|
|
75
|
+
- **Per-node icons** — `icon: '📁'` renders before the label.
|
|
76
|
+
- **Multi-line labels** — extra lines align with proper continuation glyph. CRLF normalized to LF.
|
|
77
|
+
- **Collapsed subtrees** — `collapse: N` hides the first N children, shows `[+N hidden]`.
|
|
78
|
+
- **Max depth truncation** — `maxDepth` truncates deep trees, shows `[+N more]` markers.
|
|
79
|
+
- **Indent option** — pad the entire tree with N leading spaces.
|
|
80
|
+
- **`renderTreeStream(root, opts)`** — generator that yields one rendered line at a time.
|
|
81
|
+
- **`measureTree(root, opts)`** — `{ width, height }` for layout decisions.
|
|
82
|
+
- **`walkTree(root, visitor)`** — depth-first traversal with cycle detection.
|
|
83
|
+
- **`findInTree(root, predicate)`** — locate first matching node.
|
|
84
|
+
- **`countNodes(root)`** — total node count.
|
|
85
|
+
- **`mapTree(root, fn)`** — transform every node, returns new tree (input untouched).
|
|
86
|
+
- **`filterTree(root, predicate, opts?)`** — keep matching nodes with optional `prune` mode.
|
|
87
|
+
- **Cycle detection** — `walkTree` / `mapTree` throw a clear error on circular references instead of stack overflow.
|
|
88
|
+
- **Strict validation** — non-string labels coerced, null/array root rejected.
|
|
89
|
+
|
|
90
|
+
### Added — Configuration (`configure.ts`)
|
|
91
|
+
|
|
92
|
+
- **`onConfigKeyChange(key, listener)`** — subscribe to changes of a specific config key only. Listener fires with `(newValue, oldValue)`.
|
|
93
|
+
- **`pauseListeners()`/`resumeListeners()`** — batch multiple updates without flooding subscribers; resume flushes a single notification.
|
|
94
|
+
- **`withConfig(overrides, fn)`** — temporarily override config for a sync or async block, restoring previous state automatically (even on throw).
|
|
95
|
+
- **`strict` mode** — `configure(opts, { strict: true })` rejects unknown keys with `RangeError`; useful for catching typos in config files.
|
|
96
|
+
- **`DEFAULTS` exported** — `Object.freeze`d, accessible to consumers as `CONFIG_DEFAULTS`.
|
|
97
|
+
- **No-op detection** — `configure({})` or setting unchanged values no longer fires listeners.
|
|
98
|
+
- **Soft theme fallback** — uses `themes.tryUse` instead of `themes.use` so configure() doesn't throw on themes registered later.
|
|
99
|
+
- **Validation hardening** — `null`/array opts rejected, empty-string `theme`/`locale`/`asciiFont` rejected.
|
|
100
|
+
|
|
101
|
+
### Added — Colors (`colors/index.ts`)
|
|
102
|
+
|
|
103
|
+
- **Adaptive escape cache** — `_fgEscCache` / `_bgEscCache` packed-RGB keyed, bounded LRU (512 entries). Gradient animations now 10–50× faster on repeated colors.
|
|
104
|
+
- **`clearColorCache()`** exported for tests and post-level-change cleanup.
|
|
105
|
+
- **`registerPreset(name, stops)`** — register custom gradient presets accessible via `color.<name>`.
|
|
106
|
+
- **`listPresets()`** — runtime list of available presets.
|
|
107
|
+
- **Reserved-preset guard** — registering presets with names like `bold`, `red`, `gradient` throws with a clear conflict message.
|
|
108
|
+
- **Text coercion** — `color.red(42)` now returns `"\x1b[31m42\x1b[0m"` (chalk/kleur compatibility).
|
|
109
|
+
- **NaN/Infinity-safe RGB** — `Infinity → 255`, `-Infinity → 0`, `NaN → 0` in `clampRgb`/`clamp256`.
|
|
110
|
+
- **`compose` filters non-functions** — `compose(red, null, bold)` works (null silently ignored).
|
|
111
|
+
- **`compose` swallows extractor errors** — user fns that throw on `extractOpen` skipped.
|
|
112
|
+
- **`gradient` single-stop colors statically** — consistent with CSS `linear-gradient` UX.
|
|
113
|
+
- **`gradient` defensive** — null/undefined/empty stops return text unchanged. Non-string text coerced. Grapheme iteration preserves emoji.
|
|
114
|
+
- **Bare `\x1b` literal in gradient** — malformed ANSI doesn't corrupt output.
|
|
115
|
+
|
|
116
|
+
### Added — Themes (`themes/index.ts`)
|
|
117
|
+
|
|
118
|
+
- **Per-instance isolation** — `createTheme()` instances have their own registry. Registering a theme on one no longer leaks into others. Critical for multi-tenant SSR.
|
|
119
|
+
- **`tryUse(name)`** — tolerant theme switch returning `boolean` instead of throwing.
|
|
120
|
+
- **`onChange(listener)`** — subscribe to theme changes, returns unsubscribe. Errors swallowed.
|
|
121
|
+
- **`unregister(name)`** — remove themes, throws if removing the active one.
|
|
122
|
+
- **Background color helpers** — `bgPrimary`, `bgSecondary`, `bgAccent`, `bgSuccess`, `bgWarning`, `bgError`, `bgInfo`, `bgMuted`, `bgSurface`.
|
|
123
|
+
- **`success` color with fallback** — built-ins define it; user themes without `success` fall back to `accent`.
|
|
124
|
+
- **`style(name)` dynamic accessor** — `theme.style('primary')(text)` for config-driven styling. Identity fn for unknown names (no throw).
|
|
125
|
+
- **HEX_RE consistent** — `#` optional, matches colors module.
|
|
126
|
+
- **Strict validation** — `register()` rejects non-string/empty names, null/array defs, non-array gradient, short gradient (< 2 stops).
|
|
127
|
+
- **`BannerOpts` interface** — explicit type instead of fragile `Omit<Parameters<...>>` derivation.
|
|
128
|
+
|
|
129
|
+
### Added — Animations (`animations/index.ts`)
|
|
130
|
+
|
|
131
|
+
- **Crash-safe cursor restore** — `exit/SIGINT/SIGTERM` handlers force-restore cursor even on uncaught exceptions.
|
|
132
|
+
- **Reference-counted `hideCursor`/`showCursor`** — concurrent animations don't reveal the cursor early.
|
|
133
|
+
- **`animate.delay(ms)` helper** — compatible with `sequence`/`chain`. Pause respecting signal.
|
|
134
|
+
- **`animate.parallel({ timeout })`** — race steps against a timeout to prevent hangs.
|
|
135
|
+
- **`animate.parallel` swallows per-step errors** — one failing step doesn't reject the whole Promise.all.
|
|
136
|
+
- **Signal propagation** — parallel steps receive the parent signal.
|
|
137
|
+
- **`wave` with single color** — renders statically with that color (better UX than skip).
|
|
138
|
+
- **`wave` with empty palette** — renders plain.
|
|
139
|
+
- **`reveal` with `steps` option** — scales with text length by default (`Math.min(60, Math.max(10, len*2))`).
|
|
140
|
+
- **`pulse`/`glitch` final write use `safeWriteAsync`** — backpressure-aware.
|
|
141
|
+
- **`safeWriteAsync`/`safeWrite`** wrappers swallow stream errors.
|
|
142
|
+
- **Hooks errors swallowed** — `onFrame`/`onDone`/`onAbort` errors don't break the loop.
|
|
143
|
+
|
|
144
|
+
### Added — ASCII (`ascii/index.ts`)
|
|
145
|
+
|
|
146
|
+
- **Strict input validation** — `ensureString()` throws `TypeError` with clear messages for non-string text.
|
|
147
|
+
- **`ensureFontMap` validates type** — rejects null/array/non-object font maps.
|
|
148
|
+
- **`hasFont(name)`** — check if a font is registered without throwing.
|
|
149
|
+
- **`measure(text, font?, letterSpacing?)`** — get `{ width, height }` without paying full render cost.
|
|
150
|
+
- **`stream(text, { signal })`** — pre-aborted yields nothing; aborted mid-stream stops at next poll.
|
|
151
|
+
- **Cache key uses `\u0001` separator** — eliminates collision risk from font names containing `|`.
|
|
152
|
+
- **Grapheme iteration** in `renderFont` — preserves surrogate pairs and emoji.
|
|
153
|
+
- **`box`/`divider`/`logo` defensive** against -Infinity from `Math.max([])`, width 0, empty text.
|
|
154
|
+
- **`colorEachVisibleChar` bare `\x1b` literal** — non-CSI escapes emitted instead of consumed.
|
|
155
|
+
|
|
156
|
+
### Added — Loaders (`loaders/index.ts`)
|
|
157
|
+
|
|
158
|
+
- **`spin` coerces non-string text/prefix/suffix**.
|
|
159
|
+
- **`spin` clamps NaN interval** to default 80.
|
|
160
|
+
- **`progress` clamps NaN/Infinity percent** to 0.
|
|
161
|
+
- **`progress` empty-char fallback**.
|
|
162
|
+
- **`countdown(NaN)` → 0**, negative → 0.
|
|
163
|
+
- **`tasks(non-array)` → `[]`** instead of crash.
|
|
164
|
+
|
|
165
|
+
### Added — Frames (`frames/index.ts`)
|
|
166
|
+
|
|
167
|
+
- **Reference-counted cursor** — concurrent `play()` + `live()` + animations safe.
|
|
168
|
+
- **`registerCrashHandlers()`** — restore cursor on exit/SIGINT/SIGTERM.
|
|
169
|
+
- **`resetFramesCursorCount()`** exported for tests.
|
|
170
|
+
- **`play(non-array)`** — no-op controller instead of crash.
|
|
171
|
+
- **`play({ repeat: 0 })`** — explicit infinite loop.
|
|
172
|
+
- **`play({ repeat: -N })`** — negative now falls back to 1 (was infinite — dangerous on bad input).
|
|
173
|
+
- **`fps` capped at 60** — prevents CPU saturation with `fps: 9999`.
|
|
174
|
+
- **`generate` swallows per-frame errors** — one bad frame doesn't poison the sequence.
|
|
175
|
+
- **`generate` coerces non-string returns**.
|
|
176
|
+
- **`morph(steps=1)` clamps to 2** — avoids division by zero.
|
|
177
|
+
- **All presets defensive** — width=0 OK, NaN fallback, empty char fallback.
|
|
178
|
+
- **`live` with stop() idempotent** — multiple stops safe via `wasRunning` flag.
|
|
179
|
+
|
|
180
|
+
### Added — Components (`components/index.ts`)
|
|
181
|
+
|
|
182
|
+
- **`progressBar(NaN/Infinity)` → 0%** — defensive numeric inputs.
|
|
183
|
+
- **`progressBar` with single-stop gradient** — colors statically (consistent with `gradient()` UX).
|
|
184
|
+
- **`badge` SGR codes validated** — NaN fg/bg falls back to defaults.
|
|
185
|
+
- **`table(non-array)` → `''`**, filters non-array rows, coerces non-string cells.
|
|
186
|
+
- **`columns(cols<1)` clamps to default** — no longer throws.
|
|
187
|
+
- **`columns(non-array)` → `''`**.
|
|
188
|
+
- **`timeline(non-array)` → `''`**, coerces event labels.
|
|
189
|
+
- **`section(NaN width)` falls back to terminal cols**.
|
|
190
|
+
- **`status({ icon: '' })` omits icon** — coherent with `icon: null`.
|
|
191
|
+
- **`menu([])` returns `MENU_CANCELLED`** instead of throwing (safer for runtime data).
|
|
192
|
+
- **`menu(non-array)` → `MENU_CANCELLED`**.
|
|
193
|
+
- **`menu` cleanup symmetric** — every path that hides cursor also restores it.
|
|
194
|
+
- **`menu` `safeResolve` prevents double-resolve** races.
|
|
195
|
+
|
|
196
|
+
### Added — Images (`images/index.ts`)
|
|
197
|
+
|
|
198
|
+
- **All numeric inputs clamped** — `MAX_DIMENSION = 10000` prevents OOM on `Infinity`.
|
|
199
|
+
- **`renderPixelArt(non-array)` → `''`** instead of crash.
|
|
200
|
+
- **`ensurePixelGrid`** filters malformed rows.
|
|
201
|
+
- **`flipHorizontal`/`flipVertical`/`rotate90` defensive** — non-array input returns `[]`.
|
|
202
|
+
- **`gradientRect` validates colors array** — clear errors for empty/all-invalid.
|
|
203
|
+
- **`gradientRect` single-stop renders solid fill** — better UX.
|
|
204
|
+
- **`gradientRect(Infinity width)` clamps** to MAX_DIMENSION.
|
|
205
|
+
- **`createCanvas(NaN/0)` → 1×1**.
|
|
206
|
+
- **`createCanvas(Infinity)`** clamps to MAX_DIMENSION.
|
|
207
|
+
- **`canvas.set/get` reject non-finite coords** as no-op.
|
|
208
|
+
- **`canvas.drawRect/Circle/Sprite` defensive** against NaN, negative dims, non-array sprites.
|
|
209
|
+
- **`canvas.pixels` getter returns deep clone** — callers can't mutate canvas state.
|
|
210
|
+
- **`canvas.print` with try/catch** — stream torn down doesn't crash.
|
|
211
|
+
- **ANSI cache LRU bounded** at 1024 entries — survives massive color counts.
|
|
212
|
+
- **`Pixel` and `PixelGrid` exported** for typed consumers.
|
|
213
|
+
|
|
214
|
+
### Added — Utils (`utils/ansi.ts`)
|
|
215
|
+
|
|
216
|
+
- **`OSC`, `ST`, `BEL` constants** exported.
|
|
217
|
+
- **`setTitle(text)`** — set terminal window title (OSC 2). Control chars stripped.
|
|
218
|
+
- **`link(text, url)`** — clickable hyperlink (OSC 8). Supported in iTerm2, Terminal.app, WezTerm, Kitty, modern xterm.
|
|
219
|
+
- **`bell()`** — terminal bell.
|
|
220
|
+
- **`cursor.position()`** — query position (CSI 6n).
|
|
221
|
+
- **`cursor.nextLine()`/`prevLine()`** — line-aware navigation.
|
|
222
|
+
- **`screen.clearAll()`** — alias for `clear()`.
|
|
223
|
+
- **`DEFAULT_TERM_COLS = 80`, `DEFAULT_TERM_ROWS = 24`** exported.
|
|
224
|
+
- **`writeAsync({ timeout })`** option — prevents infinite hangs on broken streams.
|
|
225
|
+
- **`OutputBuffer.pushIf(cond, str)`** — conditional append.
|
|
226
|
+
- **`detectColorSupport`** improved — TERM truecolor/24bit detection, 256 substring match, rxvt support, try/catch around `os.release()`.
|
|
227
|
+
- **All numeric inputs clamped** — `cursor.up(NaN)` → min=1, `fgRgb(Infinity, ...)` → 0.
|
|
228
|
+
- **`ensureString` coercion** — all writes accept any input.
|
|
229
|
+
- **`sleep(NaN/negative)` clamped** to 0.
|
|
230
|
+
|
|
231
|
+
### Added — Utils (`utils/helpers.ts`)
|
|
232
|
+
|
|
233
|
+
- **`once(fn)`** — invoke a function exactly once.
|
|
234
|
+
- **`escapeRegex(str)`** — escape regex metacharacters.
|
|
235
|
+
- **`safeJson(value, indent?)`** — JSON.stringify handling BigInt and circular references.
|
|
236
|
+
- **`padBoth(str, width, ch?)`** — pad both sides equally, Unicode-aware.
|
|
237
|
+
- **`nextTick(cb)`** — `setImmediate` fallback to `setTimeout(0)`.
|
|
238
|
+
- **`memoize` with `{ keyFn }` option** — multi-argument memoization.
|
|
239
|
+
- **`onResize` with implicit throttle (50ms default)** — coalesces rapid resize events.
|
|
240
|
+
- **`debounce` with `maxWait` option** — guarantees invocation within window.
|
|
241
|
+
- **`diffLines` with `type: 'added' | 'removed' | 'changed'`** — richer damage tracking.
|
|
242
|
+
- **`gradientColor` auto-clamps `t`** — values outside [0,1] clamped automatically. NaN → 0.
|
|
243
|
+
- **`stripAnsi`/`visibleLen` defensive** against non-string inputs.
|
|
244
|
+
- **`termSize` validates** cols/rows > 0.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### Test infrastructure
|
|
249
|
+
|
|
250
|
+
- **~1700+ tests across 16 suites**, all green.
|
|
251
|
+
- Coverage: ~98% statements, ~95% branches, ~99% functions, ~99% lines.
|
|
252
|
+
- All test files use `FORCE_COLOR=3` + `resetColorSupportCache()` in `beforeEach` for isolation.
|
|
253
|
+
- New test isolation helpers exported: `resetCursorRefCount`, `resetFramesCursorCount`, `resetLoaderCursorCount`, `clearAnsiCache`, `clearThemeColorCache`, `clearColorCache`, `clearRenderCache`, `resetConfig`.
|
|
254
|
+
|
|
255
|
+
### Examples
|
|
256
|
+
|
|
257
|
+
6 production-grade examples in `/examples`:
|
|
258
|
+
|
|
259
|
+
- `01-cli-installer.ts` — npm-create style installer (banner + hierarchical tasks + status icons + summary box).
|
|
260
|
+
- `02-live-dashboard.ts` — real-time dashboard (frames.live + service table + gradient bars + onResize + SIGINT cleanup).
|
|
261
|
+
- `03-pixel-art-game.ts` — bouncing rocket sprite (canvas + alpha blending + sunset gradient + FPS counter + drift-corrected loop).
|
|
262
|
+
- `04-interactive-deploy.ts` — interactive menu + multi-select + loader.multi + createTheme + onConfigChange.
|
|
263
|
+
- `05-tree-visualizations.ts` — filesystem + dependency + JSON + decision trees (4 scenarios, walk + measure bonus).
|
|
264
|
+
- `06-everything-together.ts` — comprehensive showcase touching every module (NEW).
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## [1.0.0] — initial release
|
|
269
|
+
|
|
270
|
+
- Core modules: `color`, `animate`, `ascii`, `loader`, `frames`, `components`, `themes`, `images`, `configure`.
|
|
271
|
+
- TypeScript types exported.
|
|
272
|
+
- Adaptive color rendering (NO_COLOR / FORCE_COLOR / TTY detection).
|
|
273
|
+
- AbortSignal support across all blocking APIs.
|
|
274
|
+
- 750+ tests, 85%+ coverage.
|
package/README.es.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
_Colores • Gradientes • Animaciones • ASCII Art • Pixel Art • Árboles • Componentes • Temas_
|
|
8
8
|
|
|
9
9
|
[](LICENSE)
|
|
10
|
-
[](https://www.npmjs.com/package/ansimax)
|
|
11
11
|
[](tsconfig.json)
|
|
12
12
|
[](#testing)
|
|
13
13
|
[](#testing)
|
|
@@ -20,6 +20,27 @@ _Colores • Gradientes • Animaciones • ASCII Art • Pixel Art • Árboles
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
<div align="center">
|
|
24
|
+
|
|
25
|
+
### 🎬 Vista previa
|
|
26
|
+
|
|
27
|
+
<table>
|
|
28
|
+
<tr>
|
|
29
|
+
<td align="center">
|
|
30
|
+
<strong>Animaciones</strong><br/>
|
|
31
|
+
<img src="media/animations.gif" alt="Demo de animaciones de Ansimax" width="420"/>
|
|
32
|
+
</td>
|
|
33
|
+
<td align="center">
|
|
34
|
+
<strong>Loaders</strong><br/>
|
|
35
|
+
<img src="media/loaders.gif" alt="Demo de loaders de Ansimax" width="420"/>
|
|
36
|
+
</td>
|
|
37
|
+
</tr>
|
|
38
|
+
</table>
|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
23
44
|
## 🌟 ¿Qué es Ansimax?
|
|
24
45
|
|
|
25
46
|
Ansimax es una **librería de renderizado todo-en-uno** para construir interfaces de terminal hermosas en Node.js. Un solo paquete reemplaza un stack de más de 8 dependencias — colores, gradientes, ASCII art, spinners, barras de progreso, tablas, menús, árboles, temas, pixel art — combinadas en una única API coherente de TypeScript con **cero dependencias en runtime**.
|
|
@@ -248,7 +269,7 @@ components.table([
|
|
|
248
269
|
['loaders', color.green('● listo'), '100%'],
|
|
249
270
|
], { borderStyle: 'rounded' });
|
|
250
271
|
|
|
251
|
-
components.badge('VERSION', 'v1.1.
|
|
272
|
+
components.badge('VERSION', 'v1.1.1');
|
|
252
273
|
components.badge('BUILD', 'passing');
|
|
253
274
|
```
|
|
254
275
|
|
|
@@ -336,21 +357,33 @@ tenantA.register('custom', miDef); // no se filtra a tenantB
|
|
|
336
357
|
|
|
337
358
|
## 📚 Ejemplos
|
|
338
359
|
|
|
339
|
-
|
|
360
|
+
Once ejemplos de calidad de producción se publican en el paquete npm y son ejecutables directamente. Los encuentras en [`/examples`](./examples) después de instalar:
|
|
340
361
|
|
|
341
362
|
| Archivo | Qué demuestra |
|
|
342
363
|
|---|---|
|
|
343
|
-
| `
|
|
344
|
-
| `
|
|
345
|
-
| `
|
|
346
|
-
| `
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
349
|
-
| `
|
|
364
|
+
| `01-quick-smoke.ts` | Test rápido de humo — verifica que cada import principal funciona |
|
|
365
|
+
| `02-colors-gradients.ts` | Toda función de color, tipos de gradiente, presets, compose, API chain |
|
|
366
|
+
| `03-ascii-banners.ts` | Banners (`big`/`small`), 6 estilos de caja, divisores, compositor de logos |
|
|
367
|
+
| `04-trees.ts` | Tree builder + API data-plana, 4 estilos, palettes, algoritmos (walk/find/map/filter) |
|
|
368
|
+
| `05-components.ts` | Tablas, badges, status, secciones, columnas, timelines, barras de progreso |
|
|
369
|
+
| `06-pixel-art.ts` | Sprites, canvas personalizado, gradient rects con dither, transforms (flip/rotate) |
|
|
370
|
+
| `07-animations.ts` | typewriter, fadeIn/Out, slide, pulse, wave, glitch, reveal |
|
|
371
|
+
| `08-loaders.ts` | Estilos de spinner, progreso animado, tareas jerárquicas, cuenta regresiva |
|
|
372
|
+
| `09-themes.ts` | Los 8 temas integrados, listeners, registro de temas personalizados, aislamiento por instancia |
|
|
373
|
+
| `10-everything.ts` | Showcase completo — cada módulo ejercitado en un demo cohesivo |
|
|
374
|
+
| `all-in-one.mjs` | Demo completo en **ESM** (JS puro con `import`) — sin necesidad de TypeScript |
|
|
375
|
+
| `all-in-one.cjs` | Demo completo en **CommonJS** (JS puro con `require`) — sin necesidad de TypeScript |
|
|
350
376
|
|
|
351
377
|
Ejecuta cualquier ejemplo con:
|
|
352
378
|
```bash
|
|
353
|
-
|
|
379
|
+
# Ejemplos en TypeScript
|
|
380
|
+
npx tsx examples/10-everything.ts
|
|
381
|
+
|
|
382
|
+
# JS puro — ESM
|
|
383
|
+
node examples/all-in-one.mjs
|
|
384
|
+
|
|
385
|
+
# JS puro — CommonJS
|
|
386
|
+
node examples/all-in-one.cjs
|
|
354
387
|
```
|
|
355
388
|
|
|
356
389
|
---
|
|
@@ -604,7 +637,7 @@ ansimax/
|
|
|
604
637
|
│ ├── trees/ Builder de árboles + algoritmos
|
|
605
638
|
│ ├── utils/ Primitivas ANSI + helpers
|
|
606
639
|
│ └── configure.ts Config global + subscribers
|
|
607
|
-
├── examples/
|
|
640
|
+
├── examples/ 10 ejemplos (TS) + 2 (JS — ESM y CJS) — todas las funciones cubiertas
|
|
608
641
|
└── __tests__/ 16 test suites, 1700+ tests
|
|
609
642
|
```
|
|
610
643
|
|
|
@@ -612,6 +645,17 @@ ansimax/
|
|
|
612
645
|
|
|
613
646
|
## 📝 Changelog
|
|
614
647
|
|
|
648
|
+
### v1.1.1 — Fixes de bugs + ejemplos limpios
|
|
649
|
+
|
|
650
|
+
Release patch que arregla dos bugs encontrados en testing real de v1.1.0, más una carpeta de ejemplos refrescada.
|
|
651
|
+
|
|
652
|
+
- 🐛 **Fix de crash en `box()`** con `padding: { x, y }` — ahora cae graciosamente al default para padding no-numérico (también maneja NaN, Infinity, strings)
|
|
653
|
+
- 🐛 **Fix de leak de cursor en `components.menu()`** al salir abruptamente (Ctrl+C, SIGTERM) — handlers de cleanup de emergencia ahora restauran el cursor incluso cuando el proceso se mata mid-menu
|
|
654
|
+
- 📚 **Nuevos ejemplos** — 10 ejemplos en TypeScript + 2 variantes en JS puro (`all-in-one.mjs` para ESM, `all-in-one.cjs` para CommonJS)
|
|
655
|
+
- 📖 **READMEs actualizados** — GIFs de preview en el header, GIF de showcase completo en el footer
|
|
656
|
+
|
|
657
|
+
Sin cambios en la API — drop-in replacement para `1.1.0`.
|
|
658
|
+
|
|
615
659
|
### v1.1.0 — Hardening exhaustivo + nuevas features
|
|
616
660
|
|
|
617
661
|
Una pasada masiva de robustez sobre todo módulo, más un nuevo módulo `trees`. **100% retrocompatible** — toda API existente funciona idéntica.
|
|
@@ -676,6 +720,18 @@ Si Ansimax te ahorra tiempo, por favor dale estrella al repo en [GitHub](https:/
|
|
|
676
720
|
|
|
677
721
|
---
|
|
678
722
|
|
|
723
|
+
## 🎬 Showcase completo
|
|
724
|
+
|
|
725
|
+
<div align="center">
|
|
726
|
+
|
|
727
|
+
<img src="media/all-ansimax.gif" alt="Showcase completo de Ansimax — todo en acción" width="720"/>
|
|
728
|
+
|
|
729
|
+
_Todas las funciones en acción — typewriter, gradientes, banners ASCII, árboles, tablas, spinners, temas y pixel art_
|
|
730
|
+
|
|
731
|
+
</div>
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
679
735
|
## 📜 Licencia
|
|
680
736
|
|
|
681
737
|
[Apache License 2.0](LICENSE) © 2026 Brashkie
|
|
@@ -694,4 +750,4 @@ Ansimax está licenciada bajo **Apache License, Version 2.0** — una licencia p
|
|
|
694
750
|
|
|
695
751
|
Si Ansimax te ayuda a hacer mejores CLIs, ¡dale ⭐ en [GitHub](https://github.com/Brashkie/ansimax)!
|
|
696
752
|
|
|
697
|
-
</div>
|
|
753
|
+
</div>
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
_Colors • Gradients • Animations • ASCII Art • Pixel Art • Trees • Components • Themes_
|
|
8
8
|
|
|
9
9
|
[](LICENSE)
|
|
10
|
-
[](https://www.npmjs.com/package/ansimax)
|
|
11
11
|
[](tsconfig.json)
|
|
12
12
|
[](#testing)
|
|
13
13
|
[](#testing)
|
|
@@ -20,6 +20,27 @@ _Colors • Gradients • Animations • ASCII Art • Pixel Art • Trees • C
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
<div align="center">
|
|
24
|
+
|
|
25
|
+
### 🎬 Preview
|
|
26
|
+
|
|
27
|
+
<table>
|
|
28
|
+
<tr>
|
|
29
|
+
<td align="center">
|
|
30
|
+
<strong>Animations</strong><br/>
|
|
31
|
+
<img src="media/animations.gif" alt="Ansimax animations demo" width="420"/>
|
|
32
|
+
</td>
|
|
33
|
+
<td align="center">
|
|
34
|
+
<strong>Loaders</strong><br/>
|
|
35
|
+
<img src="media/loaders.gif" alt="Ansimax loaders demo" width="420"/>
|
|
36
|
+
</td>
|
|
37
|
+
</tr>
|
|
38
|
+
</table>
|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
23
44
|
## 🌟 What is Ansimax?
|
|
24
45
|
|
|
25
46
|
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**.
|
|
@@ -248,7 +269,7 @@ components.table([
|
|
|
248
269
|
['loaders', color.green('● ready'), '100%'],
|
|
249
270
|
], { borderStyle: 'rounded' });
|
|
250
271
|
|
|
251
|
-
components.badge('VERSION', 'v1.1.
|
|
272
|
+
components.badge('VERSION', 'v1.1.1');
|
|
252
273
|
components.badge('BUILD', 'passing');
|
|
253
274
|
```
|
|
254
275
|
|
|
@@ -336,21 +357,33 @@ tenantA.register('custom', myDef); // doesn't leak to tenantB
|
|
|
336
357
|
|
|
337
358
|
## 📚 Examples
|
|
338
359
|
|
|
339
|
-
|
|
360
|
+
Eleven production-grade examples ship in the npm package and are runnable directly. Find them in [`/examples`](./examples) once you install:
|
|
340
361
|
|
|
341
362
|
| File | What it demonstrates |
|
|
342
363
|
|---|---|
|
|
343
|
-
| `
|
|
344
|
-
| `
|
|
345
|
-
| `
|
|
346
|
-
| `
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
349
|
-
| `
|
|
364
|
+
| `01-quick-smoke.ts` | Quick smoke test — verifies every major import works |
|
|
365
|
+
| `02-colors-gradients.ts` | Every color fn, gradient types, presets, compose, chain API |
|
|
366
|
+
| `03-ascii-banners.ts` | Banners (`big`/`small`), 6 box styles, dividers, logo composer |
|
|
367
|
+
| `04-trees.ts` | Tree builder + plain-data API, 4 styles, palettes, algorithms (walk/find/map/filter) |
|
|
368
|
+
| `05-components.ts` | Tables, badges, status, sections, columns, timelines, progress bars |
|
|
369
|
+
| `06-pixel-art.ts` | Sprites, custom canvas, gradient rects with dither, transforms (flip/rotate) |
|
|
370
|
+
| `07-animations.ts` | typewriter, fadeIn/Out, slide, pulse, wave, glitch, reveal |
|
|
371
|
+
| `08-loaders.ts` | spinner styles, animated progress, hierarchical tasks, countdown |
|
|
372
|
+
| `09-themes.ts` | All 8 built-in themes, listeners, custom theme registration, per-instance isolation |
|
|
373
|
+
| `10-everything.ts` | Comprehensive showcase — every module exercised in one cohesive demo |
|
|
374
|
+
| `all-in-one.mjs` | Full demo in **ESM** (plain JS with `import`) — no TypeScript needed |
|
|
375
|
+
| `all-in-one.cjs` | Full demo in **CommonJS** (plain JS with `require`) — no TypeScript needed |
|
|
350
376
|
|
|
351
377
|
Run any example with:
|
|
352
378
|
```bash
|
|
353
|
-
|
|
379
|
+
# TypeScript examples
|
|
380
|
+
npx tsx examples/10-everything.ts
|
|
381
|
+
|
|
382
|
+
# Plain JS — ESM
|
|
383
|
+
node examples/all-in-one.mjs
|
|
384
|
+
|
|
385
|
+
# Plain JS — CommonJS
|
|
386
|
+
node examples/all-in-one.cjs
|
|
354
387
|
```
|
|
355
388
|
|
|
356
389
|
---
|
|
@@ -604,7 +637,7 @@ ansimax/
|
|
|
604
637
|
│ ├── trees/ Tree builder + algorithms
|
|
605
638
|
│ ├── utils/ ANSI primitives + helpers
|
|
606
639
|
│ └── configure.ts Global config + subscribers
|
|
607
|
-
├── examples/
|
|
640
|
+
├── examples/ 10 examples (TS) + 2 (JS — ESM & CJS) — all features covered
|
|
608
641
|
└── __tests__/ 16 test suites, 1700+ tests
|
|
609
642
|
```
|
|
610
643
|
|
|
@@ -612,6 +645,17 @@ ansimax/
|
|
|
612
645
|
|
|
613
646
|
## 📝 Changelog
|
|
614
647
|
|
|
648
|
+
### v1.1.1 — Bug fixes + cleaner examples
|
|
649
|
+
|
|
650
|
+
Patch release fixing two bugs from real-world v1.1.0 testing, plus a refreshed examples folder.
|
|
651
|
+
|
|
652
|
+
- 🐛 **Fixed `box()` crash** with `padding: { x, y }` — now gracefully falls back to default for non-numeric padding (also handles NaN, Infinity, strings)
|
|
653
|
+
- 🐛 **Fixed `components.menu()` cursor leak** on abrupt exit (Ctrl+C, SIGTERM) — emergency cleanup handlers now restore the cursor even when the process is killed mid-menu
|
|
654
|
+
- 📚 **New examples** — 10 TypeScript examples + 2 plain JS variants (`all-in-one.mjs` for ESM, `all-in-one.cjs` for CommonJS)
|
|
655
|
+
- 📖 **READMEs updated** — preview GIFs in the header, comprehensive showcase GIF in the footer
|
|
656
|
+
|
|
657
|
+
No API changes — drop-in replacement for `1.1.0`.
|
|
658
|
+
|
|
615
659
|
### v1.1.0 — Comprehensive hardening + new features
|
|
616
660
|
|
|
617
661
|
A massive robustness pass across every module, plus a new `trees` module. **100% backward compatible** — every existing API works identically.
|
|
@@ -676,6 +720,18 @@ If Ansimax saves you time, please star the repo on [GitHub](https://github.com/B
|
|
|
676
720
|
|
|
677
721
|
---
|
|
678
722
|
|
|
723
|
+
## 🎬 Full showcase
|
|
724
|
+
|
|
725
|
+
<div align="center">
|
|
726
|
+
|
|
727
|
+
<img src="media/all-ansimax.gif" alt="Ansimax full showcase — everything in action" width="720"/>
|
|
728
|
+
|
|
729
|
+
_All features in action — typewriter, gradients, ASCII banners, trees, tables, spinners, themes, and pixel art_
|
|
730
|
+
|
|
731
|
+
</div>
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
679
735
|
## 📜 License
|
|
680
736
|
|
|
681
737
|
[Apache License 2.0](LICENSE) © 2026 Brashkie
|
package/dist/index.js
CHANGED
|
@@ -2264,7 +2264,8 @@ var banner = (text, opts = {}) => {
|
|
|
2264
2264
|
var box = (text, opts = {}) => {
|
|
2265
2265
|
const safe = ensureString2(text, "box(text)");
|
|
2266
2266
|
const { padding = 1, borderStyle = "rounded", width = null } = opts;
|
|
2267
|
-
const
|
|
2267
|
+
const padNum = typeof padding === "number" && Number.isFinite(padding) ? padding : 1;
|
|
2268
|
+
const safePadding = Math.max(0, Math.floor(padNum));
|
|
2268
2269
|
const b = BOX_STYLES[borderStyle] ?? BOX_STYLES.rounded;
|
|
2269
2270
|
const lines = safe.split("\n");
|
|
2270
2271
|
const inner = width != null ? lines.map((l) => padEnd(truncateAnsi(l, width, ""), width)) : lines;
|
|
@@ -3677,7 +3678,32 @@ var menu = (items, opts = {}) => {
|
|
|
3677
3678
|
}
|
|
3678
3679
|
cursorHidden = false;
|
|
3679
3680
|
}
|
|
3681
|
+
try {
|
|
3682
|
+
process.off("SIGINT", emergencyCleanup);
|
|
3683
|
+
process.off("SIGTERM", emergencyCleanup);
|
|
3684
|
+
process.off("exit", emergencyCleanup);
|
|
3685
|
+
} catch {
|
|
3686
|
+
}
|
|
3680
3687
|
};
|
|
3688
|
+
const emergencyCleanup = () => {
|
|
3689
|
+
if (cursorHidden) {
|
|
3690
|
+
try {
|
|
3691
|
+
out.write(cursor.show());
|
|
3692
|
+
} catch {
|
|
3693
|
+
}
|
|
3694
|
+
cursorHidden = false;
|
|
3695
|
+
}
|
|
3696
|
+
try {
|
|
3697
|
+
if (inp.setRawMode) inp.setRawMode(false);
|
|
3698
|
+
} catch {
|
|
3699
|
+
}
|
|
3700
|
+
};
|
|
3701
|
+
try {
|
|
3702
|
+
process.once("SIGINT", emergencyCleanup);
|
|
3703
|
+
process.once("SIGTERM", emergencyCleanup);
|
|
3704
|
+
process.once("exit", emergencyCleanup);
|
|
3705
|
+
} catch {
|
|
3706
|
+
}
|
|
3681
3707
|
try {
|
|
3682
3708
|
emit(cursor.hide());
|
|
3683
3709
|
cursorHidden = true;
|
package/dist/index.mjs
CHANGED
|
@@ -2092,7 +2092,8 @@ var banner = (text, opts = {}) => {
|
|
|
2092
2092
|
var box = (text, opts = {}) => {
|
|
2093
2093
|
const safe = ensureString2(text, "box(text)");
|
|
2094
2094
|
const { padding = 1, borderStyle = "rounded", width = null } = opts;
|
|
2095
|
-
const
|
|
2095
|
+
const padNum = typeof padding === "number" && Number.isFinite(padding) ? padding : 1;
|
|
2096
|
+
const safePadding = Math.max(0, Math.floor(padNum));
|
|
2096
2097
|
const b = BOX_STYLES[borderStyle] ?? BOX_STYLES.rounded;
|
|
2097
2098
|
const lines = safe.split("\n");
|
|
2098
2099
|
const inner = width != null ? lines.map((l) => padEnd(truncateAnsi(l, width, ""), width)) : lines;
|
|
@@ -3505,7 +3506,32 @@ var menu = (items, opts = {}) => {
|
|
|
3505
3506
|
}
|
|
3506
3507
|
cursorHidden = false;
|
|
3507
3508
|
}
|
|
3509
|
+
try {
|
|
3510
|
+
process.off("SIGINT", emergencyCleanup);
|
|
3511
|
+
process.off("SIGTERM", emergencyCleanup);
|
|
3512
|
+
process.off("exit", emergencyCleanup);
|
|
3513
|
+
} catch {
|
|
3514
|
+
}
|
|
3508
3515
|
};
|
|
3516
|
+
const emergencyCleanup = () => {
|
|
3517
|
+
if (cursorHidden) {
|
|
3518
|
+
try {
|
|
3519
|
+
out.write(cursor.show());
|
|
3520
|
+
} catch {
|
|
3521
|
+
}
|
|
3522
|
+
cursorHidden = false;
|
|
3523
|
+
}
|
|
3524
|
+
try {
|
|
3525
|
+
if (inp.setRawMode) inp.setRawMode(false);
|
|
3526
|
+
} catch {
|
|
3527
|
+
}
|
|
3528
|
+
};
|
|
3529
|
+
try {
|
|
3530
|
+
process.once("SIGINT", emergencyCleanup);
|
|
3531
|
+
process.once("SIGTERM", emergencyCleanup);
|
|
3532
|
+
process.once("exit", emergencyCleanup);
|
|
3533
|
+
} catch {
|
|
3534
|
+
}
|
|
3509
3535
|
try {
|
|
3510
3536
|
emit(cursor.hide());
|
|
3511
3537
|
cursorHidden = true;
|