ansimax 1.3.1 β†’ 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/dist/index.mjs CHANGED
@@ -2270,23 +2270,61 @@ var banner = (text, opts = {}) => {
2270
2270
  };
2271
2271
  var box = (text, opts = {}) => {
2272
2272
  const safe = ensureString2(text, "box(text)");
2273
- const { padding = 1, borderStyle = "rounded", width = null } = opts;
2273
+ const {
2274
+ padding = 1,
2275
+ borderStyle = "rounded",
2276
+ width = null,
2277
+ title = null,
2278
+ // v1.3.3
2279
+ titleAlign = "center"
2280
+ // v1.3.3
2281
+ } = opts;
2274
2282
  const padNum = typeof padding === "number" && Number.isFinite(padding) ? padding : 1;
2275
2283
  const safePadding = Math.max(0, Math.floor(padNum));
2276
2284
  const b = BOX_STYLES[borderStyle] ?? BOX_STYLES.rounded;
2277
2285
  const lines = safe.split("\n");
2278
2286
  const inner = width != null ? lines.map((l) => padEnd(truncateAnsi(l, width, ""), width)) : lines;
2279
- const w = inner.length > 0 ? Math.max(0, ...inner.map((l) => visibleLen(l))) : 0;
2287
+ const contentW = inner.length > 0 ? Math.max(0, ...inner.map((l) => visibleLen(l))) : 0;
2288
+ let w = contentW;
2289
+ let titleStr = "";
2290
+ let titleW = 0;
2291
+ if (typeof title === "string" && title.length > 0) {
2292
+ titleStr = ` ${title} `;
2293
+ titleW = visibleLen(titleStr);
2294
+ const titleNeeded = titleW + 2;
2295
+ const innerNeeded = w + safePadding * 2;
2296
+ if (titleNeeded > innerNeeded) {
2297
+ w = titleNeeded - safePadding * 2;
2298
+ }
2299
+ }
2300
+ const innerW = w + safePadding * 2;
2280
2301
  const pad = " ".repeat(safePadding);
2281
- const top = b.tl + b.h.repeat(w + safePadding * 2) + b.tr;
2282
- const bottom = b.bl + b.h.repeat(w + safePadding * 2) + b.br;
2283
- const emptyRow = b.v + " ".repeat(w + safePadding * 2) + b.v;
2302
+ let top;
2303
+ if (titleStr.length > 0 && titleW < innerW) {
2304
+ let before;
2305
+ let after;
2306
+ if (titleAlign === "left") {
2307
+ before = 1;
2308
+ after = innerW - titleW - before;
2309
+ } else if (titleAlign === "right") {
2310
+ after = 1;
2311
+ before = innerW - titleW - after;
2312
+ } else {
2313
+ before = Math.floor((innerW - titleW) / 2);
2314
+ after = innerW - titleW - before;
2315
+ }
2316
+ top = b.tl + b.h.repeat(before) + titleStr + b.h.repeat(after) + b.tr;
2317
+ } else {
2318
+ top = b.tl + b.h.repeat(innerW) + b.tr;
2319
+ }
2320
+ const bottom = b.bl + b.h.repeat(innerW) + b.br;
2321
+ const emptyRow = b.v + " ".repeat(innerW) + b.v;
2284
2322
  const rows = inner.map((l) => b.v + pad + padEnd(l, w) + pad + b.v);
2285
2323
  const vPad = Array(safePadding).fill(emptyRow);
2286
2324
  return [top, ...vPad, ...rows, ...vPad, bottom].join("\n");
2287
2325
  };
2288
2326
  var divider = (opts = {}) => {
2289
- const { char, width = null, label = null, style = "single" } = opts;
2327
+ const { char, width = null, label = null, style = "single", align = "center" } = opts;
2290
2328
  const { cols } = termSize();
2291
2329
  const w = Math.max(0, width ?? cols);
2292
2330
  const b = BOX_STYLES[style] ?? BOX_STYLES.single;
@@ -2295,8 +2333,18 @@ var divider = (opts = {}) => {
2295
2333
  if (label) {
2296
2334
  const labelLen = visibleLen(label);
2297
2335
  if (labelLen >= w - 2) return label;
2298
- const side = Math.max(0, Math.floor((w - labelLen - 2) / 2));
2299
- const trailLen = Math.max(0, w - side - labelLen - 2);
2336
+ let side;
2337
+ let trailLen;
2338
+ if (align === "left") {
2339
+ side = 1;
2340
+ trailLen = Math.max(0, w - labelLen - side - 2);
2341
+ } else if (align === "right") {
2342
+ trailLen = 1;
2343
+ side = Math.max(0, w - labelLen - trailLen - 2);
2344
+ } else {
2345
+ side = Math.max(0, Math.floor((w - labelLen - 2) / 2));
2346
+ trailLen = Math.max(0, w - side - labelLen - 2);
2347
+ }
2300
2348
  return fill.repeat(side) + " " + label + " " + fill.repeat(trailLen);
2301
2349
  }
2302
2350
  return fill.repeat(w);
@@ -4949,16 +4997,16 @@ var ensurePixelGrid = (pixels) => {
4949
4997
  return pixels.map((row) => Array.isArray(row) ? row : []);
4950
4998
  };
4951
4999
  var renderPixelArt = (pixels, opts = {}) => {
4952
- const grid = ensurePixelGrid(pixels);
4953
- if (!grid || grid.length === 0) return "";
5000
+ const grid2 = ensurePixelGrid(pixels);
5001
+ if (!grid2 || grid2.length === 0) return "";
4954
5002
  const { scale = 1, halfBlock = true, braille = false } = opts;
4955
5003
  const safeScale = clampInt(scale, 1, 100, 1);
4956
- if (braille) return _renderBraille(grid);
5004
+ if (braille) return _renderBraille(grid2);
4957
5005
  const lines = [];
4958
5006
  if (halfBlock) {
4959
- for (let row = 0; row < grid.length; row += 2) {
4960
- const topRow = grid[row] ?? [];
4961
- const botRow = grid[row + 1] ?? [];
5007
+ for (let row = 0; row < grid2.length; row += 2) {
5008
+ const topRow = grid2[row] ?? [];
5009
+ const botRow = grid2[row + 1] ?? [];
4962
5010
  const colCount = Math.max(topRow.length, botRow.length);
4963
5011
  let line = "";
4964
5012
  let curFg = -1;
@@ -5015,7 +5063,7 @@ var renderPixelArt = (pixels, opts = {}) => {
5015
5063
  lines.push(line);
5016
5064
  }
5017
5065
  } else {
5018
- for (const row of grid) {
5066
+ for (const row of grid2) {
5019
5067
  let line = "";
5020
5068
  let curFg = -1;
5021
5069
  for (const pixel of row) {
@@ -5130,24 +5178,24 @@ var SPRITES = {
5130
5178
  ] }
5131
5179
  };
5132
5180
  var flipHorizontal = (pixels) => {
5133
- const grid = ensurePixelGrid(pixels);
5134
- if (!grid) return [];
5135
- return grid.map((row) => [...row].reverse());
5181
+ const grid2 = ensurePixelGrid(pixels);
5182
+ if (!grid2) return [];
5183
+ return grid2.map((row) => [...row].reverse());
5136
5184
  };
5137
5185
  var flipVertical = (pixels) => {
5138
- const grid = ensurePixelGrid(pixels);
5139
- if (!grid) return [];
5140
- return [...grid].reverse();
5186
+ const grid2 = ensurePixelGrid(pixels);
5187
+ if (!grid2) return [];
5188
+ return [...grid2].reverse();
5141
5189
  };
5142
5190
  var rotate90 = (pixels) => {
5143
- const grid = ensurePixelGrid(pixels);
5144
- if (!grid || grid.length === 0) return [];
5145
- const rows = grid.length;
5146
- const cols = Math.max(0, ...grid.map((r) => r.length));
5191
+ const grid2 = ensurePixelGrid(pixels);
5192
+ if (!grid2 || grid2.length === 0) return [];
5193
+ const rows = grid2.length;
5194
+ const cols = Math.max(0, ...grid2.map((r) => r.length));
5147
5195
  if (cols === 0) return [];
5148
5196
  return Array.from(
5149
5197
  { length: cols },
5150
- (_, c) => Array.from({ length: rows }, (__, r) => grid[rows - 1 - r]?.[c] ?? null)
5198
+ (_, c) => Array.from({ length: rows }, (__, r) => grid2[rows - 1 - r]?.[c] ?? null)
5151
5199
  );
5152
5200
  };
5153
5201
  var BAYER_4x4 = [
@@ -5542,7 +5590,9 @@ var frame = (block, opts = {}) => {
5542
5590
  paddingX,
5543
5591
  topChar = "\u2500",
5544
5592
  bottomChar,
5545
- title
5593
+ title,
5594
+ titleAlign = "center"
5595
+ // v1.3.3
5546
5596
  } = opts;
5547
5597
  const safePadY = Math.max(0, Math.floor(paddingY ?? padding));
5548
5598
  const safePadX = Math.max(0, Math.floor(paddingX ?? padding));
@@ -5563,8 +5613,18 @@ var frame = (block, opts = {}) => {
5563
5613
  }
5564
5614
  let topLine;
5565
5615
  if (titleStr.length > 0 && titleW < innerW) {
5566
- const before = Math.floor((innerW - titleW) / 2);
5567
- const after = innerW - titleW - before;
5616
+ let before;
5617
+ let after;
5618
+ if (titleAlign === "left") {
5619
+ before = 1;
5620
+ after = innerW - titleW - before;
5621
+ } else if (titleAlign === "right") {
5622
+ after = 1;
5623
+ before = innerW - titleW - after;
5624
+ } else {
5625
+ before = Math.floor((innerW - titleW) / 2);
5626
+ after = innerW - titleW - before;
5627
+ }
5568
5628
  topLine = safeTop.repeat(before) + titleStr + safeTop.repeat(after);
5569
5629
  } else {
5570
5630
  topLine = safeTop.repeat(innerW);
@@ -5585,12 +5645,54 @@ var frame = (block, opts = {}) => {
5585
5645
  out.push(bottomLine);
5586
5646
  return out.join("\n");
5587
5647
  };
5648
+ var grid = (blocks, opts) => {
5649
+ if (!Array.isArray(blocks) || blocks.length === 0) return "";
5650
+ if (!opts || typeof opts !== "object") return "";
5651
+ const columns2 = Math.max(1, Math.floor(opts.columns ?? 1));
5652
+ const gapX = Math.max(0, Math.floor(opts.gapX ?? 1));
5653
+ const gapY = Math.max(0, Math.floor(opts.gapY ?? 0));
5654
+ const alignX = opts.alignX ?? "start";
5655
+ const alignY = opts.alignY ?? "start";
5656
+ const cellW = opts.cellWidth != null ? Math.max(0, Math.floor(opts.cellWidth)) : null;
5657
+ const rows = [];
5658
+ for (let i = 0; i < blocks.length; i += columns2) {
5659
+ rows.push(blocks.slice(i, i + columns2));
5660
+ }
5661
+ let widths = null;
5662
+ if (cellW != null) {
5663
+ widths = Array(columns2).fill(cellW);
5664
+ } else {
5665
+ widths = Array(columns2).fill(0);
5666
+ for (const row of rows) {
5667
+ for (let c = 0; c < row.length; c++) {
5668
+ const { maxWidth } = _splitBlock(row[c]);
5669
+ if (maxWidth > widths[c]) {
5670
+ widths[c] = maxWidth;
5671
+ }
5672
+ }
5673
+ }
5674
+ }
5675
+ const renderedRows = rows.map((row) => {
5676
+ const padded = [];
5677
+ for (let c = 0; c < columns2; c++) {
5678
+ padded.push(row[c] ?? "");
5679
+ }
5680
+ return vsplit(padded, {
5681
+ gap: gapX,
5682
+ align: alignY,
5683
+ widths
5684
+ });
5685
+ });
5686
+ return hsplit(renderedRows, { gap: gapY, align: alignX });
5687
+ };
5588
5688
  var panels = {
5589
5689
  vsplit,
5590
5690
  hsplit,
5591
5691
  // v1.3.1
5592
5692
  center: center2,
5593
- frame
5693
+ frame,
5694
+ // v1.3.3
5695
+ grid
5594
5696
  };
5595
5697
 
5596
5698
  // src/json/index.ts
@@ -5610,11 +5712,12 @@ var _truncString = (s, maxLength) => {
5610
5712
  return s.slice(0, maxLength - 3) + "...";
5611
5713
  };
5612
5714
  var _formatPrimitive = (value, opts) => {
5613
- const { useColor, maxStringLength } = opts;
5715
+ const { useColor, maxStringLength, mode } = opts;
5614
5716
  if (value === null) {
5615
5717
  return _c("null", COLORS.null, useColor);
5616
5718
  }
5617
5719
  if (value === void 0) {
5720
+ if (mode === "json") return _c("null", COLORS.null, useColor);
5618
5721
  return _c("undefined", COLORS.null, useColor);
5619
5722
  }
5620
5723
  if (typeof value === "string") {
@@ -5623,8 +5726,12 @@ var _formatPrimitive = (value, opts) => {
5623
5726
  return _c(quoted, COLORS.string, useColor);
5624
5727
  }
5625
5728
  if (typeof value === "number") {
5626
- if (Number.isNaN(value)) return _c("NaN", COLORS.number, useColor);
5729
+ if (Number.isNaN(value)) {
5730
+ if (mode === "json") return _c("null", COLORS.null, useColor);
5731
+ return _c("NaN", COLORS.number, useColor);
5732
+ }
5627
5733
  if (!Number.isFinite(value)) {
5734
+ if (mode === "json") return _c("null", COLORS.null, useColor);
5628
5735
  return _c(value > 0 ? "Infinity" : "-Infinity", COLORS.number, useColor);
5629
5736
  }
5630
5737
  return _c(String(value), COLORS.number, useColor);
@@ -5633,16 +5740,23 @@ var _formatPrimitive = (value, opts) => {
5633
5740
  return _c(String(value), COLORS.boolean, useColor);
5634
5741
  }
5635
5742
  if (typeof value === "bigint") {
5743
+ if (mode === "json") {
5744
+ const big2 = value;
5745
+ const asNum = Number(big2);
5746
+ if (Number.isSafeInteger(asNum) && BigInt(asNum) === big2) {
5747
+ return _c(String(asNum), COLORS.number, useColor);
5748
+ }
5749
+ return _c(JSON.stringify(String(big2)), COLORS.string, useColor);
5750
+ }
5636
5751
  return _c(`${value}n`, COLORS.number, useColor);
5637
5752
  }
5638
5753
  if (typeof value === "function") {
5754
+ if (mode === "json") return _c("null", COLORS.null, useColor);
5639
5755
  const name = value.name || "anonymous";
5640
5756
  return _c(`[Function: ${name}]`, COLORS.comment, useColor);
5641
5757
  }
5642
- if (typeof value === "symbol") {
5643
- return _c(value.toString(), COLORS.comment, useColor);
5644
- }
5645
- return _c(String(value), COLORS.comment, useColor);
5758
+ if (mode === "json") return _c("null", COLORS.null, useColor);
5759
+ return _c(value.toString(), COLORS.comment, useColor);
5646
5760
  };
5647
5761
  var _renderValue = (value, depth, config) => {
5648
5762
  const {
@@ -5653,15 +5767,56 @@ var _renderValue = (value, depth, config) => {
5653
5767
  useColor,
5654
5768
  seen,
5655
5769
  sortKeys,
5656
- inlineArrayMaxLength
5770
+ inlineArrayMaxLength,
5771
+ mode
5657
5772
  } = config;
5658
5773
  if (value === null || typeof value !== "object") {
5659
- return _formatPrimitive(value, { useColor, maxStringLength });
5774
+ return _formatPrimitive(value, { useColor, maxStringLength, mode });
5660
5775
  }
5661
5776
  if (seen.has(value)) {
5777
+ if (mode === "json") {
5778
+ throw new TypeError("Cannot serialize circular reference to JSON");
5779
+ }
5662
5780
  return _c("[Circular]", COLORS.comment, useColor);
5663
5781
  }
5664
5782
  seen.add(value);
5783
+ if (value instanceof Date) {
5784
+ const iso = value.toISOString();
5785
+ if (mode === "json") {
5786
+ return _c(JSON.stringify(iso), COLORS.string, useColor);
5787
+ }
5788
+ return _c(`Date(${iso})`, COLORS.comment, useColor);
5789
+ }
5790
+ if (value instanceof Map) {
5791
+ if (mode === "json") {
5792
+ const obj = {};
5793
+ for (const [k, v] of value) {
5794
+ if (typeof k === "string") obj[k] = v;
5795
+ else obj[String(k)] = v;
5796
+ }
5797
+ return _renderValue(obj, depth, config);
5798
+ }
5799
+ const entries2 = Array.from(value);
5800
+ const sizeLabel = _c(`Map(${entries2.length})`, COLORS.comment, useColor);
5801
+ if (entries2.length === 0) {
5802
+ return `${sizeLabel} ${_c("{}", COLORS.bracket, useColor)}`;
5803
+ }
5804
+ if (depth >= maxDepth - 1 && depth > 0) {
5805
+ return `${sizeLabel} ${_c("{...}", COLORS.bracket, useColor)}`;
5806
+ }
5807
+ return `${sizeLabel} ${_renderValue(entries2, depth, config)}`;
5808
+ }
5809
+ if (value instanceof Set) {
5810
+ if (mode === "json") {
5811
+ return _renderValue(Array.from(value), depth, config);
5812
+ }
5813
+ const arr = Array.from(value);
5814
+ const sizeLabel = _c(`Set(${arr.length})`, COLORS.comment, useColor);
5815
+ if (arr.length === 0) {
5816
+ return `${sizeLabel} ${_c("[]", COLORS.bracket, useColor)}`;
5817
+ }
5818
+ return `${sizeLabel} ${_renderValue(arr, depth, config)}`;
5819
+ }
5665
5820
  if (depth >= maxDepth) {
5666
5821
  if (Array.isArray(value)) {
5667
5822
  return _c("[", COLORS.bracket, useColor) + _c("...", COLORS.comment, useColor) + _c("]", COLORS.bracket, useColor);
@@ -5680,7 +5835,7 @@ var _renderValue = (value, depth, config) => {
5680
5835
  const displayCount2 = Math.min(value.length, maxItems);
5681
5836
  const inlineItems = [];
5682
5837
  for (let i = 0; i < displayCount2; i++) {
5683
- inlineItems.push(_formatPrimitive(value[i], { useColor, maxStringLength }));
5838
+ inlineItems.push(_formatPrimitive(value[i], { useColor, maxStringLength, mode }));
5684
5839
  }
5685
5840
  if (value.length > maxItems) {
5686
5841
  const remaining = value.length - maxItems;
@@ -5706,6 +5861,12 @@ var _renderValue = (value, depth, config) => {
5706
5861
  return _c("[", COLORS.bracket, useColor) + "\n" + items.join(",\n") + "\n" + closePad + _c("]", COLORS.bracket, useColor);
5707
5862
  }
5708
5863
  let keys = Object.keys(value);
5864
+ if (mode === "json") {
5865
+ keys = keys.filter((k) => {
5866
+ const v = value[k];
5867
+ return typeof v !== "function" && typeof v !== "symbol" && v !== void 0;
5868
+ });
5869
+ }
5709
5870
  if (keys.length === 0) {
5710
5871
  return _c("{}", COLORS.bracket, useColor);
5711
5872
  }
@@ -5734,14 +5895,17 @@ var pretty = (value, opts = {}) => {
5734
5895
  maxStringLength = Infinity,
5735
5896
  // v1.3.1
5736
5897
  sortKeys = false,
5737
- inlineArrayMaxLength = 60
5898
+ inlineArrayMaxLength = 60,
5899
+ // v1.3.3
5900
+ mode = "display"
5738
5901
  } = opts;
5739
5902
  const safeIndent = Math.max(0, Math.floor(Number(indent) || 0));
5740
5903
  const safeMaxDepth = Number.isFinite(maxDepth) ? Math.max(0, Math.floor(maxDepth)) : Infinity;
5741
5904
  const safeMaxItems = Number.isFinite(maxItems) ? Math.max(0, Math.floor(maxItems)) : Infinity;
5742
5905
  const safeMaxStrLen = Number.isFinite(maxStringLength) ? Math.max(0, Math.floor(maxStringLength)) : Infinity;
5743
5906
  const safeInlineMax = Number.isFinite(inlineArrayMaxLength) ? Math.max(0, Math.floor(inlineArrayMaxLength)) : 60;
5744
- const useColor = colors && !isNoColor();
5907
+ const safeMode = mode === "json" ? "json" : "display";
5908
+ const useColor = safeMode === "json" ? false : colors && !isNoColor();
5745
5909
  return _renderValue(value, 0, {
5746
5910
  indent: safeIndent,
5747
5911
  maxDepth: safeMaxDepth,
@@ -5750,7 +5914,8 @@ var pretty = (value, opts = {}) => {
5750
5914
  useColor,
5751
5915
  seen: /* @__PURE__ */ new WeakSet(),
5752
5916
  sortKeys: Boolean(sortKeys),
5753
- inlineArrayMaxLength: safeInlineMax
5917
+ inlineArrayMaxLength: safeInlineMax,
5918
+ mode: safeMode
5754
5919
  });
5755
5920
  };
5756
5921
  var json = {
@@ -6044,6 +6209,7 @@ export {
6044
6209
  gradientColor,
6045
6210
  gradientRect,
6046
6211
  graphemes,
6212
+ grid,
6047
6213
  hasFont,
6048
6214
  hexToRgb,
6049
6215
  hideCursor,
package/docs/README.md ADDED
@@ -0,0 +1,110 @@
1
+ <div align="center">
2
+
3
+ # πŸ“š ansimax β€” Documentation
4
+
5
+ Detailed examples covering every module in **ansimax**, with copy-paste runnable code in three formats.
6
+
7
+ </div>
8
+
9
+ ---
10
+
11
+ ## πŸ“– Where to start
12
+
13
+ Pick the document that matches your project setup:
14
+
15
+ | File | Format | Use when… |
16
+ |---|---|---|
17
+ | [`examples-ts.md`](./examples-ts.md) | **TypeScript** (`.ts`) | Your project uses TypeScript (with `ts-node`, `tsx`, or compiled output) |
18
+ | [`examples-mjs.md`](./examples-mjs.md) | **JavaScript ESM** (`.mjs` / `"type":"module"`) | Modern JavaScript with `import`/`export` |
19
+ | [`examples-cjs.md`](./examples-cjs.md) | **JavaScript CommonJS** (`.cjs` / classic Node) | Legacy or simple Node scripts with `require()` |
20
+ | [`showcase.md`](./showcase.md) | **Complete app** | A single end-to-end JavaScript app combining all modules |
21
+
22
+ Each `examples-*.md` file covers **all 11 modules** with **3 runnable examples each** (33 examples per file).
23
+
24
+ ---
25
+
26
+ ## 🧩 Modules covered in every file
27
+
28
+ Each examples file is organized in this order:
29
+
30
+ 1. **`color`** β€” basic colors, ANSI styling, gradients
31
+ 2. **`gradient`** β€” multi-stop, animated, eased, conic gradients
32
+ 3. **`ascii`** β€” boxes, banners (figlet), image-to-ASCII
33
+ 4. **`animations`** β€” typewriter, fadeIn, fadeOut
34
+ 5. **`loaders`** β€” spinners, tasks, progress bars
35
+ 6. **`components`** β€” table, badge, status, timeline
36
+ 7. **`themes`** β€” built-in themes, custom themes, registry
37
+ 8. **`trees`** β€” hierarchical rendering, max-depth, cycles
38
+ 9. **`frames`** β€” frame-by-frame animation, morphing
39
+ 10. **`images`** β€” pixel art, canvas, gradient rectangles
40
+ 11. **`panels`** + **`json`** β€” split layouts + pretty-printing (v1.3.0+)
41
+
42
+ ---
43
+
44
+ ## πŸ’‘ Running the examples
45
+
46
+ ### TypeScript (`examples-ts.md`)
47
+
48
+ ```bash
49
+ # Quickest: tsx (no compile step)
50
+ npx tsx my-example.ts
51
+
52
+ # Or with ts-node
53
+ npx ts-node my-example.ts
54
+
55
+ # Or compile then run
56
+ npx tsc my-example.ts && node my-example.js
57
+ ```
58
+
59
+ ### JavaScript ESM (`examples-mjs.md`)
60
+
61
+ Either:
62
+ - Save the file with `.mjs` extension, OR
63
+ - Set `"type": "module"` in your `package.json` and use `.js`
64
+
65
+ ```bash
66
+ node my-example.mjs
67
+ ```
68
+
69
+ ### JavaScript CommonJS (`examples-cjs.md`)
70
+
71
+ ```bash
72
+ node my-example.cjs
73
+ ```
74
+
75
+ ### Showcase (`showcase.md`)
76
+
77
+ A complete demo app. Save as `.mjs`, run with `node`. Demonstrates how multiple modules compose in a real CLI.
78
+
79
+ ---
80
+
81
+ ## βš™οΈ Project setup
82
+
83
+ For any of these, your `package.json` needs:
84
+
85
+ ```json
86
+ {
87
+ "dependencies": {
88
+ "ansimax": "^1.3.2"
89
+ }
90
+ }
91
+ ```
92
+
93
+ Then `npm install` and pick an example.
94
+
95
+ ---
96
+
97
+ ## πŸ”— More resources
98
+
99
+ - [Main README](../README.md) β€” overview, badges, comparison table, roadmap
100
+ - [CHANGELOG](../CHANGELOG.md) β€” version history
101
+ - [npm package](https://www.npmjs.com/package/ansimax)
102
+ - [GitHub](https://github.com/Brashkie/ansimax)
103
+
104
+ ---
105
+
106
+ <div align="center">
107
+
108
+ If any example doesn't work, [open an issue](https://github.com/Brashkie/ansimax/issues) β€” we'll fix it.
109
+
110
+ </div>