ansimax 1.3.2 → 1.3.3

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 CHANGED
@@ -3,6 +3,147 @@
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.3.3] — Feature additions to panels, json, ascii
7
+
8
+ Patch release adding new functionality to three modules. No breaking changes —
9
+ all additions are opt-in via new options/exports.
10
+
11
+ ### Added — `panels.grid(blocks, opts)`
12
+
13
+ N-column grid layout with auto-flow (reading order). Each row auto-sizes to
14
+ the tallest block; each column auto-sizes to its widest member.
15
+
16
+ ```js
17
+ import { panels, ascii } from 'ansimax';
18
+
19
+ const cards = [
20
+ ascii.box('FILES\n42', { padding: 1 }),
21
+ ascii.box('LINES\n1247', { padding: 1 }),
22
+ ascii.box('TESTS\n38', { padding: 1 }),
23
+ ascii.box('COV\n98%', { padding: 1 }),
24
+ ];
25
+
26
+ // 2×2 grid
27
+ console.log(panels.grid(cards, { columns: 2, gapX: 2, gapY: 1 }));
28
+
29
+ // 3-column with auto-flow (7 items → 3 rows: [3, 3, 1])
30
+ console.log(panels.grid(items, { columns: 3, gapX: 4 }));
31
+
32
+ // Fixed cell width for uniform appearance
33
+ console.log(panels.grid(blocks, {
34
+ columns: 4,
35
+ cellWidth: 15,
36
+ alignX: 'center',
37
+ }));
38
+ ```
39
+
40
+ Options: `columns` (required), `gapX`, `gapY`, `alignX`, `alignY`, `cellWidth`.
41
+
42
+ ### Added — `panels.frame` option `titleAlign`
43
+
44
+ Frame titles can now be aligned `'left'`, `'center'` (default), or `'right'`.
45
+
46
+ ```js
47
+ panels.frame('Body', { title: 'Section', titleAlign: 'left' });
48
+ // ─ Section ───────────
49
+ //
50
+ // Body
51
+ // ────────────────────
52
+
53
+ panels.frame('Body', { title: 'Section', titleAlign: 'right' });
54
+ // ─────────── Section ─
55
+ //
56
+ // Body
57
+ // ────────────────────
58
+ ```
59
+
60
+ ### Added — `ascii.box` options `title` + `titleAlign`
61
+
62
+ Boxes can now have a title in the top border. When the title is wider than the
63
+ content, the box expands to fit it.
64
+
65
+ ```js
66
+ console.log(ascii.box('Body content', {
67
+ title: 'Header',
68
+ titleAlign: 'left', // 'left' | 'center' (default) | 'right'
69
+ borderStyle: 'rounded',
70
+ }));
71
+
72
+ // ╭─ Header ──────╮
73
+ // │ Body content │
74
+ // ╰───────────────╯
75
+ ```
76
+
77
+ ### Added — `ascii.divider` option `align`
78
+
79
+ Divider labels can now be aligned similar to box titles.
80
+
81
+ ```js
82
+ ascii.divider({ label: 'Section', align: 'left', width: 40 });
83
+ // ─ Section ──────────────────────────────
84
+ ascii.divider({ label: 'Section', align: 'center', width: 40 });
85
+ // ─────────────── Section ────────────────
86
+ ascii.divider({ label: 'Section', align: 'right', width: 40 });
87
+ // ────────────────────────────── Section ─
88
+ ```
89
+
90
+ ### Added — `json.pretty` native types support: `Map`, `Set`, `Date`
91
+
92
+ ```js
93
+ import { json } from 'ansimax';
94
+
95
+ const data = {
96
+ created: new Date('2026-06-13'),
97
+ cache: new Map([['user1', 'Alice'], ['user2', 'Bob']]),
98
+ tags: new Set(['frontend', 'react']),
99
+ };
100
+
101
+ console.log(json.pretty(data));
102
+ // {
103
+ // "created": Date(2026-06-13T00:00:00.000Z),
104
+ // "cache": Map(2) [...],
105
+ // "tags": Set(2) ["frontend", "react"]
106
+ // }
107
+ ```
108
+
109
+ ### Added — `json.pretty` option `mode: 'json'`
110
+
111
+ Produces **strict, parseable JSON** instead of display-only output. Useful
112
+ for piping to files, scripts, or other tools.
113
+
114
+ ```js
115
+ const out = json.pretty(myData, { mode: 'json' });
116
+ const parsed = JSON.parse(out); // ✓ works
117
+ ```
118
+
119
+ In `'json'` mode:
120
+ - Colors are forced off (output is always plain text)
121
+ - `undefined` / functions / symbols are dropped from objects, become `null` in arrays
122
+ - `NaN` / `Infinity` / `-Infinity` become `null`
123
+ - `BigInt` becomes a number (if safe) or a string (if out of safe range)
124
+ - `Date` becomes its ISO string
125
+ - `Map` becomes a plain object (string-keys only)
126
+ - `Set` becomes an array
127
+ - Circular references throw `TypeError` (matches `JSON.stringify` behavior)
128
+
129
+ Default `'display'` mode preserves all v1.3.2 behavior. **No breaking changes.**
130
+
131
+ ### Improved — Tests
132
+
133
+ - `+8` tests for `panels.grid`
134
+ - `+3` tests for `panels.frame` titleAlign
135
+ - `+5` tests for `ascii.box` title/titleAlign
136
+ - `+4` tests for `ascii.divider` align
137
+ - `+18` tests for `json` Map/Set/Date/mode
138
+
139
+ ### Notes
140
+
141
+ - No runtime dependencies — still zero
142
+ - No breaking changes — drop-in replacement for `1.3.2`
143
+ - All new options have backward-compatible defaults
144
+
145
+ ---
146
+
6
147
  ## [1.3.2] — Documentation polish for frames + images
7
148
 
8
149
  Patch release improving JSDoc + IntelliSense coverage for the two largest
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](https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square)](LICENSE)
10
- [![npm](https://img.shields.io/badge/npm-v1.3.2-cb3837.svg?style=flat-square)](https://www.npmjs.com/package/ansimax)
10
+ [![npm](https://img.shields.io/badge/npm-v1.3.3-cb3837.svg?style=flat-square)](https://www.npmjs.com/package/ansimax)
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6.svg?style=flat-square)](tsconfig.json)
12
12
  [![Coverage](https://img.shields.io/badge/coverage-98%25-brightgreen.svg?style=flat-square)](#testing)
13
13
  [![Tests](https://img.shields.io/badge/tests-2000%2B%20passing-brightgreen.svg?style=flat-square)](#testing)
@@ -215,7 +215,7 @@ console.log(rainbow('preset rainbow integrado'));
215
215
 
216
216
  ### Gradientes animados (v1.2.0)
217
217
 
218
- <img src="media/animated_gradients.png" alt="Vista previa de gradientes animados" />
218
+ <img src="media/animated-gradients.gif" alt="Vista previa de gradientes animados" />
219
219
 
220
220
  ```js
221
221
  import { animateGradient, sleep } from 'ansimax';
@@ -275,7 +275,7 @@ console.log(gradientRect({
275
275
 
276
276
  ### Gradientes reusables (v1.2.3)
277
277
 
278
- <img src="media/reusable_gradients.png" alt="Vista previa de gradientes reusables" />
278
+ <img src="media/reusable-gradients.gif" alt="Vista previa de gradientes reusables" />
279
279
 
280
280
  ```js
281
281
  import { createGradient, reverseGradient, ascii } from 'ansimax';
@@ -323,7 +323,32 @@ console.log(ascii.box('¡Caja arcoiris!', { padding: 1, borderStyle: 'rounded' }
323
323
 
324
324
  ### Imagen → ASCII (v1.2.5)
325
325
 
326
- <img src="media/image_to_ascii.png" alt="Vista previa de Image-to-ASCII" />
326
+ <div align="center">
327
+ <img src="media/image-ascii-original.png" alt="Foto original" width="40%" />
328
+ </div>
329
+
330
+ <table>
331
+ <tr>
332
+ <td align="center">
333
+ <img src="media/image-ascii-1.png" alt="Modo monocromo" /><br/>
334
+ <sub><b>1. Monocromo</b></sub>
335
+ </td>
336
+ <td align="center">
337
+ <img src="media/image-ascii-2.png" alt="Color + dithering Floyd-Steinberg" /><br/>
338
+ <sub><b>2. Color + Floyd-Steinberg</b></sub>
339
+ </td>
340
+ </tr>
341
+ <tr>
342
+ <td align="center">
343
+ <img src="media/image-ascii-3.png" alt="Detección de bordes (Sobel)" /><br/>
344
+ <sub><b>3. Detección de bordes (Sobel)</b></sub>
345
+ </td>
346
+ <td align="center">
347
+ <img src="media/image-ascii-4.png" alt="Modo rostro para retratos" /><br/>
348
+ <sub><b>4. Modo rostro (retratos)</b></sub>
349
+ </td>
350
+ </tr>
351
+ </table>
327
352
 
328
353
  ```js
329
354
  import { ascii } from 'ansimax';
@@ -348,10 +373,10 @@ for (let y = 0; y < info.height; y++) {
348
373
 
349
374
  // Ahora usa ansimax — varias formas:
350
375
 
351
- // Monocromo
376
+ // 1. Monocromo
352
377
  console.log(ascii.fromImage(pixels, { width: 80 }));
353
378
 
354
- // Color + dithering Floyd-Steinberg + ramp detallado
379
+ // 2. Color + dithering Floyd-Steinberg + ramp detallado
355
380
  console.log(ascii.fromImage(pixels, {
356
381
  width: 100,
357
382
  color: true,
@@ -359,7 +384,7 @@ console.log(ascii.fromImage(pixels, {
359
384
  ramp: 'detailed',
360
385
  }));
361
386
 
362
- // Modo detección de bordes (line art)
387
+ // 3. Modo detección de bordes (line art)
363
388
  console.log(ascii.fromImage(pixels, {
364
389
  width: 80,
365
390
  edgeDetect: 'sobel',
@@ -367,7 +392,7 @@ console.log(ascii.fromImage(pixels, {
367
392
  ramp: 'blocks',
368
393
  }));
369
394
 
370
- // Modo rostro para retratos (mejora contraste de tonos medios)
395
+ // 4. Modo rostro para retratos (mejora contraste de tonos medios)
371
396
  console.log(ascii.fromImage(pixels, {
372
397
  width: 60,
373
398
  ramp: 'detailed',
@@ -453,7 +478,7 @@ console.log(components.table([
453
478
  ['loaders', color.green('● listo'), '100%'],
454
479
  ], { borderStyle: 'rounded' }));
455
480
 
456
- console.log(components.badge('VERSION', 'v1.3.2'));
481
+ console.log(components.badge('VERSION', 'v1.3.3'));
457
482
  console.log(components.badge('BUILD', 'passing'));
458
483
  ```
459
484
 
@@ -505,7 +530,7 @@ await loader.tasks([
505
530
 
506
531
  ### Animaciones
507
532
 
508
- <img src="media/animations.png" alt="Vista previa de animaciones" />
533
+ <img src="media/animations-1.gif" alt="Vista previa de animaciones" />
509
534
 
510
535
  ```js
511
536
  import { animate, gradient, sleep } from 'ansimax';
@@ -615,10 +640,12 @@ console.log(json.pretty({
615
640
  }));
616
641
 
617
642
  // Límite de profundidad — colapsa objetos profundos a {...}
643
+ const deeplyNested = { a: { b: { c: { d: { e: 'muy profundo' } } } } };
618
644
  console.log(json.pretty(deeplyNested, { maxDepth: 2 }));
619
645
 
620
646
  // Límite de items — arrays grandes muestran "... (N more)"
621
- console.log(json.pretty(largeArray, { maxItems: 10 }));
647
+ const largeArray = Array.from({ length: 50 }, (_, i) => `item_${i}`);
648
+ console.log(json.pretty(largeArray, { maxItems: 5 }));
622
649
 
623
650
  // Referencias circulares manejadas con gracia
624
651
  const obj = { name: 'foo' };
@@ -1038,6 +1065,33 @@ ansimax/
1038
1065
 
1039
1066
  ## 📝 Changelog
1040
1067
 
1068
+ ### v1.3.3 — Features para panels, json, ascii
1069
+
1070
+ Release patch con nuevas features opt-in. Cero breaking changes:
1071
+
1072
+ - 📐 **`panels.grid(blocks, opts)`** — layout de grid en N columnas con auto-flow, gaps, alineación, y opción de ancho uniforme de celda
1073
+ - 🎯 **`panels.frame` + `ascii.box` + `ascii.divider`** todos reciben opción `titleAlign` (o `align`): `'left'`, `'center'` (default), `'right'`
1074
+ - 🏷️ **`ascii.box` nueva opción `title`** — muestra un label en el borde superior (expande el box si el title es más ancho que el contenido)
1075
+ - 📅 **`json.pretty` soporta Map / Set / Date nativamente** — `Date(...)`, `Map(N) [...]`, `Set(N) [...]` en modo display
1076
+ - 📤 **`json.pretty` nueva opción `mode: 'json'`** — produce JSON estricto y parseable (sin colores, descarta undefined/functions/symbols, lanza en circulares)
1077
+ - 🧪 **+38 tests** entre panels + json + ascii
1078
+
1079
+ ```js
1080
+ import { panels, json, ascii } from 'ansimax';
1081
+
1082
+ // Grid de 2×2 con metric cards
1083
+ console.log(panels.grid(cards, { columns: 2, gapX: 2 }));
1084
+
1085
+ // Title en el borde del box
1086
+ console.log(ascii.box('content', { title: 'Section', titleAlign: 'left' }));
1087
+
1088
+ // Output JSON estricto (parseable)
1089
+ const out = json.pretty(data, { mode: 'json' });
1090
+ JSON.parse(out); // ✓ funciona
1091
+ ```
1092
+
1093
+ Drop-in replacement para `1.3.2`.
1094
+
1041
1095
  ### v1.3.2 — Pulido de documentación para frames + images
1042
1096
 
1043
1097
  Release patch con cobertura JSDoc + IntelliSense significativamente mejorada:
@@ -1404,4 +1458,4 @@ Ansimax está licenciada bajo **Apache License, Version 2.0** — una licencia p
1404
1458
 
1405
1459
  Si Ansimax te ayuda a hacer mejores CLIs, ¡dale ⭐ en [GitHub](https://github.com/Brashkie/ansimax)!
1406
1460
 
1407
- </div>
1461
+ </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](https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square)](LICENSE)
10
- [![npm](https://img.shields.io/badge/npm-v1.3.2-cb3837.svg?style=flat-square)](https://www.npmjs.com/package/ansimax)
10
+ [![npm](https://img.shields.io/badge/npm-v1.3.3-cb3837.svg?style=flat-square)](https://www.npmjs.com/package/ansimax)
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6.svg?style=flat-square)](tsconfig.json)
12
12
  [![Coverage](https://img.shields.io/badge/coverage-98%25-brightgreen.svg?style=flat-square)](#testing)
13
13
  [![Tests](https://img.shields.io/badge/tests-2000%2B%20passing-brightgreen.svg?style=flat-square)](#testing)
@@ -215,7 +215,7 @@ console.log(rainbow('built-in rainbow preset'));
215
215
 
216
216
  ### Animated Gradients (v1.2.0)
217
217
 
218
- <img src="media/animated_gradients.png" alt="Animated gradients preview" />
218
+ <img src="media/animated-gradients.gif" alt="Animated gradients preview" />
219
219
 
220
220
  ```js
221
221
  import { animateGradient, sleep } from 'ansimax';
@@ -275,7 +275,7 @@ console.log(gradientRect({
275
275
 
276
276
  ### Reusable Gradients (v1.2.3)
277
277
 
278
- <img src="media/reusable_gradients.png" alt="Reusable gradients preview" />
278
+ <img src="media/reusable-gradients.gif" alt="Reusable gradients preview" />
279
279
 
280
280
  ```js
281
281
  import { createGradient, reverseGradient, ascii } from 'ansimax';
@@ -323,7 +323,32 @@ console.log(ascii.box('Rainbow box!', { padding: 1, borderStyle: 'rounded' }));
323
323
 
324
324
  ### Image → ASCII (v1.2.5)
325
325
 
326
- <img src="media/image_to_ascii.png" alt="Image-to-ASCII preview" />
326
+ <div align="center">
327
+ <img src="media/image-ascii-original.png" alt="Original photo" width="40%" />
328
+ </div>
329
+
330
+ <table>
331
+ <tr>
332
+ <td align="center">
333
+ <img src="media/image-ascii-1.png" alt="Monochrome mode" /><br/>
334
+ <sub><b>1. Monochrome</b></sub>
335
+ </td>
336
+ <td align="center">
337
+ <img src="media/image-ascii-2.png" alt="Color + Floyd-Steinberg dithering" /><br/>
338
+ <sub><b>2. Color + Floyd-Steinberg</b></sub>
339
+ </td>
340
+ </tr>
341
+ <tr>
342
+ <td align="center">
343
+ <img src="media/image-ascii-3.png" alt="Edge detection (Sobel)" /><br/>
344
+ <sub><b>3. Edge detection (Sobel)</b></sub>
345
+ </td>
346
+ <td align="center">
347
+ <img src="media/image-ascii-4.png" alt="Face mode for portraits" /><br/>
348
+ <sub><b>4. Face mode (portraits)</b></sub>
349
+ </td>
350
+ </tr>
351
+ </table>
327
352
 
328
353
  ```js
329
354
  import { ascii } from 'ansimax';
@@ -348,10 +373,10 @@ for (let y = 0; y < info.height; y++) {
348
373
 
349
374
  // Now use ansimax — multiple ways:
350
375
 
351
- // Monochrome
376
+ // 1. Monochrome
352
377
  console.log(ascii.fromImage(pixels, { width: 80 }));
353
378
 
354
- // Color + Floyd-Steinberg dithering + detailed ramp
379
+ // 2. Color + Floyd-Steinberg dithering + detailed ramp
355
380
  console.log(ascii.fromImage(pixels, {
356
381
  width: 100,
357
382
  color: true,
@@ -359,7 +384,7 @@ console.log(ascii.fromImage(pixels, {
359
384
  ramp: 'detailed',
360
385
  }));
361
386
 
362
- // Edge-detection mode (line art)
387
+ // 3. Edge-detection mode (line art)
363
388
  console.log(ascii.fromImage(pixels, {
364
389
  width: 80,
365
390
  edgeDetect: 'sobel',
@@ -367,7 +392,7 @@ console.log(ascii.fromImage(pixels, {
367
392
  ramp: 'blocks',
368
393
  }));
369
394
 
370
- // Face mode for portraits (boosts midtone contrast)
395
+ // 4. Face mode for portraits (boosts midtone contrast)
371
396
  console.log(ascii.fromImage(pixels, {
372
397
  width: 60,
373
398
  ramp: 'detailed',
@@ -453,7 +478,7 @@ console.log(components.table([
453
478
  ['loaders', color.green('● ready'), '100%'],
454
479
  ], { borderStyle: 'rounded' }));
455
480
 
456
- console.log(components.badge('VERSION', 'v1.3.2'));
481
+ console.log(components.badge('VERSION', 'v1.3.3'));
457
482
  console.log(components.badge('BUILD', 'passing'));
458
483
  ```
459
484
 
@@ -505,7 +530,7 @@ await loader.tasks([
505
530
 
506
531
  ### Animations
507
532
 
508
- <img src="media/animations.png" alt="Animations preview" />
533
+ <img src="media/animations-1.gif" alt="Animations preview" />
509
534
 
510
535
  ```js
511
536
  import { animate, gradient, sleep } from 'ansimax';
@@ -615,10 +640,12 @@ console.log(json.pretty({
615
640
  }));
616
641
 
617
642
  // Depth limit — collapses deep objects to {...}
643
+ const deeplyNested = { a: { b: { c: { d: { e: 'too deep' } } } } };
618
644
  console.log(json.pretty(deeplyNested, { maxDepth: 2 }));
619
645
 
620
646
  // Item limit — huge arrays show "... (N more)"
621
- console.log(json.pretty(largeArray, { maxItems: 10 }));
647
+ const largeArray = Array.from({ length: 50 }, (_, i) => `item_${i}`);
648
+ console.log(json.pretty(largeArray, { maxItems: 5 }));
622
649
 
623
650
  // Circular references handled gracefully
624
651
  const obj = { name: 'foo' };
@@ -1038,6 +1065,33 @@ ansimax/
1038
1065
 
1039
1066
  ## 📝 Changelog
1040
1067
 
1068
+ ### v1.3.3 — Features for panels, json, ascii
1069
+
1070
+ Patch release with new opt-in features. Zero breaking changes:
1071
+
1072
+ - 📐 **`panels.grid(blocks, opts)`** — N-column grid layout with auto-flow, gaps, alignment, and optional uniform cell width
1073
+ - 🎯 **`panels.frame` + `ascii.box` + `ascii.divider`** all get a `titleAlign` (or `align`) option: `'left'`, `'center'` (default), `'right'`
1074
+ - 🏷️ **`ascii.box` new `title` option** — show a label in the top border (expands box if title is wider than content)
1075
+ - 📅 **`json.pretty` supports Map / Set / Date natively** — `Date(...)`, `Map(N) [...]`, `Set(N) [...]` in display mode
1076
+ - 📤 **`json.pretty` new `mode: 'json'`** — produces strict, parseable JSON (no colors, drops undefined/functions/symbols, throws on circular)
1077
+ - 🧪 **+38 tests** across panels + json + ascii
1078
+
1079
+ ```js
1080
+ import { panels, json, ascii } from 'ansimax';
1081
+
1082
+ // 2×2 grid of metric cards
1083
+ console.log(panels.grid(cards, { columns: 2, gapX: 2 }));
1084
+
1085
+ // Title in box top border
1086
+ console.log(ascii.box('content', { title: 'Section', titleAlign: 'left' }));
1087
+
1088
+ // Strict JSON output (parseable)
1089
+ const out = json.pretty(data, { mode: 'json' });
1090
+ JSON.parse(out); // ✓ works
1091
+ ```
1092
+
1093
+ Drop-in replacement for `1.3.2`.
1094
+
1041
1095
  ### v1.3.2 — Documentation polish for frames + images
1042
1096
 
1043
1097
  Patch release with significantly improved JSDoc + IntelliSense coverage:
@@ -1404,4 +1458,4 @@ Ansimax is licensed under the **Apache License, Version 2.0** — a permissive l
1404
1458
 
1405
1459
  If Ansimax helps you ship better CLIs, give it a ⭐ on [GitHub](https://github.com/Brashkie/ansimax)!
1406
1460
 
1407
- </div>
1461
+ </div>
package/dist/index.d.mts CHANGED
@@ -1279,6 +1279,20 @@ interface BoxOptions {
1279
1279
  borderStyle?: BoxStyle;
1280
1280
  /** Fix inner content width. Lines are padded/truncated to fit. */
1281
1281
  width?: number | null;
1282
+ /**
1283
+ * Optional title shown in the top border, e.g. `─ Title ─────`.
1284
+ * When set, the box expands to fit the title if content is narrower.
1285
+ *
1286
+ * @since 1.3.3
1287
+ */
1288
+ title?: string | null;
1289
+ /**
1290
+ * Title alignment in the top border: `'left'` | `'center'` (default) | `'right'`.
1291
+ * Only applies when `title` is set.
1292
+ *
1293
+ * @since 1.3.3
1294
+ */
1295
+ titleAlign?: 'left' | 'center' | 'right';
1282
1296
  }
1283
1297
  interface BannerOptions {
1284
1298
  font?: FontName | string;
@@ -1294,6 +1308,13 @@ interface DividerOptions {
1294
1308
  width?: number | null;
1295
1309
  label?: string | null;
1296
1310
  style?: BoxStyle;
1311
+ /**
1312
+ * Label alignment: `'left'` | `'center'` (default) | `'right'`.
1313
+ * Only applies when `label` is set.
1314
+ *
1315
+ * @since 1.3.3
1316
+ */
1317
+ align?: 'left' | 'center' | 'right';
1297
1318
  }
1298
1319
  interface LogoOptions {
1299
1320
  gradient?: ColorFn | null;
@@ -2299,9 +2320,16 @@ interface FrameOptions {
2299
2320
  */
2300
2321
  bottomChar?: string;
2301
2322
  /**
2302
- * Optional title shown centered in the top edge.
2323
+ * Optional title shown in the top edge.
2303
2324
  */
2304
2325
  title?: string;
2326
+ /**
2327
+ * Title alignment: `'left'` | `'center'` (default) | `'right'`.
2328
+ * Only applies when `title` is set.
2329
+ *
2330
+ * @since 1.3.3
2331
+ */
2332
+ titleAlign?: 'left' | 'center' | 'right';
2305
2333
  }
2306
2334
  /**
2307
2335
  * Add decorative top/bottom rule lines around a block (lighter than `ascii.box`
@@ -2339,11 +2367,71 @@ interface FrameOptions {
2339
2367
  * ```
2340
2368
  */
2341
2369
  declare const frame: (block: string, opts?: FrameOptions) => string;
2370
+ interface GridOptions {
2371
+ /** Number of columns. Required. */
2372
+ columns: number;
2373
+ /** Horizontal gap between cells. Default `1`. */
2374
+ gapX?: number;
2375
+ /** Vertical gap between rows. Default `0`. */
2376
+ gapY?: number;
2377
+ /** Horizontal alignment of content within each cell. Default `'start'`. */
2378
+ alignX?: Alignment;
2379
+ /** Vertical alignment of content within each cell. Default `'start'`. */
2380
+ alignY?: Alignment;
2381
+ /**
2382
+ * Fix each cell to this width (in visible characters). If omitted, cells
2383
+ * use the max width of the widest block in their column.
2384
+ */
2385
+ cellWidth?: number | null;
2386
+ }
2387
+ /**
2388
+ * Arrange blocks in a grid of N columns, flowing left-to-right then
2389
+ * top-to-bottom. Each row is auto-sized to its tallest block, and each
2390
+ * column is auto-sized to its widest block (unless `cellWidth` is fixed).
2391
+ *
2392
+ * Internally uses `vsplit` for rows + `hsplit` for the column stack, so
2393
+ * all alignment + ANSI rules behave consistently.
2394
+ *
2395
+ * @param blocks - Pre-rendered string blocks. Flows in reading order.
2396
+ * @param opts - Grid configuration. `columns` is required.
2397
+ *
2398
+ * @example 2×2 grid of stats cards
2399
+ * ```js
2400
+ * import { panels, ascii } from 'ansimax';
2401
+ *
2402
+ * const cards = [
2403
+ * ascii.box('FILES\n42', { borderStyle: 'rounded', padding: 1 }),
2404
+ * ascii.box('LINES\n1247', { borderStyle: 'rounded', padding: 1 }),
2405
+ * ascii.box('TESTS\n38', { borderStyle: 'rounded', padding: 1 }),
2406
+ * ascii.box('COV\n98%', { borderStyle: 'rounded', padding: 1 }),
2407
+ * ];
2408
+ *
2409
+ * console.log(panels.grid(cards, { columns: 2, gapX: 2, gapY: 1 }));
2410
+ * ```
2411
+ *
2412
+ * @example 3-column gallery with auto-flow
2413
+ * ```js
2414
+ * // 7 items in 3 columns → 3 rows: [3, 3, 1]
2415
+ * const items = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven'];
2416
+ * console.log(panels.grid(items, { columns: 3, gapX: 4 }));
2417
+ * ```
2418
+ *
2419
+ * @example uniform cell width for visual consistency
2420
+ * ```js
2421
+ * console.log(panels.grid(blocks, {
2422
+ * columns: 4,
2423
+ * cellWidth: 15,
2424
+ * alignX: 'center',
2425
+ * }));
2426
+ * ```
2427
+ */
2428
+ declare const grid: (blocks: string[], opts: GridOptions) => string;
2342
2429
  declare const panels: {
2343
2430
  vsplit: (blocks: string[], opts?: VsplitOptions) => string;
2344
2431
  hsplit: (blocks: string[], opts?: HsplitOptions) => string;
2345
2432
  center: (block: string, opts: CenterOptions) => string;
2346
2433
  frame: (block: string, opts?: FrameOptions) => string;
2434
+ grid: (blocks: string[], opts: GridOptions) => string;
2347
2435
  };
2348
2436
 
2349
2437
  /**
@@ -2401,6 +2489,23 @@ interface PrettyOptions {
2401
2489
  * @since 1.3.1
2402
2490
  */
2403
2491
  inlineArrayMaxLength?: number;
2492
+ /**
2493
+ * Output mode controls what kind of string is produced:
2494
+ *
2495
+ * - `'display'` (default) — terminal-friendly output with colors (when
2496
+ * enabled) and informational placeholders like `[Circular]`,
2497
+ * `[Function: name]`, `Symbol(x)`, `Map(2) {...}`, `Set(3) {...}`,
2498
+ * `Date(2026-06-13T...)`, `BigInt(123n)`. Not parseable as JSON.
2499
+ *
2500
+ * - `'json'` — strict, parseable JSON output. Colors are forced off,
2501
+ * circular references throw `TypeError`, and types not representable
2502
+ * in JSON (functions, symbols, undefined) are omitted from objects /
2503
+ * replaced with `null` in arrays. Map/Set are converted to objects/arrays.
2504
+ * Dates become ISO strings. BigInt becomes a number (or string if too large).
2505
+ *
2506
+ * @since 1.3.3
2507
+ */
2508
+ mode?: 'display' | 'json';
2404
2509
  }
2405
2510
  /**
2406
2511
  * Pretty-print a JavaScript value with colored output suitable for
@@ -2618,4 +2723,4 @@ declare const ansimax: {
2618
2723
  configure: (opts?: AnsimaxConfig, meta?: ConfigureOptions) => void;
2619
2724
  };
2620
2725
 
2621
- export { ASCII_RAMPS, type Alignment, type AnimateGradientController, type AnimateGradientOptions, type AnimationHooks, type AnimationSpeed, type AnsiCode, type AnsimaxConfig, type AsciiRamp, BEL, BG, type BadgeOptions, type BallOptions, type BannerOptions, type BoxOptions, type BoxStyle, type BreatheOptions, DEFAULTS as CONFIG_DEFAULTS, CSI, type Canvas, type CanvasRenderOptions, type CenterOptions, type ColorChain, type ColorFn, type ColorLevel, type ColorMode, type ColorSupport, type ColumnsOptions, type ConfigChangeListener, type ConfigKey, type ConfigKeyListener, type ConfigureOptions, type CountdownOptions, type CustomOptions, DEFAULT_TERM_COLS, DEFAULT_TERM_ROWS, type DebounceOptions, type DiffType, type Dimensions, type DividerOptions, type DotsOptions, ESC, type EasingFn, type EasingName, type EraseMode, FG, FRAME_MS, type FadeOptions, type FigletFont, type FigletOptions, type FontMap, type FontName, type FrameCallback, type FrameHandle, type FrameOptions, type FromImageOptions, type GlitchOptions, type Glyph, type GradientOptions, type GradientRectOptions, type HsplitOptions, type PrettyOptions as JsonPrettyOptions, type LineDiff, type LiveController, type LiveOptions, type LoadingBarOptions, type LogoOptions, MENU_CANCELLED, type MemoizeOptions, type MenuInput, type MenuOptions, type MenuOutput, type MenuResult, type MultiLoader, type MultiLoaderItem, OSC, type OnResizeOptions, type OutputBuffer, type ParallelOptions, type ParallelStep, type Pixel, type PixelGrid, type PlayController, type PlayOptions, type PresetName, type ProgressAnimateOptions, type ProgressBarOptions, type ProgressOptions, type PulseOptions, type RGB, type RGBA, type RegisterFontOptions, type RenderOptions$1 as RenderOptions, type ResizeListener, type ReusableGradient, type RevealOptions, SPINNERS, SPRITES, ST, STYLE, type SectionOptions, type SleepOptions, type SlideOptions, type SpinOptions, type SpinnerType, type StatusOptions, type StatusType, type StopFn, type StreamOptions, type TableBorderStyle, type TableOptions, type Task, type TaskResult, type TasksOptions, type Theme, type BannerOpts as ThemeBannerOpts, type ThemeChangeListener, type ThemeInstance, type ThemeStyleName, type TimelineEvent, type TimelineOptions, type TreeData, type TreeDimensions, type TreeNode, type RenderOptions as TreeRenderOptions, type TreeStyle, type TypeDeleteOptions, type TypewriterOptions, type VsplitOptions, type WalkVisitor, type WaveOptions, type WriteAsyncOptions, animate, animateGradient, ascii, bell, bg256, bgRgb, box, canAnimate, cancelTerminalFrame, center$1 as center, center as centerBlock, chain, charWidth, clamp, clearAnsiCache, clearColorCache, clearRenderCache, clearThemeColorCache, color, colorLevel, presets as colorPresets, components, compose, configure, countNodes, createCanvas, createGradient, createOutputBuffer, createTheme, cursor, debounce, ansimax as default, diffLines, escapeRegex, fg256, fgRgb, figletText, filterTree, findInTree, flipHorizontal, flipVertical, frame, frames, fromImage, getConfig, getConfigValue, getRenderCacheSize, getSpeedMultiplier, getTerminalHeight, getTerminalWidth, gradient, gradientColor, gradientRect, graphemes, hasFont, hexToRgb, hideCursor, hsplit, images, isHexColor, isNoColor, json, pretty as jsonPretty, lerp, lerpColor, link, listFonts, listPresets, loader, mapTree, measureTree, memoize, nextTick, onConfigChange, onConfigKeyChange, onResize, once, padBoth, padEnd, padStart, panels, parseFiglet, pauseListeners, presetNames, presets, rainbow, registerFont, registerPreset, renderPixelArt, renderTree, renderTreeStream, repeatVisible, requestTerminalFrame, reset, resetColorSupportCache, resetConfig, resetCursorRefCount, resetFramesCursorCount, resetLoaderCursorCount, resetNoColor, resumeListeners, reverseGradient, rgbTo256, rgbToHex, rotate90, safeJson, screen, setNoColor, setTitle, sgr, showCursor, sleep, sleepFrame, sliceAnsi, stripAnsi$1 as stripAnsi, stripAnsi$2 as stripAnsiCodes, stripAnsi as stripAnsiColors, supportsColor, supportsColorLevel, termSize, themes, throttle, tree, trees, truncateAnsi, visibleLen, vsplit, walkTree, withConfig, wordWrap, wrapAnsi, write, writeAsync, writeErr, writeln, writelnErr };
2726
+ export { ASCII_RAMPS, type Alignment, type AnimateGradientController, type AnimateGradientOptions, type AnimationHooks, type AnimationSpeed, type AnsiCode, type AnsimaxConfig, type AsciiRamp, BEL, BG, type BadgeOptions, type BallOptions, type BannerOptions, type BoxOptions, type BoxStyle, type BreatheOptions, DEFAULTS as CONFIG_DEFAULTS, CSI, type Canvas, type CanvasRenderOptions, type CenterOptions, type ColorChain, type ColorFn, type ColorLevel, type ColorMode, type ColorSupport, type ColumnsOptions, type ConfigChangeListener, type ConfigKey, type ConfigKeyListener, type ConfigureOptions, type CountdownOptions, type CustomOptions, DEFAULT_TERM_COLS, DEFAULT_TERM_ROWS, type DebounceOptions, type DiffType, type Dimensions, type DividerOptions, type DotsOptions, ESC, type EasingFn, type EasingName, type EraseMode, FG, FRAME_MS, type FadeOptions, type FigletFont, type FigletOptions, type FontMap, type FontName, type FrameCallback, type FrameHandle, type FrameOptions, type FromImageOptions, type GlitchOptions, type Glyph, type GradientOptions, type GradientRectOptions, type GridOptions, type HsplitOptions, type PrettyOptions as JsonPrettyOptions, type LineDiff, type LiveController, type LiveOptions, type LoadingBarOptions, type LogoOptions, MENU_CANCELLED, type MemoizeOptions, type MenuInput, type MenuOptions, type MenuOutput, type MenuResult, type MultiLoader, type MultiLoaderItem, OSC, type OnResizeOptions, type OutputBuffer, type ParallelOptions, type ParallelStep, type Pixel, type PixelGrid, type PlayController, type PlayOptions, type PresetName, type ProgressAnimateOptions, type ProgressBarOptions, type ProgressOptions, type PulseOptions, type RGB, type RGBA, type RegisterFontOptions, type RenderOptions$1 as RenderOptions, type ResizeListener, type ReusableGradient, type RevealOptions, SPINNERS, SPRITES, ST, STYLE, type SectionOptions, type SleepOptions, type SlideOptions, type SpinOptions, type SpinnerType, type StatusOptions, type StatusType, type StopFn, type StreamOptions, type TableBorderStyle, type TableOptions, type Task, type TaskResult, type TasksOptions, type Theme, type BannerOpts as ThemeBannerOpts, type ThemeChangeListener, type ThemeInstance, type ThemeStyleName, type TimelineEvent, type TimelineOptions, type TreeData, type TreeDimensions, type TreeNode, type RenderOptions as TreeRenderOptions, type TreeStyle, type TypeDeleteOptions, type TypewriterOptions, type VsplitOptions, type WalkVisitor, type WaveOptions, type WriteAsyncOptions, animate, animateGradient, ascii, bell, bg256, bgRgb, box, canAnimate, cancelTerminalFrame, center$1 as center, center as centerBlock, chain, charWidth, clamp, clearAnsiCache, clearColorCache, clearRenderCache, clearThemeColorCache, color, colorLevel, presets as colorPresets, components, compose, configure, countNodes, createCanvas, createGradient, createOutputBuffer, createTheme, cursor, debounce, ansimax as default, diffLines, escapeRegex, fg256, fgRgb, figletText, filterTree, findInTree, flipHorizontal, flipVertical, frame, frames, fromImage, getConfig, getConfigValue, getRenderCacheSize, getSpeedMultiplier, getTerminalHeight, getTerminalWidth, gradient, gradientColor, gradientRect, graphemes, grid, hasFont, hexToRgb, hideCursor, hsplit, images, isHexColor, isNoColor, json, pretty as jsonPretty, lerp, lerpColor, link, listFonts, listPresets, loader, mapTree, measureTree, memoize, nextTick, onConfigChange, onConfigKeyChange, onResize, once, padBoth, padEnd, padStart, panels, parseFiglet, pauseListeners, presetNames, presets, rainbow, registerFont, registerPreset, renderPixelArt, renderTree, renderTreeStream, repeatVisible, requestTerminalFrame, reset, resetColorSupportCache, resetConfig, resetCursorRefCount, resetFramesCursorCount, resetLoaderCursorCount, resetNoColor, resumeListeners, reverseGradient, rgbTo256, rgbToHex, rotate90, safeJson, screen, setNoColor, setTitle, sgr, showCursor, sleep, sleepFrame, sliceAnsi, stripAnsi$1 as stripAnsi, stripAnsi$2 as stripAnsiCodes, stripAnsi as stripAnsiColors, supportsColor, supportsColorLevel, termSize, themes, throttle, tree, trees, truncateAnsi, visibleLen, vsplit, walkTree, withConfig, wordWrap, wrapAnsi, write, writeAsync, writeErr, writeln, writelnErr };