ansimax 1.2.4 → 1.2.6
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 +215 -0
- package/README.es.md +167 -29
- package/README.md +167 -29
- package/dist/index.d.mts +308 -103
- package/dist/index.d.ts +308 -103
- package/dist/index.js +310 -0
- package/dist/index.mjs +306 -0
- package/examples/05-components.ts +4 -4
- package/examples/all-in-one.cjs +5 -5
- package/examples/all-in-one.mjs +5 -5
- package/package.json +1 -1
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,6 +121,7 @@ __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,
|
|
123
127
|
presets: () => presets,
|
|
@@ -2515,6 +2519,304 @@ var stream = async function* (text, opts = {}) {
|
|
|
2515
2519
|
yield i === lines.length - 1 ? line : line + "\n";
|
|
2516
2520
|
}
|
|
2517
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
|
+
// v1.2.6 — new ramps
|
|
2528
|
+
binary: " \u2588",
|
|
2529
|
+
dots: " \u2801\u2803\u2807\u2827\u2837\u2877\u28F7\u28FF",
|
|
2530
|
+
shades: " \u2801\u2803\u2807\u2827\u2837\u2877\u28F7\u28FF\u2588",
|
|
2531
|
+
ascii64: " `.'^,_:-+=<>i!lI?/\\|()1{}[]rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#@"
|
|
2532
|
+
};
|
|
2533
|
+
var _resolveRamp = (r) => {
|
|
2534
|
+
if (typeof r === "string" && r.length > 0) {
|
|
2535
|
+
if (r in ASCII_RAMPS) return ASCII_RAMPS[r];
|
|
2536
|
+
return r;
|
|
2537
|
+
}
|
|
2538
|
+
return ASCII_RAMPS.standard;
|
|
2539
|
+
};
|
|
2540
|
+
var _luminance = (p) => {
|
|
2541
|
+
if (!p) return 0;
|
|
2542
|
+
return 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b;
|
|
2543
|
+
};
|
|
2544
|
+
var _sobelEdges = (pixels) => {
|
|
2545
|
+
const h = pixels.length;
|
|
2546
|
+
const w = h > 0 ? pixels[0].length : 0;
|
|
2547
|
+
const out = Array.from({ length: h }, () => new Array(w).fill(0));
|
|
2548
|
+
for (let y = 1; y < h - 1; y++) {
|
|
2549
|
+
const rowPrev = pixels[y - 1];
|
|
2550
|
+
const row = pixels[y];
|
|
2551
|
+
const rowNext = pixels[y + 1];
|
|
2552
|
+
const outRow = out[y];
|
|
2553
|
+
for (let x = 1; x < w - 1; x++) {
|
|
2554
|
+
const tl = _luminance(rowPrev[x - 1]);
|
|
2555
|
+
const t = _luminance(rowPrev[x]);
|
|
2556
|
+
const tr = _luminance(rowPrev[x + 1]);
|
|
2557
|
+
const l = _luminance(row[x - 1]);
|
|
2558
|
+
const r = _luminance(row[x + 1]);
|
|
2559
|
+
const bl = _luminance(rowNext[x - 1]);
|
|
2560
|
+
const b = _luminance(rowNext[x]);
|
|
2561
|
+
const br = _luminance(rowNext[x + 1]);
|
|
2562
|
+
const gx = -tl + tr - 2 * l + 2 * r - bl + br;
|
|
2563
|
+
const gy = -tl - 2 * t - tr + bl + 2 * b + br;
|
|
2564
|
+
const mag = Math.sqrt(gx * gx + gy * gy);
|
|
2565
|
+
outRow[x] = Math.min(255, mag);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
return out;
|
|
2569
|
+
};
|
|
2570
|
+
var _floydSteinberg = (lum, levels) => {
|
|
2571
|
+
const h = lum.length;
|
|
2572
|
+
if (h === 0) return lum;
|
|
2573
|
+
const w = lum[0].length;
|
|
2574
|
+
if (w === 0) return lum;
|
|
2575
|
+
const out = lum.map((row) => [...row]);
|
|
2576
|
+
const step = 255 / Math.max(1, levels - 1);
|
|
2577
|
+
for (let y = 0; y < h; y++) {
|
|
2578
|
+
const row = out[y];
|
|
2579
|
+
for (let x = 0; x < w; x++) {
|
|
2580
|
+
const oldPixel = row[x];
|
|
2581
|
+
const quantLevel = Math.round(oldPixel / step);
|
|
2582
|
+
const newPixel = quantLevel * step;
|
|
2583
|
+
row[x] = newPixel;
|
|
2584
|
+
const err = oldPixel - newPixel;
|
|
2585
|
+
if (x + 1 < w) {
|
|
2586
|
+
row[x + 1] = row[x + 1] + err * 7 / 16;
|
|
2587
|
+
}
|
|
2588
|
+
if (y + 1 < h) {
|
|
2589
|
+
const next = out[y + 1];
|
|
2590
|
+
if (x > 0) next[x - 1] = next[x - 1] + err * 3 / 16;
|
|
2591
|
+
next[x] = next[x] + err * 5 / 16;
|
|
2592
|
+
if (x + 1 < w) next[x + 1] = next[x + 1] + err * 1 / 16;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
return out;
|
|
2597
|
+
};
|
|
2598
|
+
var _resizePixels = (pixels, targetW, targetH) => {
|
|
2599
|
+
const srcH = pixels.length;
|
|
2600
|
+
if (srcH === 0) return [];
|
|
2601
|
+
const srcW = pixels[0].length;
|
|
2602
|
+
if (srcW === 0) return [];
|
|
2603
|
+
const out = [];
|
|
2604
|
+
for (let y = 0; y < targetH; y++) {
|
|
2605
|
+
const sy = Math.min(srcH - 1, Math.floor(y / targetH * srcH));
|
|
2606
|
+
const srcRow = pixels[sy];
|
|
2607
|
+
const newRow = new Array(targetW);
|
|
2608
|
+
for (let x = 0; x < targetW; x++) {
|
|
2609
|
+
const sx = Math.min(srcW - 1, Math.floor(x / targetW * srcW));
|
|
2610
|
+
newRow[x] = srcRow[sx];
|
|
2611
|
+
}
|
|
2612
|
+
out.push(newRow);
|
|
2613
|
+
}
|
|
2614
|
+
return out;
|
|
2615
|
+
};
|
|
2616
|
+
var _toLuminanceGrid = (pixels) => {
|
|
2617
|
+
return pixels.map((row) => row.map((p) => _luminance(p)));
|
|
2618
|
+
};
|
|
2619
|
+
var _enhanceForFace = (lum) => {
|
|
2620
|
+
const flat = [];
|
|
2621
|
+
for (const row of lum) for (const v of row) flat.push(v);
|
|
2622
|
+
if (flat.length === 0) return lum;
|
|
2623
|
+
flat.sort((a, b) => a - b);
|
|
2624
|
+
const lo = flat[Math.floor(flat.length * 0.1)];
|
|
2625
|
+
const hi = flat[Math.floor(flat.length * 0.9)];
|
|
2626
|
+
const range = Math.max(1, hi - lo);
|
|
2627
|
+
return lum.map(
|
|
2628
|
+
(row) => row.map((v) => {
|
|
2629
|
+
const stretched = (v - lo) / range * 255;
|
|
2630
|
+
return Math.max(0, Math.min(255, stretched));
|
|
2631
|
+
})
|
|
2632
|
+
);
|
|
2633
|
+
};
|
|
2634
|
+
var _adjustBrightnessContrast = (lum, brightness, contrast) => {
|
|
2635
|
+
const safeBrightness = Math.max(-1, Math.min(1, brightness)) * 255;
|
|
2636
|
+
const safeContrast = Math.max(-1, Math.min(1, contrast));
|
|
2637
|
+
const contrastFactor = 1 + safeContrast;
|
|
2638
|
+
return lum.map(
|
|
2639
|
+
(row) => row.map((v) => {
|
|
2640
|
+
const adjusted = (v - 128) * contrastFactor + 128 + safeBrightness;
|
|
2641
|
+
return Math.max(0, Math.min(255, adjusted));
|
|
2642
|
+
})
|
|
2643
|
+
);
|
|
2644
|
+
};
|
|
2645
|
+
var fromImage = (pixels, opts = {}) => {
|
|
2646
|
+
if (!Array.isArray(pixels) || pixels.length === 0) return "";
|
|
2647
|
+
const firstRow = pixels[0];
|
|
2648
|
+
if (!Array.isArray(firstRow) || firstRow.length === 0) return "";
|
|
2649
|
+
const {
|
|
2650
|
+
width = 80,
|
|
2651
|
+
ramp = "standard",
|
|
2652
|
+
invert = false,
|
|
2653
|
+
dither = "none",
|
|
2654
|
+
edgeDetect = "none",
|
|
2655
|
+
edgeThreshold = 40,
|
|
2656
|
+
color: color2 = false,
|
|
2657
|
+
faceMode = false,
|
|
2658
|
+
// v1.2.6
|
|
2659
|
+
bgColor = false,
|
|
2660
|
+
brightness = 0,
|
|
2661
|
+
contrast = 0
|
|
2662
|
+
} = opts;
|
|
2663
|
+
const srcH = pixels.length;
|
|
2664
|
+
const srcW = pixels[0].length;
|
|
2665
|
+
const safeW = Math.max(1, Math.floor(width));
|
|
2666
|
+
const computedH = Math.max(1, Math.round(srcH / srcW * safeW * 0.5));
|
|
2667
|
+
const safeH = opts.height != null ? Math.max(1, Math.floor(opts.height)) : computedH;
|
|
2668
|
+
const resized = _resizePixels(pixels, safeW, safeH);
|
|
2669
|
+
let lum = _toLuminanceGrid(resized);
|
|
2670
|
+
if (brightness !== 0 || contrast !== 0) {
|
|
2671
|
+
lum = _adjustBrightnessContrast(lum, brightness, contrast);
|
|
2672
|
+
}
|
|
2673
|
+
if (faceMode) lum = _enhanceForFace(lum);
|
|
2674
|
+
let edgeGrid = null;
|
|
2675
|
+
if (edgeDetect === "sobel") {
|
|
2676
|
+
edgeGrid = _sobelEdges(resized);
|
|
2677
|
+
}
|
|
2678
|
+
const rampStr = _resolveRamp(ramp);
|
|
2679
|
+
const rampLen = rampStr.length;
|
|
2680
|
+
if (dither === "floyd-steinberg" && !edgeGrid) {
|
|
2681
|
+
lum = _floydSteinberg(lum, rampLen);
|
|
2682
|
+
}
|
|
2683
|
+
const useColor = (color2 || bgColor) && !isNoColor();
|
|
2684
|
+
const lines = [];
|
|
2685
|
+
for (let y = 0; y < safeH; y++) {
|
|
2686
|
+
const lumRow = lum[y];
|
|
2687
|
+
const pxRow = resized[y];
|
|
2688
|
+
const edgeRow = edgeGrid ? edgeGrid[y] : null;
|
|
2689
|
+
let line = "";
|
|
2690
|
+
for (let x = 0; x < safeW; x++) {
|
|
2691
|
+
let charIdx;
|
|
2692
|
+
if (edgeRow) {
|
|
2693
|
+
const edge = edgeRow[x];
|
|
2694
|
+
const t = edge >= edgeThreshold ? Math.min(1, edge / 255) : 0;
|
|
2695
|
+
charIdx = invert ? Math.round((1 - t) * (rampLen - 1)) : Math.round(t * (rampLen - 1));
|
|
2696
|
+
} else {
|
|
2697
|
+
const l = lumRow[x] / 255;
|
|
2698
|
+
const tNorm = invert ? 1 - l : l;
|
|
2699
|
+
charIdx = Math.min(rampLen - 1, Math.max(0, Math.round(tNorm * (rampLen - 1))));
|
|
2700
|
+
}
|
|
2701
|
+
const ch = rampStr[charIdx];
|
|
2702
|
+
if (useColor) {
|
|
2703
|
+
const p = pxRow[x];
|
|
2704
|
+
if (p) {
|
|
2705
|
+
if (bgColor) {
|
|
2706
|
+
line += bgRgb(p.r, p.g, p.b) + ch;
|
|
2707
|
+
} else {
|
|
2708
|
+
line += fgRgb(p.r, p.g, p.b) + ch;
|
|
2709
|
+
}
|
|
2710
|
+
} else {
|
|
2711
|
+
line += ch;
|
|
2712
|
+
}
|
|
2713
|
+
} else {
|
|
2714
|
+
line += ch;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
if (useColor) line += reset();
|
|
2718
|
+
lines.push(line);
|
|
2719
|
+
}
|
|
2720
|
+
return lines.join("\n");
|
|
2721
|
+
};
|
|
2722
|
+
var parseFiglet = (flfContent) => {
|
|
2723
|
+
if (typeof flfContent !== "string" || flfContent.length === 0) {
|
|
2724
|
+
throw new TypeError("parseFiglet: input must be a non-empty string");
|
|
2725
|
+
}
|
|
2726
|
+
const lines = flfContent.split(/\r?\n/);
|
|
2727
|
+
if (lines.length === 0) {
|
|
2728
|
+
throw new TypeError("parseFiglet: empty content");
|
|
2729
|
+
}
|
|
2730
|
+
const header = lines[0];
|
|
2731
|
+
const m = /^flf2.\s*(\S)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(\d+)/.exec(header);
|
|
2732
|
+
if (!m) {
|
|
2733
|
+
throw new TypeError('parseFiglet: invalid FIGfont header (expected "flf2a$..." line)');
|
|
2734
|
+
}
|
|
2735
|
+
const hardblank = m[1];
|
|
2736
|
+
const height = parseInt(m[2], 10);
|
|
2737
|
+
const commentLines = parseInt(m[6], 10);
|
|
2738
|
+
if (!Number.isFinite(height) || height <= 0) {
|
|
2739
|
+
throw new TypeError(`parseFiglet: invalid height ${m[2]}`);
|
|
2740
|
+
}
|
|
2741
|
+
let cursor2 = 1 + Math.max(0, commentLines);
|
|
2742
|
+
const glyphs = /* @__PURE__ */ new Map();
|
|
2743
|
+
for (let code = 32; code <= 126; code++) {
|
|
2744
|
+
if (cursor2 + height > lines.length) break;
|
|
2745
|
+
const rows = [];
|
|
2746
|
+
for (let r = 0; r < height; r++) {
|
|
2747
|
+
const raw = lines[cursor2 + r];
|
|
2748
|
+
const endmark = raw.charAt(raw.length - 1);
|
|
2749
|
+
let stripped = raw;
|
|
2750
|
+
while (stripped.length > 0 && stripped.charAt(stripped.length - 1) === endmark) {
|
|
2751
|
+
stripped = stripped.slice(0, -1);
|
|
2752
|
+
}
|
|
2753
|
+
rows.push(stripped);
|
|
2754
|
+
}
|
|
2755
|
+
glyphs.set(code, rows);
|
|
2756
|
+
cursor2 += height;
|
|
2757
|
+
}
|
|
2758
|
+
return { hardblank, height, glyphs };
|
|
2759
|
+
};
|
|
2760
|
+
var figletText = (text, font, opts = {}) => {
|
|
2761
|
+
if (typeof text !== "string") return "";
|
|
2762
|
+
if (!font || !font.glyphs || font.height <= 0) return "";
|
|
2763
|
+
const {
|
|
2764
|
+
trim = true,
|
|
2765
|
+
colorFn = null,
|
|
2766
|
+
// v1.2.6
|
|
2767
|
+
kerning = 0,
|
|
2768
|
+
lineSpacing = 0
|
|
2769
|
+
} = opts;
|
|
2770
|
+
const safeKerning = Math.max(0, Math.floor(kerning));
|
|
2771
|
+
const safeLineSpacing = Math.max(0, Math.floor(lineSpacing));
|
|
2772
|
+
if (text.includes("\n")) {
|
|
2773
|
+
const linesOfText = text.split("\n");
|
|
2774
|
+
const renderedBlocks = linesOfText.map(
|
|
2775
|
+
(line) => _renderFigletLine(line, font, safeKerning, trim)
|
|
2776
|
+
);
|
|
2777
|
+
const spacer = safeLineSpacing > 0 ? "\n".repeat(safeLineSpacing + 1) : "\n";
|
|
2778
|
+
let multiResult = renderedBlocks.join(spacer);
|
|
2779
|
+
if (colorFn) multiResult = colorFn(multiResult);
|
|
2780
|
+
return multiResult;
|
|
2781
|
+
}
|
|
2782
|
+
let result = _renderFigletLine(text, font, safeKerning, trim);
|
|
2783
|
+
if (colorFn) result = colorFn(result);
|
|
2784
|
+
return result;
|
|
2785
|
+
};
|
|
2786
|
+
var _renderFigletLine = (text, font, kerning, trim) => {
|
|
2787
|
+
const glyphsForText = [];
|
|
2788
|
+
for (const ch of text) {
|
|
2789
|
+
const code = ch.codePointAt(0) ?? 32;
|
|
2790
|
+
const glyph = font.glyphs.get(code);
|
|
2791
|
+
if (glyph) {
|
|
2792
|
+
glyphsForText.push(glyph);
|
|
2793
|
+
} else {
|
|
2794
|
+
const fallback = font.glyphs.get(32);
|
|
2795
|
+
glyphsForText.push(fallback ?? new Array(font.height).fill(""));
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
const kerningSpacer = kerning > 0 ? " ".repeat(kerning) : "";
|
|
2799
|
+
const hardblankRe = new RegExp(_escapeRe(font.hardblank), "g");
|
|
2800
|
+
const rows = [];
|
|
2801
|
+
for (let r = 0; r < font.height; r++) {
|
|
2802
|
+
let row = "";
|
|
2803
|
+
for (let i = 0; i < glyphsForText.length; i++) {
|
|
2804
|
+
const g = glyphsForText[i];
|
|
2805
|
+
row += g[r] ?? "";
|
|
2806
|
+
if (i < glyphsForText.length - 1 && kerningSpacer) {
|
|
2807
|
+
row += kerningSpacer;
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
row = row.replace(hardblankRe, " ");
|
|
2811
|
+
rows.push(row);
|
|
2812
|
+
}
|
|
2813
|
+
if (trim) {
|
|
2814
|
+
const trimmed = rows.filter((row) => row.trim().length > 0);
|
|
2815
|
+
return trimmed.length > 0 ? trimmed.join("\n") : rows.join("\n");
|
|
2816
|
+
}
|
|
2817
|
+
return rows.join("\n");
|
|
2818
|
+
};
|
|
2819
|
+
var _escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2518
2820
|
var ascii = {
|
|
2519
2821
|
big,
|
|
2520
2822
|
small,
|
|
@@ -2525,6 +2827,10 @@ var ascii = {
|
|
|
2525
2827
|
logo,
|
|
2526
2828
|
stream,
|
|
2527
2829
|
measure,
|
|
2830
|
+
// v1.2.5 — Phase 3 closure
|
|
2831
|
+
fromImage,
|
|
2832
|
+
figletText,
|
|
2833
|
+
parseFiglet,
|
|
2528
2834
|
// Pipeline stages — exposed for custom compositions
|
|
2529
2835
|
stageRender,
|
|
2530
2836
|
stageAlign,
|
|
@@ -5497,6 +5803,7 @@ var ansimax = { color, animate, ascii, loader, frames, components, trees, themes
|
|
|
5497
5803
|
var index_default = ansimax;
|
|
5498
5804
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5499
5805
|
0 && (module.exports = {
|
|
5806
|
+
ASCII_RAMPS,
|
|
5500
5807
|
BEL,
|
|
5501
5808
|
BG,
|
|
5502
5809
|
CONFIG_DEFAULTS,
|
|
@@ -5546,11 +5853,13 @@ var index_default = ansimax;
|
|
|
5546
5853
|
escapeRegex,
|
|
5547
5854
|
fg256,
|
|
5548
5855
|
fgRgb,
|
|
5856
|
+
figletText,
|
|
5549
5857
|
filterTree,
|
|
5550
5858
|
findInTree,
|
|
5551
5859
|
flipHorizontal,
|
|
5552
5860
|
flipVertical,
|
|
5553
5861
|
frames,
|
|
5862
|
+
fromImage,
|
|
5554
5863
|
getConfig,
|
|
5555
5864
|
getConfigValue,
|
|
5556
5865
|
getRenderCacheSize,
|
|
@@ -5584,6 +5893,7 @@ var index_default = ansimax;
|
|
|
5584
5893
|
padBoth,
|
|
5585
5894
|
padEnd,
|
|
5586
5895
|
padStart,
|
|
5896
|
+
parseFiglet,
|
|
5587
5897
|
pauseListeners,
|
|
5588
5898
|
presetNames,
|
|
5589
5899
|
presets,
|
package/dist/index.mjs
CHANGED
|
@@ -2339,6 +2339,304 @@ var stream = async function* (text, opts = {}) {
|
|
|
2339
2339
|
yield i === lines.length - 1 ? line : line + "\n";
|
|
2340
2340
|
}
|
|
2341
2341
|
};
|
|
2342
|
+
var ASCII_RAMPS = {
|
|
2343
|
+
standard: " .:-=+*#%@",
|
|
2344
|
+
detailed: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
|
|
2345
|
+
blocks: " \u2591\u2592\u2593\u2588",
|
|
2346
|
+
simple: " .+#",
|
|
2347
|
+
// v1.2.6 — new ramps
|
|
2348
|
+
binary: " \u2588",
|
|
2349
|
+
dots: " \u2801\u2803\u2807\u2827\u2837\u2877\u28F7\u28FF",
|
|
2350
|
+
shades: " \u2801\u2803\u2807\u2827\u2837\u2877\u28F7\u28FF\u2588",
|
|
2351
|
+
ascii64: " `.'^,_:-+=<>i!lI?/\\|()1{}[]rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#@"
|
|
2352
|
+
};
|
|
2353
|
+
var _resolveRamp = (r) => {
|
|
2354
|
+
if (typeof r === "string" && r.length > 0) {
|
|
2355
|
+
if (r in ASCII_RAMPS) return ASCII_RAMPS[r];
|
|
2356
|
+
return r;
|
|
2357
|
+
}
|
|
2358
|
+
return ASCII_RAMPS.standard;
|
|
2359
|
+
};
|
|
2360
|
+
var _luminance = (p) => {
|
|
2361
|
+
if (!p) return 0;
|
|
2362
|
+
return 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b;
|
|
2363
|
+
};
|
|
2364
|
+
var _sobelEdges = (pixels) => {
|
|
2365
|
+
const h = pixels.length;
|
|
2366
|
+
const w = h > 0 ? pixels[0].length : 0;
|
|
2367
|
+
const out = Array.from({ length: h }, () => new Array(w).fill(0));
|
|
2368
|
+
for (let y = 1; y < h - 1; y++) {
|
|
2369
|
+
const rowPrev = pixels[y - 1];
|
|
2370
|
+
const row = pixels[y];
|
|
2371
|
+
const rowNext = pixels[y + 1];
|
|
2372
|
+
const outRow = out[y];
|
|
2373
|
+
for (let x = 1; x < w - 1; x++) {
|
|
2374
|
+
const tl = _luminance(rowPrev[x - 1]);
|
|
2375
|
+
const t = _luminance(rowPrev[x]);
|
|
2376
|
+
const tr = _luminance(rowPrev[x + 1]);
|
|
2377
|
+
const l = _luminance(row[x - 1]);
|
|
2378
|
+
const r = _luminance(row[x + 1]);
|
|
2379
|
+
const bl = _luminance(rowNext[x - 1]);
|
|
2380
|
+
const b = _luminance(rowNext[x]);
|
|
2381
|
+
const br = _luminance(rowNext[x + 1]);
|
|
2382
|
+
const gx = -tl + tr - 2 * l + 2 * r - bl + br;
|
|
2383
|
+
const gy = -tl - 2 * t - tr + bl + 2 * b + br;
|
|
2384
|
+
const mag = Math.sqrt(gx * gx + gy * gy);
|
|
2385
|
+
outRow[x] = Math.min(255, mag);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
return out;
|
|
2389
|
+
};
|
|
2390
|
+
var _floydSteinberg = (lum, levels) => {
|
|
2391
|
+
const h = lum.length;
|
|
2392
|
+
if (h === 0) return lum;
|
|
2393
|
+
const w = lum[0].length;
|
|
2394
|
+
if (w === 0) return lum;
|
|
2395
|
+
const out = lum.map((row) => [...row]);
|
|
2396
|
+
const step = 255 / Math.max(1, levels - 1);
|
|
2397
|
+
for (let y = 0; y < h; y++) {
|
|
2398
|
+
const row = out[y];
|
|
2399
|
+
for (let x = 0; x < w; x++) {
|
|
2400
|
+
const oldPixel = row[x];
|
|
2401
|
+
const quantLevel = Math.round(oldPixel / step);
|
|
2402
|
+
const newPixel = quantLevel * step;
|
|
2403
|
+
row[x] = newPixel;
|
|
2404
|
+
const err = oldPixel - newPixel;
|
|
2405
|
+
if (x + 1 < w) {
|
|
2406
|
+
row[x + 1] = row[x + 1] + err * 7 / 16;
|
|
2407
|
+
}
|
|
2408
|
+
if (y + 1 < h) {
|
|
2409
|
+
const next = out[y + 1];
|
|
2410
|
+
if (x > 0) next[x - 1] = next[x - 1] + err * 3 / 16;
|
|
2411
|
+
next[x] = next[x] + err * 5 / 16;
|
|
2412
|
+
if (x + 1 < w) next[x + 1] = next[x + 1] + err * 1 / 16;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
return out;
|
|
2417
|
+
};
|
|
2418
|
+
var _resizePixels = (pixels, targetW, targetH) => {
|
|
2419
|
+
const srcH = pixels.length;
|
|
2420
|
+
if (srcH === 0) return [];
|
|
2421
|
+
const srcW = pixels[0].length;
|
|
2422
|
+
if (srcW === 0) return [];
|
|
2423
|
+
const out = [];
|
|
2424
|
+
for (let y = 0; y < targetH; y++) {
|
|
2425
|
+
const sy = Math.min(srcH - 1, Math.floor(y / targetH * srcH));
|
|
2426
|
+
const srcRow = pixels[sy];
|
|
2427
|
+
const newRow = new Array(targetW);
|
|
2428
|
+
for (let x = 0; x < targetW; x++) {
|
|
2429
|
+
const sx = Math.min(srcW - 1, Math.floor(x / targetW * srcW));
|
|
2430
|
+
newRow[x] = srcRow[sx];
|
|
2431
|
+
}
|
|
2432
|
+
out.push(newRow);
|
|
2433
|
+
}
|
|
2434
|
+
return out;
|
|
2435
|
+
};
|
|
2436
|
+
var _toLuminanceGrid = (pixels) => {
|
|
2437
|
+
return pixels.map((row) => row.map((p) => _luminance(p)));
|
|
2438
|
+
};
|
|
2439
|
+
var _enhanceForFace = (lum) => {
|
|
2440
|
+
const flat = [];
|
|
2441
|
+
for (const row of lum) for (const v of row) flat.push(v);
|
|
2442
|
+
if (flat.length === 0) return lum;
|
|
2443
|
+
flat.sort((a, b) => a - b);
|
|
2444
|
+
const lo = flat[Math.floor(flat.length * 0.1)];
|
|
2445
|
+
const hi = flat[Math.floor(flat.length * 0.9)];
|
|
2446
|
+
const range = Math.max(1, hi - lo);
|
|
2447
|
+
return lum.map(
|
|
2448
|
+
(row) => row.map((v) => {
|
|
2449
|
+
const stretched = (v - lo) / range * 255;
|
|
2450
|
+
return Math.max(0, Math.min(255, stretched));
|
|
2451
|
+
})
|
|
2452
|
+
);
|
|
2453
|
+
};
|
|
2454
|
+
var _adjustBrightnessContrast = (lum, brightness, contrast) => {
|
|
2455
|
+
const safeBrightness = Math.max(-1, Math.min(1, brightness)) * 255;
|
|
2456
|
+
const safeContrast = Math.max(-1, Math.min(1, contrast));
|
|
2457
|
+
const contrastFactor = 1 + safeContrast;
|
|
2458
|
+
return lum.map(
|
|
2459
|
+
(row) => row.map((v) => {
|
|
2460
|
+
const adjusted = (v - 128) * contrastFactor + 128 + safeBrightness;
|
|
2461
|
+
return Math.max(0, Math.min(255, adjusted));
|
|
2462
|
+
})
|
|
2463
|
+
);
|
|
2464
|
+
};
|
|
2465
|
+
var fromImage = (pixels, opts = {}) => {
|
|
2466
|
+
if (!Array.isArray(pixels) || pixels.length === 0) return "";
|
|
2467
|
+
const firstRow = pixels[0];
|
|
2468
|
+
if (!Array.isArray(firstRow) || firstRow.length === 0) return "";
|
|
2469
|
+
const {
|
|
2470
|
+
width = 80,
|
|
2471
|
+
ramp = "standard",
|
|
2472
|
+
invert = false,
|
|
2473
|
+
dither = "none",
|
|
2474
|
+
edgeDetect = "none",
|
|
2475
|
+
edgeThreshold = 40,
|
|
2476
|
+
color: color2 = false,
|
|
2477
|
+
faceMode = false,
|
|
2478
|
+
// v1.2.6
|
|
2479
|
+
bgColor = false,
|
|
2480
|
+
brightness = 0,
|
|
2481
|
+
contrast = 0
|
|
2482
|
+
} = opts;
|
|
2483
|
+
const srcH = pixels.length;
|
|
2484
|
+
const srcW = pixels[0].length;
|
|
2485
|
+
const safeW = Math.max(1, Math.floor(width));
|
|
2486
|
+
const computedH = Math.max(1, Math.round(srcH / srcW * safeW * 0.5));
|
|
2487
|
+
const safeH = opts.height != null ? Math.max(1, Math.floor(opts.height)) : computedH;
|
|
2488
|
+
const resized = _resizePixels(pixels, safeW, safeH);
|
|
2489
|
+
let lum = _toLuminanceGrid(resized);
|
|
2490
|
+
if (brightness !== 0 || contrast !== 0) {
|
|
2491
|
+
lum = _adjustBrightnessContrast(lum, brightness, contrast);
|
|
2492
|
+
}
|
|
2493
|
+
if (faceMode) lum = _enhanceForFace(lum);
|
|
2494
|
+
let edgeGrid = null;
|
|
2495
|
+
if (edgeDetect === "sobel") {
|
|
2496
|
+
edgeGrid = _sobelEdges(resized);
|
|
2497
|
+
}
|
|
2498
|
+
const rampStr = _resolveRamp(ramp);
|
|
2499
|
+
const rampLen = rampStr.length;
|
|
2500
|
+
if (dither === "floyd-steinberg" && !edgeGrid) {
|
|
2501
|
+
lum = _floydSteinberg(lum, rampLen);
|
|
2502
|
+
}
|
|
2503
|
+
const useColor = (color2 || bgColor) && !isNoColor();
|
|
2504
|
+
const lines = [];
|
|
2505
|
+
for (let y = 0; y < safeH; y++) {
|
|
2506
|
+
const lumRow = lum[y];
|
|
2507
|
+
const pxRow = resized[y];
|
|
2508
|
+
const edgeRow = edgeGrid ? edgeGrid[y] : null;
|
|
2509
|
+
let line = "";
|
|
2510
|
+
for (let x = 0; x < safeW; x++) {
|
|
2511
|
+
let charIdx;
|
|
2512
|
+
if (edgeRow) {
|
|
2513
|
+
const edge = edgeRow[x];
|
|
2514
|
+
const t = edge >= edgeThreshold ? Math.min(1, edge / 255) : 0;
|
|
2515
|
+
charIdx = invert ? Math.round((1 - t) * (rampLen - 1)) : Math.round(t * (rampLen - 1));
|
|
2516
|
+
} else {
|
|
2517
|
+
const l = lumRow[x] / 255;
|
|
2518
|
+
const tNorm = invert ? 1 - l : l;
|
|
2519
|
+
charIdx = Math.min(rampLen - 1, Math.max(0, Math.round(tNorm * (rampLen - 1))));
|
|
2520
|
+
}
|
|
2521
|
+
const ch = rampStr[charIdx];
|
|
2522
|
+
if (useColor) {
|
|
2523
|
+
const p = pxRow[x];
|
|
2524
|
+
if (p) {
|
|
2525
|
+
if (bgColor) {
|
|
2526
|
+
line += bgRgb(p.r, p.g, p.b) + ch;
|
|
2527
|
+
} else {
|
|
2528
|
+
line += fgRgb(p.r, p.g, p.b) + ch;
|
|
2529
|
+
}
|
|
2530
|
+
} else {
|
|
2531
|
+
line += ch;
|
|
2532
|
+
}
|
|
2533
|
+
} else {
|
|
2534
|
+
line += ch;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
if (useColor) line += reset();
|
|
2538
|
+
lines.push(line);
|
|
2539
|
+
}
|
|
2540
|
+
return lines.join("\n");
|
|
2541
|
+
};
|
|
2542
|
+
var parseFiglet = (flfContent) => {
|
|
2543
|
+
if (typeof flfContent !== "string" || flfContent.length === 0) {
|
|
2544
|
+
throw new TypeError("parseFiglet: input must be a non-empty string");
|
|
2545
|
+
}
|
|
2546
|
+
const lines = flfContent.split(/\r?\n/);
|
|
2547
|
+
if (lines.length === 0) {
|
|
2548
|
+
throw new TypeError("parseFiglet: empty content");
|
|
2549
|
+
}
|
|
2550
|
+
const header = lines[0];
|
|
2551
|
+
const m = /^flf2.\s*(\S)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(\d+)/.exec(header);
|
|
2552
|
+
if (!m) {
|
|
2553
|
+
throw new TypeError('parseFiglet: invalid FIGfont header (expected "flf2a$..." line)');
|
|
2554
|
+
}
|
|
2555
|
+
const hardblank = m[1];
|
|
2556
|
+
const height = parseInt(m[2], 10);
|
|
2557
|
+
const commentLines = parseInt(m[6], 10);
|
|
2558
|
+
if (!Number.isFinite(height) || height <= 0) {
|
|
2559
|
+
throw new TypeError(`parseFiglet: invalid height ${m[2]}`);
|
|
2560
|
+
}
|
|
2561
|
+
let cursor2 = 1 + Math.max(0, commentLines);
|
|
2562
|
+
const glyphs = /* @__PURE__ */ new Map();
|
|
2563
|
+
for (let code = 32; code <= 126; code++) {
|
|
2564
|
+
if (cursor2 + height > lines.length) break;
|
|
2565
|
+
const rows = [];
|
|
2566
|
+
for (let r = 0; r < height; r++) {
|
|
2567
|
+
const raw = lines[cursor2 + r];
|
|
2568
|
+
const endmark = raw.charAt(raw.length - 1);
|
|
2569
|
+
let stripped = raw;
|
|
2570
|
+
while (stripped.length > 0 && stripped.charAt(stripped.length - 1) === endmark) {
|
|
2571
|
+
stripped = stripped.slice(0, -1);
|
|
2572
|
+
}
|
|
2573
|
+
rows.push(stripped);
|
|
2574
|
+
}
|
|
2575
|
+
glyphs.set(code, rows);
|
|
2576
|
+
cursor2 += height;
|
|
2577
|
+
}
|
|
2578
|
+
return { hardblank, height, glyphs };
|
|
2579
|
+
};
|
|
2580
|
+
var figletText = (text, font, opts = {}) => {
|
|
2581
|
+
if (typeof text !== "string") return "";
|
|
2582
|
+
if (!font || !font.glyphs || font.height <= 0) return "";
|
|
2583
|
+
const {
|
|
2584
|
+
trim = true,
|
|
2585
|
+
colorFn = null,
|
|
2586
|
+
// v1.2.6
|
|
2587
|
+
kerning = 0,
|
|
2588
|
+
lineSpacing = 0
|
|
2589
|
+
} = opts;
|
|
2590
|
+
const safeKerning = Math.max(0, Math.floor(kerning));
|
|
2591
|
+
const safeLineSpacing = Math.max(0, Math.floor(lineSpacing));
|
|
2592
|
+
if (text.includes("\n")) {
|
|
2593
|
+
const linesOfText = text.split("\n");
|
|
2594
|
+
const renderedBlocks = linesOfText.map(
|
|
2595
|
+
(line) => _renderFigletLine(line, font, safeKerning, trim)
|
|
2596
|
+
);
|
|
2597
|
+
const spacer = safeLineSpacing > 0 ? "\n".repeat(safeLineSpacing + 1) : "\n";
|
|
2598
|
+
let multiResult = renderedBlocks.join(spacer);
|
|
2599
|
+
if (colorFn) multiResult = colorFn(multiResult);
|
|
2600
|
+
return multiResult;
|
|
2601
|
+
}
|
|
2602
|
+
let result = _renderFigletLine(text, font, safeKerning, trim);
|
|
2603
|
+
if (colorFn) result = colorFn(result);
|
|
2604
|
+
return result;
|
|
2605
|
+
};
|
|
2606
|
+
var _renderFigletLine = (text, font, kerning, trim) => {
|
|
2607
|
+
const glyphsForText = [];
|
|
2608
|
+
for (const ch of text) {
|
|
2609
|
+
const code = ch.codePointAt(0) ?? 32;
|
|
2610
|
+
const glyph = font.glyphs.get(code);
|
|
2611
|
+
if (glyph) {
|
|
2612
|
+
glyphsForText.push(glyph);
|
|
2613
|
+
} else {
|
|
2614
|
+
const fallback = font.glyphs.get(32);
|
|
2615
|
+
glyphsForText.push(fallback ?? new Array(font.height).fill(""));
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
const kerningSpacer = kerning > 0 ? " ".repeat(kerning) : "";
|
|
2619
|
+
const hardblankRe = new RegExp(_escapeRe(font.hardblank), "g");
|
|
2620
|
+
const rows = [];
|
|
2621
|
+
for (let r = 0; r < font.height; r++) {
|
|
2622
|
+
let row = "";
|
|
2623
|
+
for (let i = 0; i < glyphsForText.length; i++) {
|
|
2624
|
+
const g = glyphsForText[i];
|
|
2625
|
+
row += g[r] ?? "";
|
|
2626
|
+
if (i < glyphsForText.length - 1 && kerningSpacer) {
|
|
2627
|
+
row += kerningSpacer;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
row = row.replace(hardblankRe, " ");
|
|
2631
|
+
rows.push(row);
|
|
2632
|
+
}
|
|
2633
|
+
if (trim) {
|
|
2634
|
+
const trimmed = rows.filter((row) => row.trim().length > 0);
|
|
2635
|
+
return trimmed.length > 0 ? trimmed.join("\n") : rows.join("\n");
|
|
2636
|
+
}
|
|
2637
|
+
return rows.join("\n");
|
|
2638
|
+
};
|
|
2639
|
+
var _escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2342
2640
|
var ascii = {
|
|
2343
2641
|
big,
|
|
2344
2642
|
small,
|
|
@@ -2349,6 +2647,10 @@ var ascii = {
|
|
|
2349
2647
|
logo,
|
|
2350
2648
|
stream,
|
|
2351
2649
|
measure,
|
|
2650
|
+
// v1.2.5 — Phase 3 closure
|
|
2651
|
+
fromImage,
|
|
2652
|
+
figletText,
|
|
2653
|
+
parseFiglet,
|
|
2352
2654
|
// Pipeline stages — exposed for custom compositions
|
|
2353
2655
|
stageRender,
|
|
2354
2656
|
stageAlign,
|
|
@@ -5320,6 +5622,7 @@ var withConfig = (overrides, fn) => {
|
|
|
5320
5622
|
var ansimax = { color, animate, ascii, loader, frames, components, trees, themes, images, configure };
|
|
5321
5623
|
var index_default = ansimax;
|
|
5322
5624
|
export {
|
|
5625
|
+
ASCII_RAMPS,
|
|
5323
5626
|
BEL,
|
|
5324
5627
|
BG,
|
|
5325
5628
|
DEFAULTS as CONFIG_DEFAULTS,
|
|
@@ -5370,11 +5673,13 @@ export {
|
|
|
5370
5673
|
escapeRegex,
|
|
5371
5674
|
fg256,
|
|
5372
5675
|
fgRgb,
|
|
5676
|
+
figletText,
|
|
5373
5677
|
filterTree,
|
|
5374
5678
|
findInTree,
|
|
5375
5679
|
flipHorizontal,
|
|
5376
5680
|
flipVertical,
|
|
5377
5681
|
frames,
|
|
5682
|
+
fromImage,
|
|
5378
5683
|
getConfig,
|
|
5379
5684
|
getConfigValue,
|
|
5380
5685
|
getRenderCacheSize,
|
|
@@ -5408,6 +5713,7 @@ export {
|
|
|
5408
5713
|
padBoth,
|
|
5409
5714
|
padEnd,
|
|
5410
5715
|
padStart,
|
|
5716
|
+
parseFiglet,
|
|
5411
5717
|
pauseListeners,
|
|
5412
5718
|
presetNames,
|
|
5413
5719
|
presets,
|