ansimax 1.1.1 → 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 +51 -1
- package/README.es.md +81 -36
- package/README.md +81 -36
- package/dist/index.js +12 -7
- package/dist/index.mjs +12 -7
- package/examples/all-in-one.cjs +1 -1
- package/examples/all-in-one.mjs +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,56 @@
|
|
|
3
3
|
All notable changes to **ansimax** are documented in this file.
|
|
4
4
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
5
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
|
+
|
|
6
56
|
## [1.1.1] — bug fixes + improved examples
|
|
7
57
|
|
|
8
58
|
Patch release with two bug fixes from real-world testing of v1.1.0, plus
|
|
@@ -271,4 +321,4 @@ Hierarchical text renderer inspired by Rich's `Tree`.
|
|
|
271
321
|
- TypeScript types exported.
|
|
272
322
|
- Adaptive color rendering (NO_COLOR / FORCE_COLOR / TTY detection).
|
|
273
323
|
- AbortSignal support across all blocking APIs.
|
|
274
|
-
- 750+ tests, 85%+ coverage.
|
|
324
|
+
- 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)
|
|
@@ -50,14 +50,14 @@ npm install ansimax
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
```ts
|
|
53
|
-
import { color, gradient, ascii, loader } from 'ansimax';
|
|
53
|
+
import { color, gradient, ascii, loader, sleep } from 'ansimax';
|
|
54
54
|
|
|
55
55
|
console.log(ascii.banner('hola', {
|
|
56
56
|
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9', '#8be9fd']),
|
|
57
57
|
}));
|
|
58
58
|
|
|
59
59
|
const stop = loader.spin('Construyendo proyecto', { color: '#bd93f9' });
|
|
60
|
-
await
|
|
60
|
+
await sleep(1500);
|
|
61
61
|
stop('Build completado', true);
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -123,14 +123,14 @@ yarn add ansimax
|
|
|
123
123
|
## ⚡ Ejemplo en 30 segundos
|
|
124
124
|
|
|
125
125
|
```ts
|
|
126
|
-
import { color, gradient, loader, ascii } from 'ansimax';
|
|
126
|
+
import { color, gradient, loader, ascii, sleep } from 'ansimax';
|
|
127
127
|
|
|
128
128
|
console.log(ascii.banner('deploy', {
|
|
129
129
|
colorFn: (t) => gradient(t, ['#ff6b6b', '#feca57', '#48dbfb']),
|
|
130
130
|
}));
|
|
131
131
|
|
|
132
132
|
const stop = loader.spin('Construyendo proyecto', { color: '#bd93f9' });
|
|
133
|
-
await
|
|
133
|
+
await sleep(1500); // simula trabajo asíncrono
|
|
134
134
|
stop('Build completado', true); // ✓ + color de éxito
|
|
135
135
|
|
|
136
136
|
console.log(color.green('✓') + ' Listo en ' + color.bold('1.4s'));
|
|
@@ -187,12 +187,19 @@ console.log(themes.primary('primary de cyberpunk'));
|
|
|
187
187
|
<img src="media/colors.png" alt="Colores y gradientes" />
|
|
188
188
|
|
|
189
189
|
```ts
|
|
190
|
-
import { color, gradient } from 'ansimax';
|
|
190
|
+
import { color, gradient, rainbow } from 'ansimax';
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
color.
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
// Colores básicos
|
|
193
|
+
console.log(color.red('rojo'), color.green('verde'), color.blue('azul'));
|
|
194
|
+
|
|
195
|
+
// Modificadores de estilo
|
|
196
|
+
console.log(color.bold('negrita'), color.italic('itálica'), color.underline('subrayado'));
|
|
197
|
+
|
|
198
|
+
// Gradiente multi-stop
|
|
199
|
+
console.log(gradient('fuego a océano', ['#ff6b6b', '#feca57', '#48dbfb']));
|
|
200
|
+
|
|
201
|
+
// Preset rainbow integrado
|
|
202
|
+
console.log(rainbow('preset rainbow integrado'));
|
|
196
203
|
```
|
|
197
204
|
|
|
198
205
|
### ASCII Art
|
|
@@ -202,13 +209,13 @@ color.rainbow('preset rainbow integrado');
|
|
|
202
209
|
```ts
|
|
203
210
|
import { ascii, gradient } from 'ansimax';
|
|
204
211
|
|
|
205
|
-
ascii.banner('HOLA', {
|
|
212
|
+
console.log(ascii.banner('HOLA', {
|
|
206
213
|
font: 'big',
|
|
207
214
|
align: 'center',
|
|
208
215
|
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9']),
|
|
209
|
-
});
|
|
216
|
+
}));
|
|
210
217
|
|
|
211
|
-
ascii.box('¡Caja arcoiris!', { padding: 1, borderStyle: 'rounded' });
|
|
218
|
+
console.log(ascii.box('¡Caja arcoiris!', { padding: 1, borderStyle: 'rounded' }));
|
|
212
219
|
```
|
|
213
220
|
|
|
214
221
|
### Árboles
|
|
@@ -235,7 +242,7 @@ console.log(proyecto.render({
|
|
|
235
242
|
<img src="media/pixel_art.png" alt="Pixel art" />
|
|
236
243
|
|
|
237
244
|
```ts
|
|
238
|
-
import { images, createCanvas, gradientRect } from 'ansimax';
|
|
245
|
+
import { images, createCanvas, gradientRect, SPRITES } from 'ansimax';
|
|
239
246
|
|
|
240
247
|
// Sprite integrado
|
|
241
248
|
console.log(images.sprite('heart'));
|
|
@@ -251,7 +258,8 @@ console.log(gradientRect({
|
|
|
251
258
|
const c = createCanvas(40, 10);
|
|
252
259
|
c.fill({ r: 18, g: 18, b: 38 });
|
|
253
260
|
c.drawCircle(20, 5, 4, { r: 255, g: 200, b: 0 }, true);
|
|
254
|
-
|
|
261
|
+
const starSprite = SPRITES.star;
|
|
262
|
+
if (starSprite) c.drawSprite(2, 2, starSprite.pixels);
|
|
255
263
|
c.print();
|
|
256
264
|
```
|
|
257
265
|
|
|
@@ -262,15 +270,15 @@ c.print();
|
|
|
262
270
|
```ts
|
|
263
271
|
import { components, color } from 'ansimax';
|
|
264
272
|
|
|
265
|
-
components.table([
|
|
273
|
+
console.log(components.table([
|
|
266
274
|
['Módulo', 'Estado', 'Cobertura'],
|
|
267
275
|
['colors', color.green('● listo'), '100%'],
|
|
268
276
|
['animations', color.green('● listo'), '100%'],
|
|
269
277
|
['loaders', color.green('● listo'), '100%'],
|
|
270
|
-
], { borderStyle: 'rounded' });
|
|
278
|
+
], { borderStyle: 'rounded' }));
|
|
271
279
|
|
|
272
|
-
components.badge('VERSION', 'v1.1.
|
|
273
|
-
components.badge('BUILD', 'passing');
|
|
280
|
+
console.log(components.badge('VERSION', 'v1.1.2'));
|
|
281
|
+
console.log(components.badge('BUILD', 'passing'));
|
|
274
282
|
```
|
|
275
283
|
|
|
276
284
|
### Timeline
|
|
@@ -278,22 +286,24 @@ components.badge('BUILD', 'passing');
|
|
|
278
286
|
<img src="media/timeline.png" alt="Timeline" />
|
|
279
287
|
|
|
280
288
|
```ts
|
|
281
|
-
components
|
|
289
|
+
import { components } from 'ansimax';
|
|
290
|
+
|
|
291
|
+
console.log(components.timeline([
|
|
282
292
|
{ label: 'Init del proyecto', done: true, time: '10:00' },
|
|
283
293
|
{ label: 'Pipeline de build', done: true, time: '10:15' },
|
|
284
294
|
{ label: 'Correr tests', done: false, time: '10:32' },
|
|
285
295
|
{ label: 'Deploy a npm', done: false },
|
|
286
|
-
]);
|
|
296
|
+
]));
|
|
287
297
|
```
|
|
288
298
|
|
|
289
299
|
### Loaders y Progreso
|
|
290
300
|
|
|
291
301
|
```ts
|
|
292
|
-
import { loader } from 'ansimax';
|
|
302
|
+
import { loader, sleep } from 'ansimax';
|
|
293
303
|
|
|
294
304
|
// Spinner con éxito/fallo
|
|
295
305
|
const stop = loader.spin('Cargando...', { color: '#bd93f9' });
|
|
296
|
-
await
|
|
306
|
+
await sleep(1500);
|
|
297
307
|
stop('¡Listo!', true); // ✓ ícono verde
|
|
298
308
|
|
|
299
309
|
// Barra de progreso animada
|
|
@@ -303,18 +313,22 @@ await loader.progressAnimate(100, 'Descargando', {
|
|
|
303
313
|
|
|
304
314
|
// Tareas jerárquicas con ejecución paralela
|
|
305
315
|
await loader.tasks([
|
|
306
|
-
{
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
316
|
+
{
|
|
317
|
+
text: 'Build',
|
|
318
|
+
fn: async () => await sleep(500),
|
|
319
|
+
subtasks: [
|
|
320
|
+
{ text: 'TypeScript', fn: async () => await sleep(800) },
|
|
321
|
+
{ text: 'Bundle', fn: async () => await sleep(600) },
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
{ text: 'Test', fn: async () => await sleep(700) },
|
|
311
325
|
], { parallel: true });
|
|
312
326
|
```
|
|
313
327
|
|
|
314
328
|
### Animaciones
|
|
315
329
|
|
|
316
330
|
```ts
|
|
317
|
-
import { animate, gradient } from 'ansimax';
|
|
331
|
+
import { animate, gradient, sleep } from 'ansimax';
|
|
318
332
|
|
|
319
333
|
await animate.typewriter('Bienvenido al wizard de deployment...', {
|
|
320
334
|
speed: 30,
|
|
@@ -325,9 +339,9 @@ await animate.fadeIn('Carga completa', { duration: 600 });
|
|
|
325
339
|
|
|
326
340
|
// Carrera de pasos contra timeout — nunca se cuelga
|
|
327
341
|
await animate.parallel([
|
|
328
|
-
async () => await
|
|
329
|
-
async () => await
|
|
330
|
-
async () => await
|
|
342
|
+
async () => await sleep(500), // simulación de chequeo de red
|
|
343
|
+
async () => await sleep(700), // simulación de chequeo de base de datos
|
|
344
|
+
async () => await sleep(400), // simulación de chequeo de auth
|
|
331
345
|
], { timeout: 5000 });
|
|
332
346
|
```
|
|
333
347
|
|
|
@@ -340,7 +354,7 @@ import { themes, createTheme } from 'ansimax';
|
|
|
340
354
|
|
|
341
355
|
// Temas built-in
|
|
342
356
|
themes.use('dracula');
|
|
343
|
-
themes.primary('hola');
|
|
357
|
+
console.log(themes.primary('hola'));
|
|
344
358
|
|
|
345
359
|
// Escuchar cambios
|
|
346
360
|
const off = themes.onChange((nuevo, anterior) => {
|
|
@@ -350,7 +364,27 @@ const off = themes.onChange((nuevo, anterior) => {
|
|
|
350
364
|
// Multi-tenant: cada instancia totalmente aislada
|
|
351
365
|
const tenantA = createTheme('nord');
|
|
352
366
|
const tenantB = createTheme('matrix');
|
|
353
|
-
|
|
367
|
+
|
|
368
|
+
// Definir un tema personalizado y registrarlo SÓLO en tenantA
|
|
369
|
+
tenantA.register('custom', {
|
|
370
|
+
name: 'Custom',
|
|
371
|
+
primary: '#ff5e5e',
|
|
372
|
+
secondary: '#5e5eff',
|
|
373
|
+
accent: '#5eff5e',
|
|
374
|
+
success: '#10b981',
|
|
375
|
+
warning: '#fbbf24',
|
|
376
|
+
error: '#ef4444',
|
|
377
|
+
info: '#06b6d4',
|
|
378
|
+
muted: '#6b7280',
|
|
379
|
+
bg: '#1e293b',
|
|
380
|
+
surface: '#334155',
|
|
381
|
+
text: '#f1f5f9',
|
|
382
|
+
gradient: ['#ff5e5e', '#5eff5e', '#5e5eff'],
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
console.log('tenantA incluye custom?', tenantA.list().includes('custom'));
|
|
386
|
+
console.log('tenantB incluye custom?', tenantB.list().includes('custom'));
|
|
387
|
+
// ↑ false — aislamiento total
|
|
354
388
|
```
|
|
355
389
|
|
|
356
390
|
---
|
|
@@ -421,11 +455,11 @@ const off = onConfigKeyChange('theme', (nuevo, anterior) => {
|
|
|
421
455
|
|
|
422
456
|
// Override temporal + restauración automática al completar o lanzar
|
|
423
457
|
await withConfig({ animationSpeed: 'fast' }, async () => {
|
|
424
|
-
|
|
458
|
+
// ...tu código en modo fast aquí...
|
|
425
459
|
});
|
|
426
460
|
|
|
427
461
|
// Modo strict captura typos en config
|
|
428
|
-
configure({ unknwnKey: 'x' }, { strict: true }); // lanza RangeError
|
|
462
|
+
// configure({ unknwnKey: 'x' }, { strict: true }); // lanza RangeError
|
|
429
463
|
```
|
|
430
464
|
|
|
431
465
|
---
|
|
@@ -645,6 +679,17 @@ ansimax/
|
|
|
645
679
|
|
|
646
680
|
## 📝 Changelog
|
|
647
681
|
|
|
682
|
+
### v1.1.2 — Madurez y robustez
|
|
683
|
+
|
|
684
|
+
Release patch enfocado en refinamientos de calidad — sin cambios en la API.
|
|
685
|
+
|
|
686
|
+
- 🛡️ **Bump defensivo de `process.setMaxListeners`** — previene `MaxListenersExceededWarning` en setups con HMR / nodemon / ts-node-dev donde los módulos de ansimax re-registran handlers de restauración de cursor
|
|
687
|
+
- 🧪 **`TypeError` uniforme para validación de themes** — `themes.register()` ahora arroja consistentemente `TypeError` para errores estructurales / de tipo (antes era mezcla de `Error` y `TypeError`)
|
|
688
|
+
- 🎯 **`themes.use()` arroja `RangeError`** para nombres de tema desconocidos (antes `Error`) — mejor match semántico con "valor fuera del set permitido"
|
|
689
|
+
- 📝 **Re-exports más limpios en el barrel** — comentario de header ahora documenta aliases legacy y recomienda nombres canónicos
|
|
690
|
+
|
|
691
|
+
Drop-in replacement para `1.1.1`.
|
|
692
|
+
|
|
648
693
|
### v1.1.1 — Fixes de bugs + ejemplos limpios
|
|
649
694
|
|
|
650
695
|
Release patch que arregla dos bugs encontrados en testing real de v1.1.0, más una carpeta de ejemplos refrescada.
|
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)
|
|
@@ -50,14 +50,14 @@ npm install ansimax
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
```ts
|
|
53
|
-
import { color, gradient, ascii, loader } from 'ansimax';
|
|
53
|
+
import { color, gradient, ascii, loader, sleep } from 'ansimax';
|
|
54
54
|
|
|
55
55
|
console.log(ascii.banner('hello', {
|
|
56
56
|
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9', '#8be9fd']),
|
|
57
57
|
}));
|
|
58
58
|
|
|
59
59
|
const stop = loader.spin('Building project', { color: '#bd93f9' });
|
|
60
|
-
await
|
|
60
|
+
await sleep(1500);
|
|
61
61
|
stop('Build complete', true);
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -123,14 +123,14 @@ yarn add ansimax
|
|
|
123
123
|
## ⚡ 30-second example
|
|
124
124
|
|
|
125
125
|
```ts
|
|
126
|
-
import { color, gradient, loader, ascii } from 'ansimax';
|
|
126
|
+
import { color, gradient, loader, ascii, sleep } from 'ansimax';
|
|
127
127
|
|
|
128
128
|
console.log(ascii.banner('deploy', {
|
|
129
129
|
colorFn: (t) => gradient(t, ['#ff6b6b', '#feca57', '#48dbfb']),
|
|
130
130
|
}));
|
|
131
131
|
|
|
132
132
|
const stop = loader.spin('Building project', { color: '#bd93f9' });
|
|
133
|
-
await
|
|
133
|
+
await sleep(1500); // simulate async work
|
|
134
134
|
stop('Build complete', true); // ✓ + success color
|
|
135
135
|
|
|
136
136
|
console.log(color.green('✓') + ' Ready in ' + color.bold('1.4s'));
|
|
@@ -187,12 +187,19 @@ console.log(themes.primary('cyberpunk primary'));
|
|
|
187
187
|
<img src="media/colors.png" alt="Colors and gradients" />
|
|
188
188
|
|
|
189
189
|
```ts
|
|
190
|
-
import { color, gradient } from 'ansimax';
|
|
190
|
+
import { color, gradient, rainbow } from 'ansimax';
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
color.
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
// Basic colors
|
|
193
|
+
console.log(color.red('red'), color.green('green'), color.blue('blue'));
|
|
194
|
+
|
|
195
|
+
// Style modifiers
|
|
196
|
+
console.log(color.bold('bold'), color.italic('italic'), color.underline('underlined'));
|
|
197
|
+
|
|
198
|
+
// Multi-stop gradient
|
|
199
|
+
console.log(gradient('fire to ocean', ['#ff6b6b', '#feca57', '#48dbfb']));
|
|
200
|
+
|
|
201
|
+
// Built-in rainbow preset
|
|
202
|
+
console.log(rainbow('built-in rainbow preset'));
|
|
196
203
|
```
|
|
197
204
|
|
|
198
205
|
### ASCII Art
|
|
@@ -202,13 +209,13 @@ color.rainbow('built-in rainbow preset');
|
|
|
202
209
|
```ts
|
|
203
210
|
import { ascii, gradient } from 'ansimax';
|
|
204
211
|
|
|
205
|
-
ascii.banner('HELLO', {
|
|
212
|
+
console.log(ascii.banner('HELLO', {
|
|
206
213
|
font: 'big',
|
|
207
214
|
align: 'center',
|
|
208
215
|
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9']),
|
|
209
|
-
});
|
|
216
|
+
}));
|
|
210
217
|
|
|
211
|
-
ascii.box('Rainbow box!', { padding: 1, borderStyle: 'rounded' });
|
|
218
|
+
console.log(ascii.box('Rainbow box!', { padding: 1, borderStyle: 'rounded' }));
|
|
212
219
|
```
|
|
213
220
|
|
|
214
221
|
### Trees
|
|
@@ -235,7 +242,7 @@ console.log(project.render({
|
|
|
235
242
|
<img src="media/pixel_art.png" alt="Pixel art" />
|
|
236
243
|
|
|
237
244
|
```ts
|
|
238
|
-
import { images, createCanvas, gradientRect } from 'ansimax';
|
|
245
|
+
import { images, createCanvas, gradientRect, SPRITES } from 'ansimax';
|
|
239
246
|
|
|
240
247
|
// Built-in sprite
|
|
241
248
|
console.log(images.sprite('heart'));
|
|
@@ -251,7 +258,8 @@ console.log(gradientRect({
|
|
|
251
258
|
const c = createCanvas(40, 10);
|
|
252
259
|
c.fill({ r: 18, g: 18, b: 38 });
|
|
253
260
|
c.drawCircle(20, 5, 4, { r: 255, g: 200, b: 0 }, true);
|
|
254
|
-
|
|
261
|
+
const starSprite = SPRITES.star;
|
|
262
|
+
if (starSprite) c.drawSprite(2, 2, starSprite.pixels);
|
|
255
263
|
c.print();
|
|
256
264
|
```
|
|
257
265
|
|
|
@@ -262,15 +270,15 @@ c.print();
|
|
|
262
270
|
```ts
|
|
263
271
|
import { components, color } from 'ansimax';
|
|
264
272
|
|
|
265
|
-
components.table([
|
|
273
|
+
console.log(components.table([
|
|
266
274
|
['Module', 'Status', 'Coverage'],
|
|
267
275
|
['colors', color.green('● ready'), '100%'],
|
|
268
276
|
['animations', color.green('● ready'), '100%'],
|
|
269
277
|
['loaders', color.green('● ready'), '100%'],
|
|
270
|
-
], { borderStyle: 'rounded' });
|
|
278
|
+
], { borderStyle: 'rounded' }));
|
|
271
279
|
|
|
272
|
-
components.badge('VERSION', 'v1.1.
|
|
273
|
-
components.badge('BUILD', 'passing');
|
|
280
|
+
console.log(components.badge('VERSION', 'v1.1.2'));
|
|
281
|
+
console.log(components.badge('BUILD', 'passing'));
|
|
274
282
|
```
|
|
275
283
|
|
|
276
284
|
### Timeline
|
|
@@ -278,22 +286,24 @@ components.badge('BUILD', 'passing');
|
|
|
278
286
|
<img src="media/timeline.png" alt="Timeline" />
|
|
279
287
|
|
|
280
288
|
```ts
|
|
281
|
-
components
|
|
289
|
+
import { components } from 'ansimax';
|
|
290
|
+
|
|
291
|
+
console.log(components.timeline([
|
|
282
292
|
{ label: 'Project init', done: true, time: '10:00' },
|
|
283
293
|
{ label: 'Build pipeline', done: true, time: '10:15' },
|
|
284
294
|
{ label: 'Run tests', done: false, time: '10:32' },
|
|
285
295
|
{ label: 'Deploy to npm', done: false },
|
|
286
|
-
]);
|
|
296
|
+
]));
|
|
287
297
|
```
|
|
288
298
|
|
|
289
299
|
### Loaders & Progress
|
|
290
300
|
|
|
291
301
|
```ts
|
|
292
|
-
import { loader } from 'ansimax';
|
|
302
|
+
import { loader, sleep } from 'ansimax';
|
|
293
303
|
|
|
294
304
|
// Spinner with success/failure
|
|
295
305
|
const stop = loader.spin('Loading...', { color: '#bd93f9' });
|
|
296
|
-
await
|
|
306
|
+
await sleep(1500);
|
|
297
307
|
stop('Done!', true); // ✓ green icon
|
|
298
308
|
|
|
299
309
|
// Animated progress bar
|
|
@@ -303,18 +313,22 @@ await loader.progressAnimate(100, 'Downloading', {
|
|
|
303
313
|
|
|
304
314
|
// Hierarchical tasks with parallel execution
|
|
305
315
|
await loader.tasks([
|
|
306
|
-
{
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
316
|
+
{
|
|
317
|
+
text: 'Build',
|
|
318
|
+
fn: async () => await sleep(500),
|
|
319
|
+
subtasks: [
|
|
320
|
+
{ text: 'TypeScript', fn: async () => await sleep(800) },
|
|
321
|
+
{ text: 'Bundle', fn: async () => await sleep(600) },
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
{ text: 'Test', fn: async () => await sleep(700) },
|
|
311
325
|
], { parallel: true });
|
|
312
326
|
```
|
|
313
327
|
|
|
314
328
|
### Animations
|
|
315
329
|
|
|
316
330
|
```ts
|
|
317
|
-
import { animate, gradient } from 'ansimax';
|
|
331
|
+
import { animate, gradient, sleep } from 'ansimax';
|
|
318
332
|
|
|
319
333
|
await animate.typewriter('Welcome to the deployment wizard...', {
|
|
320
334
|
speed: 30,
|
|
@@ -325,9 +339,9 @@ await animate.fadeIn('Loading complete', { duration: 600 });
|
|
|
325
339
|
|
|
326
340
|
// Race steps against a timeout — never hang
|
|
327
341
|
await animate.parallel([
|
|
328
|
-
async () => await
|
|
329
|
-
async () => await
|
|
330
|
-
async () => await
|
|
342
|
+
async () => await sleep(500), // simulated network check
|
|
343
|
+
async () => await sleep(700), // simulated database check
|
|
344
|
+
async () => await sleep(400), // simulated auth check
|
|
331
345
|
], { timeout: 5000 });
|
|
332
346
|
```
|
|
333
347
|
|
|
@@ -340,7 +354,7 @@ import { themes, createTheme } from 'ansimax';
|
|
|
340
354
|
|
|
341
355
|
// Built-in themes
|
|
342
356
|
themes.use('dracula');
|
|
343
|
-
themes.primary('hello');
|
|
357
|
+
console.log(themes.primary('hello'));
|
|
344
358
|
|
|
345
359
|
// Listen for changes
|
|
346
360
|
const off = themes.onChange((newTheme, oldTheme) => {
|
|
@@ -350,7 +364,27 @@ const off = themes.onChange((newTheme, oldTheme) => {
|
|
|
350
364
|
// Multi-tenant: each instance fully isolated
|
|
351
365
|
const tenantA = createTheme('nord');
|
|
352
366
|
const tenantB = createTheme('matrix');
|
|
353
|
-
|
|
367
|
+
|
|
368
|
+
// Define a custom theme and register it ONLY in tenantA
|
|
369
|
+
tenantA.register('custom', {
|
|
370
|
+
name: 'Custom',
|
|
371
|
+
primary: '#ff5e5e',
|
|
372
|
+
secondary: '#5e5eff',
|
|
373
|
+
accent: '#5eff5e',
|
|
374
|
+
success: '#10b981',
|
|
375
|
+
warning: '#fbbf24',
|
|
376
|
+
error: '#ef4444',
|
|
377
|
+
info: '#06b6d4',
|
|
378
|
+
muted: '#6b7280',
|
|
379
|
+
bg: '#1e293b',
|
|
380
|
+
surface: '#334155',
|
|
381
|
+
text: '#f1f5f9',
|
|
382
|
+
gradient: ['#ff5e5e', '#5eff5e', '#5e5eff'],
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
console.log('tenantA themes include custom?', tenantA.list().includes('custom'));
|
|
386
|
+
console.log('tenantB themes include custom?', tenantB.list().includes('custom'));
|
|
387
|
+
// ↑ false — full isolation
|
|
354
388
|
```
|
|
355
389
|
|
|
356
390
|
---
|
|
@@ -421,11 +455,11 @@ const off = onConfigKeyChange('theme', (newTheme, oldTheme) => {
|
|
|
421
455
|
|
|
422
456
|
// Temporary override + auto-restore on completion or throw
|
|
423
457
|
await withConfig({ animationSpeed: 'fast' }, async () => {
|
|
424
|
-
|
|
458
|
+
// ...your fast-mode code here...
|
|
425
459
|
});
|
|
426
460
|
|
|
427
461
|
// Strict mode catches config typos
|
|
428
|
-
configure({ unknwnKey: 'x' }, { strict: true }); // throws RangeError
|
|
462
|
+
// configure({ unknwnKey: 'x' }, { strict: true }); // throws RangeError
|
|
429
463
|
```
|
|
430
464
|
|
|
431
465
|
---
|
|
@@ -645,6 +679,17 @@ ansimax/
|
|
|
645
679
|
|
|
646
680
|
## 📝 Changelog
|
|
647
681
|
|
|
682
|
+
### v1.1.2 — Maturity & robustness
|
|
683
|
+
|
|
684
|
+
Patch release focused on quality refinements — no API changes.
|
|
685
|
+
|
|
686
|
+
- 🛡️ **`process.setMaxListeners` defensive bump** — prevents `MaxListenersExceededWarning` in HMR / nodemon / ts-node-dev setups where ansimax modules re-register cursor-restore handlers
|
|
687
|
+
- 🧪 **Uniform `TypeError` for theme validation** — `themes.register()` now consistently throws `TypeError` for structural / type errors (was a mix of `Error` and `TypeError`)
|
|
688
|
+
- 🎯 **`themes.use()` throws `RangeError`** for unknown theme names (was `Error`) — better semantic match for "value out of allowed set"
|
|
689
|
+
- 📝 **Cleaner barrel re-exports** — header comment now documents legacy aliases and recommends canonical names
|
|
690
|
+
|
|
691
|
+
Drop-in replacement for `1.1.1`.
|
|
692
|
+
|
|
648
693
|
### v1.1.1 — Bug fixes + cleaner examples
|
|
649
694
|
|
|
650
695
|
Patch release fixing two bugs from real-world v1.1.0 testing, plus a refreshed examples folder.
|
package/dist/index.js
CHANGED
|
@@ -225,6 +225,11 @@ var cursor = {
|
|
|
225
225
|
var _exitHandlerRegistered = false;
|
|
226
226
|
var _isTestEnv = () => process.env["JEST_WORKER_ID"] !== void 0 || process.env["VITEST"] !== void 0 || process.env["NODE_ENV"] === "test";
|
|
227
227
|
var _installCursorRestoreImpl = () => {
|
|
228
|
+
try {
|
|
229
|
+
const current = process.getMaxListeners?.() ?? 10;
|
|
230
|
+
if (current < 20) process.setMaxListeners?.(20);
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
228
233
|
const restore = () => {
|
|
229
234
|
try {
|
|
230
235
|
safeStreamWrite(process.stdout, cursor.show());
|
|
@@ -4113,12 +4118,12 @@ var validateTheme = (t) => {
|
|
|
4113
4118
|
}
|
|
4114
4119
|
const obj = t;
|
|
4115
4120
|
if (typeof obj.name !== "string" || obj.name.length === 0) {
|
|
4116
|
-
throw new
|
|
4121
|
+
throw new TypeError('Theme must have a non-empty "name" string.');
|
|
4117
4122
|
}
|
|
4118
4123
|
for (const key of REQUIRED_COLOR_KEYS) {
|
|
4119
4124
|
const value = obj[key];
|
|
4120
4125
|
if (typeof value !== "string" || !HEX_RE4.test(value)) {
|
|
4121
|
-
throw new
|
|
4126
|
+
throw new TypeError(
|
|
4122
4127
|
`Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}. Expected #RGB or #RRGGBB.`
|
|
4123
4128
|
);
|
|
4124
4129
|
}
|
|
@@ -4127,20 +4132,20 @@ var validateTheme = (t) => {
|
|
|
4127
4132
|
const value = obj[key];
|
|
4128
4133
|
if (value === void 0) continue;
|
|
4129
4134
|
if (typeof value !== "string" || !HEX_RE4.test(value)) {
|
|
4130
|
-
throw new
|
|
4135
|
+
throw new TypeError(
|
|
4131
4136
|
`Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}.`
|
|
4132
4137
|
);
|
|
4133
4138
|
}
|
|
4134
4139
|
}
|
|
4135
4140
|
if (!Array.isArray(obj.gradient) || obj.gradient.length < 2) {
|
|
4136
|
-
throw new
|
|
4141
|
+
throw new TypeError(
|
|
4137
4142
|
`Theme "${obj.name}" must define a "gradient" array with at least 2 colors.`
|
|
4138
4143
|
);
|
|
4139
4144
|
}
|
|
4140
4145
|
for (let i = 0; i < obj.gradient.length; i++) {
|
|
4141
4146
|
const stop = obj.gradient[i];
|
|
4142
4147
|
if (typeof stop !== "string" || !HEX_RE4.test(stop)) {
|
|
4143
|
-
throw new
|
|
4148
|
+
throw new TypeError(
|
|
4144
4149
|
`Invalid hex in theme "${obj.name}" \u2192 gradient[${i}]: ${JSON.stringify(stop)}.`
|
|
4145
4150
|
);
|
|
4146
4151
|
}
|
|
@@ -4452,8 +4457,8 @@ var themes = {
|
|
|
4452
4457
|
use(name) {
|
|
4453
4458
|
const t = _globalRegistry.get(name);
|
|
4454
4459
|
if (!t) {
|
|
4455
|
-
throw new
|
|
4456
|
-
`Theme "${name}" not found. Available: ${[..._globalRegistry.keys()].join(", ")}`
|
|
4460
|
+
throw new RangeError(
|
|
4461
|
+
`Theme "${name}" not found. Available themes: ${[..._globalRegistry.keys()].join(", ")}`
|
|
4457
4462
|
);
|
|
4458
4463
|
}
|
|
4459
4464
|
const old = _globalActive;
|
package/dist/index.mjs
CHANGED
|
@@ -53,6 +53,11 @@ var cursor = {
|
|
|
53
53
|
var _exitHandlerRegistered = false;
|
|
54
54
|
var _isTestEnv = () => process.env["JEST_WORKER_ID"] !== void 0 || process.env["VITEST"] !== void 0 || process.env["NODE_ENV"] === "test";
|
|
55
55
|
var _installCursorRestoreImpl = () => {
|
|
56
|
+
try {
|
|
57
|
+
const current = process.getMaxListeners?.() ?? 10;
|
|
58
|
+
if (current < 20) process.setMaxListeners?.(20);
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
56
61
|
const restore = () => {
|
|
57
62
|
try {
|
|
58
63
|
safeStreamWrite(process.stdout, cursor.show());
|
|
@@ -3941,12 +3946,12 @@ var validateTheme = (t) => {
|
|
|
3941
3946
|
}
|
|
3942
3947
|
const obj = t;
|
|
3943
3948
|
if (typeof obj.name !== "string" || obj.name.length === 0) {
|
|
3944
|
-
throw new
|
|
3949
|
+
throw new TypeError('Theme must have a non-empty "name" string.');
|
|
3945
3950
|
}
|
|
3946
3951
|
for (const key of REQUIRED_COLOR_KEYS) {
|
|
3947
3952
|
const value = obj[key];
|
|
3948
3953
|
if (typeof value !== "string" || !HEX_RE4.test(value)) {
|
|
3949
|
-
throw new
|
|
3954
|
+
throw new TypeError(
|
|
3950
3955
|
`Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}. Expected #RGB or #RRGGBB.`
|
|
3951
3956
|
);
|
|
3952
3957
|
}
|
|
@@ -3955,20 +3960,20 @@ var validateTheme = (t) => {
|
|
|
3955
3960
|
const value = obj[key];
|
|
3956
3961
|
if (value === void 0) continue;
|
|
3957
3962
|
if (typeof value !== "string" || !HEX_RE4.test(value)) {
|
|
3958
|
-
throw new
|
|
3963
|
+
throw new TypeError(
|
|
3959
3964
|
`Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}.`
|
|
3960
3965
|
);
|
|
3961
3966
|
}
|
|
3962
3967
|
}
|
|
3963
3968
|
if (!Array.isArray(obj.gradient) || obj.gradient.length < 2) {
|
|
3964
|
-
throw new
|
|
3969
|
+
throw new TypeError(
|
|
3965
3970
|
`Theme "${obj.name}" must define a "gradient" array with at least 2 colors.`
|
|
3966
3971
|
);
|
|
3967
3972
|
}
|
|
3968
3973
|
for (let i = 0; i < obj.gradient.length; i++) {
|
|
3969
3974
|
const stop = obj.gradient[i];
|
|
3970
3975
|
if (typeof stop !== "string" || !HEX_RE4.test(stop)) {
|
|
3971
|
-
throw new
|
|
3976
|
+
throw new TypeError(
|
|
3972
3977
|
`Invalid hex in theme "${obj.name}" \u2192 gradient[${i}]: ${JSON.stringify(stop)}.`
|
|
3973
3978
|
);
|
|
3974
3979
|
}
|
|
@@ -4280,8 +4285,8 @@ var themes = {
|
|
|
4280
4285
|
use(name) {
|
|
4281
4286
|
const t = _globalRegistry.get(name);
|
|
4282
4287
|
if (!t) {
|
|
4283
|
-
throw new
|
|
4284
|
-
`Theme "${name}" not found. Available: ${[..._globalRegistry.keys()].join(", ")}`
|
|
4288
|
+
throw new RangeError(
|
|
4289
|
+
`Theme "${name}" not found. Available themes: ${[..._globalRegistry.keys()].join(", ")}`
|
|
4285
4290
|
);
|
|
4286
4291
|
}
|
|
4287
4292
|
const old = _globalActive;
|
package/examples/all-in-one.cjs
CHANGED
|
@@ -118,7 +118,7 @@ async function main() {
|
|
|
118
118
|
console.log(components.section('🏷️ Badges & Status', { width: 60 }));
|
|
119
119
|
console.log();
|
|
120
120
|
console.log(' ',
|
|
121
|
-
components.badge('VERSION', 'v1.1.
|
|
121
|
+
components.badge('VERSION', 'v1.1.2'),
|
|
122
122
|
components.badge('BUILD', 'passing'),
|
|
123
123
|
components.badge('LICENSE', 'Apache 2.0'));
|
|
124
124
|
console.log();
|
package/examples/all-in-one.mjs
CHANGED
|
@@ -117,7 +117,7 @@ console.log();
|
|
|
117
117
|
console.log(components.section('🏷️ Badges & Status', { width: 60 }));
|
|
118
118
|
console.log();
|
|
119
119
|
console.log(' ',
|
|
120
|
-
components.badge('VERSION', 'v1.1.
|
|
120
|
+
components.badge('VERSION', 'v1.1.2'),
|
|
121
121
|
components.badge('BUILD', 'passing'),
|
|
122
122
|
components.badge('LICENSE', 'Apache 2.0'));
|
|
123
123
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ansimax",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Zero-dependency CLI rendering library: colors, gradients, animations, ASCII art, pixel art, components, and themes \u2014 all in TypeScript.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|