ansimax 1.2.6 → 1.2.8
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 +98 -0
- package/README.es.md +75 -3
- package/README.md +75 -3
- package/dist/index.d.mts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +32 -9
- package/dist/index.mjs +32 -9
- 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,104 @@
|
|
|
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.2.8] — Documentation polish
|
|
7
|
+
|
|
8
|
+
Patch release improving JSDoc and IntelliSense coverage across previously
|
|
9
|
+
under-documented modules. No code changes — pure documentation upgrade.
|
|
10
|
+
|
|
11
|
+
### Improved — JSDoc with runnable examples
|
|
12
|
+
|
|
13
|
+
The following functions now have full JSDoc with `@example` blocks visible
|
|
14
|
+
in editor IntelliSense (VS Code, IntelliJ, etc.):
|
|
15
|
+
|
|
16
|
+
**`components/` (previously 0 examples → now 4):**
|
|
17
|
+
- `components.table` — 3 examples (basic, custom borders, colored cells)
|
|
18
|
+
- `components.badge` — 3 examples (basic, custom colors, inline composition)
|
|
19
|
+
- `components.status` — 3 examples (basic, multiline, custom icons)
|
|
20
|
+
- `components.timeline` — 3 examples (basic, done/pending, custom symbols)
|
|
21
|
+
|
|
22
|
+
**`loaders/` (previously 0 examples → now 2):**
|
|
23
|
+
- `loader.spin` — 3 examples (basic, custom type/color, try/finally pattern)
|
|
24
|
+
- `loader.tasks` — 4 examples (serial, subtasks, parallel mode, error handling)
|
|
25
|
+
|
|
26
|
+
**`themes/` (previously 0 examples → now 4):**
|
|
27
|
+
- `themes` object — 4 examples (switching, registering, subscribing, fallback)
|
|
28
|
+
|
|
29
|
+
**`animations/` (previously 1 example → now 6):**
|
|
30
|
+
- `animate.typewriter` — 4 examples (basic, colored, abortable, reduced-motion)
|
|
31
|
+
- `animate.fadeIn` — 3 examples (basic, custom timing, abortable)
|
|
32
|
+
|
|
33
|
+
**`ascii/`:**
|
|
34
|
+
- `ascii.box` — 4 examples (basic, multiline, fixed-width, with color)
|
|
35
|
+
|
|
36
|
+
### Notes
|
|
37
|
+
|
|
38
|
+
- No new tests required — pure documentation changes
|
|
39
|
+
- No runtime dependencies — still zero
|
|
40
|
+
- No API changes — drop-in replacement for `1.2.7`
|
|
41
|
+
- IntelliSense quality dramatically improved for new users
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## [1.2.7] — Bug fixes + robustness
|
|
46
|
+
|
|
47
|
+
Patch release focused on edge case handling, better error messages, and
|
|
48
|
+
defensive coding. No breaking changes — every 1.2.x program runs identically.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **`ascii.fromImage()` silently accepted `width: 0`, `NaN`, `Infinity`** —
|
|
53
|
+
now returns an empty string explicitly. Previously it would clamp to 1
|
|
54
|
+
and produce a single-character-wide output, which was confusing:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// Before (v1.2.6 and earlier):
|
|
58
|
+
ascii.fromImage(pixels, { width: 0 }); // → 1-char-wide output 😞
|
|
59
|
+
ascii.fromImage(pixels, { width: NaN }); // → 1-char-wide output 😞
|
|
60
|
+
|
|
61
|
+
// Now (v1.2.7):
|
|
62
|
+
ascii.fromImage(pixels, { width: 0 }); // → '' (explicit)
|
|
63
|
+
ascii.fromImage(pixels, { width: NaN }); // → ''
|
|
64
|
+
ascii.fromImage(pixels, { width: -10 }); // → ''
|
|
65
|
+
ascii.fromImage(pixels, { width: 1 }); // → still works, 1-char wide
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Same validation applies to `height` when explicitly set.
|
|
69
|
+
|
|
70
|
+
- **`ascii.figletText('', font)` returned `font.height - 1` empty lines**
|
|
71
|
+
instead of an empty string. Now returns `''` immediately.
|
|
72
|
+
|
|
73
|
+
- **`ascii.fromImage()` could crash on non-rectangular grids** (rows of
|
|
74
|
+
different widths) because `_resizePixels` assumed all rows had the same
|
|
75
|
+
length as the first row. Now each row is sampled by its actual width,
|
|
76
|
+
with missing pixels coalesced to `null` (the standard "transparent"
|
|
77
|
+
marker).
|
|
78
|
+
|
|
79
|
+
### Improved — Error codes everywhere
|
|
80
|
+
|
|
81
|
+
Errors thrown by the ASCII module now carry stable `.code` properties for
|
|
82
|
+
programmatic catching:
|
|
83
|
+
|
|
84
|
+
| Function | Error condition | `.code` |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `parseFiglet` | non-string / empty input | `ANSIMAX_INVALID_FIGLET_INPUT` |
|
|
87
|
+
| `parseFiglet` | unrecognized header | `ANSIMAX_INVALID_FIGLET_HEADER` |
|
|
88
|
+
| `parseFiglet` | non-positive height | `ANSIMAX_INVALID_FIGLET_HEIGHT` |
|
|
89
|
+
| `ascii.registerFont` | empty name | `ANSIMAX_INVALID_FONT_NAME` |
|
|
90
|
+
| `ascii.registerFont` | reserved name without `force` | `ANSIMAX_RESERVED_FONT_NAME` |
|
|
91
|
+
|
|
92
|
+
Error messages also include a debug snippet for `parseFiglet` header
|
|
93
|
+
errors (truncated at 60 chars), so you immediately see what was wrong.
|
|
94
|
+
|
|
95
|
+
### Notes
|
|
96
|
+
|
|
97
|
+
- Error message text may have changed slightly — if you were matching exact
|
|
98
|
+
strings in tests, switch to `.code` checks (which are stable forever)
|
|
99
|
+
- All 1983 + 17 new tests pass
|
|
100
|
+
- No new runtime dependencies — still zero
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
6
104
|
## [1.2.6] — ASCII module improvements
|
|
7
105
|
|
|
8
106
|
Patch release focused on ASCII module quality and feature additions. No
|
package/README.es.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
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
|
-
[](#testing)
|
|
14
14
|
[](#)
|
|
15
15
|
[](#)
|
|
16
16
|
|
|
@@ -434,7 +434,7 @@ console.log(components.table([
|
|
|
434
434
|
['loaders', color.green('● listo'), '100%'],
|
|
435
435
|
], { borderStyle: 'rounded' }));
|
|
436
436
|
|
|
437
|
-
console.log(components.badge('VERSION', 'v1.2.
|
|
437
|
+
console.log(components.badge('VERSION', 'v1.2.8'));
|
|
438
438
|
console.log(components.badge('BUILD', 'passing'));
|
|
439
439
|
```
|
|
440
440
|
|
|
@@ -621,6 +621,40 @@ await withConfig({ animationSpeed: 'fast' }, async () => {
|
|
|
621
621
|
|
|
622
622
|
---
|
|
623
623
|
|
|
624
|
+
## ⚠️ Códigos de error
|
|
625
|
+
|
|
626
|
+
Varias funciones de ansimax lanzan `Error` / `TypeError` / `RangeError` para inputs inválidos.
|
|
627
|
+
Hacer `catch` por código de error es la forma **estable y recomendada** para manejarlos programáticamente — el texto del mensaje puede cambiar, pero los valores `.code` están garantizados como semver-estables.
|
|
628
|
+
|
|
629
|
+
```js
|
|
630
|
+
import { themes, ascii, parseFiglet } from 'ansimax';
|
|
631
|
+
|
|
632
|
+
try {
|
|
633
|
+
themes.use('tema-inexistente');
|
|
634
|
+
} catch (e) {
|
|
635
|
+
if (e.code === 'ANSIMAX_UNKNOWN_THEME') {
|
|
636
|
+
themes.use('dracula'); // fallback
|
|
637
|
+
} else {
|
|
638
|
+
throw e; // re-lanzar errores inesperados
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Todos los códigos de error
|
|
644
|
+
|
|
645
|
+
| Código | Lanzado por | Tipo | Cuándo |
|
|
646
|
+
|---|---|---|---|
|
|
647
|
+
| `ANSIMAX_INVALID_THEME` | `themes.register` | `TypeError` | El valor del tema no es un objeto plano |
|
|
648
|
+
| `ANSIMAX_INVALID_THEME_NAME` | `themes.register` | `TypeError` | El tema tiene `name` vacío o ausente |
|
|
649
|
+
| `ANSIMAX_UNKNOWN_THEME` | `themes.use` | `RangeError` | El nombre del tema solicitado no está registrado |
|
|
650
|
+
| `ANSIMAX_INVALID_FONT_NAME` | `ascii.registerFont` | `TypeError` | Nombre de fuente vacío o no string |
|
|
651
|
+
| `ANSIMAX_RESERVED_FONT_NAME` | `ascii.registerFont` | `Error` | Sobrescribir fuente built-in sin `{ force: true }` |
|
|
652
|
+
| `ANSIMAX_INVALID_FIGLET_INPUT` | `parseFiglet` | `TypeError` | Contenido `.flf` vacío o no string |
|
|
653
|
+
| `ANSIMAX_INVALID_FIGLET_HEADER` | `parseFiglet` | `TypeError` | La primera línea no es un header FIGfont válido |
|
|
654
|
+
| `ANSIMAX_INVALID_FIGLET_HEIGHT` | `parseFiglet` | `TypeError` | El header declara altura cero o negativa |
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
624
658
|
## 🛣️ Roadmap
|
|
625
659
|
|
|
626
660
|
Ansimax se está construyendo hacia una **plataforma completa de renderizado de terminal** — una respuesta nativa de Node a lo que los desarrolladores de Python obtienen de `rich` + `textual` combinados, con mejoras específicas de Node donde importa.
|
|
@@ -837,6 +871,44 @@ ansimax/
|
|
|
837
871
|
|
|
838
872
|
## 📝 Changelog
|
|
839
873
|
|
|
874
|
+
### v1.2.8 — Pulido de documentación
|
|
875
|
+
|
|
876
|
+
Release patch con cobertura JSDoc + IntelliSense masivamente mejorada:
|
|
877
|
+
|
|
878
|
+
- 📝 **Módulo `components`** — `table`, `badge`, `status`, `timeline` ahora tienen JSDoc completo con ejemplos ejecutables
|
|
879
|
+
- 📝 **Módulo `loaders`** — `loader.spin` y `loader.tasks` ahora muestran patrones de uso en IntelliSense
|
|
880
|
+
- 📝 **Módulo `themes`** — JSDoc completo con ejemplos de cambio, registro y suscripción
|
|
881
|
+
- 📝 **Módulo `animations`** — `animate.typewriter` y `animate.fadeIn` muestran cómo usar abort signals, reduced-motion, colores custom
|
|
882
|
+
- 📝 **`ascii.box`** — 4 ejemplos (básico, multilínea, ancho fijo, con color)
|
|
883
|
+
- 📖 **Sección Códigos de error** en README — los 8 códigos `ANSIMAX_*` documentados
|
|
884
|
+
|
|
885
|
+
Total: ~30 nuevos bloques `@example` visibles en tu editor. Cero cambios en código —
|
|
886
|
+
tus programas existentes corren idéntico.
|
|
887
|
+
|
|
888
|
+
Drop-in replacement para `1.2.7`.
|
|
889
|
+
|
|
890
|
+
### v1.2.7 — Correcciones + robustez
|
|
891
|
+
|
|
892
|
+
Release patch enfocado en manejo de edge cases y mejor DX:
|
|
893
|
+
|
|
894
|
+
- 🐛 **`fromImage` rechaza dimensiones inválidas explícitamente** — `width: 0`, `NaN`, `Infinity` ahora retornan `''` en vez de clampear silenciosamente
|
|
895
|
+
- 🐛 **`figletText('')` retorna `''`** — antes retornaba filas vacías
|
|
896
|
+
- 🛡️ **Grids no-rectangulares manejados con gracia** — filas de distinto largo ya no crashean `fromImage`
|
|
897
|
+
- 🎯 **Códigos de error añadidos en todo el módulo ASCII** — `ANSIMAX_INVALID_FIGLET_HEADER`, `ANSIMAX_INVALID_FONT_NAME`, etc. para `catch` programático
|
|
898
|
+
- 📝 **Mejores mensajes de error** — `parseFiglet` ahora incluye snippet del input problemático
|
|
899
|
+
|
|
900
|
+
```js
|
|
901
|
+
try {
|
|
902
|
+
ascii.registerFont('big', myFont); // 'big' está reservado
|
|
903
|
+
} catch (e) {
|
|
904
|
+
if (e.code === 'ANSIMAX_RESERVED_FONT_NAME') {
|
|
905
|
+
ascii.registerFont('big', myFont, { force: true }); // override
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Drop-in replacement para `1.2.6`.
|
|
911
|
+
|
|
840
912
|
### v1.2.6 — Mejoras del módulo ASCII
|
|
841
913
|
|
|
842
914
|
Release patch con mejoras al motor ASCII:
|
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
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
|
-
[](#testing)
|
|
14
14
|
[](#)
|
|
15
15
|
[](#)
|
|
16
16
|
|
|
@@ -434,7 +434,7 @@ console.log(components.table([
|
|
|
434
434
|
['loaders', color.green('● ready'), '100%'],
|
|
435
435
|
], { borderStyle: 'rounded' }));
|
|
436
436
|
|
|
437
|
-
console.log(components.badge('VERSION', 'v1.2.
|
|
437
|
+
console.log(components.badge('VERSION', 'v1.2.8'));
|
|
438
438
|
console.log(components.badge('BUILD', 'passing'));
|
|
439
439
|
```
|
|
440
440
|
|
|
@@ -621,6 +621,40 @@ await withConfig({ animationSpeed: 'fast' }, async () => {
|
|
|
621
621
|
|
|
622
622
|
---
|
|
623
623
|
|
|
624
|
+
## ⚠️ Error codes
|
|
625
|
+
|
|
626
|
+
Several ansimax functions throw `Error` / `TypeError` / `RangeError` for invalid input.
|
|
627
|
+
Catching by error code is the **stable, recommended** way to handle them programmatically — message text may evolve, but `.code` values are guaranteed semver-stable.
|
|
628
|
+
|
|
629
|
+
```js
|
|
630
|
+
import { themes, ascii, parseFiglet } from 'ansimax';
|
|
631
|
+
|
|
632
|
+
try {
|
|
633
|
+
themes.use('inexistent-theme');
|
|
634
|
+
} catch (e) {
|
|
635
|
+
if (e.code === 'ANSIMAX_UNKNOWN_THEME') {
|
|
636
|
+
themes.use('dracula'); // fallback
|
|
637
|
+
} else {
|
|
638
|
+
throw e; // re-throw unexpected errors
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### All error codes
|
|
644
|
+
|
|
645
|
+
| Code | Thrown by | Type | When |
|
|
646
|
+
|---|---|---|---|
|
|
647
|
+
| `ANSIMAX_INVALID_THEME` | `themes.register` | `TypeError` | Theme value is not a plain object |
|
|
648
|
+
| `ANSIMAX_INVALID_THEME_NAME` | `themes.register` | `TypeError` | Theme has missing/empty `name` |
|
|
649
|
+
| `ANSIMAX_UNKNOWN_THEME` | `themes.use` | `RangeError` | Requested theme name not registered |
|
|
650
|
+
| `ANSIMAX_INVALID_FONT_NAME` | `ascii.registerFont` | `TypeError` | Empty or non-string font name |
|
|
651
|
+
| `ANSIMAX_RESERVED_FONT_NAME` | `ascii.registerFont` | `Error` | Overwriting built-in font without `{ force: true }` |
|
|
652
|
+
| `ANSIMAX_INVALID_FIGLET_INPUT` | `parseFiglet` | `TypeError` | Non-string or empty `.flf` content |
|
|
653
|
+
| `ANSIMAX_INVALID_FIGLET_HEADER` | `parseFiglet` | `TypeError` | First line is not a valid FIGfont header |
|
|
654
|
+
| `ANSIMAX_INVALID_FIGLET_HEIGHT` | `parseFiglet` | `TypeError` | Header declared zero/negative height |
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
624
658
|
## 🛣️ Roadmap
|
|
625
659
|
|
|
626
660
|
Ansimax is being built toward a **full terminal rendering platform** — a Node-native answer to what Python developers get from `rich` + `textual` combined, with Node-specific improvements where it matters.
|
|
@@ -837,6 +871,44 @@ ansimax/
|
|
|
837
871
|
|
|
838
872
|
## 📝 Changelog
|
|
839
873
|
|
|
874
|
+
### v1.2.8 — Documentation polish
|
|
875
|
+
|
|
876
|
+
Patch release with massively improved JSDoc + IntelliSense coverage:
|
|
877
|
+
|
|
878
|
+
- 📝 **`components` module** — `table`, `badge`, `status`, `timeline` now have full JSDoc with runnable examples
|
|
879
|
+
- 📝 **`loaders` module** — `loader.spin` and `loader.tasks` now show usage patterns in IntelliSense
|
|
880
|
+
- 📝 **`themes` module** — full JSDoc with switching, registering, subscribing examples
|
|
881
|
+
- 📝 **`animations` module** — `animate.typewriter` and `animate.fadeIn` show how to use abort signals, reduced-motion, custom colors
|
|
882
|
+
- 📝 **`ascii.box`** — 4 examples (basic, multiline, fixed-width, colored content)
|
|
883
|
+
- 📖 **Error codes section** in README — all 8 `ANSIMAX_*` codes documented
|
|
884
|
+
|
|
885
|
+
Total: ~30 new `@example` blocks visible in your editor. No code changes —
|
|
886
|
+
your existing programs run identically.
|
|
887
|
+
|
|
888
|
+
Drop-in replacement for `1.2.7`.
|
|
889
|
+
|
|
890
|
+
### v1.2.7 — Bug fixes + robustness
|
|
891
|
+
|
|
892
|
+
Patch release focused on edge case handling and better DX:
|
|
893
|
+
|
|
894
|
+
- 🐛 **`fromImage` rejects invalid dimensions explicitly** — `width: 0`, `NaN`, `Infinity` now return `''` instead of clamping silently
|
|
895
|
+
- 🐛 **`figletText('')` returns `''`** — previously returned empty rows
|
|
896
|
+
- 🛡️ **Non-rectangular grids handled gracefully** — rows of different widths no longer crash `fromImage`
|
|
897
|
+
- 🎯 **Error codes added throughout ASCII module** — `ANSIMAX_INVALID_FIGLET_HEADER`, `ANSIMAX_INVALID_FONT_NAME`, etc. for programmatic catch
|
|
898
|
+
- 📝 **Better error messages** — `parseFiglet` now includes a snippet of the problematic input
|
|
899
|
+
|
|
900
|
+
```js
|
|
901
|
+
try {
|
|
902
|
+
ascii.registerFont('big', myFont); // 'big' is reserved
|
|
903
|
+
} catch (e) {
|
|
904
|
+
if (e.code === 'ANSIMAX_RESERVED_FONT_NAME') {
|
|
905
|
+
ascii.registerFont('big', myFont, { force: true }); // override
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Drop-in replacement for `1.2.6`.
|
|
911
|
+
|
|
840
912
|
### v1.2.6 — ASCII module improvements
|
|
841
913
|
|
|
842
914
|
Patch release with ASCII engine improvements:
|
package/dist/index.d.mts
CHANGED
|
@@ -1844,6 +1844,64 @@ interface BannerOpts {
|
|
|
1844
1844
|
declare const createTheme: (initial?: string) => ThemeInstance;
|
|
1845
1845
|
/** Clear the global singleton's color cache. */
|
|
1846
1846
|
declare const clearThemeColorCache: () => void;
|
|
1847
|
+
/**
|
|
1848
|
+
* The global theme registry — a singleton that holds all registered themes
|
|
1849
|
+
* (built-in + custom) and tracks the active one.
|
|
1850
|
+
*
|
|
1851
|
+
* @example switching the active theme
|
|
1852
|
+
* ```js
|
|
1853
|
+
* import { themes } from 'ansimax';
|
|
1854
|
+
*
|
|
1855
|
+
* themes.use('dracula'); // throws if name doesn't exist
|
|
1856
|
+
* themes.tryUse('nord'); // returns true/false, never throws
|
|
1857
|
+
*
|
|
1858
|
+
* const active = themes.current(); // get current Theme definition
|
|
1859
|
+
* console.log(active.primary); // → '#bd93f9'
|
|
1860
|
+
* ```
|
|
1861
|
+
*
|
|
1862
|
+
* @example registering a custom theme
|
|
1863
|
+
* ```js
|
|
1864
|
+
* themes.register('synthwave', {
|
|
1865
|
+
* name: 'synthwave',
|
|
1866
|
+
* primary: '#ff6ec7',
|
|
1867
|
+
* secondary: '#36d6e7',
|
|
1868
|
+
* accent: '#ffd93d',
|
|
1869
|
+
* success: '#06d6a0',
|
|
1870
|
+
* warning: '#ffd93d',
|
|
1871
|
+
* error: '#ff5e5b',
|
|
1872
|
+
* info: '#36d6e7',
|
|
1873
|
+
* muted: '#6c757d',
|
|
1874
|
+
* bg: '#241734',
|
|
1875
|
+
* surface: '#34174f',
|
|
1876
|
+
* text: '#ffffff',
|
|
1877
|
+
* gradient: ['#ff6ec7', '#36d6e7'],
|
|
1878
|
+
* });
|
|
1879
|
+
*
|
|
1880
|
+
* themes.use('synthwave');
|
|
1881
|
+
* ```
|
|
1882
|
+
*
|
|
1883
|
+
* @example subscribing to theme changes
|
|
1884
|
+
* ```js
|
|
1885
|
+
* const unsubscribe = themes.onChange((newT, oldT) => {
|
|
1886
|
+
* console.log(`Theme changed: ${oldT.name} → ${newT.name}`);
|
|
1887
|
+
* // re-render your UI here
|
|
1888
|
+
* });
|
|
1889
|
+
*
|
|
1890
|
+
* // Later: stop listening
|
|
1891
|
+
* unsubscribe();
|
|
1892
|
+
* ```
|
|
1893
|
+
*
|
|
1894
|
+
* @example handling missing themes gracefully
|
|
1895
|
+
* ```js
|
|
1896
|
+
* try {
|
|
1897
|
+
* themes.use('not-real');
|
|
1898
|
+
* } catch (e) {
|
|
1899
|
+
* if (e.code === 'ANSIMAX_UNKNOWN_THEME') {
|
|
1900
|
+
* themes.use('dracula'); // fallback
|
|
1901
|
+
* }
|
|
1902
|
+
* }
|
|
1903
|
+
* ```
|
|
1904
|
+
*/
|
|
1847
1905
|
declare const themes: ThemeInstance;
|
|
1848
1906
|
|
|
1849
1907
|
declare const ansimax: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1844,6 +1844,64 @@ interface BannerOpts {
|
|
|
1844
1844
|
declare const createTheme: (initial?: string) => ThemeInstance;
|
|
1845
1845
|
/** Clear the global singleton's color cache. */
|
|
1846
1846
|
declare const clearThemeColorCache: () => void;
|
|
1847
|
+
/**
|
|
1848
|
+
* The global theme registry — a singleton that holds all registered themes
|
|
1849
|
+
* (built-in + custom) and tracks the active one.
|
|
1850
|
+
*
|
|
1851
|
+
* @example switching the active theme
|
|
1852
|
+
* ```js
|
|
1853
|
+
* import { themes } from 'ansimax';
|
|
1854
|
+
*
|
|
1855
|
+
* themes.use('dracula'); // throws if name doesn't exist
|
|
1856
|
+
* themes.tryUse('nord'); // returns true/false, never throws
|
|
1857
|
+
*
|
|
1858
|
+
* const active = themes.current(); // get current Theme definition
|
|
1859
|
+
* console.log(active.primary); // → '#bd93f9'
|
|
1860
|
+
* ```
|
|
1861
|
+
*
|
|
1862
|
+
* @example registering a custom theme
|
|
1863
|
+
* ```js
|
|
1864
|
+
* themes.register('synthwave', {
|
|
1865
|
+
* name: 'synthwave',
|
|
1866
|
+
* primary: '#ff6ec7',
|
|
1867
|
+
* secondary: '#36d6e7',
|
|
1868
|
+
* accent: '#ffd93d',
|
|
1869
|
+
* success: '#06d6a0',
|
|
1870
|
+
* warning: '#ffd93d',
|
|
1871
|
+
* error: '#ff5e5b',
|
|
1872
|
+
* info: '#36d6e7',
|
|
1873
|
+
* muted: '#6c757d',
|
|
1874
|
+
* bg: '#241734',
|
|
1875
|
+
* surface: '#34174f',
|
|
1876
|
+
* text: '#ffffff',
|
|
1877
|
+
* gradient: ['#ff6ec7', '#36d6e7'],
|
|
1878
|
+
* });
|
|
1879
|
+
*
|
|
1880
|
+
* themes.use('synthwave');
|
|
1881
|
+
* ```
|
|
1882
|
+
*
|
|
1883
|
+
* @example subscribing to theme changes
|
|
1884
|
+
* ```js
|
|
1885
|
+
* const unsubscribe = themes.onChange((newT, oldT) => {
|
|
1886
|
+
* console.log(`Theme changed: ${oldT.name} → ${newT.name}`);
|
|
1887
|
+
* // re-render your UI here
|
|
1888
|
+
* });
|
|
1889
|
+
*
|
|
1890
|
+
* // Later: stop listening
|
|
1891
|
+
* unsubscribe();
|
|
1892
|
+
* ```
|
|
1893
|
+
*
|
|
1894
|
+
* @example handling missing themes gracefully
|
|
1895
|
+
* ```js
|
|
1896
|
+
* try {
|
|
1897
|
+
* themes.use('not-real');
|
|
1898
|
+
* } catch (e) {
|
|
1899
|
+
* if (e.code === 'ANSIMAX_UNKNOWN_THEME') {
|
|
1900
|
+
* themes.use('dracula'); // fallback
|
|
1901
|
+
* }
|
|
1902
|
+
* }
|
|
1903
|
+
* ```
|
|
1904
|
+
*/
|
|
1847
1905
|
declare const themes: ThemeInstance;
|
|
1848
1906
|
|
|
1849
1907
|
declare const ansimax: {
|
package/dist/index.js
CHANGED
|
@@ -2346,12 +2346,16 @@ var validateFont = (name, fontMap) => {
|
|
|
2346
2346
|
};
|
|
2347
2347
|
var registerFont = (name, fontMap, opts = {}) => {
|
|
2348
2348
|
if (typeof name !== "string" || !name.length) {
|
|
2349
|
-
|
|
2349
|
+
const err = new TypeError("ascii.registerFont: name must be a non-empty string");
|
|
2350
|
+
err.code = "ANSIMAX_INVALID_FONT_NAME";
|
|
2351
|
+
throw err;
|
|
2350
2352
|
}
|
|
2351
2353
|
if (RESERVED_FONT_NAMES.has(name) && !opts.force) {
|
|
2352
|
-
|
|
2354
|
+
const err = new Error(
|
|
2353
2355
|
`Font name "${name}" is reserved. Pass { force: true } to override.`
|
|
2354
2356
|
);
|
|
2357
|
+
err.code = "ANSIMAX_RESERVED_FONT_NAME";
|
|
2358
|
+
throw err;
|
|
2355
2359
|
}
|
|
2356
2360
|
const safeMap = ensureFontMap(fontMap, name);
|
|
2357
2361
|
validateFont(name, safeMap);
|
|
@@ -2604,10 +2608,15 @@ var _resizePixels = (pixels, targetW, targetH) => {
|
|
|
2604
2608
|
for (let y = 0; y < targetH; y++) {
|
|
2605
2609
|
const sy = Math.min(srcH - 1, Math.floor(y / targetH * srcH));
|
|
2606
2610
|
const srcRow = pixels[sy];
|
|
2611
|
+
const actualRowW = Array.isArray(srcRow) ? srcRow.length : 0;
|
|
2607
2612
|
const newRow = new Array(targetW);
|
|
2608
2613
|
for (let x = 0; x < targetW; x++) {
|
|
2609
|
-
|
|
2610
|
-
|
|
2614
|
+
if (actualRowW === 0) {
|
|
2615
|
+
newRow[x] = null;
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
const sx = Math.min(actualRowW - 1, Math.floor(x / targetW * actualRowW));
|
|
2619
|
+
newRow[x] = srcRow[sx] ?? null;
|
|
2611
2620
|
}
|
|
2612
2621
|
out.push(newRow);
|
|
2613
2622
|
}
|
|
@@ -2646,8 +2655,12 @@ var fromImage = (pixels, opts = {}) => {
|
|
|
2646
2655
|
if (!Array.isArray(pixels) || pixels.length === 0) return "";
|
|
2647
2656
|
const firstRow = pixels[0];
|
|
2648
2657
|
if (!Array.isArray(firstRow) || firstRow.length === 0) return "";
|
|
2658
|
+
const requestedW = opts.width ?? 80;
|
|
2659
|
+
if (!Number.isFinite(requestedW) || requestedW <= 0) return "";
|
|
2660
|
+
if (opts.height !== void 0) {
|
|
2661
|
+
if (!Number.isFinite(opts.height) || opts.height <= 0) return "";
|
|
2662
|
+
}
|
|
2649
2663
|
const {
|
|
2650
|
-
width = 80,
|
|
2651
2664
|
ramp = "standard",
|
|
2652
2665
|
invert = false,
|
|
2653
2666
|
dither = "none",
|
|
@@ -2662,7 +2675,7 @@ var fromImage = (pixels, opts = {}) => {
|
|
|
2662
2675
|
} = opts;
|
|
2663
2676
|
const srcH = pixels.length;
|
|
2664
2677
|
const srcW = pixels[0].length;
|
|
2665
|
-
const safeW = Math.max(1, Math.floor(
|
|
2678
|
+
const safeW = Math.max(1, Math.floor(requestedW));
|
|
2666
2679
|
const computedH = Math.max(1, Math.round(srcH / srcW * safeW * 0.5));
|
|
2667
2680
|
const safeH = opts.height != null ? Math.max(1, Math.floor(opts.height)) : computedH;
|
|
2668
2681
|
const resized = _resizePixels(pixels, safeW, safeH);
|
|
@@ -2721,7 +2734,9 @@ var fromImage = (pixels, opts = {}) => {
|
|
|
2721
2734
|
};
|
|
2722
2735
|
var parseFiglet = (flfContent) => {
|
|
2723
2736
|
if (typeof flfContent !== "string" || flfContent.length === 0) {
|
|
2724
|
-
|
|
2737
|
+
const err = new TypeError("parseFiglet: input must be a non-empty string");
|
|
2738
|
+
err.code = "ANSIMAX_INVALID_FIGLET_INPUT";
|
|
2739
|
+
throw err;
|
|
2725
2740
|
}
|
|
2726
2741
|
const lines = flfContent.split(/\r?\n/);
|
|
2727
2742
|
if (lines.length === 0) {
|
|
@@ -2730,13 +2745,20 @@ var parseFiglet = (flfContent) => {
|
|
|
2730
2745
|
const header = lines[0];
|
|
2731
2746
|
const m = /^flf2.\s*(\S)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(\d+)/.exec(header);
|
|
2732
2747
|
if (!m) {
|
|
2733
|
-
|
|
2748
|
+
const snippet = header.length > 60 ? header.slice(0, 60) + "\u2026" : header;
|
|
2749
|
+
const err = new TypeError(
|
|
2750
|
+
`parseFiglet: invalid FIGfont header. Expected "flf2a$..." prefix, got: "${snippet}"`
|
|
2751
|
+
);
|
|
2752
|
+
err.code = "ANSIMAX_INVALID_FIGLET_HEADER";
|
|
2753
|
+
throw err;
|
|
2734
2754
|
}
|
|
2735
2755
|
const hardblank = m[1];
|
|
2736
2756
|
const height = parseInt(m[2], 10);
|
|
2737
2757
|
const commentLines = parseInt(m[6], 10);
|
|
2738
2758
|
if (!Number.isFinite(height) || height <= 0) {
|
|
2739
|
-
|
|
2759
|
+
const err = new TypeError(`parseFiglet: invalid height ${m[2]} (must be positive integer)`);
|
|
2760
|
+
err.code = "ANSIMAX_INVALID_FIGLET_HEIGHT";
|
|
2761
|
+
throw err;
|
|
2740
2762
|
}
|
|
2741
2763
|
let cursor2 = 1 + Math.max(0, commentLines);
|
|
2742
2764
|
const glyphs = /* @__PURE__ */ new Map();
|
|
@@ -2759,6 +2781,7 @@ var parseFiglet = (flfContent) => {
|
|
|
2759
2781
|
};
|
|
2760
2782
|
var figletText = (text, font, opts = {}) => {
|
|
2761
2783
|
if (typeof text !== "string") return "";
|
|
2784
|
+
if (text.length === 0) return "";
|
|
2762
2785
|
if (!font || !font.glyphs || font.height <= 0) return "";
|
|
2763
2786
|
const {
|
|
2764
2787
|
trim = true,
|
package/dist/index.mjs
CHANGED
|
@@ -2166,12 +2166,16 @@ var validateFont = (name, fontMap) => {
|
|
|
2166
2166
|
};
|
|
2167
2167
|
var registerFont = (name, fontMap, opts = {}) => {
|
|
2168
2168
|
if (typeof name !== "string" || !name.length) {
|
|
2169
|
-
|
|
2169
|
+
const err = new TypeError("ascii.registerFont: name must be a non-empty string");
|
|
2170
|
+
err.code = "ANSIMAX_INVALID_FONT_NAME";
|
|
2171
|
+
throw err;
|
|
2170
2172
|
}
|
|
2171
2173
|
if (RESERVED_FONT_NAMES.has(name) && !opts.force) {
|
|
2172
|
-
|
|
2174
|
+
const err = new Error(
|
|
2173
2175
|
`Font name "${name}" is reserved. Pass { force: true } to override.`
|
|
2174
2176
|
);
|
|
2177
|
+
err.code = "ANSIMAX_RESERVED_FONT_NAME";
|
|
2178
|
+
throw err;
|
|
2175
2179
|
}
|
|
2176
2180
|
const safeMap = ensureFontMap(fontMap, name);
|
|
2177
2181
|
validateFont(name, safeMap);
|
|
@@ -2424,10 +2428,15 @@ var _resizePixels = (pixels, targetW, targetH) => {
|
|
|
2424
2428
|
for (let y = 0; y < targetH; y++) {
|
|
2425
2429
|
const sy = Math.min(srcH - 1, Math.floor(y / targetH * srcH));
|
|
2426
2430
|
const srcRow = pixels[sy];
|
|
2431
|
+
const actualRowW = Array.isArray(srcRow) ? srcRow.length : 0;
|
|
2427
2432
|
const newRow = new Array(targetW);
|
|
2428
2433
|
for (let x = 0; x < targetW; x++) {
|
|
2429
|
-
|
|
2430
|
-
|
|
2434
|
+
if (actualRowW === 0) {
|
|
2435
|
+
newRow[x] = null;
|
|
2436
|
+
continue;
|
|
2437
|
+
}
|
|
2438
|
+
const sx = Math.min(actualRowW - 1, Math.floor(x / targetW * actualRowW));
|
|
2439
|
+
newRow[x] = srcRow[sx] ?? null;
|
|
2431
2440
|
}
|
|
2432
2441
|
out.push(newRow);
|
|
2433
2442
|
}
|
|
@@ -2466,8 +2475,12 @@ var fromImage = (pixels, opts = {}) => {
|
|
|
2466
2475
|
if (!Array.isArray(pixels) || pixels.length === 0) return "";
|
|
2467
2476
|
const firstRow = pixels[0];
|
|
2468
2477
|
if (!Array.isArray(firstRow) || firstRow.length === 0) return "";
|
|
2478
|
+
const requestedW = opts.width ?? 80;
|
|
2479
|
+
if (!Number.isFinite(requestedW) || requestedW <= 0) return "";
|
|
2480
|
+
if (opts.height !== void 0) {
|
|
2481
|
+
if (!Number.isFinite(opts.height) || opts.height <= 0) return "";
|
|
2482
|
+
}
|
|
2469
2483
|
const {
|
|
2470
|
-
width = 80,
|
|
2471
2484
|
ramp = "standard",
|
|
2472
2485
|
invert = false,
|
|
2473
2486
|
dither = "none",
|
|
@@ -2482,7 +2495,7 @@ var fromImage = (pixels, opts = {}) => {
|
|
|
2482
2495
|
} = opts;
|
|
2483
2496
|
const srcH = pixels.length;
|
|
2484
2497
|
const srcW = pixels[0].length;
|
|
2485
|
-
const safeW = Math.max(1, Math.floor(
|
|
2498
|
+
const safeW = Math.max(1, Math.floor(requestedW));
|
|
2486
2499
|
const computedH = Math.max(1, Math.round(srcH / srcW * safeW * 0.5));
|
|
2487
2500
|
const safeH = opts.height != null ? Math.max(1, Math.floor(opts.height)) : computedH;
|
|
2488
2501
|
const resized = _resizePixels(pixels, safeW, safeH);
|
|
@@ -2541,7 +2554,9 @@ var fromImage = (pixels, opts = {}) => {
|
|
|
2541
2554
|
};
|
|
2542
2555
|
var parseFiglet = (flfContent) => {
|
|
2543
2556
|
if (typeof flfContent !== "string" || flfContent.length === 0) {
|
|
2544
|
-
|
|
2557
|
+
const err = new TypeError("parseFiglet: input must be a non-empty string");
|
|
2558
|
+
err.code = "ANSIMAX_INVALID_FIGLET_INPUT";
|
|
2559
|
+
throw err;
|
|
2545
2560
|
}
|
|
2546
2561
|
const lines = flfContent.split(/\r?\n/);
|
|
2547
2562
|
if (lines.length === 0) {
|
|
@@ -2550,13 +2565,20 @@ var parseFiglet = (flfContent) => {
|
|
|
2550
2565
|
const header = lines[0];
|
|
2551
2566
|
const m = /^flf2.\s*(\S)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(\d+)/.exec(header);
|
|
2552
2567
|
if (!m) {
|
|
2553
|
-
|
|
2568
|
+
const snippet = header.length > 60 ? header.slice(0, 60) + "\u2026" : header;
|
|
2569
|
+
const err = new TypeError(
|
|
2570
|
+
`parseFiglet: invalid FIGfont header. Expected "flf2a$..." prefix, got: "${snippet}"`
|
|
2571
|
+
);
|
|
2572
|
+
err.code = "ANSIMAX_INVALID_FIGLET_HEADER";
|
|
2573
|
+
throw err;
|
|
2554
2574
|
}
|
|
2555
2575
|
const hardblank = m[1];
|
|
2556
2576
|
const height = parseInt(m[2], 10);
|
|
2557
2577
|
const commentLines = parseInt(m[6], 10);
|
|
2558
2578
|
if (!Number.isFinite(height) || height <= 0) {
|
|
2559
|
-
|
|
2579
|
+
const err = new TypeError(`parseFiglet: invalid height ${m[2]} (must be positive integer)`);
|
|
2580
|
+
err.code = "ANSIMAX_INVALID_FIGLET_HEIGHT";
|
|
2581
|
+
throw err;
|
|
2560
2582
|
}
|
|
2561
2583
|
let cursor2 = 1 + Math.max(0, commentLines);
|
|
2562
2584
|
const glyphs = /* @__PURE__ */ new Map();
|
|
@@ -2579,6 +2601,7 @@ var parseFiglet = (flfContent) => {
|
|
|
2579
2601
|
};
|
|
2580
2602
|
var figletText = (text, font, opts = {}) => {
|
|
2581
2603
|
if (typeof text !== "string") return "";
|
|
2604
|
+
if (text.length === 0) return "";
|
|
2582
2605
|
if (!font || !font.glyphs || font.height <= 0) return "";
|
|
2583
2606
|
const {
|
|
2584
2607
|
trim = true,
|
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.2.
|
|
121
|
+
components.badge('VERSION', 'v1.2.8'),
|
|
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.2.
|
|
120
|
+
components.badge('VERSION', 'v1.2.8'),
|
|
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.2.
|
|
3
|
+
"version": "1.2.8",
|
|
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",
|