asciify-engine 1.0.23 → 1.0.25
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.cjs +646 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +210 -8
- package/dist/index.d.ts +210 -8
- package/dist/index.js +638 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { parseGIF, decompressFrames } from 'gifuct-js';
|
|
2
2
|
|
|
3
3
|
// src/types.ts
|
|
4
|
+
var PALETTE_THEMES = {
|
|
5
|
+
dracula: { name: "Dracula", accent: "#bd93f9", bg: "#282a36", fg: "#f8f8f2" },
|
|
6
|
+
monokai: { name: "Monokai", accent: "#a6e22e", bg: "#272822", fg: "#f8f8f2" },
|
|
7
|
+
nord: { name: "Nord", accent: "#88c0d0", bg: "#2e3440", fg: "#eceff4" },
|
|
8
|
+
catppuccin: { name: "Catppuccin", accent: "#cba6f7", bg: "#1e1e2e", fg: "#cdd6f4" },
|
|
9
|
+
solarized: { name: "Solarized", accent: "#268bd2", bg: "#002b36", fg: "#839496" },
|
|
10
|
+
gruvbox: { name: "Gruvbox", accent: "#b8bb26", bg: "#282828", fg: "#ebdbb2" }
|
|
11
|
+
};
|
|
4
12
|
var CHARSETS = {
|
|
5
13
|
standard: " .:-=+*#%@",
|
|
6
14
|
blocks: " \u2591\u2592\u2593\u2588",
|
|
7
15
|
minimal: " .:+",
|
|
8
|
-
dense: " .'`^\",:;Il!i><~+_-?][}{1)(
|
|
16
|
+
dense: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
|
|
9
17
|
binary: "01",
|
|
10
18
|
dots: " \u2801\u2803\u2807\u2847\u28C7\u28E7\u28F7\u28FF",
|
|
11
19
|
letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
12
20
|
claudeCode: " \u2554\u2557\u255A\u255D\u2551\u2550\u2560\u2563\u2566\u2569\u256C\u2591\u2592\u2593\u2588\u2502\u2500\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C",
|
|
13
21
|
box: " \u25AA\u25FE\u25FC\u25A0\u2588",
|
|
14
|
-
lines: " \u02D7\u2010\u2013\u2014\u2015\u2501"
|
|
22
|
+
lines: " \u02D7\u2010\u2013\u2014\u2015\u2501",
|
|
23
|
+
braille: " \u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280A\u280B\u280C\u280D\u280E\u280F\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281A\u281B\u281C\u281D\u281E\u281F\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829\u282A\u282B\u282C\u282D\u282E\u282F\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839\u283A\u283B\u283C\u283D\u283E\u283F\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847\u28C0\u28C1\u28C2\u28C3\u28C4\u28C5\u28C6\u28C7\u28C8\u28C9\u28CA\u28CB\u28CC\u28CD\u28CE\u28CF\u28D0\u28D1\u28D2\u28D3\u28D4\u28D5\u28D6\u28D7\u28D8\u28D9\u28DA\u28DB\u28DC\u28DD\u28DE\u28DF\u28E0\u28E1\u28E2\u28E3\u28E4\u28E5\u28E6\u28E7\u28E8\u28E9\u28EA\u28EB\u28EC\u28ED\u28EE\u28EF\u28F0\u28F1\u28F2\u28F3\u28F4\u28F5\u28F6\u28F7\u28F8\u28F9\u28FA\u28FB\u28FC\u28FD\u28FE\u28FF",
|
|
24
|
+
katakana: " \uFF66\uFF67\uFF68\uFF69\uFF6A\uFF6B\uFF6C\uFF6D\uFF6E\uFF6F\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF83\uFF84\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E\uFF8F\uFF90\uFF91\uFF92\uFF93\uFF94\uFF95\uFF96\uFF97\uFF98\uFF99\uFF9A\uFF9B\uFF9C\uFF9D",
|
|
25
|
+
musical: " \u2669\u266A\u266B\u266C\u266D\u266E\u266F",
|
|
26
|
+
emoji: " \u2B1B\u{1F7EB}\u{1F7E5}\u{1F7E7}\u{1F7E8}\u{1F7E9}\u{1F7E6}\u{1F7EA}\u2B1C"
|
|
15
27
|
};
|
|
16
28
|
var ART_STYLE_PRESETS = {
|
|
17
29
|
classic: {
|
|
@@ -54,6 +66,27 @@ var ART_STYLE_PRESETS = {
|
|
|
54
66
|
renderMode: "ascii",
|
|
55
67
|
charset: CHARSETS.lines,
|
|
56
68
|
colorMode: "fullcolor"
|
|
69
|
+
},
|
|
70
|
+
braille: {
|
|
71
|
+
renderMode: "ascii",
|
|
72
|
+
charset: CHARSETS.braille,
|
|
73
|
+
colorMode: "fullcolor"
|
|
74
|
+
},
|
|
75
|
+
katakana: {
|
|
76
|
+
renderMode: "ascii",
|
|
77
|
+
charset: CHARSETS.katakana,
|
|
78
|
+
colorMode: "matrix"
|
|
79
|
+
},
|
|
80
|
+
musical: {
|
|
81
|
+
renderMode: "ascii",
|
|
82
|
+
charset: CHARSETS.musical,
|
|
83
|
+
colorMode: "accent",
|
|
84
|
+
accentColor: "#e040fb"
|
|
85
|
+
},
|
|
86
|
+
emoji: {
|
|
87
|
+
renderMode: "ascii",
|
|
88
|
+
charset: CHARSETS.emoji,
|
|
89
|
+
colorMode: "fullcolor"
|
|
57
90
|
}
|
|
58
91
|
};
|
|
59
92
|
var DEFAULT_OPTIONS = {
|
|
@@ -109,6 +142,18 @@ var HOVER_PRESETS = {
|
|
|
109
142
|
ice: {
|
|
110
143
|
label: "Ice",
|
|
111
144
|
options: { hoverStrength: 0.5, hoverEffect: "glow", hoverRadius: 0.15, hoverColor: "#60d5f7" }
|
|
145
|
+
},
|
|
146
|
+
gravity: {
|
|
147
|
+
label: "Gravity",
|
|
148
|
+
options: { hoverStrength: 0.7, hoverEffect: "attract", hoverRadius: 0.18, hoverColor: "#a5d6ff" }
|
|
149
|
+
},
|
|
150
|
+
shatter: {
|
|
151
|
+
label: "Shatter",
|
|
152
|
+
options: { hoverStrength: 0.8, hoverEffect: "shatter", hoverRadius: 0.14, hoverColor: "#ff6090" }
|
|
153
|
+
},
|
|
154
|
+
ghost: {
|
|
155
|
+
label: "Ghost",
|
|
156
|
+
options: { hoverStrength: 0.55, hoverEffect: "trail", hoverRadius: 0.2, hoverColor: "#b39ddb" }
|
|
112
157
|
}
|
|
113
158
|
};
|
|
114
159
|
|
|
@@ -128,14 +173,16 @@ function adjustLuminance(lum, brightness, contrast) {
|
|
|
128
173
|
}
|
|
129
174
|
function luminanceToChar(lum, charset, invert) {
|
|
130
175
|
const normalized = invert ? 1 - lum / 255 : lum / 255;
|
|
131
|
-
const
|
|
132
|
-
|
|
176
|
+
const chars = [...charset];
|
|
177
|
+
const index = Math.floor(normalized * (chars.length - 1));
|
|
178
|
+
return chars[Math.max(0, Math.min(chars.length - 1, index))];
|
|
133
179
|
}
|
|
134
180
|
function customTextToChar(lum, text, x, y, cols, invert) {
|
|
135
181
|
const normalized = invert ? 1 - lum / 255 : lum / 255;
|
|
136
182
|
if (normalized < 0.12) return " ";
|
|
183
|
+
const chars = [...text];
|
|
137
184
|
const pos = y * cols + x;
|
|
138
|
-
return
|
|
185
|
+
return chars[pos % chars.length];
|
|
139
186
|
}
|
|
140
187
|
var BAYER_4X4 = [
|
|
141
188
|
[0, 8, 2, 10],
|
|
@@ -283,6 +330,44 @@ function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
|
|
|
283
330
|
}
|
|
284
331
|
case "waveField":
|
|
285
332
|
return 1;
|
|
333
|
+
case "ripple": {
|
|
334
|
+
const cx2 = cols / 2;
|
|
335
|
+
const cy2 = rows / 2;
|
|
336
|
+
const dx2 = x - cx2;
|
|
337
|
+
const dy2 = y - cy2;
|
|
338
|
+
const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
|
339
|
+
const maxDist2 = Math.sqrt(cx2 * cx2 + cy2 * cy2);
|
|
340
|
+
const ripple = Math.sin(dist2 / maxDist2 * Math.PI * 10 - t * 5) * 0.5 + 0.5;
|
|
341
|
+
const fadeEdge = 1 - Math.min(1, dist2 / (maxDist2 * 1.1));
|
|
342
|
+
return 0.1 + 0.9 * ripple * (0.4 + fadeEdge * 0.6);
|
|
343
|
+
}
|
|
344
|
+
case "melt": {
|
|
345
|
+
const lagPhase = y / rows * Math.PI;
|
|
346
|
+
const drip = Math.sin(lagPhase - t * 1.8 + x * 0.15) * 0.5 + 0.5;
|
|
347
|
+
const gravity = Math.max(0, y / rows - 0.1) / 0.9;
|
|
348
|
+
return 0.05 + 0.95 * (drip * (1 - gravity * 0.6));
|
|
349
|
+
}
|
|
350
|
+
case "orbit": {
|
|
351
|
+
const ocx = cols / 2;
|
|
352
|
+
const ocy = rows / 2;
|
|
353
|
+
const odx = x - ocx;
|
|
354
|
+
const ody = y - ocy;
|
|
355
|
+
const oAngle = Math.atan2(ody, odx);
|
|
356
|
+
const oDist = Math.sqrt(odx * odx + ody * ody) / Math.sqrt(ocx * ocx + ocy * ocy);
|
|
357
|
+
const orbit = Math.sin(oAngle * 2 + oDist * 6 - t * 2.5) * 0.5 + 0.5;
|
|
358
|
+
return 0.1 + 0.9 * orbit;
|
|
359
|
+
}
|
|
360
|
+
case "cellular": {
|
|
361
|
+
const tick = Math.floor(t * 4);
|
|
362
|
+
const alive = (cx, cy) => {
|
|
363
|
+
const h = Math.sin(cx * 127.1 + cy * 311.7 + tick * 43758.5453) * 43758.5453;
|
|
364
|
+
return h - Math.floor(h) > 0.38 ? 1 : 0;
|
|
365
|
+
};
|
|
366
|
+
const self = alive(x, y);
|
|
367
|
+
const neighbours = alive(x - 1, y) + alive(x + 1, y) + alive(x, y - 1) + alive(x, y + 1) + alive(x - 1, y - 1) + alive(x + 1, y - 1) + alive(x - 1, y + 1) + alive(x + 1, y + 1);
|
|
368
|
+
const nextAlive = self === 1 ? neighbours === 2 || neighbours === 3 ? 1 : 0 : neighbours === 3 ? 1 : 0;
|
|
369
|
+
return nextAlive === 1 ? 1 : 0.05;
|
|
370
|
+
}
|
|
286
371
|
default:
|
|
287
372
|
return 1;
|
|
288
373
|
}
|
|
@@ -342,6 +427,30 @@ function computeHoverEffect(nx, ny, hoverX, hoverY, hoverIntensity, strength, ce
|
|
|
342
427
|
glow = eased * strength * 0.2;
|
|
343
428
|
colorBlend = eased * strength * 0.7;
|
|
344
429
|
break;
|
|
430
|
+
case "attract": {
|
|
431
|
+
const angle3 = Math.atan2(dy, dx);
|
|
432
|
+
const pull = eased * eased * strength * 1;
|
|
433
|
+
offsetX = -Math.cos(angle3) * pull * cellW;
|
|
434
|
+
offsetY = -Math.sin(angle3) * pull * cellH;
|
|
435
|
+
glow = eased * strength * 0.3;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case "shatter": {
|
|
439
|
+
const angle4 = Math.atan2(dy, dx);
|
|
440
|
+
const jitter = Math.sin(dx * 43.7 + dy * 29.3) * 0.5;
|
|
441
|
+
const scatter = eased * strength * 1.4 * (0.7 + jitter * 0.3);
|
|
442
|
+
offsetX = Math.cos(angle4 + jitter) * scatter * cellW;
|
|
443
|
+
offsetY = Math.sin(angle4 + jitter) * scatter * cellH;
|
|
444
|
+
scale = Math.max(0.1, 1 - eased * strength * 0.6);
|
|
445
|
+
glow = eased * strength * 0.25;
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case "trail": {
|
|
449
|
+
colorBlend = eased * strength * 0.9;
|
|
450
|
+
glow = eased * strength * 0.6;
|
|
451
|
+
scale = 1 + eased * strength * 0.15;
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
345
454
|
}
|
|
346
455
|
_hoverResult.scale = scale;
|
|
347
456
|
_hoverResult.offsetX = offsetX;
|
|
@@ -472,7 +581,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
472
581
|
} else if (baseColor) {
|
|
473
582
|
ctx.fillStyle = baseColor.replace("{a}", String(alpha));
|
|
474
583
|
} else if (lightMode) {
|
|
475
|
-
ctx.fillStyle = `rgba(
|
|
584
|
+
ctx.fillStyle = `rgba(55,55,55,${alpha * 7})`;
|
|
476
585
|
} else {
|
|
477
586
|
ctx.fillStyle = `rgba(255,255,255,${alpha})`;
|
|
478
587
|
}
|
|
@@ -753,17 +862,18 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
753
862
|
const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
|
|
754
863
|
const useFastRect = fontSize < 6;
|
|
755
864
|
if (!useFastRect) {
|
|
756
|
-
|
|
865
|
+
const isEmoji = options.artStyle === "emoji";
|
|
866
|
+
ctx.font = isEmoji ? `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif` : `${fontSize}px "JetBrains Mono", monospace`;
|
|
757
867
|
ctx.textAlign = "center";
|
|
758
868
|
ctx.textBaseline = "middle";
|
|
759
869
|
}
|
|
760
870
|
let charWeights = null;
|
|
761
871
|
if (useFastRect) {
|
|
762
872
|
charWeights = {};
|
|
763
|
-
const
|
|
764
|
-
const csLen =
|
|
873
|
+
const csChars = [...options.charset];
|
|
874
|
+
const csLen = csChars.length;
|
|
765
875
|
for (let i = 0; i < csLen; i++) {
|
|
766
|
-
charWeights[
|
|
876
|
+
charWeights[csChars[i]] = Math.max(0.1, (i + 0.3) / csLen);
|
|
767
877
|
}
|
|
768
878
|
}
|
|
769
879
|
const baseTransform = !useFastRect ? ctx.getTransform() : null;
|
|
@@ -1042,9 +1152,9 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1042
1152
|
ctx.textBaseline = "top";
|
|
1043
1153
|
let br = 255, bg = 255, bb = 255;
|
|
1044
1154
|
if (lightMode) {
|
|
1045
|
-
br =
|
|
1046
|
-
bg =
|
|
1047
|
-
bb =
|
|
1155
|
+
br = 55;
|
|
1156
|
+
bg = 55;
|
|
1157
|
+
bb = 55;
|
|
1048
1158
|
}
|
|
1049
1159
|
if (color) {
|
|
1050
1160
|
const p = parseColor(color);
|
|
@@ -1106,9 +1216,9 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1106
1216
|
const maxR = Math.sqrt(width * width + height * height) * 0.65;
|
|
1107
1217
|
let br = 255, bg = 255, bb = 255;
|
|
1108
1218
|
if (lightMode) {
|
|
1109
|
-
br =
|
|
1110
|
-
bg =
|
|
1111
|
-
bb =
|
|
1219
|
+
br = 55;
|
|
1220
|
+
bg = 55;
|
|
1221
|
+
bb = 55;
|
|
1112
1222
|
}
|
|
1113
1223
|
if (color) {
|
|
1114
1224
|
const p = parseColor(color);
|
|
@@ -1168,9 +1278,9 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1168
1278
|
const maxDist = Math.sqrt(cx * cx + cy * cy) * 1.6 + Math.sqrt(width * width + height * height) * 0.2;
|
|
1169
1279
|
let br = 255, bg = 255, bb = 255;
|
|
1170
1280
|
if (lightMode) {
|
|
1171
|
-
br =
|
|
1172
|
-
bg =
|
|
1173
|
-
bb =
|
|
1281
|
+
br = 55;
|
|
1282
|
+
bg = 55;
|
|
1283
|
+
bb = 55;
|
|
1174
1284
|
}
|
|
1175
1285
|
if (color) {
|
|
1176
1286
|
const p = parseColor(color);
|
|
@@ -1245,9 +1355,9 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1245
1355
|
ctx.textBaseline = "top";
|
|
1246
1356
|
let br = 255, bgc = 255, bb = 255;
|
|
1247
1357
|
if (lightMode) {
|
|
1248
|
-
br =
|
|
1249
|
-
bgc =
|
|
1250
|
-
bb =
|
|
1358
|
+
br = 55;
|
|
1359
|
+
bgc = 55;
|
|
1360
|
+
bb = 55;
|
|
1251
1361
|
}
|
|
1252
1362
|
if (color) {
|
|
1253
1363
|
const p = parseColor(color);
|
|
@@ -1324,9 +1434,9 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1324
1434
|
ctx.textBaseline = "top";
|
|
1325
1435
|
let br = 255, bgv = 255, bb = 255;
|
|
1326
1436
|
if (lightMode) {
|
|
1327
|
-
br =
|
|
1328
|
-
bgv =
|
|
1329
|
-
bb =
|
|
1437
|
+
br = 55;
|
|
1438
|
+
bgv = 55;
|
|
1439
|
+
bb = 55;
|
|
1330
1440
|
}
|
|
1331
1441
|
if (color) {
|
|
1332
1442
|
const p = parseColor(color);
|
|
@@ -1396,9 +1506,9 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1396
1506
|
ctx.textBaseline = "top";
|
|
1397
1507
|
let cr = 255, cg = 255, cb = 255;
|
|
1398
1508
|
if (lightMode) {
|
|
1399
|
-
cr =
|
|
1400
|
-
cg =
|
|
1401
|
-
cb =
|
|
1509
|
+
cr = 55;
|
|
1510
|
+
cg = 55;
|
|
1511
|
+
cb = 55;
|
|
1402
1512
|
}
|
|
1403
1513
|
if (color) {
|
|
1404
1514
|
const p = parseColor(color);
|
|
@@ -1481,9 +1591,9 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
|
|
|
1481
1591
|
ctx.textBaseline = "top";
|
|
1482
1592
|
let cr = 255, cg = 255, cb = 255;
|
|
1483
1593
|
if (lightMode) {
|
|
1484
|
-
cr =
|
|
1485
|
-
cg =
|
|
1486
|
-
cb =
|
|
1594
|
+
cr = 55;
|
|
1595
|
+
cg = 55;
|
|
1596
|
+
cb = 55;
|
|
1487
1597
|
}
|
|
1488
1598
|
if (color) {
|
|
1489
1599
|
const p = parseColor(color);
|
|
@@ -1558,9 +1668,9 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1558
1668
|
ctx.textBaseline = "top";
|
|
1559
1669
|
let cr = 255, cg = 255, cb = 255;
|
|
1560
1670
|
if (lightMode) {
|
|
1561
|
-
cr =
|
|
1562
|
-
cg =
|
|
1563
|
-
cb =
|
|
1671
|
+
cr = 55;
|
|
1672
|
+
cg = 55;
|
|
1673
|
+
cb = 55;
|
|
1564
1674
|
}
|
|
1565
1675
|
if (color) {
|
|
1566
1676
|
const p = parseColor(color);
|
|
@@ -1630,9 +1740,9 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1630
1740
|
ctx.textBaseline = "top";
|
|
1631
1741
|
let cr = 255, cg = 255, cb = 255;
|
|
1632
1742
|
if (lightMode) {
|
|
1633
|
-
cr =
|
|
1634
|
-
cg =
|
|
1635
|
-
cb =
|
|
1743
|
+
cr = 55;
|
|
1744
|
+
cg = 55;
|
|
1745
|
+
cb = 55;
|
|
1636
1746
|
}
|
|
1637
1747
|
if (color) {
|
|
1638
1748
|
const p = parseColor(color);
|
|
@@ -1674,6 +1784,292 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1674
1784
|
}
|
|
1675
1785
|
}
|
|
1676
1786
|
|
|
1787
|
+
// src/backgrounds/fire.ts
|
|
1788
|
+
function renderFireBackground(ctx, width, height, time, options = {}) {
|
|
1789
|
+
const {
|
|
1790
|
+
fontSize = 13,
|
|
1791
|
+
chars = " .,:;i+xX#&@",
|
|
1792
|
+
color = "#ff4500",
|
|
1793
|
+
hotColor = "#ffe066",
|
|
1794
|
+
intensity = 0.85,
|
|
1795
|
+
wind = 0,
|
|
1796
|
+
speed = 1,
|
|
1797
|
+
lightMode = false
|
|
1798
|
+
} = options;
|
|
1799
|
+
const charW = fontSize * 0.62;
|
|
1800
|
+
const lineH = fontSize * 1.4;
|
|
1801
|
+
const cols = Math.ceil(width / charW);
|
|
1802
|
+
const rows = Math.ceil(height / lineH);
|
|
1803
|
+
const len = cols * rows;
|
|
1804
|
+
const key = "__fire_heat__";
|
|
1805
|
+
const canvasAny = ctx.canvas;
|
|
1806
|
+
let heat = canvasAny[key];
|
|
1807
|
+
if (!heat || heat.length !== len) {
|
|
1808
|
+
heat = new Float32Array(len);
|
|
1809
|
+
canvasAny[key] = heat;
|
|
1810
|
+
}
|
|
1811
|
+
const dt = 0.016 * speed;
|
|
1812
|
+
const coolingRate = 0.18 * dt;
|
|
1813
|
+
const windShift = wind * speed * 0.8;
|
|
1814
|
+
const baseRow = rows - 1;
|
|
1815
|
+
const t = time * speed;
|
|
1816
|
+
for (let c = 0; c < cols; c++) {
|
|
1817
|
+
const flicker = Math.sin(c * 0.31 + t * 4.1) * 0.5 + 0.5;
|
|
1818
|
+
const flicker2 = Math.sin(c * 0.73 - t * 2.7) * 0.5 + 0.5;
|
|
1819
|
+
const seed = (flicker * 0.6 + flicker2 * 0.4) * intensity;
|
|
1820
|
+
heat[baseRow * cols + c] = Math.min(1, seed + Math.random() * 0.15 * intensity);
|
|
1821
|
+
if (baseRow > 0) heat[(baseRow - 1) * cols + c] = Math.min(1, seed * 0.85 + Math.random() * 0.1 * intensity);
|
|
1822
|
+
}
|
|
1823
|
+
const newHeat = new Float32Array(len);
|
|
1824
|
+
for (let r = 0; r < rows - 2; r++) {
|
|
1825
|
+
for (let c = 0; c < cols; c++) {
|
|
1826
|
+
const below = heat[(r + 1) * cols + c];
|
|
1827
|
+
const below2 = heat[(r + 2) * cols + Math.max(0, Math.min(cols - 1, c + Math.round(windShift)))];
|
|
1828
|
+
const left = heat[(r + 1) * cols + Math.max(0, c - 1)];
|
|
1829
|
+
const right = heat[(r + 1) * cols + Math.min(cols - 1, c + 1)];
|
|
1830
|
+
const avg = below * 0.4 + below2 * 0.25 + left * 0.175 + right * 0.175;
|
|
1831
|
+
newHeat[r * cols + c] = Math.max(0, avg - coolingRate - Math.random() * 0.02 * speed);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
for (let c = 0; c < cols; c++) {
|
|
1835
|
+
newHeat[(rows - 1) * cols + c] = heat[(rows - 1) * cols + c];
|
|
1836
|
+
if (rows > 1) newHeat[(rows - 2) * cols + c] = heat[(rows - 2) * cols + c];
|
|
1837
|
+
}
|
|
1838
|
+
canvasAny[key] = newHeat;
|
|
1839
|
+
const cp = parseColor(color) ?? { r: 255, g: 69, b: 0 };
|
|
1840
|
+
const hp = parseColor(hotColor) ?? { r: 255, g: 224, b: 102 };
|
|
1841
|
+
ctx.clearRect(0, 0, width, height);
|
|
1842
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1843
|
+
ctx.textBaseline = "top";
|
|
1844
|
+
for (let r = 0; r < rows; r++) {
|
|
1845
|
+
for (let c = 0; c < cols; c++) {
|
|
1846
|
+
const v = newHeat[r * cols + c];
|
|
1847
|
+
if (v < 0.03) continue;
|
|
1848
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(v * chars.length));
|
|
1849
|
+
const ch = chars[charIdx];
|
|
1850
|
+
if (ch === " ") continue;
|
|
1851
|
+
const blend = Math.min(1, v * 1.2);
|
|
1852
|
+
const r2 = cp.r + (hp.r - cp.r) * blend | 0;
|
|
1853
|
+
const g2 = cp.g + (hp.g - cp.g) * blend | 0;
|
|
1854
|
+
const b2 = cp.b + (hp.b - cp.b) * blend | 0;
|
|
1855
|
+
const alpha = lightMode ? 1 - v * 0.3 : Math.min(1, v + 0.15);
|
|
1856
|
+
ctx.globalAlpha = alpha;
|
|
1857
|
+
ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
|
|
1858
|
+
ctx.fillText(ch, c * charW, r * lineH);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
ctx.globalAlpha = 1;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// src/backgrounds/dna.ts
|
|
1865
|
+
function renderDnaBackground(ctx, width, height, time, options = {}) {
|
|
1866
|
+
const {
|
|
1867
|
+
fontSize = 13,
|
|
1868
|
+
baseChars = "ATCG",
|
|
1869
|
+
bridgeChars = "-=\u2261",
|
|
1870
|
+
color = "#00e5ff",
|
|
1871
|
+
color2 = "#ff4081",
|
|
1872
|
+
bridgeColor = "#88ffcc",
|
|
1873
|
+
speed = 1,
|
|
1874
|
+
helixCount,
|
|
1875
|
+
lightMode = false
|
|
1876
|
+
} = options;
|
|
1877
|
+
const charW = fontSize * 0.62;
|
|
1878
|
+
const lineH = fontSize * 1.4;
|
|
1879
|
+
const cols = Math.ceil(width / charW);
|
|
1880
|
+
const rows = Math.ceil(height / lineH);
|
|
1881
|
+
const numHelix = helixCount ?? Math.max(1, Math.floor(width / 80));
|
|
1882
|
+
const sectionW = cols / numHelix;
|
|
1883
|
+
const cp = parseColor(color) ?? { r: 0, g: 229, b: 255 };
|
|
1884
|
+
const cp2 = parseColor(color2) ?? { r: 255, g: 64, b: 129 };
|
|
1885
|
+
const bp = parseColor(bridgeColor) ?? { r: 136, g: 255, b: 204 };
|
|
1886
|
+
ctx.clearRect(0, 0, width, height);
|
|
1887
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1888
|
+
ctx.textBaseline = "top";
|
|
1889
|
+
const t = time * speed;
|
|
1890
|
+
const amplitude = sectionW * 0.35;
|
|
1891
|
+
for (let h = 0; h < numHelix; h++) {
|
|
1892
|
+
const centerCol = sectionW * (h + 0.5);
|
|
1893
|
+
for (let r = 0; r < rows; r++) {
|
|
1894
|
+
const phase = r / rows * Math.PI * 6 - t * 1.8;
|
|
1895
|
+
const strand1ColF = centerCol + Math.sin(phase) * amplitude;
|
|
1896
|
+
const strand2ColF = centerCol + Math.sin(phase + Math.PI) * amplitude;
|
|
1897
|
+
const strand1Col = Math.round(strand1ColF);
|
|
1898
|
+
const strand2Col = Math.round(strand2ColF);
|
|
1899
|
+
if (strand1Col < 0 || strand1Col >= cols) continue;
|
|
1900
|
+
const baseSeed1 = hash2(h * 31 + r * 7, 3);
|
|
1901
|
+
const ch1 = baseChars[Math.floor(baseSeed1 * baseChars.length)];
|
|
1902
|
+
const depth1 = (Math.sin(phase) + 1) * 0.5;
|
|
1903
|
+
const depth2 = (Math.sin(phase + Math.PI) + 1) * 0.5;
|
|
1904
|
+
ctx.globalAlpha = 0.35 + depth1 * 0.65;
|
|
1905
|
+
ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
|
|
1906
|
+
ctx.fillText(ch1, strand1Col * charW, r * lineH);
|
|
1907
|
+
if (strand2Col >= 0 && strand2Col < cols) {
|
|
1908
|
+
const baseSeed2 = hash2(h * 53 + r * 11, 7);
|
|
1909
|
+
const ch2 = baseChars[Math.floor(baseSeed2 * baseChars.length)];
|
|
1910
|
+
ctx.globalAlpha = 0.35 + depth2 * 0.65;
|
|
1911
|
+
ctx.fillStyle = `rgb(${cp2.r},${cp2.g},${cp2.b})`;
|
|
1912
|
+
ctx.fillText(ch2, strand2Col * charW, r * lineH);
|
|
1913
|
+
}
|
|
1914
|
+
const bridgeInterval = 3;
|
|
1915
|
+
if (r % bridgeInterval === 0) {
|
|
1916
|
+
const minC = Math.min(strand1Col, strand2Col);
|
|
1917
|
+
const maxC = Math.max(strand1Col, strand2Col);
|
|
1918
|
+
const bridgeLen = maxC - minC;
|
|
1919
|
+
if (bridgeLen > 1) {
|
|
1920
|
+
const bSeed = hash2(r * 17 + h * 43, 5);
|
|
1921
|
+
const bCh = bridgeChars[Math.floor(bSeed * bridgeChars.length)];
|
|
1922
|
+
const midBridgeAlpha = (depth1 + depth2) * 0.25 + 0.2;
|
|
1923
|
+
ctx.globalAlpha = midBridgeAlpha;
|
|
1924
|
+
ctx.fillStyle = `rgb(${bp.r},${bp.g},${bp.b})`;
|
|
1925
|
+
for (let bc = minC + 1; bc < maxC; bc++) {
|
|
1926
|
+
ctx.fillText(bCh, bc * charW, r * lineH);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
ctx.globalAlpha = 1;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// src/backgrounds/terrain.ts
|
|
1936
|
+
function renderTerrainBackground(ctx, width, height, time, options = {}) {
|
|
1937
|
+
const {
|
|
1938
|
+
fontSize = 13,
|
|
1939
|
+
chars = " .,:;+*#@",
|
|
1940
|
+
color = "#4caf50",
|
|
1941
|
+
skyColor = "#1a237e",
|
|
1942
|
+
peakColor = "#e0e0e0",
|
|
1943
|
+
speed = 1,
|
|
1944
|
+
roughness = 0.55,
|
|
1945
|
+
heightScale = 0.55,
|
|
1946
|
+
stars = true,
|
|
1947
|
+
lightMode = false
|
|
1948
|
+
} = options;
|
|
1949
|
+
const charW = fontSize * 0.62;
|
|
1950
|
+
const lineH = fontSize * 1.4;
|
|
1951
|
+
const cols = Math.ceil(width / charW);
|
|
1952
|
+
const rows = Math.ceil(height / lineH);
|
|
1953
|
+
const cp = parseColor(color) ?? { r: 76, g: 175, b: 80 };
|
|
1954
|
+
const sky = parseColor(skyColor) ?? { r: 26, g: 35, b: 126 };
|
|
1955
|
+
const peak = parseColor(peakColor) ?? { r: 224, g: 224, b: 224 };
|
|
1956
|
+
ctx.clearRect(0, 0, width, height);
|
|
1957
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1958
|
+
ctx.textBaseline = "top";
|
|
1959
|
+
const scroll = time * speed * 0.4;
|
|
1960
|
+
const terrainRow = new Array(cols);
|
|
1961
|
+
for (let c = 0; c < cols; c++) {
|
|
1962
|
+
const nx = (c / cols + scroll) * roughness * 3;
|
|
1963
|
+
const h = (fbm(nx, 0.5) * 0.5 + 0.5) * heightScale;
|
|
1964
|
+
terrainRow[c] = Math.floor(h * rows);
|
|
1965
|
+
}
|
|
1966
|
+
for (let r = 0; r < rows; r++) {
|
|
1967
|
+
for (let c = 0; c < cols; c++) {
|
|
1968
|
+
const terrainStart = rows - 1 - terrainRow[c];
|
|
1969
|
+
const isGround = r >= terrainStart;
|
|
1970
|
+
const isNearPeak = r === terrainStart;
|
|
1971
|
+
if (!isGround) {
|
|
1972
|
+
if (stars) {
|
|
1973
|
+
const starSeed = hash2(c * 7 + Math.floor(scroll * 0.3), r * 13);
|
|
1974
|
+
if (starSeed > 0.97) {
|
|
1975
|
+
const twinkle = Math.sin(time * 2 + starSeed * 100) * 0.3 + 0.7;
|
|
1976
|
+
ctx.globalAlpha = twinkle * 0.5;
|
|
1977
|
+
ctx.fillStyle = `rgb(${sky.r + 60},${sky.g + 60},${sky.b + 80})`;
|
|
1978
|
+
ctx.fillText("\xB7", c * charW, r * lineH);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
const depth = (r - terrainStart) / Math.max(1, terrainRow[c]);
|
|
1984
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(depth * chars.length));
|
|
1985
|
+
const ch = chars[charIdx];
|
|
1986
|
+
if (ch === " " && !isNearPeak) continue;
|
|
1987
|
+
const blendPeak = isNearPeak ? 1 : Math.max(0, 1 - depth * 4);
|
|
1988
|
+
const r2 = cp.r + (peak.r - cp.r) * blendPeak | 0;
|
|
1989
|
+
const g2 = cp.g + (peak.g - cp.g) * blendPeak | 0;
|
|
1990
|
+
const b2 = cp.b + (peak.b - cp.b) * blendPeak | 0;
|
|
1991
|
+
ctx.globalAlpha = 0.5 + depth * 0.5;
|
|
1992
|
+
ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
|
|
1993
|
+
ctx.fillText(isNearPeak ? chars[chars.length - 1] : ch, c * charW, r * lineH);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
ctx.globalAlpha = 1;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/backgrounds/circuit.ts
|
|
2000
|
+
var EAST = 1;
|
|
2001
|
+
var WEST = 2;
|
|
2002
|
+
var NORTH = 4;
|
|
2003
|
+
var SOUTH = 8;
|
|
2004
|
+
var DIR_CHARS = {
|
|
2005
|
+
[EAST | WEST]: "\u2500",
|
|
2006
|
+
[NORTH | SOUTH]: "\u2502",
|
|
2007
|
+
[EAST | SOUTH]: "\u250C",
|
|
2008
|
+
[WEST | SOUTH]: "\u2510",
|
|
2009
|
+
[EAST | NORTH]: "\u2514",
|
|
2010
|
+
[WEST | NORTH]: "\u2518",
|
|
2011
|
+
[EAST | WEST | SOUTH]: "\u252C",
|
|
2012
|
+
[EAST | WEST | NORTH]: "\u2534",
|
|
2013
|
+
[NORTH | SOUTH | EAST]: "\u251C",
|
|
2014
|
+
[NORTH | SOUTH | WEST]: "\u2524",
|
|
2015
|
+
[EAST | WEST | NORTH | SOUTH]: "\u253C",
|
|
2016
|
+
[EAST]: "\u2576",
|
|
2017
|
+
[WEST]: "\u2574",
|
|
2018
|
+
[NORTH]: "\u2575",
|
|
2019
|
+
[SOUTH]: "\u2577"
|
|
2020
|
+
};
|
|
2021
|
+
function renderCircuitBackground(ctx, width, height, time, options = {}) {
|
|
2022
|
+
const {
|
|
2023
|
+
fontSize = 13,
|
|
2024
|
+
pulseColor = "#ffffff",
|
|
2025
|
+
color = "#00ff88",
|
|
2026
|
+
density = 0.38,
|
|
2027
|
+
speed = 1,
|
|
2028
|
+
lightMode = false
|
|
2029
|
+
} = options;
|
|
2030
|
+
const charW = fontSize * 0.62;
|
|
2031
|
+
const lineH = fontSize * 1.4;
|
|
2032
|
+
const cols = Math.ceil(width / charW);
|
|
2033
|
+
const rows = Math.ceil(height / lineH);
|
|
2034
|
+
const cp = parseColor(color) ?? { r: 0, g: 255, b: 136 };
|
|
2035
|
+
const pp = parseColor(pulseColor) ?? { r: 255, g: 255, b: 255 };
|
|
2036
|
+
const getConnections = (c, r) => {
|
|
2037
|
+
if (hash2(c * 17 + 1, r * 7 + 2) > density) return 0;
|
|
2038
|
+
let mask = 0;
|
|
2039
|
+
if (c + 1 < cols && hash2(c * 17 + 1, r * 7 + 2) > 0.15) mask |= EAST;
|
|
2040
|
+
if (c - 1 >= 0 && hash2((c - 1) * 17 + 1, r * 7 + 2) > 0.15) mask |= WEST;
|
|
2041
|
+
if (r + 1 < rows && hash2(c * 17 + 1, (r + 1) * 7 + 2) > 0.15) mask |= SOUTH;
|
|
2042
|
+
if (r - 1 >= 0 && hash2(c * 17 + 1, (r - 1) * 7 + 2) > 0.15) mask |= NORTH;
|
|
2043
|
+
return mask;
|
|
2044
|
+
};
|
|
2045
|
+
ctx.clearRect(0, 0, width, height);
|
|
2046
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
2047
|
+
ctx.textBaseline = "top";
|
|
2048
|
+
const t = time * speed;
|
|
2049
|
+
for (let r = 0; r < rows; r++) {
|
|
2050
|
+
for (let c = 0; c < cols; c++) {
|
|
2051
|
+
const mask = getConnections(c, r);
|
|
2052
|
+
if (mask === 0) continue;
|
|
2053
|
+
const ch = DIR_CHARS[mask] ?? "\xB7";
|
|
2054
|
+
const pulsePosH = (t * 8 + hash2(c, r * 23) * 40) % cols;
|
|
2055
|
+
const pulsePosV = (t * 6 + hash2(r * 17, c * 11) * 40) % rows;
|
|
2056
|
+
const nearH = (mask & EAST || mask & WEST) && Math.abs(c - pulsePosH) < 1.5;
|
|
2057
|
+
const nearV = (mask & NORTH || mask & SOUTH) && Math.abs(r - pulsePosV) < 1.5;
|
|
2058
|
+
const isPulse = nearH || nearV;
|
|
2059
|
+
const baseAlpha = 0.25 + hash2(c * 3, r * 5) * 0.35;
|
|
2060
|
+
if (isPulse) {
|
|
2061
|
+
ctx.globalAlpha = 0.95;
|
|
2062
|
+
ctx.fillStyle = `rgb(${pp.r},${pp.g},${pp.b})`;
|
|
2063
|
+
} else {
|
|
2064
|
+
ctx.globalAlpha = baseAlpha;
|
|
2065
|
+
ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
|
|
2066
|
+
}
|
|
2067
|
+
ctx.fillText(ch, c * charW, r * lineH);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
ctx.globalAlpha = 1;
|
|
2071
|
+
}
|
|
2072
|
+
|
|
1677
2073
|
// src/backgrounds/index.ts
|
|
1678
2074
|
function _parseColor(c) {
|
|
1679
2075
|
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
@@ -1778,6 +2174,24 @@ function asciiBackground(target, options = {}) {
|
|
|
1778
2174
|
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1779
2175
|
color: color ?? renderOpts.color
|
|
1780
2176
|
});
|
|
2177
|
+
const buildFireOpts = () => ({
|
|
2178
|
+
...renderOpts,
|
|
2179
|
+
color: color ?? renderOpts.color
|
|
2180
|
+
});
|
|
2181
|
+
const buildDnaOpts = () => ({
|
|
2182
|
+
...renderOpts,
|
|
2183
|
+
color: color ?? renderOpts.color
|
|
2184
|
+
});
|
|
2185
|
+
const buildTerrainOpts = () => ({
|
|
2186
|
+
...renderOpts,
|
|
2187
|
+
color: color ?? renderOpts.color,
|
|
2188
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
|
|
2189
|
+
});
|
|
2190
|
+
const buildCircuitOpts = () => ({
|
|
2191
|
+
...renderOpts,
|
|
2192
|
+
color: color ?? renderOpts.color,
|
|
2193
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
|
|
2194
|
+
});
|
|
1781
2195
|
const optsRef = { current: buildWaveOpts() };
|
|
1782
2196
|
const rebuildOpts = () => {
|
|
1783
2197
|
if (type === "rain") optsRef.current = buildRainOpts();
|
|
@@ -1789,6 +2203,10 @@ function asciiBackground(target, options = {}) {
|
|
|
1789
2203
|
else if (type === "silk") optsRef.current = buildSilkOpts();
|
|
1790
2204
|
else if (type === "void") optsRef.current = buildVoidOpts();
|
|
1791
2205
|
else if (type === "morph") optsRef.current = buildMorphOpts();
|
|
2206
|
+
else if (type === "fire") optsRef.current = buildFireOpts();
|
|
2207
|
+
else if (type === "dna") optsRef.current = buildDnaOpts();
|
|
2208
|
+
else if (type === "terrain") optsRef.current = buildTerrainOpts();
|
|
2209
|
+
else if (type === "circuit") optsRef.current = buildCircuitOpts();
|
|
1792
2210
|
else optsRef.current = buildWaveOpts();
|
|
1793
2211
|
};
|
|
1794
2212
|
rebuildOpts();
|
|
@@ -1835,6 +2253,14 @@ function asciiBackground(target, options = {}) {
|
|
|
1835
2253
|
renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1836
2254
|
} else if (type === "morph") {
|
|
1837
2255
|
renderMorphBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2256
|
+
} else if (type === "fire") {
|
|
2257
|
+
renderFireBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2258
|
+
} else if (type === "dna") {
|
|
2259
|
+
renderDnaBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2260
|
+
} else if (type === "terrain") {
|
|
2261
|
+
renderTerrainBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2262
|
+
} else if (type === "circuit") {
|
|
2263
|
+
renderCircuitBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
1838
2264
|
} else {
|
|
1839
2265
|
renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1840
2266
|
}
|
|
@@ -1855,6 +2281,178 @@ function asciiBackground(target, options = {}) {
|
|
|
1855
2281
|
}
|
|
1856
2282
|
var mountWaveBackground = asciiBackground;
|
|
1857
2283
|
|
|
2284
|
+
// src/core/ascii-text.ts
|
|
2285
|
+
function asciiText(source, options = {}, targetWidth, targetHeight) {
|
|
2286
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2287
|
+
const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
|
|
2288
|
+
if (!frame.length || cols === 0) return "";
|
|
2289
|
+
const lines = [];
|
|
2290
|
+
for (const row of frame) {
|
|
2291
|
+
lines.push(row.map((cell) => cell.char).join(""));
|
|
2292
|
+
}
|
|
2293
|
+
return lines.join("\n");
|
|
2294
|
+
}
|
|
2295
|
+
function asciiTextAnsi(source, options = {}, targetWidth, targetHeight) {
|
|
2296
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2297
|
+
const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
|
|
2298
|
+
if (!frame.length || cols === 0) return "";
|
|
2299
|
+
const RESET = "\x1B[0m";
|
|
2300
|
+
const lines = [];
|
|
2301
|
+
for (const row of frame) {
|
|
2302
|
+
let line = "";
|
|
2303
|
+
for (const cell of row) {
|
|
2304
|
+
if (cell.char === " " || cell.a < 10) {
|
|
2305
|
+
line += " ";
|
|
2306
|
+
} else {
|
|
2307
|
+
line += `\x1B[38;2;${cell.r};${cell.g};${cell.b}m${cell.char}${RESET}`;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
lines.push(line);
|
|
2311
|
+
}
|
|
2312
|
+
return lines.join("\n");
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// src/core/record.ts
|
|
2316
|
+
function createRecorder(canvas, options = {}) {
|
|
2317
|
+
const {
|
|
2318
|
+
fps = 15,
|
|
2319
|
+
maxFrames = 120,
|
|
2320
|
+
format = "gif",
|
|
2321
|
+
quality = 10,
|
|
2322
|
+
scale = 1
|
|
2323
|
+
} = options;
|
|
2324
|
+
const interval = 1e3 / fps;
|
|
2325
|
+
let recording = false;
|
|
2326
|
+
let timerId = -1;
|
|
2327
|
+
const blobs = [];
|
|
2328
|
+
const captureFrame = () => {
|
|
2329
|
+
if (!recording || blobs.length >= maxFrames) return;
|
|
2330
|
+
let src = canvas;
|
|
2331
|
+
if (scale !== 1) {
|
|
2332
|
+
const off = document.createElement("canvas");
|
|
2333
|
+
off.width = Math.round(canvas.width * scale);
|
|
2334
|
+
off.height = Math.round(canvas.height * scale);
|
|
2335
|
+
const offCtx = off.getContext("2d");
|
|
2336
|
+
offCtx.drawImage(canvas, 0, 0, off.width, off.height);
|
|
2337
|
+
src = off;
|
|
2338
|
+
}
|
|
2339
|
+
blobs.push(src.toDataURL("image/png"));
|
|
2340
|
+
};
|
|
2341
|
+
const encodeGif = async (frames) => {
|
|
2342
|
+
return new Promise((resolve, reject) => {
|
|
2343
|
+
if (typeof GIF === "undefined") {
|
|
2344
|
+
reject(new Error('[asciify recorder] gif.js not found. Add <script src="/gif.worker.js"> to your page.'));
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
const gif = new GIF({
|
|
2348
|
+
workers: 2,
|
|
2349
|
+
quality,
|
|
2350
|
+
workerScript: "/gif.worker.js"
|
|
2351
|
+
});
|
|
2352
|
+
let loaded = 0;
|
|
2353
|
+
const total = frames.length;
|
|
2354
|
+
frames.forEach((dataUrl) => {
|
|
2355
|
+
const img = new Image();
|
|
2356
|
+
img.onload = () => {
|
|
2357
|
+
gif.addFrame(img, { delay: interval, copy: true });
|
|
2358
|
+
loaded++;
|
|
2359
|
+
if (loaded === total) gif.render();
|
|
2360
|
+
};
|
|
2361
|
+
img.src = dataUrl;
|
|
2362
|
+
});
|
|
2363
|
+
gif.on("finished", (blob) => {
|
|
2364
|
+
const reader = new FileReader();
|
|
2365
|
+
reader.onload = () => resolve(reader.result);
|
|
2366
|
+
reader.readAsDataURL(blob);
|
|
2367
|
+
});
|
|
2368
|
+
gif.on("error", reject);
|
|
2369
|
+
});
|
|
2370
|
+
};
|
|
2371
|
+
const encodeWebP = async (frames, _fps) => {
|
|
2372
|
+
if (typeof MediaRecorder === "undefined") {
|
|
2373
|
+
throw new Error("[asciify recorder] MediaRecorder not available in this browser.");
|
|
2374
|
+
}
|
|
2375
|
+
const off = document.createElement("canvas");
|
|
2376
|
+
if (frames.length === 0) return "";
|
|
2377
|
+
const probe = new Image();
|
|
2378
|
+
await new Promise((res) => {
|
|
2379
|
+
probe.onload = () => res();
|
|
2380
|
+
probe.src = frames[0];
|
|
2381
|
+
});
|
|
2382
|
+
off.width = probe.naturalWidth;
|
|
2383
|
+
off.height = probe.naturalHeight;
|
|
2384
|
+
const offCtx = off.getContext("2d");
|
|
2385
|
+
const stream = off.captureStream(_fps);
|
|
2386
|
+
const recorder = new MediaRecorder(stream, { mimeType: "video/webm;codecs=vp9" });
|
|
2387
|
+
const chunks = [];
|
|
2388
|
+
recorder.ondataavailable = (e) => chunks.push(e.data);
|
|
2389
|
+
return new Promise((resolve, reject) => {
|
|
2390
|
+
recorder.onstop = () => {
|
|
2391
|
+
const blob = new Blob(chunks, { type: "video/webm" });
|
|
2392
|
+
const reader = new FileReader();
|
|
2393
|
+
reader.onload = () => resolve(reader.result);
|
|
2394
|
+
reader.readAsDataURL(blob);
|
|
2395
|
+
};
|
|
2396
|
+
recorder.onerror = reject;
|
|
2397
|
+
recorder.start();
|
|
2398
|
+
let idx = 0;
|
|
2399
|
+
const drawNext = () => {
|
|
2400
|
+
if (idx >= frames.length) {
|
|
2401
|
+
recorder.stop();
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
const img = new Image();
|
|
2405
|
+
img.onload = () => {
|
|
2406
|
+
offCtx.drawImage(img, 0, 0);
|
|
2407
|
+
idx++;
|
|
2408
|
+
setTimeout(drawNext, interval);
|
|
2409
|
+
};
|
|
2410
|
+
img.src = frames[idx];
|
|
2411
|
+
};
|
|
2412
|
+
drawNext();
|
|
2413
|
+
});
|
|
2414
|
+
};
|
|
2415
|
+
return {
|
|
2416
|
+
get isRecording() {
|
|
2417
|
+
return recording;
|
|
2418
|
+
},
|
|
2419
|
+
get frameCount() {
|
|
2420
|
+
return blobs.length;
|
|
2421
|
+
},
|
|
2422
|
+
start() {
|
|
2423
|
+
if (recording) return;
|
|
2424
|
+
blobs.length = 0;
|
|
2425
|
+
recording = true;
|
|
2426
|
+
timerId = window.setInterval(captureFrame, interval);
|
|
2427
|
+
},
|
|
2428
|
+
async stop() {
|
|
2429
|
+
if (!recording) return "";
|
|
2430
|
+
recording = false;
|
|
2431
|
+
clearInterval(timerId);
|
|
2432
|
+
const frames = blobs.slice();
|
|
2433
|
+
if (format === "png-sequence") {
|
|
2434
|
+
return JSON.stringify(frames);
|
|
2435
|
+
}
|
|
2436
|
+
if (format === "webp") {
|
|
2437
|
+
return encodeWebP(frames, fps);
|
|
2438
|
+
}
|
|
2439
|
+
return encodeGif(frames);
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
async function recordAndDownload(canvas, durationMs, options = {}) {
|
|
2444
|
+
const { filename = "asciify-recording", ...recOpts } = options;
|
|
2445
|
+
const recorder = createRecorder(canvas, recOpts);
|
|
2446
|
+
recorder.start();
|
|
2447
|
+
await new Promise((res) => setTimeout(res, durationMs));
|
|
2448
|
+
const dataUrl = await recorder.stop();
|
|
2449
|
+
const ext = options.format === "webp" ? "webm" : options.format === "png-sequence" ? "json" : "gif";
|
|
2450
|
+
const a = document.createElement("a");
|
|
2451
|
+
a.href = dataUrl;
|
|
2452
|
+
a.download = `${filename}.${ext}`;
|
|
2453
|
+
a.click();
|
|
2454
|
+
}
|
|
2455
|
+
|
|
1858
2456
|
// src/webgl-engine.ts
|
|
1859
2457
|
var VERT_SRC = (
|
|
1860
2458
|
/* glsl */
|
|
@@ -2117,8 +2715,8 @@ function makeTexture(gl, filter = gl.NEAREST) {
|
|
|
2117
2715
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2118
2716
|
return t;
|
|
2119
2717
|
}
|
|
2120
|
-
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField"];
|
|
2121
|
-
var HOVER_EFFECTS = ["spotlight", "magnify", "repel", "glow", "colorShift"];
|
|
2718
|
+
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField", "ripple", "melt", "orbit", "cellular"];
|
|
2719
|
+
var HOVER_EFFECTS = ["spotlight", "magnify", "repel", "glow", "colorShift", "attract", "shatter", "trail"];
|
|
2122
2720
|
var COLOR_MODES = ["grayscale", "fullcolor", "matrix", "accent"];
|
|
2123
2721
|
function tryCreateWebGLRenderer(canvas) {
|
|
2124
2722
|
const glOrNull = canvas.getContext("webgl2", {
|
|
@@ -2303,6 +2901,6 @@ function tryCreateWebGLRenderer(canvas) {
|
|
|
2303
2901
|
}
|
|
2304
2902
|
}
|
|
2305
2903
|
|
|
2306
|
-
export { ART_STYLE_PRESETS, CHARSETS, DEFAULT_OPTIONS, HOVER_PRESETS, asciiBackground, asciify, asciifyGif, asciifyVideo, generateAnimatedEmbedCode, generateEmbedCode, gifToAsciiFrames, imageToAsciiFrame, mountWaveBackground, renderAuroraBackground, renderFrameToCanvas, renderGridBackground, renderMorphBackground, renderNoiseBackground, renderPulseBackground, renderRainBackground, renderSilkBackground, renderStarsBackground, renderVoidBackground, renderWaveBackground, tryCreateWebGLRenderer, videoToAsciiFrames };
|
|
2904
|
+
export { ART_STYLE_PRESETS, CHARSETS, DEFAULT_OPTIONS, HOVER_PRESETS, PALETTE_THEMES, asciiBackground, asciiText, asciiTextAnsi, asciify, asciifyGif, asciifyVideo, createRecorder, generateAnimatedEmbedCode, generateEmbedCode, gifToAsciiFrames, imageToAsciiFrame, mountWaveBackground, recordAndDownload, renderAuroraBackground, renderCircuitBackground, renderDnaBackground, renderFireBackground, renderFrameToCanvas, renderGridBackground, renderMorphBackground, renderNoiseBackground, renderPulseBackground, renderRainBackground, renderSilkBackground, renderStarsBackground, renderTerrainBackground, renderVoidBackground, renderWaveBackground, tryCreateWebGLRenderer, videoToAsciiFrames };
|
|
2307
2905
|
//# sourceMappingURL=index.js.map
|
|
2308
2906
|
//# sourceMappingURL=index.js.map
|