ansimax 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,715 @@
1
+ # šŸ“˜ ansimax — TypeScript examples
2
+
3
+ All examples are written in **TypeScript** with explicit types. Save any snippet as `example.ts` and run with:
4
+
5
+ ```bash
6
+ npx tsx example.ts
7
+ # or: npx ts-node example.ts
8
+ # or: npx tsc example.ts && node example.js
9
+ ```
10
+
11
+ For JavaScript versions, see [`examples-mjs.md`](./examples-mjs.md) (ESM) or [`examples-cjs.md`](./examples-cjs.md) (CommonJS).
12
+
13
+ ---
14
+
15
+ ## šŸŽØ 1. `color` — Terminal colors & styles
16
+
17
+ ### 1.1 — Basic colored output
18
+
19
+ ```ts
20
+ import { color } from 'ansimax';
21
+
22
+ console.log(color.green('āœ“ Build successful'));
23
+ console.log(color.red('āœ— Build failed'));
24
+ console.log(color.yellow('⚠ 3 warnings'));
25
+ console.log(color.cyan('ℹ Check the logs above'));
26
+
27
+ // Composable styles
28
+ const heading: string = color.bold(color.magenta('Application started'));
29
+ console.log(heading);
30
+ ```
31
+
32
+ ### 1.2 — Status report with mixed colors
33
+
34
+ ```ts
35
+ import { color } from 'ansimax';
36
+
37
+ interface Service {
38
+ name: string;
39
+ status: 'up' | 'down' | 'degraded';
40
+ latency: number;
41
+ }
42
+
43
+ const services: Service[] = [
44
+ { name: 'api', status: 'up', latency: 45 },
45
+ { name: 'database', status: 'up', latency: 12 },
46
+ { name: 'cache', status: 'degraded', latency: 240 },
47
+ { name: 'auth', status: 'down', latency: 0 },
48
+ ];
49
+
50
+ console.log(color.bold('Service Health Report\n'));
51
+
52
+ for (const svc of services) {
53
+ const icon = svc.status === 'up' ? color.green('ā—')
54
+ : svc.status === 'degraded' ? color.yellow('ā—')
55
+ : color.red('ā—');
56
+ const lat = svc.latency > 100 ? color.red(`${svc.latency}ms`)
57
+ : svc.latency > 50 ? color.yellow(`${svc.latency}ms`)
58
+ : color.green(`${svc.latency}ms`);
59
+
60
+ console.log(`${icon} ${svc.name.padEnd(10)} ${lat}`);
61
+ }
62
+ ```
63
+
64
+ ### 1.3 — Truecolor & hex colors
65
+
66
+ ```ts
67
+ import { color } from 'ansimax';
68
+
69
+ // Hex colors via background helpers
70
+ console.log(color.bgHex('#bd93f9')(' Dracula purple '));
71
+ console.log(color.bgHex('#ff79c6')(' Pink '));
72
+ console.log(color.bgHex('#50fa7b')(' Green '));
73
+
74
+ // Foreground hex
75
+ console.log(color.hex('#ff6b6b')('Bright red text'));
76
+ console.log(color.hex('#4ecdc4')('Turquoise text'));
77
+
78
+ // ANSI escapes are stripped automatically when piped to a non-TTY:
79
+ // node example.ts → colored
80
+ // node example.ts | cat → plain text
81
+ ```
82
+
83
+ ---
84
+
85
+ ## 🌈 2. `gradient` — Multi-stop gradients
86
+
87
+ ### 2.1 — Simple two-color gradient
88
+
89
+ ```ts
90
+ import { gradient } from 'ansimax';
91
+
92
+ console.log(gradient('Hello, gradients!', ['#ff79c6', '#bd93f9']));
93
+ console.log(gradient('Pink to purple', ['#ff79c6', '#bd93f9']));
94
+ console.log(gradient('Sunset', ['#ff5e62', '#ff9966', '#ffd86f']));
95
+ ```
96
+
97
+ ### 2.2 — Reusable gradient with easing
98
+
99
+ ```ts
100
+ import { createGradient, type EasingName } from 'ansimax';
101
+
102
+ const easings: EasingName[] = ['linear', 'ease-in', 'ease-out', 'ease-in-out'];
103
+
104
+ for (const ease of easings) {
105
+ const grad = createGradient(['#8be9fd', '#bd93f9', '#ff79c6'], { easing: ease });
106
+ console.log(`${ease.padEnd(15)} ${grad('ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ')}`);
107
+ }
108
+
109
+ // Custom easing function (t ∈ [0, 1] → eased t)
110
+ const cubic = createGradient(['#ff0000', '#0000ff'], {
111
+ easing: (t: number) => t * t * t,
112
+ });
113
+ console.log(cubic('Custom cubic easing across this whole line'));
114
+ ```
115
+
116
+ ### 2.3 — Animated gradient (color cycles over time)
117
+
118
+ ```ts
119
+ import { animateGradient } from 'ansimax';
120
+
121
+ const ctrl = animateGradient(
122
+ '╔═══ Loading data... ═══╗',
123
+ ['#ff79c6', '#bd93f9', '#8be9fd', '#50fa7b'],
124
+ {
125
+ interval: 80,
126
+ cycles: 3, // play 3 full color cycles
127
+ duration: 800, // ms per cycle
128
+ infinite: false,
129
+ },
130
+ );
131
+
132
+ await ctrl.promise;
133
+ console.log('\nāœ“ Done');
134
+ ```
135
+
136
+ ---
137
+
138
+ ## šŸ”¤ 3. `ascii` — Boxes, banners, image-to-ASCII
139
+
140
+ ### 3.1 — Boxes with multiple styles
141
+
142
+ ```ts
143
+ import { ascii, gradient } from 'ansimax';
144
+
145
+ console.log(ascii.box('Hello world!', { borderStyle: 'rounded' }));
146
+ console.log(ascii.box('Important!', { borderStyle: 'double', padding: 2 }));
147
+
148
+ // Box around colored content — border stays uncolored
149
+ const colored = gradient('Rainbow inside', ['#ff79c6', '#bd93f9', '#8be9fd']);
150
+ console.log(ascii.box(colored, { borderStyle: 'heavy', padding: 1 }));
151
+
152
+ // Multi-line box
153
+ const multi = 'Line 1\nLine 2\nLine 3 longer\n\nWith blank line';
154
+ console.log(ascii.box(multi, { borderStyle: 'rounded', padding: 1 }));
155
+ ```
156
+
157
+ ### 3.2 — Banner with figlet font
158
+
159
+ ```ts
160
+ import { ascii, gradient } from 'ansimax';
161
+
162
+ const banner: string = ascii.figletText('ANSIMAX', { font: 'standard' });
163
+ const colored: string = gradient(banner, ['#ff79c6', '#bd93f9', '#8be9fd']);
164
+ console.log(colored);
165
+
166
+ // Different font
167
+ const compact: string = ascii.figletText('v1.3.2', { font: 'small' });
168
+ console.log(ascii.box(compact, { borderStyle: 'rounded' }));
169
+ ```
170
+
171
+ ### 3.3 — Image-to-ASCII from a PixelGrid
172
+
173
+ ```ts
174
+ import { ascii, type PixelGrid } from 'ansimax';
175
+
176
+ // Build a small "image" — a smiley face — programmatically
177
+ const N = null;
178
+ const Y = { r: 255, g: 220, b: 0 };
179
+ const K = { r: 0, g: 0, b: 0 };
180
+
181
+ const smiley: PixelGrid = [
182
+ [N, N, Y, Y, Y, Y, N, N],
183
+ [N, Y, Y, Y, Y, Y, Y, N],
184
+ [Y, Y, K, Y, Y, K, Y, Y],
185
+ [Y, Y, Y, Y, Y, Y, Y, Y],
186
+ [Y, K, Y, Y, Y, Y, K, Y],
187
+ [Y, Y, K, K, K, K, Y, Y],
188
+ [N, Y, Y, Y, Y, Y, Y, N],
189
+ [N, N, Y, Y, Y, Y, N, N],
190
+ ];
191
+
192
+ console.log(ascii.fromImage(smiley, { width: 30, ramp: 'block' }));
193
+ ```
194
+
195
+ ---
196
+
197
+ ## ✨ 4. `animations` — Typewriter, fadeIn, fadeOut
198
+
199
+ ### 4.1 — Typewriter effect
200
+
201
+ ```ts
202
+ import { animate } from 'ansimax';
203
+
204
+ await animate.typewriter('Loading project configuration...', { speed: 40 });
205
+ console.log();
206
+
207
+ // Abortable — stops after 500ms even on a long string
208
+ const ctrl = new AbortController();
209
+ setTimeout(() => ctrl.abort(), 500);
210
+
211
+ try {
212
+ await animate.typewriter(
213
+ 'This is a very long sentence that will get cut off mid-print.',
214
+ { speed: 30, signal: ctrl.signal },
215
+ );
216
+ } catch {
217
+ console.log('\n(aborted)');
218
+ }
219
+ ```
220
+
221
+ ### 4.2 — Fade in with custom color
222
+
223
+ ```ts
224
+ import { animate } from 'ansimax';
225
+
226
+ await animate.fadeIn('ā˜… Welcome to the demo ā˜…', {
227
+ duration: 1200,
228
+ color: '#bd93f9',
229
+ steps: 25,
230
+ });
231
+ console.log();
232
+ ```
233
+
234
+ ### 4.3 — Sequenced reveals for a fake intro
235
+
236
+ ```ts
237
+ import { animate, color } from 'ansimax';
238
+
239
+ const lines: string[] = [
240
+ '> Initializing system...',
241
+ '> Loading modules: 12/12 āœ“',
242
+ '> Connecting to mainframe...',
243
+ '> Access granted',
244
+ '> Welcome, operator.',
245
+ ];
246
+
247
+ for (const line of lines) {
248
+ await animate.typewriter(color.green(line), { speed: 25 });
249
+ console.log();
250
+ await new Promise<void>((resolve) => setTimeout(resolve, 200));
251
+ }
252
+ ```
253
+
254
+ ---
255
+
256
+ ## ā³ 5. `loaders` — Spinners, tasks, progress
257
+
258
+ ### 5.1 — Spinner with success/error states
259
+
260
+ ```ts
261
+ import { loader } from 'ansimax';
262
+
263
+ const stop = loader.spin('Fetching data from API...', {
264
+ type: 'dots',
265
+ color: '#bd93f9',
266
+ });
267
+
268
+ try {
269
+ await new Promise<void>((resolve) => setTimeout(resolve, 1500));
270
+ stop('Data loaded successfully!', true);
271
+ } catch {
272
+ stop('Request failed', false);
273
+ }
274
+ ```
275
+
276
+ ### 5.2 — Hierarchical task runner
277
+
278
+ ```ts
279
+ import { loader } from 'ansimax';
280
+
281
+ const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
282
+
283
+ await loader.tasks([
284
+ {
285
+ text: 'Build pipeline',
286
+ fn: async () => sleep(200),
287
+ subtasks: [
288
+ { text: 'Compile TypeScript', fn: async () => sleep(400) },
289
+ { text: 'Bundle for production', fn: async () => sleep(300) },
290
+ { text: 'Generate type defs', fn: async () => sleep(200) },
291
+ ],
292
+ },
293
+ {
294
+ text: 'Quality checks',
295
+ fn: async () => sleep(150),
296
+ subtasks: [
297
+ { text: 'Lint', fn: async () => sleep(250) },
298
+ { text: 'Test', fn: async () => sleep(500) },
299
+ ],
300
+ },
301
+ { text: 'Publish to npm', fn: async () => sleep(300) },
302
+ ]);
303
+ ```
304
+
305
+ ### 5.3 — Parallel task execution with results
306
+
307
+ ```ts
308
+ import { loader } from 'ansimax';
309
+
310
+ interface CheckResult {
311
+ ok: boolean;
312
+ duration: number;
313
+ }
314
+
315
+ const ping = async (name: string): Promise<CheckResult> => {
316
+ const start = Date.now();
317
+ await new Promise<void>((r) => setTimeout(r, 200 + Math.random() * 600));
318
+ return { ok: Math.random() > 0.2, duration: Date.now() - start };
319
+ };
320
+
321
+ const results = await loader.tasks([
322
+ { text: 'Check api.example.com', fn: async () => { await ping('api'); } },
323
+ { text: 'Check db.example.com', fn: async () => { await ping('db'); } },
324
+ { text: 'Check auth.example.com', fn: async () => { await ping('auth'); } },
325
+ { text: 'Check cdn.example.com', fn: async () => { await ping('cdn'); } },
326
+ ], { parallel: true });
327
+
328
+ const failed = results.filter((r) => !r.success);
329
+ console.log(`\n${results.length - failed.length}/${results.length} services healthy`);
330
+ ```
331
+
332
+ ---
333
+
334
+ ## 🧱 6. `components` — Table, badge, status, timeline
335
+
336
+ ### 6.1 — Status report with badges
337
+
338
+ ```ts
339
+ import { components } from 'ansimax';
340
+
341
+ console.log(
342
+ components.badge('VERSION', 'v1.3.2'),
343
+ components.badge('BUILD', 'passing'),
344
+ components.badge('TESTS', '2000+ passing'),
345
+ components.badge('LICENSE', 'Apache 2.0'),
346
+ );
347
+
348
+ console.log();
349
+ console.log(components.status('success', 'Build completed in 4.2s'));
350
+ console.log(components.status('warn', '3 deprecation warnings'));
351
+ console.log(components.status('error', 'Type error in src/main.ts:42'));
352
+ console.log(components.status('info', 'Documentation generated at docs/'));
353
+ ```
354
+
355
+ ### 6.2 — Table with ANSI-aware cells
356
+
357
+ ```ts
358
+ import { components, color } from 'ansimax';
359
+
360
+ const rows: string[][] = [
361
+ ['Service', 'Status', 'Latency', 'Uptime'],
362
+ ['api', color.green('āœ“ healthy'), '45ms', '99.99%'],
363
+ ['db', color.green('āœ“ healthy'), '12ms', '99.95%'],
364
+ ['cache', color.yellow('⚠ degraded'), '240ms', '98.20%'],
365
+ ['auth', color.red('āœ— down'), '—', '95.10%'],
366
+ ];
367
+
368
+ console.log(components.table(rows, {
369
+ borderStyle: 'rounded',
370
+ padding: 1,
371
+ }));
372
+ ```
373
+
374
+ ### 6.3 — Project timeline with done/pending
375
+
376
+ ```ts
377
+ import { components } from 'ansimax';
378
+
379
+ interface ProjectEvent {
380
+ label: string;
381
+ time?: string;
382
+ done?: boolean;
383
+ }
384
+
385
+ const events: ProjectEvent[] = [
386
+ { label: 'Project initialized', time: 'Mon', done: true },
387
+ { label: 'Dependencies set up', time: 'Tue', done: true },
388
+ { label: 'Core modules done', time: 'Wed', done: true },
389
+ { label: 'Tests passing', time: 'Thu', done: true },
390
+ { label: 'Documentation', time: 'Fri', done: false }, // current
391
+ { label: 'Publish to npm', done: false },
392
+ ];
393
+
394
+ console.log(components.timeline(events, { node: 'ā—', connector: '│' }));
395
+ ```
396
+
397
+ ---
398
+
399
+ ## šŸŽØ 7. `themes` — Built-in and custom themes
400
+
401
+ ### 7.1 — Switching active theme
402
+
403
+ ```ts
404
+ import { themes, type Theme } from 'ansimax';
405
+
406
+ themes.use('dracula');
407
+ const active: Theme = themes.current();
408
+
409
+ console.log(`Active theme: ${active.name}`);
410
+ console.log(`Primary color: ${active.primary}`);
411
+ console.log(`Available themes: ${themes.list().join(', ')}`);
412
+ ```
413
+
414
+ ### 7.2 — Registering a custom theme
415
+
416
+ ```ts
417
+ import { themes, type Theme } from 'ansimax';
418
+
419
+ const synthwave: Theme = {
420
+ name: 'synthwave',
421
+ primary: '#ff6ec7',
422
+ secondary: '#36d6e7',
423
+ accent: '#ffd93d',
424
+ success: '#06d6a0',
425
+ warning: '#ffd93d',
426
+ error: '#ff5e5b',
427
+ info: '#36d6e7',
428
+ muted: '#6c757d',
429
+ bg: '#241734',
430
+ surface: '#34174f',
431
+ text: '#ffffff',
432
+ gradient: ['#ff6ec7', '#36d6e7'],
433
+ };
434
+
435
+ themes.register('synthwave', synthwave);
436
+ themes.use('synthwave');
437
+
438
+ console.log(`Now using: ${themes.current().name}`);
439
+ ```
440
+
441
+ ### 7.3 — Subscribing to theme changes (live UI updates)
442
+
443
+ ```ts
444
+ import { themes, type Theme } from 'ansimax';
445
+
446
+ const unsubscribe = themes.onChange((newT: Theme, oldT: Theme) => {
447
+ console.log(`Theme changed: ${oldT.name} → ${newT.name}`);
448
+ // In a real app: trigger UI re-render here
449
+ });
450
+
451
+ themes.use('dracula'); // logs "default → dracula"
452
+ themes.use('nord'); // logs "dracula → nord"
453
+ themes.use('monokai'); // logs "nord → monokai"
454
+
455
+ unsubscribe(); // stop listening
456
+ themes.use('dracula'); // no log this time
457
+ ```
458
+
459
+ ---
460
+
461
+ ## 🌳 8. `trees` — Hierarchical rendering
462
+
463
+ ### 8.1 — Simple file tree
464
+
465
+ ```ts
466
+ import { trees, type TreeNode } from 'ansimax';
467
+
468
+ const projectTree: TreeNode = {
469
+ label: 'my-app/',
470
+ children: [
471
+ {
472
+ label: 'src/',
473
+ children: [
474
+ { label: 'index.ts' },
475
+ { label: 'utils.ts' },
476
+ { label: 'config.ts' },
477
+ ],
478
+ },
479
+ {
480
+ label: 'tests/',
481
+ children: [
482
+ { label: 'unit/' },
483
+ { label: 'e2e/' },
484
+ ],
485
+ },
486
+ { label: 'package.json' },
487
+ { label: 'README.md' },
488
+ ],
489
+ };
490
+
491
+ console.log(trees.tree(projectTree, { style: 'unicode' }));
492
+ ```
493
+
494
+ ### 8.2 — Limit depth for large trees
495
+
496
+ ```ts
497
+ import { trees, type TreeNode } from 'ansimax';
498
+
499
+ const deep: TreeNode = {
500
+ label: 'root',
501
+ children: [
502
+ { label: 'level-1', children: [
503
+ { label: 'level-2', children: [
504
+ { label: 'level-3', children: [
505
+ { label: 'level-4 (hidden)' },
506
+ ]},
507
+ ]},
508
+ ]},
509
+ ],
510
+ };
511
+
512
+ // Only show 2 levels deep, collapse rest with "..."
513
+ console.log(trees.tree(deep, { maxDepth: 2 }));
514
+ ```
515
+
516
+ ### 8.3 — Cycle-safe rendering (no infinite recursion)
517
+
518
+ ```ts
519
+ import { trees, type TreeNode } from 'ansimax';
520
+
521
+ // Two nodes referencing each other → cycle
522
+ const a: TreeNode = { label: 'Node A', children: [] };
523
+ const b: TreeNode = { label: 'Node B', children: [] };
524
+ a.children = [b];
525
+ b.children = [a]; // ← cycle!
526
+
527
+ // trees.tree() detects the cycle and shows [Circular] instead of crashing
528
+ console.log(trees.tree(a, { maxDepth: 5 }));
529
+ ```
530
+
531
+ ---
532
+
533
+ ## šŸŽ¬ 9. `frames` — Frame-by-frame animation
534
+
535
+ ### 9.1 — Loading spinner with custom frames
536
+
537
+ ```ts
538
+ import { frames } from 'ansimax';
539
+
540
+ const spinner = ['ā ‹', 'ā ™', 'ā ¹', 'ā ø', 'ā ¼', 'ā “', 'ā ¦', 'ā §', 'ā ‡', 'ā '];
541
+
542
+ const ctrl = frames.play(
543
+ spinner.map((f) => `${f} Loading...`),
544
+ { interval: 80, loop: true },
545
+ );
546
+
547
+ setTimeout(() => ctrl.stop(), 3000);
548
+ await ctrl.promise;
549
+ console.log('Done!');
550
+ ```
551
+
552
+ ### 9.2 — Procedural frame generation
553
+
554
+ ```ts
555
+ import { frames } from 'ansimax';
556
+
557
+ // Generate a pulsing dot animation
558
+ const pulse: string[] = frames.generate(20, (i: number, total: number) => {
559
+ const intensity = Math.sin((i / total) * Math.PI * 2);
560
+ const size = Math.round(Math.abs(intensity) * 5) + 1;
561
+ return 'ā—'.repeat(size).padEnd(7);
562
+ });
563
+
564
+ await frames.play(pulse, { interval: 80, loop: true, signal: AbortSignal.timeout(2000) }).promise;
565
+ ```
566
+
567
+ ### 9.3 — Morph one text into another
568
+
569
+ ```ts
570
+ import { frames } from 'ansimax';
571
+
572
+ // Decryption-style morph effect
573
+ const morphed: string[] = frames.morph('HELLO', 'WORLD', 15, 'ā–‘ā–’ā–“ā–ˆā–“ā–’ā–‘');
574
+ await frames.play(morphed, { interval: 80, loop: false }).promise;
575
+
576
+ console.log('\nāœ“ Morph complete');
577
+ ```
578
+
579
+ ---
580
+
581
+ ## šŸ–¼ļø 10. `images` — Pixel art, canvas, gradients
582
+
583
+ ### 10.1 — Render a sprite from the built-in library
584
+
585
+ ```ts
586
+ import { renderPixelArt, SPRITES } from 'ansimax';
587
+
588
+ console.log('All sprites:', Object.keys(SPRITES).join(', '));
589
+ console.log();
590
+ console.log(renderPixelArt(SPRITES.heart.pixels, { scale: 2 }));
591
+ ```
592
+
593
+ ### 10.2 — Gradient rectangle for visual demos
594
+
595
+ ```ts
596
+ import { gradientRect } from 'ansimax';
597
+
598
+ // Sunset
599
+ console.log(gradientRect({
600
+ width: 50, height: 8,
601
+ colors: ['#ff5e62', '#ff9966', '#ffd86f'],
602
+ style: 'horizontal',
603
+ }));
604
+
605
+ console.log();
606
+
607
+ // Conic (rainbow wheel)
608
+ console.log(gradientRect({
609
+ width: 30, height: 15,
610
+ colors: ['#ff0000', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#ff00ff', '#ff0000'],
611
+ style: 'conic',
612
+ dither: 'bayer',
613
+ }));
614
+ ```
615
+
616
+ ### 10.3 — Canvas with drawing primitives
617
+
618
+ ```ts
619
+ import { createCanvas, type Canvas, type Pixel } from 'ansimax';
620
+
621
+ const canvas: Canvas = createCanvas(40, 20, { r: 30, g: 30, b: 40 });
622
+
623
+ const orange: Pixel = { r: 255, g: 165, b: 0 };
624
+ const cyan: Pixel = { r: 0, g: 200, b: 255 };
625
+ const pink: Pixel = { r: 255, g: 105, b: 180 };
626
+
627
+ canvas.drawRect(2, 2, 12, 6, cyan, true);
628
+ canvas.drawCircle(25, 8, 4, orange, true);
629
+ canvas.drawLine(0, 19, 39, 0, pink);
630
+
631
+ canvas.print();
632
+ ```
633
+
634
+ ---
635
+
636
+ ## 🪟 11. `panels` + `json` — Layouts and pretty-printing (v1.3.0+)
637
+
638
+ ### 11.1 — Side-by-side columns with nesting
639
+
640
+ ```ts
641
+ import { panels, ascii } from 'ansimax';
642
+
643
+ const sidebar: string = ascii.box('Sidebar\n• Item 1\n• Item 2\n• Item 3', { borderStyle: 'rounded' });
644
+ const main: string = ascii.box('Main content\n\nLorem ipsum dolor sit amet, consectetur.', { borderStyle: 'rounded' });
645
+
646
+ console.log(panels.vsplit([sidebar, main], { gap: 2, align: 'start' }));
647
+
648
+ console.log();
649
+
650
+ // Vertical stacking
651
+ console.log(panels.hsplit([
652
+ '── Header ──',
653
+ ascii.box('Body content'),
654
+ '── Footer ──',
655
+ ], { align: 'center', gap: 1 }));
656
+ ```
657
+
658
+ ### 11.2 — Centering and decorative frames
659
+
660
+ ```ts
661
+ import { panels, ascii } from 'ansimax';
662
+
663
+ // Center a box within an 80-column terminal
664
+ const card: string = ascii.box('Welcome!', { borderStyle: 'rounded', padding: 2 });
665
+ console.log(panels.center(card, { width: 80 }));
666
+
667
+ console.log();
668
+
669
+ // Lighter alternative to a full box — only top/bottom rules
670
+ console.log(panels.frame('Important section content here', {
671
+ title: 'Section Header',
672
+ padding: 1,
673
+ topChar: '─',
674
+ }));
675
+ ```
676
+
677
+ ### 11.3 — JSON pretty-print with all options
678
+
679
+ ```ts
680
+ import { json } from 'ansimax';
681
+
682
+ interface AppData {
683
+ name: string;
684
+ version: string;
685
+ features: string[];
686
+ stats: { tests: number; coverage: number };
687
+ meta: { active: boolean };
688
+ }
689
+
690
+ const data: AppData = {
691
+ name: 'ansimax',
692
+ version: '1.3.2',
693
+ features: ['colors', 'gradients', 'panels', 'json'],
694
+ stats: { tests: 2104, coverage: 0.98 },
695
+ meta: { active: true },
696
+ };
697
+
698
+ // Default — colored, inline short arrays
699
+ console.log(json.pretty(data));
700
+
701
+ console.log('\n--- With sortKeys (alphabetical) ---');
702
+ console.log(json.pretty(data, { sortKeys: true }));
703
+
704
+ console.log('\n--- With maxDepth: 1 (collapsed) ---');
705
+ console.log(json.pretty(data, { maxDepth: 1 }));
706
+ ```
707
+
708
+ ---
709
+
710
+ ## šŸŽÆ Next steps
711
+
712
+ - **JavaScript ESM?** → [`examples-mjs.md`](./examples-mjs.md)
713
+ - **JavaScript CommonJS?** → [`examples-cjs.md`](./examples-cjs.md)
714
+ - **Complete demo app combining everything?** → [`showcase.md`](./showcase.md)
715
+ - **Back to docs index?** → [`README.md`](./README.md)