ansimax 1.2.3 → 1.2.5
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 +195 -0
- package/README.es.md +122 -9
- package/README.md +122 -9
- package/dist/index.d.mts +287 -104
- package/dist/index.d.ts +287 -104
- package/dist/index.js +288 -3
- package/dist/index.mjs +282 -3
- 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,201 @@
|
|
|
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.5] — Phase 3 closure: image-to-ASCII engine
|
|
7
|
+
|
|
8
|
+
Minor release closing the **ASCII engine roadmap (Phase 3)** with five
|
|
9
|
+
new capabilities. All additions are fully backwards-compatible — existing
|
|
10
|
+
code runs identically.
|
|
11
|
+
|
|
12
|
+
### Added — `ascii.fromImage(pixels, opts)` — image-to-ASCII converter
|
|
13
|
+
|
|
14
|
+
Convert a `PixelGrid` (from `ansimax.images`) into ASCII art. Five
|
|
15
|
+
features in one call:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { ascii } from 'ansimax';
|
|
19
|
+
|
|
20
|
+
console.log(ascii.fromImage(pixels, {
|
|
21
|
+
width: 80,
|
|
22
|
+
ramp: 'detailed', // 'standard' | 'detailed' | 'blocks' | 'simple' | custom string
|
|
23
|
+
invert: false,
|
|
24
|
+
dither: 'floyd-steinberg', // 'none' | 'floyd-steinberg'
|
|
25
|
+
edgeDetect: 'sobel', // 'none' | 'sobel'
|
|
26
|
+
edgeThreshold: 40,
|
|
27
|
+
color: true, // preserve source colors
|
|
28
|
+
faceMode: false, // histogram stretch for portraits
|
|
29
|
+
}));
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Aspect-ratio aware**: terminal cells are ~2× as tall as wide, so the
|
|
33
|
+
output height is auto-halved to maintain visual proportion (override
|
|
34
|
+
with `height`).
|
|
35
|
+
|
|
36
|
+
**Zero-dependency**: input is a `PixelGrid` (one Pixel per cell). Users
|
|
37
|
+
of `sharp`, `jimp`, or any decoder convert their output to `PixelGrid`
|
|
38
|
+
once, then call `ascii.fromImage()`.
|
|
39
|
+
|
|
40
|
+
### Added — `ASCII_RAMPS` — pre-built character ramps
|
|
41
|
+
|
|
42
|
+
Four curated character ramps, ordered dark → light, exported as a
|
|
43
|
+
read-only object:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
ASCII_RAMPS.standard // ' .:-=+*#%@' — balanced 10-char (default)
|
|
47
|
+
ASCII_RAMPS.detailed // 70-char Paul Bourke — max detail
|
|
48
|
+
ASCII_RAMPS.blocks // ' ░▒▓█' — looks like a real photo
|
|
49
|
+
ASCII_RAMPS.simple // ' .+#' — minimal 4-char
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or pass any custom string as the `ramp` option for full control.
|
|
53
|
+
|
|
54
|
+
### Added — Sobel edge detection
|
|
55
|
+
|
|
56
|
+
Set `edgeDetect: 'sobel'` to render edges instead of luminance. Useful
|
|
57
|
+
for line-art effects or technical diagrams:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
console.log(ascii.fromImage(pixels, {
|
|
61
|
+
width: 100,
|
|
62
|
+
edgeDetect: 'sobel',
|
|
63
|
+
edgeThreshold: 50, // tune for noise/detail balance
|
|
64
|
+
ramp: 'blocks',
|
|
65
|
+
}));
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Added — Floyd-Steinberg dithering
|
|
69
|
+
|
|
70
|
+
Set `dither: 'floyd-steinberg'` for error-diffusion dithering. Produces
|
|
71
|
+
smoother tonal gradients in photos. Most useful with shorter ramps:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
ascii.fromImage(pixels, {
|
|
75
|
+
width: 80,
|
|
76
|
+
ramp: 'simple',
|
|
77
|
+
dither: 'floyd-steinberg',
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Added — Face mode
|
|
82
|
+
|
|
83
|
+
Set `faceMode: true` to apply histogram stretching ([10%, 90%] percentile
|
|
84
|
+
remap to [0, 255]). Boosts midtone contrast where facial features live,
|
|
85
|
+
producing better results on portraits:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
ascii.fromImage(portraitPixels, {
|
|
89
|
+
width: 60,
|
|
90
|
+
ramp: 'detailed',
|
|
91
|
+
faceMode: true,
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Added — Figlet (.flf) font support
|
|
96
|
+
|
|
97
|
+
Parse and render with community FIGfonts (250+ available at
|
|
98
|
+
[figlet.org](http://www.figlet.org/fontdb.cgi)):
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { readFileSync } from 'node:fs';
|
|
102
|
+
import { parseFiglet, ascii } from 'ansimax';
|
|
103
|
+
|
|
104
|
+
const fontStr = readFileSync('./standard.flf', 'utf8');
|
|
105
|
+
const font = parseFiglet(fontStr);
|
|
106
|
+
|
|
107
|
+
console.log(ascii.figletText('Hello!', font, {
|
|
108
|
+
trim: true,
|
|
109
|
+
colorFn: (t) => t, // optional colorize
|
|
110
|
+
}));
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Returns a `FigletFont` object containing the parsed glyphs. Renders
|
|
114
|
+
ASCII 32-126; unknown chars fall back to space.
|
|
115
|
+
|
|
116
|
+
### Added — Type exports
|
|
117
|
+
|
|
118
|
+
`AsciiRamp`, `FromImageOptions`, `FigletFont`, `FigletOptions` — all
|
|
119
|
+
exported from the main barrel.
|
|
120
|
+
|
|
121
|
+
### Notes
|
|
122
|
+
|
|
123
|
+
- Phase 3 of the [roadmap](README.md#%EF%B8%8F-roadmap) is now **fully complete**.
|
|
124
|
+
- Image decoding (PNG/JPEG → PixelGrid) is intentionally **not** included;
|
|
125
|
+
users pair ansimax with `sharp`/`jimp`/`pngjs` to keep zero deps.
|
|
126
|
+
- 1914 + 30 new tests pass.
|
|
127
|
+
- No new runtime dependencies — still zero.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## [1.2.4] — Gradient utilities + inspectability
|
|
132
|
+
|
|
133
|
+
Patch release adding gradient inspection and manipulation utilities.
|
|
134
|
+
No breaking changes — `createGradient()` callers from 1.2.3 continue
|
|
135
|
+
to work, but now have access to metadata.
|
|
136
|
+
|
|
137
|
+
### Added — `ReusableGradient` metadata
|
|
138
|
+
|
|
139
|
+
`createGradient()` now returns a `ReusableGradient` — still callable
|
|
140
|
+
like before, but with frozen metadata for inspection and debugging:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c'], {
|
|
144
|
+
easing: 'ease-in',
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// All still callable
|
|
148
|
+
console.log(fire('hello'));
|
|
149
|
+
|
|
150
|
+
// New: read-only inspection
|
|
151
|
+
fire.stops; // → ['#ff5555', '#ffb86c', '#f1fa8c']
|
|
152
|
+
fire.resolvedStops; // → [{r:255,g:85,b:85}, {r:255,g:184,b:108}, ...]
|
|
153
|
+
fire.defaultOptions; // → { easing: 'ease-in' }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
All three properties are frozen — attempting to mutate them throws in
|
|
157
|
+
strict mode and silently fails in sloppy mode.
|
|
158
|
+
|
|
159
|
+
### Added — `reverseGradient()` helper
|
|
160
|
+
|
|
161
|
+
Returns a new gradient with stops in reverse order. Works with both
|
|
162
|
+
plain arrays and `ReusableGradient` instances. Default options are
|
|
163
|
+
preserved when reversing a `ReusableGradient`:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c']);
|
|
167
|
+
const ice = reverseGradient(fire); // → '#f1fa8c' → '#ffb86c' → '#ff5555'
|
|
168
|
+
|
|
169
|
+
console.log(fire('warm side'));
|
|
170
|
+
console.log(ice('cool side'));
|
|
171
|
+
|
|
172
|
+
// Also works with plain arrays
|
|
173
|
+
reverseGradient(['#f00', '#0f0', '#00f']); // → ['#00f', '#0f0', '#f00']
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The original array / gradient is never mutated.
|
|
177
|
+
|
|
178
|
+
### Added — `presets` alias (canonical name)
|
|
179
|
+
|
|
180
|
+
Previously `presets` was exported only as `colorPresets`. Many users
|
|
181
|
+
referenced `presets` based on docs and got `ReferenceError`. Now both
|
|
182
|
+
names point to the same object:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { presets, colorPresets } from 'ansimax';
|
|
186
|
+
|
|
187
|
+
presets === colorPresets; // → true
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`colorPresets` remains for backwards compatibility; new code can use
|
|
191
|
+
either name.
|
|
192
|
+
|
|
193
|
+
### Notes
|
|
194
|
+
|
|
195
|
+
- Coverage holds steady at ~98%.
|
|
196
|
+
- No new runtime dependencies — still zero.
|
|
197
|
+
- All 1892 + 22 new tests pass.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
6
201
|
## [1.2.3] — Gradient factory + performance
|
|
7
202
|
|
|
8
203
|
Patch release adding a new performance-oriented API and refinements. No
|
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)
|
|
@@ -263,7 +263,7 @@ console.log(gradientRect({
|
|
|
263
263
|
### Gradientes reusables (v1.2.3)
|
|
264
264
|
|
|
265
265
|
```ts
|
|
266
|
-
import { createGradient, ascii } from 'ansimax';
|
|
266
|
+
import { createGradient, reverseGradient, ascii } from 'ansimax';
|
|
267
267
|
|
|
268
268
|
// Pre-resuelve los stops de hex una vez — significativamente más rápido para uso repetido
|
|
269
269
|
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c']);
|
|
@@ -275,6 +275,14 @@ console.log(fire('Tercera línea'));
|
|
|
275
275
|
// Úsalo como colorFn para banners — misma firma de ColorFn
|
|
276
276
|
console.log(ascii.banner('FIRE', { colorFn: fire }));
|
|
277
277
|
|
|
278
|
+
// v1.2.4: inspecciona metadata
|
|
279
|
+
console.log('Stops:', fire.stops); // → ['#ff5555', '#ffb86c', '#f1fa8c']
|
|
280
|
+
console.log('Resolved:', fire.resolvedStops); // → [{r:255,g:85,b:85}, ...]
|
|
281
|
+
|
|
282
|
+
// v1.2.4: invierte un gradiente (preserva las opciones por defecto)
|
|
283
|
+
const ice = reverseGradient(fire);
|
|
284
|
+
console.log(ice('Lado frío'));
|
|
285
|
+
|
|
278
286
|
// Las opciones por-llamada aún funcionan — perfecto para animación
|
|
279
287
|
for (let p = 0; p < 1; p += 0.05) {
|
|
280
288
|
process.stdout.write('\r' + fire('fluyendo', { phase: p }));
|
|
@@ -298,6 +306,60 @@ console.log(ascii.banner('HOLA', {
|
|
|
298
306
|
console.log(ascii.box('¡Caja arcoiris!', { padding: 1, borderStyle: 'rounded' }));
|
|
299
307
|
```
|
|
300
308
|
|
|
309
|
+
### Imagen → ASCII (v1.2.5)
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { ascii } from 'ansimax';
|
|
313
|
+
import type { PixelGrid } from 'ansimax';
|
|
314
|
+
|
|
315
|
+
// Obtén un PixelGrid de cualquier librería de imágenes (sharp, jimp, pngjs, etc.)
|
|
316
|
+
// Cada Pixel es `{ r, g, b }` o null
|
|
317
|
+
const pixels: PixelGrid = await loadImagePixels('./foto.png');
|
|
318
|
+
|
|
319
|
+
// Monocromo
|
|
320
|
+
console.log(ascii.fromImage(pixels, { width: 80 }));
|
|
321
|
+
|
|
322
|
+
// Color + dithering Floyd-Steinberg + ramp detallado
|
|
323
|
+
console.log(ascii.fromImage(pixels, {
|
|
324
|
+
width: 100,
|
|
325
|
+
color: true,
|
|
326
|
+
dither: 'floyd-steinberg',
|
|
327
|
+
ramp: 'detailed',
|
|
328
|
+
}));
|
|
329
|
+
|
|
330
|
+
// Modo detección de bordes (line art)
|
|
331
|
+
console.log(ascii.fromImage(pixels, {
|
|
332
|
+
width: 80,
|
|
333
|
+
edgeDetect: 'sobel',
|
|
334
|
+
edgeThreshold: 50,
|
|
335
|
+
ramp: 'blocks',
|
|
336
|
+
}));
|
|
337
|
+
|
|
338
|
+
// Modo rostro para retratos (mejora contraste de tonos medios)
|
|
339
|
+
console.log(ascii.fromImage(retratoPixels, {
|
|
340
|
+
width: 60,
|
|
341
|
+
ramp: 'detailed',
|
|
342
|
+
faceMode: true,
|
|
343
|
+
}));
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Fuentes Figlet (v1.2.5)
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
import { readFileSync } from 'node:fs';
|
|
350
|
+
import { parseFiglet, ascii } from 'ansimax';
|
|
351
|
+
|
|
352
|
+
// Descarga fuentes desde http://www.figlet.org/fontdb.cgi
|
|
353
|
+
const font = parseFiglet(readFileSync('./standard.flf', 'utf8'));
|
|
354
|
+
|
|
355
|
+
console.log(ascii.figletText('¡Hola!', font));
|
|
356
|
+
|
|
357
|
+
// Con color
|
|
358
|
+
console.log(ascii.figletText('STYLE', font, {
|
|
359
|
+
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9', '#8be9fd']),
|
|
360
|
+
}));
|
|
361
|
+
```
|
|
362
|
+
|
|
301
363
|
### Árboles
|
|
302
364
|
|
|
303
365
|
<img src="media/trees.png" alt="Árboles" />
|
|
@@ -357,7 +419,7 @@ console.log(components.table([
|
|
|
357
419
|
['loaders', color.green('● listo'), '100%'],
|
|
358
420
|
], { borderStyle: 'rounded' }));
|
|
359
421
|
|
|
360
|
-
console.log(components.badge('VERSION', 'v1.2.
|
|
422
|
+
console.log(components.badge('VERSION', 'v1.2.5'));
|
|
361
423
|
console.log(components.badge('BUILD', 'passing'));
|
|
362
424
|
```
|
|
363
425
|
|
|
@@ -571,7 +633,7 @@ El roadmap apunta intencionalmente — y busca superar — gaps que ni siquiera
|
|
|
571
633
|
- [x] **Curvas de interpolación** — `linear` / `ease-in` / `ease-out` / `ease-in-out` / `cubic-bezier` / personalizado (v1.2.0)
|
|
572
634
|
- [x] **Gradientes cónicos** — barrido radial con `style: 'conic'` (v1.2.0)
|
|
573
635
|
|
|
574
|
-
###
|
|
636
|
+
### ✅ Fase 3 — Motor ASCII
|
|
575
637
|
- [x] Fuentes de bloque (`big`, `small`)
|
|
576
638
|
- [x] Banner con gradiente + alineación + coloreado por carácter
|
|
577
639
|
- [x] Dibujo de cajas (6 estilos de borde)
|
|
@@ -579,11 +641,12 @@ El roadmap apunta intencionalmente — y busca superar — gaps que ni siquiera
|
|
|
579
641
|
- [x] Compositor de logos (gradiente + box wrapping)
|
|
580
642
|
- [x] Registro de fuentes personalizadas (`registerFont`, `hasFont`, `listFonts`)
|
|
581
643
|
- [x] API de stream (`ascii.stream()` con AbortSignal)
|
|
582
|
-
- [
|
|
583
|
-
- [
|
|
584
|
-
- [
|
|
585
|
-
- [
|
|
586
|
-
- [
|
|
644
|
+
- [x] **Conversor Imagen → ASCII** — `ascii.fromImage()` con mapeo de luminancia (v1.2.5)
|
|
645
|
+
- [x] **Renderizado ASCII en color** — preserva colores de imagen con `color: true` (v1.2.5)
|
|
646
|
+
- [x] **Dithering de imágenes** — error diffusion Floyd-Steinberg (v1.2.5)
|
|
647
|
+
- [x] **ASCII optimizado para rostros** — histogram stretching para retratos (v1.2.5)
|
|
648
|
+
- [x] **Soporte de fuentes figlet** — parser + renderer `.flf` (`parseFiglet` + `ascii.figletText`) (v1.2.5)
|
|
649
|
+
- [x] **Detección de bordes** — operador Sobel integrado en `fromImage` (v1.2.5, bonus)
|
|
587
650
|
|
|
588
651
|
### ✅ Fase 4 — Primitivas TUI
|
|
589
652
|
- [x] Tablas (filas irregulares, celdas multi-línea, conscientes de ANSI)
|
|
@@ -759,6 +822,56 @@ ansimax/
|
|
|
759
822
|
|
|
760
823
|
## 📝 Changelog
|
|
761
824
|
|
|
825
|
+
### v1.2.5 — Fase 3 completa: motor de imagen-a-ASCII
|
|
826
|
+
|
|
827
|
+
Release minor que cierra el roadmap del motor ASCII con 5 features nuevas:
|
|
828
|
+
|
|
829
|
+
- 🖼️ **`ascii.fromImage(pixels, opts)`** — Conversor Imagen → ASCII (input: `PixelGrid`)
|
|
830
|
+
- 🎨 **Renderizado ASCII en color** — `color: true` preserva los colores fuente
|
|
831
|
+
- 📐 **Dithering Floyd-Steinberg** — `dither: 'floyd-steinberg'` para gradientes tonales más suaves
|
|
832
|
+
- 👤 **Modo optimizado para rostros** — `faceMode: true` mejora contraste de tonos medios en retratos
|
|
833
|
+
- 🔠 **Soporte Figlet (.flf)** — `parseFiglet()` + `ascii.figletText()` para 250+ fuentes de comunidad
|
|
834
|
+
- ⚡ **Bonus: detección de bordes Sobel** — `edgeDetect: 'sobel'` para efectos line-art
|
|
835
|
+
|
|
836
|
+
```ts
|
|
837
|
+
import { ascii } from 'ansimax';
|
|
838
|
+
|
|
839
|
+
// Imagen a ASCII (input desde sharp/jimp/etc, sin dependencia de decoder)
|
|
840
|
+
console.log(ascii.fromImage(pixels, {
|
|
841
|
+
width: 80,
|
|
842
|
+
color: true,
|
|
843
|
+
dither: 'floyd-steinberg',
|
|
844
|
+
ramp: 'detailed',
|
|
845
|
+
}));
|
|
846
|
+
|
|
847
|
+
// Renderizado Figlet
|
|
848
|
+
const font = parseFiglet(readFileSync('./standard.flf', 'utf8'));
|
|
849
|
+
console.log(ascii.figletText('¡Hola!', font));
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
Drop-in replacement para `1.2.4`.
|
|
853
|
+
|
|
854
|
+
### v1.2.4 — Utilidades de gradiente + inspectabilidad
|
|
855
|
+
|
|
856
|
+
Release patch añadiendo metadata de inspección y un helper `reverseGradient()`:
|
|
857
|
+
|
|
858
|
+
- 🔍 **`ReusableGradient` expone `.stops`, `.resolvedStops`, `.defaultOptions`** — todos congelados, todos de solo lectura
|
|
859
|
+
- 🔄 **Helper `reverseGradient()`** — invierte el orden de stops de un gradiente (funciona con arrays o `ReusableGradient`)
|
|
860
|
+
- 🎯 **`presets` exportado con su nombre canónico** — junto al alias existente `colorPresets`
|
|
861
|
+
|
|
862
|
+
```ts
|
|
863
|
+
import { createGradient, reverseGradient } from 'ansimax';
|
|
864
|
+
|
|
865
|
+
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c']);
|
|
866
|
+
const ice = reverseGradient(fire);
|
|
867
|
+
|
|
868
|
+
console.log(fire.stops); // ['#ff5555', '#ffb86c', '#f1fa8c'] — read-only
|
|
869
|
+
console.log(fire('cálido'));
|
|
870
|
+
console.log(ice('frío'));
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
Drop-in replacement para `1.2.3`.
|
|
874
|
+
|
|
762
875
|
### v1.2.3 — Factory de gradientes + performance
|
|
763
876
|
|
|
764
877
|
Release patch añadiendo una API orientada a performance:
|
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)
|
|
@@ -263,7 +263,7 @@ console.log(gradientRect({
|
|
|
263
263
|
### Reusable Gradients (v1.2.3)
|
|
264
264
|
|
|
265
265
|
```ts
|
|
266
|
-
import { createGradient, ascii } from 'ansimax';
|
|
266
|
+
import { createGradient, reverseGradient, ascii } from 'ansimax';
|
|
267
267
|
|
|
268
268
|
// Pre-resolve hex stops once — significantly faster for repeated use
|
|
269
269
|
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c']);
|
|
@@ -275,6 +275,14 @@ console.log(fire('Third line'));
|
|
|
275
275
|
// Use as a colorFn for banners — same ColorFn signature
|
|
276
276
|
console.log(ascii.banner('FIRE', { colorFn: fire }));
|
|
277
277
|
|
|
278
|
+
// v1.2.4: inspect metadata
|
|
279
|
+
console.log('Stops:', fire.stops); // → ['#ff5555', '#ffb86c', '#f1fa8c']
|
|
280
|
+
console.log('Resolved:', fire.resolvedStops); // → [{r:255,g:85,b:85}, ...]
|
|
281
|
+
|
|
282
|
+
// v1.2.4: reverse a gradient (preserves default options)
|
|
283
|
+
const ice = reverseGradient(fire);
|
|
284
|
+
console.log(ice('Cool side'));
|
|
285
|
+
|
|
278
286
|
// Per-call options still work — perfect for animation
|
|
279
287
|
for (let p = 0; p < 1; p += 0.05) {
|
|
280
288
|
process.stdout.write('\r' + fire('flowing', { phase: p }));
|
|
@@ -298,6 +306,60 @@ console.log(ascii.banner('HELLO', {
|
|
|
298
306
|
console.log(ascii.box('Rainbow box!', { padding: 1, borderStyle: 'rounded' }));
|
|
299
307
|
```
|
|
300
308
|
|
|
309
|
+
### Image → ASCII (v1.2.5)
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { ascii } from 'ansimax';
|
|
313
|
+
import type { PixelGrid } from 'ansimax';
|
|
314
|
+
|
|
315
|
+
// Get a PixelGrid from any image library (sharp, jimp, pngjs, etc.)
|
|
316
|
+
// Each Pixel is `{ r, g, b }` or null
|
|
317
|
+
const pixels: PixelGrid = await loadImagePixels('./photo.png');
|
|
318
|
+
|
|
319
|
+
// Monochrome
|
|
320
|
+
console.log(ascii.fromImage(pixels, { width: 80 }));
|
|
321
|
+
|
|
322
|
+
// Color + Floyd-Steinberg dithering + detailed ramp
|
|
323
|
+
console.log(ascii.fromImage(pixels, {
|
|
324
|
+
width: 100,
|
|
325
|
+
color: true,
|
|
326
|
+
dither: 'floyd-steinberg',
|
|
327
|
+
ramp: 'detailed',
|
|
328
|
+
}));
|
|
329
|
+
|
|
330
|
+
// Edge-detection mode (line art)
|
|
331
|
+
console.log(ascii.fromImage(pixels, {
|
|
332
|
+
width: 80,
|
|
333
|
+
edgeDetect: 'sobel',
|
|
334
|
+
edgeThreshold: 50,
|
|
335
|
+
ramp: 'blocks',
|
|
336
|
+
}));
|
|
337
|
+
|
|
338
|
+
// Face mode for portraits (boosts midtone contrast)
|
|
339
|
+
console.log(ascii.fromImage(portraitPixels, {
|
|
340
|
+
width: 60,
|
|
341
|
+
ramp: 'detailed',
|
|
342
|
+
faceMode: true,
|
|
343
|
+
}));
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Figlet Fonts (v1.2.5)
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
import { readFileSync } from 'node:fs';
|
|
350
|
+
import { parseFiglet, ascii } from 'ansimax';
|
|
351
|
+
|
|
352
|
+
// Download fonts from http://www.figlet.org/fontdb.cgi
|
|
353
|
+
const font = parseFiglet(readFileSync('./standard.flf', 'utf8'));
|
|
354
|
+
|
|
355
|
+
console.log(ascii.figletText('Hello!', font));
|
|
356
|
+
|
|
357
|
+
// With color
|
|
358
|
+
console.log(ascii.figletText('STYLE', font, {
|
|
359
|
+
colorFn: (t) => gradient(t, ['#ff79c6', '#bd93f9', '#8be9fd']),
|
|
360
|
+
}));
|
|
361
|
+
```
|
|
362
|
+
|
|
301
363
|
### Trees
|
|
302
364
|
|
|
303
365
|
<img src="media/trees.png" alt="Trees" />
|
|
@@ -357,7 +419,7 @@ console.log(components.table([
|
|
|
357
419
|
['loaders', color.green('● ready'), '100%'],
|
|
358
420
|
], { borderStyle: 'rounded' }));
|
|
359
421
|
|
|
360
|
-
console.log(components.badge('VERSION', 'v1.2.
|
|
422
|
+
console.log(components.badge('VERSION', 'v1.2.5'));
|
|
361
423
|
console.log(components.badge('BUILD', 'passing'));
|
|
362
424
|
```
|
|
363
425
|
|
|
@@ -571,7 +633,7 @@ The roadmap intentionally targets — and aims to surpass — gaps that even mat
|
|
|
571
633
|
- [x] **Gradient interpolation curves** — `linear` / `ease-in` / `ease-out` / `ease-in-out` / `cubic-bezier` / custom (v1.2.0)
|
|
572
634
|
- [x] **Conic gradients** — radial sweep with `style: 'conic'` (v1.2.0)
|
|
573
635
|
|
|
574
|
-
###
|
|
636
|
+
### ✅ Phase 3 — ASCII engine
|
|
575
637
|
- [x] Block fonts (`big`, `small`)
|
|
576
638
|
- [x] Banner with gradient + alignment + per-char coloring
|
|
577
639
|
- [x] Box drawing (6 border styles)
|
|
@@ -579,11 +641,12 @@ The roadmap intentionally targets — and aims to surpass — gaps that even mat
|
|
|
579
641
|
- [x] Logo composer (gradient + box wrapping)
|
|
580
642
|
- [x] Custom font registry (`registerFont`, `hasFont`, `listFonts`)
|
|
581
643
|
- [x] Stream API (`ascii.stream()` with AbortSignal)
|
|
582
|
-
- [
|
|
583
|
-
- [
|
|
584
|
-
- [
|
|
585
|
-
- [
|
|
586
|
-
- [
|
|
644
|
+
- [x] **Image → ASCII** converter — `ascii.fromImage()` with luminance mapping (v1.2.5)
|
|
645
|
+
- [x] **Color ASCII** rendering — preserve image colors via `color: true` (v1.2.5)
|
|
646
|
+
- [x] **Image dithering** — Floyd-Steinberg error diffusion (v1.2.5)
|
|
647
|
+
- [x] **Face-optimized ASCII** — histogram stretching for portraits (v1.2.5)
|
|
648
|
+
- [x] **Figlet font support** — `.flf` parser + renderer (`parseFiglet` + `ascii.figletText`) (v1.2.5)
|
|
649
|
+
- [x] **Edge detection** — Sobel operator integrated in `fromImage` (v1.2.5, bonus)
|
|
587
650
|
|
|
588
651
|
### ✅ Phase 4 — Terminal UI primitives
|
|
589
652
|
- [x] Tables (irregular rows, multi-line cells, ANSI-aware)
|
|
@@ -759,6 +822,56 @@ ansimax/
|
|
|
759
822
|
|
|
760
823
|
## 📝 Changelog
|
|
761
824
|
|
|
825
|
+
### v1.2.5 — Phase 3 complete: image-to-ASCII engine
|
|
826
|
+
|
|
827
|
+
Minor release closing the ASCII engine roadmap with 5 new features:
|
|
828
|
+
|
|
829
|
+
- 🖼️ **`ascii.fromImage(pixels, opts)`** — Image → ASCII converter (input: `PixelGrid`)
|
|
830
|
+
- 🎨 **Color ASCII rendering** — `color: true` preserves source colors
|
|
831
|
+
- 📐 **Floyd-Steinberg dithering** — `dither: 'floyd-steinberg'` for smoother tonal gradients
|
|
832
|
+
- 👤 **Face-optimized mode** — `faceMode: true` boosts midtone contrast for portraits
|
|
833
|
+
- 🔠 **Figlet (.flf) support** — `parseFiglet()` + `ascii.figletText()` for 250+ community fonts
|
|
834
|
+
- ⚡ **Bonus: Sobel edge detection** — `edgeDetect: 'sobel'` for line-art effects
|
|
835
|
+
|
|
836
|
+
```ts
|
|
837
|
+
import { ascii } from 'ansimax';
|
|
838
|
+
|
|
839
|
+
// Image to ASCII (input from sharp/jimp/etc, no decoder dependency)
|
|
840
|
+
console.log(ascii.fromImage(pixels, {
|
|
841
|
+
width: 80,
|
|
842
|
+
color: true,
|
|
843
|
+
dither: 'floyd-steinberg',
|
|
844
|
+
ramp: 'detailed',
|
|
845
|
+
}));
|
|
846
|
+
|
|
847
|
+
// Figlet rendering
|
|
848
|
+
const font = parseFiglet(readFileSync('./standard.flf', 'utf8'));
|
|
849
|
+
console.log(ascii.figletText('Hello!', font));
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
Drop-in replacement for `1.2.4`.
|
|
853
|
+
|
|
854
|
+
### v1.2.4 — Gradient utilities + inspectability
|
|
855
|
+
|
|
856
|
+
Patch release adding inspection metadata and a `reverseGradient()` helper:
|
|
857
|
+
|
|
858
|
+
- 🔍 **`ReusableGradient` exposes `.stops`, `.resolvedStops`, `.defaultOptions`** — all frozen, all read-only
|
|
859
|
+
- 🔄 **`reverseGradient()` helper** — flips a gradient's stop order (works with arrays or `ReusableGradient`)
|
|
860
|
+
- 🎯 **`presets` exported as canonical name** — alongside the existing `colorPresets` alias
|
|
861
|
+
|
|
862
|
+
```ts
|
|
863
|
+
import { createGradient, reverseGradient } from 'ansimax';
|
|
864
|
+
|
|
865
|
+
const fire = createGradient(['#ff5555', '#ffb86c', '#f1fa8c']);
|
|
866
|
+
const ice = reverseGradient(fire);
|
|
867
|
+
|
|
868
|
+
console.log(fire.stops); // ['#ff5555', '#ffb86c', '#f1fa8c'] — read-only
|
|
869
|
+
console.log(fire('warm'));
|
|
870
|
+
console.log(ice('cool'));
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
Drop-in replacement for `1.2.3`.
|
|
874
|
+
|
|
762
875
|
### v1.2.3 — Gradient factory + performance
|
|
763
876
|
|
|
764
877
|
Patch release adding a performance-oriented API:
|