ansimax 1.2.4 → 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/CHANGELOG.md +125 -0
- package/README.es.md +92 -8
- package/README.md +92 -8
- package/dist/index.d.mts +246 -103
- package/dist/index.d.ts +246 -103
- package/dist/index.js +257 -0
- package/dist/index.mjs +253 -0
- package/examples/all-in-one.cjs +1 -1
- package/examples/all-in-one.mjs +1 -1
- 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,251 @@ 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
|
+
};
|
|
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, "\\$&");
|
|
2518
2767
|
var ascii = {
|
|
2519
2768
|
big,
|
|
2520
2769
|
small,
|
|
@@ -2525,6 +2774,10 @@ var ascii = {
|
|
|
2525
2774
|
logo,
|
|
2526
2775
|
stream,
|
|
2527
2776
|
measure,
|
|
2777
|
+
// v1.2.5 — Phase 3 closure
|
|
2778
|
+
fromImage,
|
|
2779
|
+
figletText,
|
|
2780
|
+
parseFiglet,
|
|
2528
2781
|
// Pipeline stages — exposed for custom compositions
|
|
2529
2782
|
stageRender,
|
|
2530
2783
|
stageAlign,
|
|
@@ -5497,6 +5750,7 @@ var ansimax = { color, animate, ascii, loader, frames, components, trees, themes
|
|
|
5497
5750
|
var index_default = ansimax;
|
|
5498
5751
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5499
5752
|
0 && (module.exports = {
|
|
5753
|
+
ASCII_RAMPS,
|
|
5500
5754
|
BEL,
|
|
5501
5755
|
BG,
|
|
5502
5756
|
CONFIG_DEFAULTS,
|
|
@@ -5546,11 +5800,13 @@ var index_default = ansimax;
|
|
|
5546
5800
|
escapeRegex,
|
|
5547
5801
|
fg256,
|
|
5548
5802
|
fgRgb,
|
|
5803
|
+
figletText,
|
|
5549
5804
|
filterTree,
|
|
5550
5805
|
findInTree,
|
|
5551
5806
|
flipHorizontal,
|
|
5552
5807
|
flipVertical,
|
|
5553
5808
|
frames,
|
|
5809
|
+
fromImage,
|
|
5554
5810
|
getConfig,
|
|
5555
5811
|
getConfigValue,
|
|
5556
5812
|
getRenderCacheSize,
|
|
@@ -5584,6 +5840,7 @@ var index_default = ansimax;
|
|
|
5584
5840
|
padBoth,
|
|
5585
5841
|
padEnd,
|
|
5586
5842
|
padStart,
|
|
5843
|
+
parseFiglet,
|
|
5587
5844
|
pauseListeners,
|
|
5588
5845
|
presetNames,
|
|
5589
5846
|
presets,
|
package/dist/index.mjs
CHANGED
|
@@ -2339,6 +2339,251 @@ 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
|
+
};
|
|
2348
|
+
var _resolveRamp = (r) => {
|
|
2349
|
+
if (typeof r === "string" && r.length > 0) {
|
|
2350
|
+
if (r in ASCII_RAMPS) return ASCII_RAMPS[r];
|
|
2351
|
+
return r;
|
|
2352
|
+
}
|
|
2353
|
+
return ASCII_RAMPS.standard;
|
|
2354
|
+
};
|
|
2355
|
+
var _luminance = (p) => {
|
|
2356
|
+
if (!p) return 0;
|
|
2357
|
+
return 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b;
|
|
2358
|
+
};
|
|
2359
|
+
var _sobelEdges = (pixels) => {
|
|
2360
|
+
const h = pixels.length;
|
|
2361
|
+
const w = h > 0 ? pixels[0].length : 0;
|
|
2362
|
+
const out = Array.from({ length: h }, () => new Array(w).fill(0));
|
|
2363
|
+
for (let y = 1; y < h - 1; y++) {
|
|
2364
|
+
const rowPrev = pixels[y - 1];
|
|
2365
|
+
const row = pixels[y];
|
|
2366
|
+
const rowNext = pixels[y + 1];
|
|
2367
|
+
const outRow = out[y];
|
|
2368
|
+
for (let x = 1; x < w - 1; x++) {
|
|
2369
|
+
const tl = _luminance(rowPrev[x - 1]);
|
|
2370
|
+
const t = _luminance(rowPrev[x]);
|
|
2371
|
+
const tr = _luminance(rowPrev[x + 1]);
|
|
2372
|
+
const l = _luminance(row[x - 1]);
|
|
2373
|
+
const r = _luminance(row[x + 1]);
|
|
2374
|
+
const bl = _luminance(rowNext[x - 1]);
|
|
2375
|
+
const b = _luminance(rowNext[x]);
|
|
2376
|
+
const br = _luminance(rowNext[x + 1]);
|
|
2377
|
+
const gx = -tl + tr - 2 * l + 2 * r - bl + br;
|
|
2378
|
+
const gy = -tl - 2 * t - tr + bl + 2 * b + br;
|
|
2379
|
+
const mag = Math.sqrt(gx * gx + gy * gy);
|
|
2380
|
+
outRow[x] = Math.min(255, mag);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
return out;
|
|
2384
|
+
};
|
|
2385
|
+
var _floydSteinberg = (lum, levels) => {
|
|
2386
|
+
const h = lum.length;
|
|
2387
|
+
if (h === 0) return lum;
|
|
2388
|
+
const w = lum[0].length;
|
|
2389
|
+
if (w === 0) return lum;
|
|
2390
|
+
const out = lum.map((row) => [...row]);
|
|
2391
|
+
const step = 255 / Math.max(1, levels - 1);
|
|
2392
|
+
for (let y = 0; y < h; y++) {
|
|
2393
|
+
const row = out[y];
|
|
2394
|
+
for (let x = 0; x < w; x++) {
|
|
2395
|
+
const oldPixel = row[x];
|
|
2396
|
+
const quantLevel = Math.round(oldPixel / step);
|
|
2397
|
+
const newPixel = quantLevel * step;
|
|
2398
|
+
row[x] = newPixel;
|
|
2399
|
+
const err = oldPixel - newPixel;
|
|
2400
|
+
if (x + 1 < w) {
|
|
2401
|
+
row[x + 1] = row[x + 1] + err * 7 / 16;
|
|
2402
|
+
}
|
|
2403
|
+
if (y + 1 < h) {
|
|
2404
|
+
const next = out[y + 1];
|
|
2405
|
+
if (x > 0) next[x - 1] = next[x - 1] + err * 3 / 16;
|
|
2406
|
+
next[x] = next[x] + err * 5 / 16;
|
|
2407
|
+
if (x + 1 < w) next[x + 1] = next[x + 1] + err * 1 / 16;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
return out;
|
|
2412
|
+
};
|
|
2413
|
+
var _resizePixels = (pixels, targetW, targetH) => {
|
|
2414
|
+
const srcH = pixels.length;
|
|
2415
|
+
if (srcH === 0) return [];
|
|
2416
|
+
const srcW = pixels[0].length;
|
|
2417
|
+
if (srcW === 0) return [];
|
|
2418
|
+
const out = [];
|
|
2419
|
+
for (let y = 0; y < targetH; y++) {
|
|
2420
|
+
const sy = Math.min(srcH - 1, Math.floor(y / targetH * srcH));
|
|
2421
|
+
const srcRow = pixels[sy];
|
|
2422
|
+
const newRow = new Array(targetW);
|
|
2423
|
+
for (let x = 0; x < targetW; x++) {
|
|
2424
|
+
const sx = Math.min(srcW - 1, Math.floor(x / targetW * srcW));
|
|
2425
|
+
newRow[x] = srcRow[sx];
|
|
2426
|
+
}
|
|
2427
|
+
out.push(newRow);
|
|
2428
|
+
}
|
|
2429
|
+
return out;
|
|
2430
|
+
};
|
|
2431
|
+
var _toLuminanceGrid = (pixels) => {
|
|
2432
|
+
return pixels.map((row) => row.map((p) => _luminance(p)));
|
|
2433
|
+
};
|
|
2434
|
+
var _enhanceForFace = (lum) => {
|
|
2435
|
+
const flat = [];
|
|
2436
|
+
for (const row of lum) for (const v of row) flat.push(v);
|
|
2437
|
+
if (flat.length === 0) return lum;
|
|
2438
|
+
flat.sort((a, b) => a - b);
|
|
2439
|
+
const lo = flat[Math.floor(flat.length * 0.1)];
|
|
2440
|
+
const hi = flat[Math.floor(flat.length * 0.9)];
|
|
2441
|
+
const range = Math.max(1, hi - lo);
|
|
2442
|
+
return lum.map(
|
|
2443
|
+
(row) => row.map((v) => {
|
|
2444
|
+
const stretched = (v - lo) / range * 255;
|
|
2445
|
+
return Math.max(0, Math.min(255, stretched));
|
|
2446
|
+
})
|
|
2447
|
+
);
|
|
2448
|
+
};
|
|
2449
|
+
var fromImage = (pixels, opts = {}) => {
|
|
2450
|
+
if (!Array.isArray(pixels) || pixels.length === 0) return "";
|
|
2451
|
+
const firstRow = pixels[0];
|
|
2452
|
+
if (!Array.isArray(firstRow) || firstRow.length === 0) return "";
|
|
2453
|
+
const {
|
|
2454
|
+
width = 80,
|
|
2455
|
+
ramp = "standard",
|
|
2456
|
+
invert = false,
|
|
2457
|
+
dither = "none",
|
|
2458
|
+
edgeDetect = "none",
|
|
2459
|
+
edgeThreshold = 40,
|
|
2460
|
+
color: color2 = false,
|
|
2461
|
+
faceMode = false
|
|
2462
|
+
} = opts;
|
|
2463
|
+
const srcH = pixels.length;
|
|
2464
|
+
const srcW = pixels[0].length;
|
|
2465
|
+
const safeW = Math.max(1, Math.floor(width));
|
|
2466
|
+
const computedH = Math.max(1, Math.round(srcH / srcW * safeW * 0.5));
|
|
2467
|
+
const safeH = opts.height != null ? Math.max(1, Math.floor(opts.height)) : computedH;
|
|
2468
|
+
const resized = _resizePixels(pixels, safeW, safeH);
|
|
2469
|
+
let lum = _toLuminanceGrid(resized);
|
|
2470
|
+
if (faceMode) lum = _enhanceForFace(lum);
|
|
2471
|
+
let edgeGrid = null;
|
|
2472
|
+
if (edgeDetect === "sobel") {
|
|
2473
|
+
edgeGrid = _sobelEdges(resized);
|
|
2474
|
+
}
|
|
2475
|
+
const rampStr = _resolveRamp(ramp);
|
|
2476
|
+
const rampLen = rampStr.length;
|
|
2477
|
+
if (dither === "floyd-steinberg" && !edgeGrid) {
|
|
2478
|
+
lum = _floydSteinberg(lum, rampLen);
|
|
2479
|
+
}
|
|
2480
|
+
const useColor = color2 && !isNoColor();
|
|
2481
|
+
const lines = [];
|
|
2482
|
+
for (let y = 0; y < safeH; y++) {
|
|
2483
|
+
const lumRow = lum[y];
|
|
2484
|
+
const pxRow = resized[y];
|
|
2485
|
+
const edgeRow = edgeGrid ? edgeGrid[y] : null;
|
|
2486
|
+
let line = "";
|
|
2487
|
+
for (let x = 0; x < safeW; x++) {
|
|
2488
|
+
let charIdx;
|
|
2489
|
+
if (edgeRow) {
|
|
2490
|
+
const edge = edgeRow[x];
|
|
2491
|
+
const t = edge >= edgeThreshold ? Math.min(1, edge / 255) : 0;
|
|
2492
|
+
charIdx = invert ? Math.round((1 - t) * (rampLen - 1)) : Math.round(t * (rampLen - 1));
|
|
2493
|
+
} else {
|
|
2494
|
+
const l = lumRow[x] / 255;
|
|
2495
|
+
const tNorm = invert ? 1 - l : l;
|
|
2496
|
+
charIdx = Math.min(rampLen - 1, Math.max(0, Math.round(tNorm * (rampLen - 1))));
|
|
2497
|
+
}
|
|
2498
|
+
const ch = rampStr[charIdx];
|
|
2499
|
+
if (useColor) {
|
|
2500
|
+
const p = pxRow[x];
|
|
2501
|
+
if (p) {
|
|
2502
|
+
line += fgRgb(p.r, p.g, p.b) + ch;
|
|
2503
|
+
} else {
|
|
2504
|
+
line += ch;
|
|
2505
|
+
}
|
|
2506
|
+
} else {
|
|
2507
|
+
line += ch;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
if (useColor) line += reset();
|
|
2511
|
+
lines.push(line);
|
|
2512
|
+
}
|
|
2513
|
+
return lines.join("\n");
|
|
2514
|
+
};
|
|
2515
|
+
var parseFiglet = (flfContent) => {
|
|
2516
|
+
if (typeof flfContent !== "string" || flfContent.length === 0) {
|
|
2517
|
+
throw new TypeError("parseFiglet: input must be a non-empty string");
|
|
2518
|
+
}
|
|
2519
|
+
const lines = flfContent.split(/\r?\n/);
|
|
2520
|
+
if (lines.length === 0) {
|
|
2521
|
+
throw new TypeError("parseFiglet: empty content");
|
|
2522
|
+
}
|
|
2523
|
+
const header = lines[0];
|
|
2524
|
+
const m = /^flf2.\s*(\S)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(\d+)/.exec(header);
|
|
2525
|
+
if (!m) {
|
|
2526
|
+
throw new TypeError('parseFiglet: invalid FIGfont header (expected "flf2a$..." line)');
|
|
2527
|
+
}
|
|
2528
|
+
const hardblank = m[1];
|
|
2529
|
+
const height = parseInt(m[2], 10);
|
|
2530
|
+
const commentLines = parseInt(m[6], 10);
|
|
2531
|
+
if (!Number.isFinite(height) || height <= 0) {
|
|
2532
|
+
throw new TypeError(`parseFiglet: invalid height ${m[2]}`);
|
|
2533
|
+
}
|
|
2534
|
+
let cursor2 = 1 + Math.max(0, commentLines);
|
|
2535
|
+
const glyphs = /* @__PURE__ */ new Map();
|
|
2536
|
+
for (let code = 32; code <= 126; code++) {
|
|
2537
|
+
if (cursor2 + height > lines.length) break;
|
|
2538
|
+
const rows = [];
|
|
2539
|
+
for (let r = 0; r < height; r++) {
|
|
2540
|
+
const raw = lines[cursor2 + r];
|
|
2541
|
+
const endmark = raw.charAt(raw.length - 1);
|
|
2542
|
+
let stripped = raw;
|
|
2543
|
+
while (stripped.length > 0 && stripped.charAt(stripped.length - 1) === endmark) {
|
|
2544
|
+
stripped = stripped.slice(0, -1);
|
|
2545
|
+
}
|
|
2546
|
+
rows.push(stripped);
|
|
2547
|
+
}
|
|
2548
|
+
glyphs.set(code, rows);
|
|
2549
|
+
cursor2 += height;
|
|
2550
|
+
}
|
|
2551
|
+
return { hardblank, height, glyphs };
|
|
2552
|
+
};
|
|
2553
|
+
var figletText = (text, font, opts = {}) => {
|
|
2554
|
+
if (typeof text !== "string") return "";
|
|
2555
|
+
if (!font || !font.glyphs || font.height <= 0) return "";
|
|
2556
|
+
const { trim = true, colorFn = null } = opts;
|
|
2557
|
+
const glyphsForText = [];
|
|
2558
|
+
for (const ch of text) {
|
|
2559
|
+
const code = ch.codePointAt(0) ?? 32;
|
|
2560
|
+
const glyph = font.glyphs.get(code);
|
|
2561
|
+
if (glyph) {
|
|
2562
|
+
glyphsForText.push(glyph);
|
|
2563
|
+
} else {
|
|
2564
|
+
const fallback = font.glyphs.get(32);
|
|
2565
|
+
glyphsForText.push(fallback ?? new Array(font.height).fill(""));
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
const hardblankRe = new RegExp(_escapeRe(font.hardblank), "g");
|
|
2569
|
+
const rows = [];
|
|
2570
|
+
for (let r = 0; r < font.height; r++) {
|
|
2571
|
+
let row = "";
|
|
2572
|
+
for (const g of glyphsForText) {
|
|
2573
|
+
row += g[r] ?? "";
|
|
2574
|
+
}
|
|
2575
|
+
row = row.replace(hardblankRe, " ");
|
|
2576
|
+
rows.push(row);
|
|
2577
|
+
}
|
|
2578
|
+
let result = rows.join("\n");
|
|
2579
|
+
if (trim) {
|
|
2580
|
+
const trimmed = rows.filter((row) => row.trim().length > 0);
|
|
2581
|
+
result = trimmed.length > 0 ? trimmed.join("\n") : rows.join("\n");
|
|
2582
|
+
}
|
|
2583
|
+
if (colorFn) result = colorFn(result);
|
|
2584
|
+
return result;
|
|
2585
|
+
};
|
|
2586
|
+
var _escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2342
2587
|
var ascii = {
|
|
2343
2588
|
big,
|
|
2344
2589
|
small,
|
|
@@ -2349,6 +2594,10 @@ var ascii = {
|
|
|
2349
2594
|
logo,
|
|
2350
2595
|
stream,
|
|
2351
2596
|
measure,
|
|
2597
|
+
// v1.2.5 — Phase 3 closure
|
|
2598
|
+
fromImage,
|
|
2599
|
+
figletText,
|
|
2600
|
+
parseFiglet,
|
|
2352
2601
|
// Pipeline stages — exposed for custom compositions
|
|
2353
2602
|
stageRender,
|
|
2354
2603
|
stageAlign,
|
|
@@ -5320,6 +5569,7 @@ var withConfig = (overrides, fn) => {
|
|
|
5320
5569
|
var ansimax = { color, animate, ascii, loader, frames, components, trees, themes, images, configure };
|
|
5321
5570
|
var index_default = ansimax;
|
|
5322
5571
|
export {
|
|
5572
|
+
ASCII_RAMPS,
|
|
5323
5573
|
BEL,
|
|
5324
5574
|
BG,
|
|
5325
5575
|
DEFAULTS as CONFIG_DEFAULTS,
|
|
@@ -5370,11 +5620,13 @@ export {
|
|
|
5370
5620
|
escapeRegex,
|
|
5371
5621
|
fg256,
|
|
5372
5622
|
fgRgb,
|
|
5623
|
+
figletText,
|
|
5373
5624
|
filterTree,
|
|
5374
5625
|
findInTree,
|
|
5375
5626
|
flipHorizontal,
|
|
5376
5627
|
flipVertical,
|
|
5377
5628
|
frames,
|
|
5629
|
+
fromImage,
|
|
5378
5630
|
getConfig,
|
|
5379
5631
|
getConfigValue,
|
|
5380
5632
|
getRenderCacheSize,
|
|
@@ -5408,6 +5660,7 @@ export {
|
|
|
5408
5660
|
padBoth,
|
|
5409
5661
|
padEnd,
|
|
5410
5662
|
padStart,
|
|
5663
|
+
parseFiglet,
|
|
5411
5664
|
pauseListeners,
|
|
5412
5665
|
presetNames,
|
|
5413
5666
|
presets,
|
package/examples/all-in-one.cjs
CHANGED
|
@@ -118,7 +118,7 @@ async function main() {
|
|
|
118
118
|
console.log(components.section('🏷️ Badges & Status', { width: 60 }));
|
|
119
119
|
console.log();
|
|
120
120
|
console.log(' ',
|
|
121
|
-
components.badge('VERSION', 'v1.2.
|
|
121
|
+
components.badge('VERSION', 'v1.2.5'),
|
|
122
122
|
components.badge('BUILD', 'passing'),
|
|
123
123
|
components.badge('LICENSE', 'Apache 2.0'));
|
|
124
124
|
console.log();
|
package/examples/all-in-one.mjs
CHANGED
|
@@ -117,7 +117,7 @@ console.log();
|
|
|
117
117
|
console.log(components.section('🏷️ Badges & Status', { width: 60 }));
|
|
118
118
|
console.log();
|
|
119
119
|
console.log(' ',
|
|
120
|
-
components.badge('VERSION', 'v1.2.
|
|
120
|
+
components.badge('VERSION', 'v1.2.5'),
|
|
121
121
|
components.badge('BUILD', 'passing'),
|
|
122
122
|
components.badge('LICENSE', 'Apache 2.0'));
|
|
123
123
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ansimax",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "Zero-dependency CLI rendering library: colors, gradients, animations, ASCII art, pixel art, components, and themes \u2014 all in TypeScript.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|