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/dist/index.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ASCII_RAMPS: () => ASCII_RAMPS,
33
34
  BEL: () => BEL,
34
35
  BG: () => BG,
35
36
  CONFIG_DEFAULTS: () => DEFAULTS,
@@ -80,11 +81,13 @@ __export(index_exports, {
80
81
  escapeRegex: () => escapeRegex,
81
82
  fg256: () => fg256,
82
83
  fgRgb: () => fgRgb,
84
+ figletText: () => figletText,
83
85
  filterTree: () => filterTree,
84
86
  findInTree: () => findInTree,
85
87
  flipHorizontal: () => flipHorizontal,
86
88
  flipVertical: () => flipVertical,
87
89
  frames: () => frames,
90
+ fromImage: () => fromImage,
88
91
  getConfig: () => getConfig,
89
92
  getConfigValue: () => getConfigValue,
90
93
  getRenderCacheSize: () => getRenderCacheSize,
@@ -118,8 +121,10 @@ __export(index_exports, {
118
121
  padBoth: () => padBoth,
119
122
  padEnd: () => padEnd,
120
123
  padStart: () => padStart,
124
+ parseFiglet: () => parseFiglet,
121
125
  pauseListeners: () => pauseListeners,
122
126
  presetNames: () => presetNames,
127
+ presets: () => presets,
123
128
  rainbow: () => rainbow,
124
129
  registerFont: () => registerFont,
125
130
  registerPreset: () => registerPreset,
@@ -136,6 +141,7 @@ __export(index_exports, {
136
141
  resetLoaderCursorCount: () => resetLoaderCursorCount,
137
142
  resetNoColor: () => resetNoColor,
138
143
  resumeListeners: () => resumeListeners,
144
+ reverseGradient: () => reverseGradient,
139
145
  rgbTo256: () => rgbTo256,
140
146
  rgbToHex: () => rgbToHex,
141
147
  rotate90: () => rotate90,
@@ -1167,10 +1173,11 @@ var gradient = (text, stops, opts = {}) => {
1167
1173
  return _gradientAnsiAware(s, colors, easingFn, phaseN);
1168
1174
  };
1169
1175
  var createGradient = (stops, defaultOpts = {}) => {
1170
- const colors = Array.isArray(stops) ? stops.map(safeHex).filter((c) => c !== null) : [];
1176
+ const originalStops = Array.isArray(stops) ? [...stops] : [];
1177
+ const colors = originalStops.map(safeHex).filter((c) => c !== null);
1171
1178
  const defaultEasingFn = resolveEasing(defaultOpts.easing);
1172
1179
  const defaultPreserveAnsi = defaultOpts.preserveAnsi ?? false;
1173
- return (text, opts = {}) => {
1180
+ const fn = ((text, opts = {}) => {
1174
1181
  const s = coerceText(text);
1175
1182
  if (!s || isNoColor()) return s;
1176
1183
  if (colors.length === 0) return s;
@@ -1186,8 +1193,31 @@ var createGradient = (stops, defaultOpts = {}) => {
1186
1193
  return _gradientPlain(s, colors, easingFn, phaseN);
1187
1194
  }
1188
1195
  return _gradientAnsiAware(s, colors, easingFn, phaseN);
1189
- };
1196
+ });
1197
+ Object.defineProperty(fn, "stops", {
1198
+ value: Object.freeze(originalStops),
1199
+ enumerable: true,
1200
+ writable: false
1201
+ });
1202
+ Object.defineProperty(fn, "resolvedStops", {
1203
+ value: Object.freeze(colors.map((c) => Object.freeze({ ...c }))),
1204
+ enumerable: true,
1205
+ writable: false
1206
+ });
1207
+ Object.defineProperty(fn, "defaultOptions", {
1208
+ value: Object.freeze({ ...defaultOpts }),
1209
+ enumerable: true,
1210
+ writable: false
1211
+ });
1212
+ return fn;
1190
1213
  };
1214
+ function reverseGradient(input) {
1215
+ if (Array.isArray(input)) {
1216
+ return [...input].reverse();
1217
+ }
1218
+ const reversedStops = [...input.stops].reverse();
1219
+ return createGradient(reversedStops, input.defaultOptions);
1220
+ }
1191
1221
  var _gradientPlain = (text, colors, easingFn, phase) => {
1192
1222
  const chars = [...text];
1193
1223
  const visible = chars.filter((c) => c !== " ").length;
@@ -2489,6 +2519,251 @@ var stream = async function* (text, opts = {}) {
2489
2519
  yield i === lines.length - 1 ? line : line + "\n";
2490
2520
  }
2491
2521
  };
2522
+ var ASCII_RAMPS = {
2523
+ standard: " .:-=+*#%@",
2524
+ detailed: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
2525
+ blocks: " \u2591\u2592\u2593\u2588",
2526
+ simple: " .+#"
2527
+ };
2528
+ var _resolveRamp = (r) => {
2529
+ if (typeof r === "string" && r.length > 0) {
2530
+ if (r in ASCII_RAMPS) return ASCII_RAMPS[r];
2531
+ return r;
2532
+ }
2533
+ return ASCII_RAMPS.standard;
2534
+ };
2535
+ var _luminance = (p) => {
2536
+ if (!p) return 0;
2537
+ return 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b;
2538
+ };
2539
+ var _sobelEdges = (pixels) => {
2540
+ const h = pixels.length;
2541
+ const w = h > 0 ? pixels[0].length : 0;
2542
+ const out = Array.from({ length: h }, () => new Array(w).fill(0));
2543
+ for (let y = 1; y < h - 1; y++) {
2544
+ const rowPrev = pixels[y - 1];
2545
+ const row = pixels[y];
2546
+ const rowNext = pixels[y + 1];
2547
+ const outRow = out[y];
2548
+ for (let x = 1; x < w - 1; x++) {
2549
+ const tl = _luminance(rowPrev[x - 1]);
2550
+ const t = _luminance(rowPrev[x]);
2551
+ const tr = _luminance(rowPrev[x + 1]);
2552
+ const l = _luminance(row[x - 1]);
2553
+ const r = _luminance(row[x + 1]);
2554
+ const bl = _luminance(rowNext[x - 1]);
2555
+ const b = _luminance(rowNext[x]);
2556
+ const br = _luminance(rowNext[x + 1]);
2557
+ const gx = -tl + tr - 2 * l + 2 * r - bl + br;
2558
+ const gy = -tl - 2 * t - tr + bl + 2 * b + br;
2559
+ const mag = Math.sqrt(gx * gx + gy * gy);
2560
+ outRow[x] = Math.min(255, mag);
2561
+ }
2562
+ }
2563
+ return out;
2564
+ };
2565
+ var _floydSteinberg = (lum, levels) => {
2566
+ const h = lum.length;
2567
+ if (h === 0) return lum;
2568
+ const w = lum[0].length;
2569
+ if (w === 0) return lum;
2570
+ const out = lum.map((row) => [...row]);
2571
+ const step = 255 / Math.max(1, levels - 1);
2572
+ for (let y = 0; y < h; y++) {
2573
+ const row = out[y];
2574
+ for (let x = 0; x < w; x++) {
2575
+ const oldPixel = row[x];
2576
+ const quantLevel = Math.round(oldPixel / step);
2577
+ const newPixel = quantLevel * step;
2578
+ row[x] = newPixel;
2579
+ const err = oldPixel - newPixel;
2580
+ if (x + 1 < w) {
2581
+ row[x + 1] = row[x + 1] + err * 7 / 16;
2582
+ }
2583
+ if (y + 1 < h) {
2584
+ const next = out[y + 1];
2585
+ if (x > 0) next[x - 1] = next[x - 1] + err * 3 / 16;
2586
+ next[x] = next[x] + err * 5 / 16;
2587
+ if (x + 1 < w) next[x + 1] = next[x + 1] + err * 1 / 16;
2588
+ }
2589
+ }
2590
+ }
2591
+ return out;
2592
+ };
2593
+ var _resizePixels = (pixels, targetW, targetH) => {
2594
+ const srcH = pixels.length;
2595
+ if (srcH === 0) return [];
2596
+ const srcW = pixels[0].length;
2597
+ if (srcW === 0) return [];
2598
+ const out = [];
2599
+ for (let y = 0; y < targetH; y++) {
2600
+ const sy = Math.min(srcH - 1, Math.floor(y / targetH * srcH));
2601
+ const srcRow = pixels[sy];
2602
+ const newRow = new Array(targetW);
2603
+ for (let x = 0; x < targetW; x++) {
2604
+ const sx = Math.min(srcW - 1, Math.floor(x / targetW * srcW));
2605
+ newRow[x] = srcRow[sx];
2606
+ }
2607
+ out.push(newRow);
2608
+ }
2609
+ return out;
2610
+ };
2611
+ var _toLuminanceGrid = (pixels) => {
2612
+ return pixels.map((row) => row.map((p) => _luminance(p)));
2613
+ };
2614
+ var _enhanceForFace = (lum) => {
2615
+ const flat = [];
2616
+ for (const row of lum) for (const v of row) flat.push(v);
2617
+ if (flat.length === 0) return lum;
2618
+ flat.sort((a, b) => a - b);
2619
+ const lo = flat[Math.floor(flat.length * 0.1)];
2620
+ const hi = flat[Math.floor(flat.length * 0.9)];
2621
+ const range = Math.max(1, hi - lo);
2622
+ return lum.map(
2623
+ (row) => row.map((v) => {
2624
+ const stretched = (v - lo) / range * 255;
2625
+ return Math.max(0, Math.min(255, stretched));
2626
+ })
2627
+ );
2628
+ };
2629
+ var fromImage = (pixels, opts = {}) => {
2630
+ if (!Array.isArray(pixels) || pixels.length === 0) return "";
2631
+ const firstRow = pixels[0];
2632
+ if (!Array.isArray(firstRow) || firstRow.length === 0) return "";
2633
+ const {
2634
+ width = 80,
2635
+ ramp = "standard",
2636
+ invert = false,
2637
+ dither = "none",
2638
+ edgeDetect = "none",
2639
+ edgeThreshold = 40,
2640
+ color: color2 = false,
2641
+ faceMode = false
2642
+ } = opts;
2643
+ const srcH = pixels.length;
2644
+ const srcW = pixels[0].length;
2645
+ const safeW = Math.max(1, Math.floor(width));
2646
+ const computedH = Math.max(1, Math.round(srcH / srcW * safeW * 0.5));
2647
+ const safeH = opts.height != null ? Math.max(1, Math.floor(opts.height)) : computedH;
2648
+ const resized = _resizePixels(pixels, safeW, safeH);
2649
+ let lum = _toLuminanceGrid(resized);
2650
+ if (faceMode) lum = _enhanceForFace(lum);
2651
+ let edgeGrid = null;
2652
+ if (edgeDetect === "sobel") {
2653
+ edgeGrid = _sobelEdges(resized);
2654
+ }
2655
+ const rampStr = _resolveRamp(ramp);
2656
+ const rampLen = rampStr.length;
2657
+ if (dither === "floyd-steinberg" && !edgeGrid) {
2658
+ lum = _floydSteinberg(lum, rampLen);
2659
+ }
2660
+ const useColor = color2 && !isNoColor();
2661
+ const lines = [];
2662
+ for (let y = 0; y < safeH; y++) {
2663
+ const lumRow = lum[y];
2664
+ const pxRow = resized[y];
2665
+ const edgeRow = edgeGrid ? edgeGrid[y] : null;
2666
+ let line = "";
2667
+ for (let x = 0; x < safeW; x++) {
2668
+ let charIdx;
2669
+ if (edgeRow) {
2670
+ const edge = edgeRow[x];
2671
+ const t = edge >= edgeThreshold ? Math.min(1, edge / 255) : 0;
2672
+ charIdx = invert ? Math.round((1 - t) * (rampLen - 1)) : Math.round(t * (rampLen - 1));
2673
+ } else {
2674
+ const l = lumRow[x] / 255;
2675
+ const tNorm = invert ? 1 - l : l;
2676
+ charIdx = Math.min(rampLen - 1, Math.max(0, Math.round(tNorm * (rampLen - 1))));
2677
+ }
2678
+ const ch = rampStr[charIdx];
2679
+ if (useColor) {
2680
+ const p = pxRow[x];
2681
+ if (p) {
2682
+ line += fgRgb(p.r, p.g, p.b) + ch;
2683
+ } else {
2684
+ line += ch;
2685
+ }
2686
+ } else {
2687
+ line += ch;
2688
+ }
2689
+ }
2690
+ if (useColor) line += reset();
2691
+ lines.push(line);
2692
+ }
2693
+ return lines.join("\n");
2694
+ };
2695
+ var parseFiglet = (flfContent) => {
2696
+ if (typeof flfContent !== "string" || flfContent.length === 0) {
2697
+ throw new TypeError("parseFiglet: input must be a non-empty string");
2698
+ }
2699
+ const lines = flfContent.split(/\r?\n/);
2700
+ if (lines.length === 0) {
2701
+ throw new TypeError("parseFiglet: empty content");
2702
+ }
2703
+ const header = lines[0];
2704
+ const m = /^flf2.\s*(\S)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(\d+)/.exec(header);
2705
+ if (!m) {
2706
+ throw new TypeError('parseFiglet: invalid FIGfont header (expected "flf2a$..." line)');
2707
+ }
2708
+ const hardblank = m[1];
2709
+ const height = parseInt(m[2], 10);
2710
+ const commentLines = parseInt(m[6], 10);
2711
+ if (!Number.isFinite(height) || height <= 0) {
2712
+ throw new TypeError(`parseFiglet: invalid height ${m[2]}`);
2713
+ }
2714
+ let cursor2 = 1 + Math.max(0, commentLines);
2715
+ const glyphs = /* @__PURE__ */ new Map();
2716
+ for (let code = 32; code <= 126; code++) {
2717
+ if (cursor2 + height > lines.length) break;
2718
+ const rows = [];
2719
+ for (let r = 0; r < height; r++) {
2720
+ const raw = lines[cursor2 + r];
2721
+ const endmark = raw.charAt(raw.length - 1);
2722
+ let stripped = raw;
2723
+ while (stripped.length > 0 && stripped.charAt(stripped.length - 1) === endmark) {
2724
+ stripped = stripped.slice(0, -1);
2725
+ }
2726
+ rows.push(stripped);
2727
+ }
2728
+ glyphs.set(code, rows);
2729
+ cursor2 += height;
2730
+ }
2731
+ return { hardblank, height, glyphs };
2732
+ };
2733
+ var figletText = (text, font, opts = {}) => {
2734
+ if (typeof text !== "string") return "";
2735
+ if (!font || !font.glyphs || font.height <= 0) return "";
2736
+ const { trim = true, colorFn = null } = opts;
2737
+ const glyphsForText = [];
2738
+ for (const ch of text) {
2739
+ const code = ch.codePointAt(0) ?? 32;
2740
+ const glyph = font.glyphs.get(code);
2741
+ if (glyph) {
2742
+ glyphsForText.push(glyph);
2743
+ } else {
2744
+ const fallback = font.glyphs.get(32);
2745
+ glyphsForText.push(fallback ?? new Array(font.height).fill(""));
2746
+ }
2747
+ }
2748
+ const hardblankRe = new RegExp(_escapeRe(font.hardblank), "g");
2749
+ const rows = [];
2750
+ for (let r = 0; r < font.height; r++) {
2751
+ let row = "";
2752
+ for (const g of glyphsForText) {
2753
+ row += g[r] ?? "";
2754
+ }
2755
+ row = row.replace(hardblankRe, " ");
2756
+ rows.push(row);
2757
+ }
2758
+ let result = rows.join("\n");
2759
+ if (trim) {
2760
+ const trimmed = rows.filter((row) => row.trim().length > 0);
2761
+ result = trimmed.length > 0 ? trimmed.join("\n") : rows.join("\n");
2762
+ }
2763
+ if (colorFn) result = colorFn(result);
2764
+ return result;
2765
+ };
2766
+ var _escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2492
2767
  var ascii = {
2493
2768
  big,
2494
2769
  small,
@@ -2499,6 +2774,10 @@ var ascii = {
2499
2774
  logo,
2500
2775
  stream,
2501
2776
  measure,
2777
+ // v1.2.5 — Phase 3 closure
2778
+ fromImage,
2779
+ figletText,
2780
+ parseFiglet,
2502
2781
  // Pipeline stages — exposed for custom compositions
2503
2782
  stageRender,
2504
2783
  stageAlign,
@@ -5471,6 +5750,7 @@ var ansimax = { color, animate, ascii, loader, frames, components, trees, themes
5471
5750
  var index_default = ansimax;
5472
5751
  // Annotate the CommonJS export names for ESM import in node:
5473
5752
  0 && (module.exports = {
5753
+ ASCII_RAMPS,
5474
5754
  BEL,
5475
5755
  BG,
5476
5756
  CONFIG_DEFAULTS,
@@ -5520,11 +5800,13 @@ var index_default = ansimax;
5520
5800
  escapeRegex,
5521
5801
  fg256,
5522
5802
  fgRgb,
5803
+ figletText,
5523
5804
  filterTree,
5524
5805
  findInTree,
5525
5806
  flipHorizontal,
5526
5807
  flipVertical,
5527
5808
  frames,
5809
+ fromImage,
5528
5810
  getConfig,
5529
5811
  getConfigValue,
5530
5812
  getRenderCacheSize,
@@ -5558,8 +5840,10 @@ var index_default = ansimax;
5558
5840
  padBoth,
5559
5841
  padEnd,
5560
5842
  padStart,
5843
+ parseFiglet,
5561
5844
  pauseListeners,
5562
5845
  presetNames,
5846
+ presets,
5563
5847
  rainbow,
5564
5848
  registerFont,
5565
5849
  registerPreset,
@@ -5576,6 +5860,7 @@ var index_default = ansimax;
5576
5860
  resetLoaderCursorCount,
5577
5861
  resetNoColor,
5578
5862
  resumeListeners,
5863
+ reverseGradient,
5579
5864
  rgbTo256,
5580
5865
  rgbToHex,
5581
5866
  rotate90,