asciify-engine 1.0.22 → 1.0.24
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 +649 -32
- 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 +641 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3,17 +3,29 @@
|
|
|
3
3
|
var gifuctJs = require('gifuct-js');
|
|
4
4
|
|
|
5
5
|
// src/types.ts
|
|
6
|
+
var PALETTE_THEMES = {
|
|
7
|
+
dracula: { name: "Dracula", accent: "#bd93f9", bg: "#282a36", fg: "#f8f8f2" },
|
|
8
|
+
monokai: { name: "Monokai", accent: "#a6e22e", bg: "#272822", fg: "#f8f8f2" },
|
|
9
|
+
nord: { name: "Nord", accent: "#88c0d0", bg: "#2e3440", fg: "#eceff4" },
|
|
10
|
+
catppuccin: { name: "Catppuccin", accent: "#cba6f7", bg: "#1e1e2e", fg: "#cdd6f4" },
|
|
11
|
+
solarized: { name: "Solarized", accent: "#268bd2", bg: "#002b36", fg: "#839496" },
|
|
12
|
+
gruvbox: { name: "Gruvbox", accent: "#b8bb26", bg: "#282828", fg: "#ebdbb2" }
|
|
13
|
+
};
|
|
6
14
|
var CHARSETS = {
|
|
7
15
|
standard: " .:-=+*#%@",
|
|
8
16
|
blocks: " \u2591\u2592\u2593\u2588",
|
|
9
17
|
minimal: " .:+",
|
|
10
|
-
dense: " .'`^\",:;Il!i><~+_-?][}{1)(
|
|
18
|
+
dense: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
|
|
11
19
|
binary: "01",
|
|
12
20
|
dots: " \u2801\u2803\u2807\u2847\u28C7\u28E7\u28F7\u28FF",
|
|
13
21
|
letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
14
22
|
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",
|
|
15
23
|
box: " \u25AA\u25FE\u25FC\u25A0\u2588",
|
|
16
|
-
lines: " \u02D7\u2010\u2013\u2014\u2015\u2501"
|
|
24
|
+
lines: " \u02D7\u2010\u2013\u2014\u2015\u2501",
|
|
25
|
+
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",
|
|
26
|
+
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",
|
|
27
|
+
musical: " \u2669\u266A\u266B\u266C\u266D\u266E\u266F",
|
|
28
|
+
emoji: " \u2B1B\u{1F7EB}\u{1F7E5}\u{1F7E7}\u{1F7E8}\u{1F7E9}\u{1F7E6}\u{1F7EA}\u2B1C"
|
|
17
29
|
};
|
|
18
30
|
var ART_STYLE_PRESETS = {
|
|
19
31
|
classic: {
|
|
@@ -56,6 +68,27 @@ var ART_STYLE_PRESETS = {
|
|
|
56
68
|
renderMode: "ascii",
|
|
57
69
|
charset: CHARSETS.lines,
|
|
58
70
|
colorMode: "fullcolor"
|
|
71
|
+
},
|
|
72
|
+
braille: {
|
|
73
|
+
renderMode: "ascii",
|
|
74
|
+
charset: CHARSETS.braille,
|
|
75
|
+
colorMode: "fullcolor"
|
|
76
|
+
},
|
|
77
|
+
katakana: {
|
|
78
|
+
renderMode: "ascii",
|
|
79
|
+
charset: CHARSETS.katakana,
|
|
80
|
+
colorMode: "matrix"
|
|
81
|
+
},
|
|
82
|
+
musical: {
|
|
83
|
+
renderMode: "ascii",
|
|
84
|
+
charset: CHARSETS.musical,
|
|
85
|
+
colorMode: "accent",
|
|
86
|
+
accentColor: "#e040fb"
|
|
87
|
+
},
|
|
88
|
+
emoji: {
|
|
89
|
+
renderMode: "ascii",
|
|
90
|
+
charset: CHARSETS.emoji,
|
|
91
|
+
colorMode: "fullcolor"
|
|
59
92
|
}
|
|
60
93
|
};
|
|
61
94
|
var DEFAULT_OPTIONS = {
|
|
@@ -111,6 +144,18 @@ var HOVER_PRESETS = {
|
|
|
111
144
|
ice: {
|
|
112
145
|
label: "Ice",
|
|
113
146
|
options: { hoverStrength: 0.5, hoverEffect: "glow", hoverRadius: 0.15, hoverColor: "#60d5f7" }
|
|
147
|
+
},
|
|
148
|
+
gravity: {
|
|
149
|
+
label: "Gravity",
|
|
150
|
+
options: { hoverStrength: 0.7, hoverEffect: "attract", hoverRadius: 0.18, hoverColor: "#a5d6ff" }
|
|
151
|
+
},
|
|
152
|
+
shatter: {
|
|
153
|
+
label: "Shatter",
|
|
154
|
+
options: { hoverStrength: 0.8, hoverEffect: "shatter", hoverRadius: 0.14, hoverColor: "#ff6090" }
|
|
155
|
+
},
|
|
156
|
+
ghost: {
|
|
157
|
+
label: "Ghost",
|
|
158
|
+
options: { hoverStrength: 0.55, hoverEffect: "trail", hoverRadius: 0.2, hoverColor: "#b39ddb" }
|
|
114
159
|
}
|
|
115
160
|
};
|
|
116
161
|
|
|
@@ -130,14 +175,16 @@ function adjustLuminance(lum, brightness, contrast) {
|
|
|
130
175
|
}
|
|
131
176
|
function luminanceToChar(lum, charset, invert) {
|
|
132
177
|
const normalized = invert ? 1 - lum / 255 : lum / 255;
|
|
133
|
-
const
|
|
134
|
-
|
|
178
|
+
const chars = [...charset];
|
|
179
|
+
const index = Math.floor(normalized * (chars.length - 1));
|
|
180
|
+
return chars[Math.max(0, Math.min(chars.length - 1, index))];
|
|
135
181
|
}
|
|
136
182
|
function customTextToChar(lum, text, x, y, cols, invert) {
|
|
137
183
|
const normalized = invert ? 1 - lum / 255 : lum / 255;
|
|
138
184
|
if (normalized < 0.12) return " ";
|
|
185
|
+
const chars = [...text];
|
|
139
186
|
const pos = y * cols + x;
|
|
140
|
-
return
|
|
187
|
+
return chars[pos % chars.length];
|
|
141
188
|
}
|
|
142
189
|
var BAYER_4X4 = [
|
|
143
190
|
[0, 8, 2, 10],
|
|
@@ -285,6 +332,44 @@ function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
|
|
|
285
332
|
}
|
|
286
333
|
case "waveField":
|
|
287
334
|
return 1;
|
|
335
|
+
case "ripple": {
|
|
336
|
+
const cx2 = cols / 2;
|
|
337
|
+
const cy2 = rows / 2;
|
|
338
|
+
const dx2 = x - cx2;
|
|
339
|
+
const dy2 = y - cy2;
|
|
340
|
+
const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
|
341
|
+
const maxDist2 = Math.sqrt(cx2 * cx2 + cy2 * cy2);
|
|
342
|
+
const ripple = Math.sin(dist2 / maxDist2 * Math.PI * 10 - t * 5) * 0.5 + 0.5;
|
|
343
|
+
const fadeEdge = 1 - Math.min(1, dist2 / (maxDist2 * 1.1));
|
|
344
|
+
return 0.1 + 0.9 * ripple * (0.4 + fadeEdge * 0.6);
|
|
345
|
+
}
|
|
346
|
+
case "melt": {
|
|
347
|
+
const lagPhase = y / rows * Math.PI;
|
|
348
|
+
const drip = Math.sin(lagPhase - t * 1.8 + x * 0.15) * 0.5 + 0.5;
|
|
349
|
+
const gravity = Math.max(0, y / rows - 0.1) / 0.9;
|
|
350
|
+
return 0.05 + 0.95 * (drip * (1 - gravity * 0.6));
|
|
351
|
+
}
|
|
352
|
+
case "orbit": {
|
|
353
|
+
const ocx = cols / 2;
|
|
354
|
+
const ocy = rows / 2;
|
|
355
|
+
const odx = x - ocx;
|
|
356
|
+
const ody = y - ocy;
|
|
357
|
+
const oAngle = Math.atan2(ody, odx);
|
|
358
|
+
const oDist = Math.sqrt(odx * odx + ody * ody) / Math.sqrt(ocx * ocx + ocy * ocy);
|
|
359
|
+
const orbit = Math.sin(oAngle * 2 + oDist * 6 - t * 2.5) * 0.5 + 0.5;
|
|
360
|
+
return 0.1 + 0.9 * orbit;
|
|
361
|
+
}
|
|
362
|
+
case "cellular": {
|
|
363
|
+
const tick = Math.floor(t * 4);
|
|
364
|
+
const alive = (cx, cy) => {
|
|
365
|
+
const h = Math.sin(cx * 127.1 + cy * 311.7 + tick * 43758.5453) * 43758.5453;
|
|
366
|
+
return h - Math.floor(h) > 0.38 ? 1 : 0;
|
|
367
|
+
};
|
|
368
|
+
const self = alive(x, y);
|
|
369
|
+
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);
|
|
370
|
+
const nextAlive = self === 1 ? neighbours === 2 || neighbours === 3 ? 1 : 0 : neighbours === 3 ? 1 : 0;
|
|
371
|
+
return nextAlive === 1 ? 1 : 0.05;
|
|
372
|
+
}
|
|
288
373
|
default:
|
|
289
374
|
return 1;
|
|
290
375
|
}
|
|
@@ -344,6 +429,30 @@ function computeHoverEffect(nx, ny, hoverX, hoverY, hoverIntensity, strength, ce
|
|
|
344
429
|
glow = eased * strength * 0.2;
|
|
345
430
|
colorBlend = eased * strength * 0.7;
|
|
346
431
|
break;
|
|
432
|
+
case "attract": {
|
|
433
|
+
const angle3 = Math.atan2(dy, dx);
|
|
434
|
+
const pull = eased * eased * strength * 1;
|
|
435
|
+
offsetX = -Math.cos(angle3) * pull * cellW;
|
|
436
|
+
offsetY = -Math.sin(angle3) * pull * cellH;
|
|
437
|
+
glow = eased * strength * 0.3;
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
case "shatter": {
|
|
441
|
+
const angle4 = Math.atan2(dy, dx);
|
|
442
|
+
const jitter = Math.sin(dx * 43.7 + dy * 29.3) * 0.5;
|
|
443
|
+
const scatter = eased * strength * 1.4 * (0.7 + jitter * 0.3);
|
|
444
|
+
offsetX = Math.cos(angle4 + jitter) * scatter * cellW;
|
|
445
|
+
offsetY = Math.sin(angle4 + jitter) * scatter * cellH;
|
|
446
|
+
scale = Math.max(0.1, 1 - eased * strength * 0.6);
|
|
447
|
+
glow = eased * strength * 0.25;
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case "trail": {
|
|
451
|
+
colorBlend = eased * strength * 0.9;
|
|
452
|
+
glow = eased * strength * 0.6;
|
|
453
|
+
scale = 1 + eased * strength * 0.15;
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
347
456
|
}
|
|
348
457
|
_hoverResult.scale = scale;
|
|
349
458
|
_hoverResult.offsetX = offsetX;
|
|
@@ -398,7 +507,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
398
507
|
lineHeightRatio = 1.4,
|
|
399
508
|
chars = " .:-=+*#%@",
|
|
400
509
|
baseColor = null,
|
|
401
|
-
accentColor =
|
|
510
|
+
accentColor = void 0,
|
|
402
511
|
accentThreshold = 0.52,
|
|
403
512
|
mouseInfluence = 0.55,
|
|
404
513
|
mouseFalloff = 2.8,
|
|
@@ -408,6 +517,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
408
517
|
breathe = true,
|
|
409
518
|
lightMode = false
|
|
410
519
|
} = options;
|
|
520
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
411
521
|
const charW = fontSize * charAspect;
|
|
412
522
|
const lineH = fontSize * lineHeightRatio;
|
|
413
523
|
const cols = Math.ceil(width / charW);
|
|
@@ -415,8 +525,8 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
415
525
|
const mx = mousePos.x;
|
|
416
526
|
const my = mousePos.y;
|
|
417
527
|
let acR = 212, acG = 255, acB = 0;
|
|
418
|
-
|
|
419
|
-
const hex =
|
|
528
|
+
{
|
|
529
|
+
const hex = resolvedAccent.replace("#", "");
|
|
420
530
|
if (hex.length === 6) {
|
|
421
531
|
acR = parseInt(hex.slice(0, 2), 16);
|
|
422
532
|
acG = parseInt(hex.slice(2, 4), 16);
|
|
@@ -754,17 +864,18 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
754
864
|
const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
|
|
755
865
|
const useFastRect = fontSize < 6;
|
|
756
866
|
if (!useFastRect) {
|
|
757
|
-
|
|
867
|
+
const isEmoji = options.artStyle === "emoji";
|
|
868
|
+
ctx.font = isEmoji ? `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif` : `${fontSize}px "JetBrains Mono", monospace`;
|
|
758
869
|
ctx.textAlign = "center";
|
|
759
870
|
ctx.textBaseline = "middle";
|
|
760
871
|
}
|
|
761
872
|
let charWeights = null;
|
|
762
873
|
if (useFastRect) {
|
|
763
874
|
charWeights = {};
|
|
764
|
-
const
|
|
765
|
-
const csLen =
|
|
875
|
+
const csChars = [...options.charset];
|
|
876
|
+
const csLen = csChars.length;
|
|
766
877
|
for (let i = 0; i < csLen; i++) {
|
|
767
|
-
charWeights[
|
|
878
|
+
charWeights[csChars[i]] = Math.max(0.1, (i + 0.3) / csLen);
|
|
768
879
|
}
|
|
769
880
|
}
|
|
770
881
|
const baseTransform = !useFastRect ? ctx.getTransform() : null;
|
|
@@ -1026,13 +1137,14 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1026
1137
|
const {
|
|
1027
1138
|
fontSize = 13,
|
|
1028
1139
|
chars = "0123456789ABCDEF@#$&*+=/<>",
|
|
1029
|
-
accentColor =
|
|
1140
|
+
accentColor = void 0,
|
|
1030
1141
|
color,
|
|
1031
1142
|
speed = 1,
|
|
1032
1143
|
density = 0.55,
|
|
1033
1144
|
tailLength = 14,
|
|
1034
1145
|
lightMode = false
|
|
1035
1146
|
} = options;
|
|
1147
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1036
1148
|
const charW = fontSize * 0.62;
|
|
1037
1149
|
const lineH = fontSize * 1.4;
|
|
1038
1150
|
const cols = Math.ceil(width / charW);
|
|
@@ -1055,7 +1167,7 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1055
1167
|
}
|
|
1056
1168
|
}
|
|
1057
1169
|
let acR = 212, acG = 255, acB = 0;
|
|
1058
|
-
const ap = parseColor(
|
|
1170
|
+
const ap = parseColor(resolvedAccent);
|
|
1059
1171
|
if (ap) {
|
|
1060
1172
|
acR = ap.r;
|
|
1061
1173
|
acG = ap.g;
|
|
@@ -1091,12 +1203,13 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1091
1203
|
const {
|
|
1092
1204
|
fontSize = 14,
|
|
1093
1205
|
chars = " . \xB7 * + \xB0 \u2605",
|
|
1094
|
-
accentColor =
|
|
1206
|
+
accentColor = void 0,
|
|
1095
1207
|
color,
|
|
1096
1208
|
speed = 1,
|
|
1097
1209
|
count = 180,
|
|
1098
1210
|
lightMode = false
|
|
1099
1211
|
} = options;
|
|
1212
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1100
1213
|
ctx.clearRect(0, 0, width, height);
|
|
1101
1214
|
ctx.textBaseline = "middle";
|
|
1102
1215
|
ctx.textAlign = "center";
|
|
@@ -1118,7 +1231,7 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1118
1231
|
}
|
|
1119
1232
|
}
|
|
1120
1233
|
let acR = 212, acG = 255, acB = 0;
|
|
1121
|
-
const ap = parseColor(
|
|
1234
|
+
const ap = parseColor(resolvedAccent);
|
|
1122
1235
|
if (ap) {
|
|
1123
1236
|
acR = ap.r;
|
|
1124
1237
|
acG = ap.g;
|
|
@@ -1151,13 +1264,14 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1151
1264
|
const {
|
|
1152
1265
|
fontSize = 14,
|
|
1153
1266
|
chars = ". \xB7 \u25CB \u25CE \u25CF",
|
|
1154
|
-
accentColor =
|
|
1267
|
+
accentColor = void 0,
|
|
1155
1268
|
color,
|
|
1156
1269
|
rings = 5,
|
|
1157
1270
|
speed = 1,
|
|
1158
1271
|
sharpness = 4,
|
|
1159
1272
|
lightMode = false
|
|
1160
1273
|
} = options;
|
|
1274
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#007a5e" : "#00ffcc");
|
|
1161
1275
|
ctx.clearRect(0, 0, width, height);
|
|
1162
1276
|
ctx.textBaseline = "middle";
|
|
1163
1277
|
ctx.textAlign = "center";
|
|
@@ -1179,7 +1293,7 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1179
1293
|
}
|
|
1180
1294
|
}
|
|
1181
1295
|
let acR = 0, acG = 255, acB = 204;
|
|
1182
|
-
const ap = parseColor(
|
|
1296
|
+
const ap = parseColor(resolvedAccent);
|
|
1183
1297
|
if (ap) {
|
|
1184
1298
|
acR = ap.r;
|
|
1185
1299
|
acG = ap.g;
|
|
@@ -1224,7 +1338,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1224
1338
|
const {
|
|
1225
1339
|
fontSize = 14,
|
|
1226
1340
|
chars = " .\xB7:;=+*#%@\u2591\u2592\u2593",
|
|
1227
|
-
accentColor =
|
|
1341
|
+
accentColor = void 0,
|
|
1228
1342
|
color,
|
|
1229
1343
|
octaves = 4,
|
|
1230
1344
|
speed = 1,
|
|
@@ -1233,6 +1347,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1233
1347
|
mouseWarp = 0.3,
|
|
1234
1348
|
lightMode = false
|
|
1235
1349
|
} = options;
|
|
1350
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1236
1351
|
const charW = fontSize * 0.62;
|
|
1237
1352
|
const lineH = fontSize * 1.4;
|
|
1238
1353
|
const cols = Math.ceil(width / charW);
|
|
@@ -1255,7 +1370,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1255
1370
|
}
|
|
1256
1371
|
}
|
|
1257
1372
|
let acR = 212, acG = 255, acB = 0;
|
|
1258
|
-
const ap = parseColor(
|
|
1373
|
+
const ap = parseColor(resolvedAccent);
|
|
1259
1374
|
if (ap) {
|
|
1260
1375
|
acR = ap.r;
|
|
1261
1376
|
acG = ap.g;
|
|
@@ -1303,7 +1418,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1303
1418
|
const {
|
|
1304
1419
|
fontSize = 12,
|
|
1305
1420
|
chars = "\xB7-=+|/",
|
|
1306
|
-
accentColor =
|
|
1421
|
+
accentColor = void 0,
|
|
1307
1422
|
color,
|
|
1308
1423
|
bands = 3,
|
|
1309
1424
|
speed = 1,
|
|
@@ -1311,6 +1426,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1311
1426
|
glitch = true,
|
|
1312
1427
|
lightMode = false
|
|
1313
1428
|
} = options;
|
|
1429
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1314
1430
|
const charW = fontSize * 0.62;
|
|
1315
1431
|
const lineH = fontSize * 1.4;
|
|
1316
1432
|
const cols = Math.ceil(width / charW);
|
|
@@ -1333,7 +1449,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1333
1449
|
}
|
|
1334
1450
|
}
|
|
1335
1451
|
let acR = 212, acG = 255, acB = 0;
|
|
1336
|
-
const ap = parseColor(
|
|
1452
|
+
const ap = parseColor(resolvedAccent);
|
|
1337
1453
|
if (ap) {
|
|
1338
1454
|
acR = ap.r;
|
|
1339
1455
|
acG = ap.g;
|
|
@@ -1375,13 +1491,14 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1375
1491
|
fontSize = 14,
|
|
1376
1492
|
chars = " \xB7\u2219\u2022:;+=\u2261\u2263#@",
|
|
1377
1493
|
color,
|
|
1378
|
-
accentColor =
|
|
1494
|
+
accentColor = void 0,
|
|
1379
1495
|
speed = 1,
|
|
1380
1496
|
layers = 5,
|
|
1381
1497
|
softness = 1.2,
|
|
1382
1498
|
mouseRipple = 0.2,
|
|
1383
1499
|
lightMode = false
|
|
1384
1500
|
} = options;
|
|
1501
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1385
1502
|
const charW = fontSize * 0.62;
|
|
1386
1503
|
const lineH = fontSize * 1.4;
|
|
1387
1504
|
const cols = Math.ceil(width / charW);
|
|
@@ -1404,7 +1521,7 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1404
1521
|
}
|
|
1405
1522
|
}
|
|
1406
1523
|
let acR = 212, acG = 255, acB = 0;
|
|
1407
|
-
const ap = parseColor(
|
|
1524
|
+
const ap = parseColor(resolvedAccent);
|
|
1408
1525
|
if (ap) {
|
|
1409
1526
|
acR = ap.r;
|
|
1410
1527
|
acG = ap.g;
|
|
@@ -1460,12 +1577,13 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
|
|
|
1460
1577
|
const {
|
|
1461
1578
|
fontSize = 13,
|
|
1462
1579
|
color,
|
|
1463
|
-
accentColor =
|
|
1580
|
+
accentColor = void 0,
|
|
1464
1581
|
speed = 0.4,
|
|
1465
1582
|
layers = 4,
|
|
1466
1583
|
turbulence = 0.8,
|
|
1467
1584
|
lightMode = false
|
|
1468
1585
|
} = options;
|
|
1586
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1469
1587
|
const charW = fontSize * 0.62;
|
|
1470
1588
|
const lineH = fontSize * 1.4;
|
|
1471
1589
|
const cols = Math.ceil(width / charW);
|
|
@@ -1488,7 +1606,7 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
|
|
|
1488
1606
|
}
|
|
1489
1607
|
}
|
|
1490
1608
|
let acR = 212, acG = 255, acB = 0;
|
|
1491
|
-
const ap = parseColor(
|
|
1609
|
+
const ap = parseColor(resolvedAccent);
|
|
1492
1610
|
if (ap) {
|
|
1493
1611
|
acR = ap.r;
|
|
1494
1612
|
acG = ap.g;
|
|
@@ -1535,12 +1653,13 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1535
1653
|
fontSize = 13,
|
|
1536
1654
|
chars = " \xB7:;=+*#%@",
|
|
1537
1655
|
color,
|
|
1538
|
-
accentColor =
|
|
1656
|
+
accentColor = void 0,
|
|
1539
1657
|
speed = 1,
|
|
1540
1658
|
radius = 0.38,
|
|
1541
1659
|
swirl = 3,
|
|
1542
1660
|
lightMode = false
|
|
1543
1661
|
} = options;
|
|
1662
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1544
1663
|
const charW = fontSize * 0.62;
|
|
1545
1664
|
const lineH = fontSize * 1.4;
|
|
1546
1665
|
const cols = Math.ceil(width / charW);
|
|
@@ -1564,7 +1683,7 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1564
1683
|
}
|
|
1565
1684
|
}
|
|
1566
1685
|
let acR = 212, acG = 255, acB = 0;
|
|
1567
|
-
const ap = parseColor(
|
|
1686
|
+
const ap = parseColor(resolvedAccent);
|
|
1568
1687
|
if (ap) {
|
|
1569
1688
|
acR = ap.r;
|
|
1570
1689
|
acG = ap.g;
|
|
@@ -1608,11 +1727,12 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1608
1727
|
fontSize = 14,
|
|
1609
1728
|
chars = " \xB7\u2219\u2022:-=+*#",
|
|
1610
1729
|
color,
|
|
1611
|
-
accentColor =
|
|
1730
|
+
accentColor = void 0,
|
|
1612
1731
|
speed = 0.5,
|
|
1613
1732
|
harmonics = 3,
|
|
1614
1733
|
lightMode = false
|
|
1615
1734
|
} = options;
|
|
1735
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1616
1736
|
const charW = fontSize * 0.62;
|
|
1617
1737
|
const lineH = fontSize * 1.4;
|
|
1618
1738
|
const cols = Math.ceil(width / charW);
|
|
@@ -1635,7 +1755,7 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1635
1755
|
}
|
|
1636
1756
|
}
|
|
1637
1757
|
let acR = 212, acG = 255, acB = 0;
|
|
1638
|
-
const ap = parseColor(
|
|
1758
|
+
const ap = parseColor(resolvedAccent);
|
|
1639
1759
|
if (ap) {
|
|
1640
1760
|
acR = ap.r;
|
|
1641
1761
|
acG = ap.g;
|
|
@@ -1666,6 +1786,292 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1666
1786
|
}
|
|
1667
1787
|
}
|
|
1668
1788
|
|
|
1789
|
+
// src/backgrounds/fire.ts
|
|
1790
|
+
function renderFireBackground(ctx, width, height, time, options = {}) {
|
|
1791
|
+
const {
|
|
1792
|
+
fontSize = 13,
|
|
1793
|
+
chars = " .,:;i+xX#&@",
|
|
1794
|
+
color = "#ff4500",
|
|
1795
|
+
hotColor = "#ffe066",
|
|
1796
|
+
intensity = 0.85,
|
|
1797
|
+
wind = 0,
|
|
1798
|
+
speed = 1,
|
|
1799
|
+
lightMode = false
|
|
1800
|
+
} = options;
|
|
1801
|
+
const charW = fontSize * 0.62;
|
|
1802
|
+
const lineH = fontSize * 1.4;
|
|
1803
|
+
const cols = Math.ceil(width / charW);
|
|
1804
|
+
const rows = Math.ceil(height / lineH);
|
|
1805
|
+
const len = cols * rows;
|
|
1806
|
+
const key = "__fire_heat__";
|
|
1807
|
+
const canvasAny = ctx.canvas;
|
|
1808
|
+
let heat = canvasAny[key];
|
|
1809
|
+
if (!heat || heat.length !== len) {
|
|
1810
|
+
heat = new Float32Array(len);
|
|
1811
|
+
canvasAny[key] = heat;
|
|
1812
|
+
}
|
|
1813
|
+
const dt = 0.016 * speed;
|
|
1814
|
+
const coolingRate = 0.18 * dt;
|
|
1815
|
+
const windShift = wind * speed * 0.8;
|
|
1816
|
+
const baseRow = rows - 1;
|
|
1817
|
+
const t = time * speed;
|
|
1818
|
+
for (let c = 0; c < cols; c++) {
|
|
1819
|
+
const flicker = Math.sin(c * 0.31 + t * 4.1) * 0.5 + 0.5;
|
|
1820
|
+
const flicker2 = Math.sin(c * 0.73 - t * 2.7) * 0.5 + 0.5;
|
|
1821
|
+
const seed = (flicker * 0.6 + flicker2 * 0.4) * intensity;
|
|
1822
|
+
heat[baseRow * cols + c] = Math.min(1, seed + Math.random() * 0.15 * intensity);
|
|
1823
|
+
if (baseRow > 0) heat[(baseRow - 1) * cols + c] = Math.min(1, seed * 0.85 + Math.random() * 0.1 * intensity);
|
|
1824
|
+
}
|
|
1825
|
+
const newHeat = new Float32Array(len);
|
|
1826
|
+
for (let r = 0; r < rows - 2; r++) {
|
|
1827
|
+
for (let c = 0; c < cols; c++) {
|
|
1828
|
+
const below = heat[(r + 1) * cols + c];
|
|
1829
|
+
const below2 = heat[(r + 2) * cols + Math.max(0, Math.min(cols - 1, c + Math.round(windShift)))];
|
|
1830
|
+
const left = heat[(r + 1) * cols + Math.max(0, c - 1)];
|
|
1831
|
+
const right = heat[(r + 1) * cols + Math.min(cols - 1, c + 1)];
|
|
1832
|
+
const avg = below * 0.4 + below2 * 0.25 + left * 0.175 + right * 0.175;
|
|
1833
|
+
newHeat[r * cols + c] = Math.max(0, avg - coolingRate - Math.random() * 0.02 * speed);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
for (let c = 0; c < cols; c++) {
|
|
1837
|
+
newHeat[(rows - 1) * cols + c] = heat[(rows - 1) * cols + c];
|
|
1838
|
+
if (rows > 1) newHeat[(rows - 2) * cols + c] = heat[(rows - 2) * cols + c];
|
|
1839
|
+
}
|
|
1840
|
+
canvasAny[key] = newHeat;
|
|
1841
|
+
const cp = parseColor(color) ?? { r: 255, g: 69, b: 0 };
|
|
1842
|
+
const hp = parseColor(hotColor) ?? { r: 255, g: 224, b: 102 };
|
|
1843
|
+
ctx.clearRect(0, 0, width, height);
|
|
1844
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1845
|
+
ctx.textBaseline = "top";
|
|
1846
|
+
for (let r = 0; r < rows; r++) {
|
|
1847
|
+
for (let c = 0; c < cols; c++) {
|
|
1848
|
+
const v = newHeat[r * cols + c];
|
|
1849
|
+
if (v < 0.03) continue;
|
|
1850
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(v * chars.length));
|
|
1851
|
+
const ch = chars[charIdx];
|
|
1852
|
+
if (ch === " ") continue;
|
|
1853
|
+
const blend = Math.min(1, v * 1.2);
|
|
1854
|
+
const r2 = cp.r + (hp.r - cp.r) * blend | 0;
|
|
1855
|
+
const g2 = cp.g + (hp.g - cp.g) * blend | 0;
|
|
1856
|
+
const b2 = cp.b + (hp.b - cp.b) * blend | 0;
|
|
1857
|
+
const alpha = lightMode ? 1 - v * 0.3 : Math.min(1, v + 0.15);
|
|
1858
|
+
ctx.globalAlpha = alpha;
|
|
1859
|
+
ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
|
|
1860
|
+
ctx.fillText(ch, c * charW, r * lineH);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
ctx.globalAlpha = 1;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// src/backgrounds/dna.ts
|
|
1867
|
+
function renderDnaBackground(ctx, width, height, time, options = {}) {
|
|
1868
|
+
const {
|
|
1869
|
+
fontSize = 13,
|
|
1870
|
+
baseChars = "ATCG",
|
|
1871
|
+
bridgeChars = "-=\u2261",
|
|
1872
|
+
color = "#00e5ff",
|
|
1873
|
+
color2 = "#ff4081",
|
|
1874
|
+
bridgeColor = "#88ffcc",
|
|
1875
|
+
speed = 1,
|
|
1876
|
+
helixCount,
|
|
1877
|
+
lightMode = false
|
|
1878
|
+
} = options;
|
|
1879
|
+
const charW = fontSize * 0.62;
|
|
1880
|
+
const lineH = fontSize * 1.4;
|
|
1881
|
+
const cols = Math.ceil(width / charW);
|
|
1882
|
+
const rows = Math.ceil(height / lineH);
|
|
1883
|
+
const numHelix = helixCount ?? Math.max(1, Math.floor(width / 80));
|
|
1884
|
+
const sectionW = cols / numHelix;
|
|
1885
|
+
const cp = parseColor(color) ?? { r: 0, g: 229, b: 255 };
|
|
1886
|
+
const cp2 = parseColor(color2) ?? { r: 255, g: 64, b: 129 };
|
|
1887
|
+
const bp = parseColor(bridgeColor) ?? { r: 136, g: 255, b: 204 };
|
|
1888
|
+
ctx.clearRect(0, 0, width, height);
|
|
1889
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1890
|
+
ctx.textBaseline = "top";
|
|
1891
|
+
const t = time * speed;
|
|
1892
|
+
const amplitude = sectionW * 0.35;
|
|
1893
|
+
for (let h = 0; h < numHelix; h++) {
|
|
1894
|
+
const centerCol = sectionW * (h + 0.5);
|
|
1895
|
+
for (let r = 0; r < rows; r++) {
|
|
1896
|
+
const phase = r / rows * Math.PI * 6 - t * 1.8;
|
|
1897
|
+
const strand1ColF = centerCol + Math.sin(phase) * amplitude;
|
|
1898
|
+
const strand2ColF = centerCol + Math.sin(phase + Math.PI) * amplitude;
|
|
1899
|
+
const strand1Col = Math.round(strand1ColF);
|
|
1900
|
+
const strand2Col = Math.round(strand2ColF);
|
|
1901
|
+
if (strand1Col < 0 || strand1Col >= cols) continue;
|
|
1902
|
+
const baseSeed1 = hash2(h * 31 + r * 7, 3);
|
|
1903
|
+
const ch1 = baseChars[Math.floor(baseSeed1 * baseChars.length)];
|
|
1904
|
+
const depth1 = (Math.sin(phase) + 1) * 0.5;
|
|
1905
|
+
const depth2 = (Math.sin(phase + Math.PI) + 1) * 0.5;
|
|
1906
|
+
ctx.globalAlpha = 0.35 + depth1 * 0.65;
|
|
1907
|
+
ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
|
|
1908
|
+
ctx.fillText(ch1, strand1Col * charW, r * lineH);
|
|
1909
|
+
if (strand2Col >= 0 && strand2Col < cols) {
|
|
1910
|
+
const baseSeed2 = hash2(h * 53 + r * 11, 7);
|
|
1911
|
+
const ch2 = baseChars[Math.floor(baseSeed2 * baseChars.length)];
|
|
1912
|
+
ctx.globalAlpha = 0.35 + depth2 * 0.65;
|
|
1913
|
+
ctx.fillStyle = `rgb(${cp2.r},${cp2.g},${cp2.b})`;
|
|
1914
|
+
ctx.fillText(ch2, strand2Col * charW, r * lineH);
|
|
1915
|
+
}
|
|
1916
|
+
const bridgeInterval = 3;
|
|
1917
|
+
if (r % bridgeInterval === 0) {
|
|
1918
|
+
const minC = Math.min(strand1Col, strand2Col);
|
|
1919
|
+
const maxC = Math.max(strand1Col, strand2Col);
|
|
1920
|
+
const bridgeLen = maxC - minC;
|
|
1921
|
+
if (bridgeLen > 1) {
|
|
1922
|
+
const bSeed = hash2(r * 17 + h * 43, 5);
|
|
1923
|
+
const bCh = bridgeChars[Math.floor(bSeed * bridgeChars.length)];
|
|
1924
|
+
const midBridgeAlpha = (depth1 + depth2) * 0.25 + 0.2;
|
|
1925
|
+
ctx.globalAlpha = midBridgeAlpha;
|
|
1926
|
+
ctx.fillStyle = `rgb(${bp.r},${bp.g},${bp.b})`;
|
|
1927
|
+
for (let bc = minC + 1; bc < maxC; bc++) {
|
|
1928
|
+
ctx.fillText(bCh, bc * charW, r * lineH);
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
ctx.globalAlpha = 1;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
// src/backgrounds/terrain.ts
|
|
1938
|
+
function renderTerrainBackground(ctx, width, height, time, options = {}) {
|
|
1939
|
+
const {
|
|
1940
|
+
fontSize = 13,
|
|
1941
|
+
chars = " .,:;+*#@",
|
|
1942
|
+
color = "#4caf50",
|
|
1943
|
+
skyColor = "#1a237e",
|
|
1944
|
+
peakColor = "#e0e0e0",
|
|
1945
|
+
speed = 1,
|
|
1946
|
+
roughness = 0.55,
|
|
1947
|
+
heightScale = 0.55,
|
|
1948
|
+
stars = true,
|
|
1949
|
+
lightMode = false
|
|
1950
|
+
} = options;
|
|
1951
|
+
const charW = fontSize * 0.62;
|
|
1952
|
+
const lineH = fontSize * 1.4;
|
|
1953
|
+
const cols = Math.ceil(width / charW);
|
|
1954
|
+
const rows = Math.ceil(height / lineH);
|
|
1955
|
+
const cp = parseColor(color) ?? { r: 76, g: 175, b: 80 };
|
|
1956
|
+
const sky = parseColor(skyColor) ?? { r: 26, g: 35, b: 126 };
|
|
1957
|
+
const peak = parseColor(peakColor) ?? { r: 224, g: 224, b: 224 };
|
|
1958
|
+
ctx.clearRect(0, 0, width, height);
|
|
1959
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1960
|
+
ctx.textBaseline = "top";
|
|
1961
|
+
const scroll = time * speed * 0.4;
|
|
1962
|
+
const terrainRow = new Array(cols);
|
|
1963
|
+
for (let c = 0; c < cols; c++) {
|
|
1964
|
+
const nx = (c / cols + scroll) * roughness * 3;
|
|
1965
|
+
const h = (fbm(nx, 0.5) * 0.5 + 0.5) * heightScale;
|
|
1966
|
+
terrainRow[c] = Math.floor(h * rows);
|
|
1967
|
+
}
|
|
1968
|
+
for (let r = 0; r < rows; r++) {
|
|
1969
|
+
for (let c = 0; c < cols; c++) {
|
|
1970
|
+
const terrainStart = rows - 1 - terrainRow[c];
|
|
1971
|
+
const isGround = r >= terrainStart;
|
|
1972
|
+
const isNearPeak = r === terrainStart;
|
|
1973
|
+
if (!isGround) {
|
|
1974
|
+
if (stars) {
|
|
1975
|
+
const starSeed = hash2(c * 7 + Math.floor(scroll * 0.3), r * 13);
|
|
1976
|
+
if (starSeed > 0.97) {
|
|
1977
|
+
const twinkle = Math.sin(time * 2 + starSeed * 100) * 0.3 + 0.7;
|
|
1978
|
+
ctx.globalAlpha = twinkle * 0.5;
|
|
1979
|
+
ctx.fillStyle = `rgb(${sky.r + 60},${sky.g + 60},${sky.b + 80})`;
|
|
1980
|
+
ctx.fillText("\xB7", c * charW, r * lineH);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
const depth = (r - terrainStart) / Math.max(1, terrainRow[c]);
|
|
1986
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(depth * chars.length));
|
|
1987
|
+
const ch = chars[charIdx];
|
|
1988
|
+
if (ch === " " && !isNearPeak) continue;
|
|
1989
|
+
const blendPeak = isNearPeak ? 1 : Math.max(0, 1 - depth * 4);
|
|
1990
|
+
const r2 = cp.r + (peak.r - cp.r) * blendPeak | 0;
|
|
1991
|
+
const g2 = cp.g + (peak.g - cp.g) * blendPeak | 0;
|
|
1992
|
+
const b2 = cp.b + (peak.b - cp.b) * blendPeak | 0;
|
|
1993
|
+
ctx.globalAlpha = 0.5 + depth * 0.5;
|
|
1994
|
+
ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
|
|
1995
|
+
ctx.fillText(isNearPeak ? chars[chars.length - 1] : ch, c * charW, r * lineH);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
ctx.globalAlpha = 1;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// src/backgrounds/circuit.ts
|
|
2002
|
+
var EAST = 1;
|
|
2003
|
+
var WEST = 2;
|
|
2004
|
+
var NORTH = 4;
|
|
2005
|
+
var SOUTH = 8;
|
|
2006
|
+
var DIR_CHARS = {
|
|
2007
|
+
[EAST | WEST]: "\u2500",
|
|
2008
|
+
[NORTH | SOUTH]: "\u2502",
|
|
2009
|
+
[EAST | SOUTH]: "\u250C",
|
|
2010
|
+
[WEST | SOUTH]: "\u2510",
|
|
2011
|
+
[EAST | NORTH]: "\u2514",
|
|
2012
|
+
[WEST | NORTH]: "\u2518",
|
|
2013
|
+
[EAST | WEST | SOUTH]: "\u252C",
|
|
2014
|
+
[EAST | WEST | NORTH]: "\u2534",
|
|
2015
|
+
[NORTH | SOUTH | EAST]: "\u251C",
|
|
2016
|
+
[NORTH | SOUTH | WEST]: "\u2524",
|
|
2017
|
+
[EAST | WEST | NORTH | SOUTH]: "\u253C",
|
|
2018
|
+
[EAST]: "\u2576",
|
|
2019
|
+
[WEST]: "\u2574",
|
|
2020
|
+
[NORTH]: "\u2575",
|
|
2021
|
+
[SOUTH]: "\u2577"
|
|
2022
|
+
};
|
|
2023
|
+
function renderCircuitBackground(ctx, width, height, time, options = {}) {
|
|
2024
|
+
const {
|
|
2025
|
+
fontSize = 13,
|
|
2026
|
+
pulseColor = "#ffffff",
|
|
2027
|
+
color = "#00ff88",
|
|
2028
|
+
density = 0.38,
|
|
2029
|
+
speed = 1,
|
|
2030
|
+
lightMode = false
|
|
2031
|
+
} = options;
|
|
2032
|
+
const charW = fontSize * 0.62;
|
|
2033
|
+
const lineH = fontSize * 1.4;
|
|
2034
|
+
const cols = Math.ceil(width / charW);
|
|
2035
|
+
const rows = Math.ceil(height / lineH);
|
|
2036
|
+
const cp = parseColor(color) ?? { r: 0, g: 255, b: 136 };
|
|
2037
|
+
const pp = parseColor(pulseColor) ?? { r: 255, g: 255, b: 255 };
|
|
2038
|
+
const getConnections = (c, r) => {
|
|
2039
|
+
if (hash2(c * 17 + 1, r * 7 + 2) > density) return 0;
|
|
2040
|
+
let mask = 0;
|
|
2041
|
+
if (c + 1 < cols && hash2(c * 17 + 1, r * 7 + 2) > 0.15) mask |= EAST;
|
|
2042
|
+
if (c - 1 >= 0 && hash2((c - 1) * 17 + 1, r * 7 + 2) > 0.15) mask |= WEST;
|
|
2043
|
+
if (r + 1 < rows && hash2(c * 17 + 1, (r + 1) * 7 + 2) > 0.15) mask |= SOUTH;
|
|
2044
|
+
if (r - 1 >= 0 && hash2(c * 17 + 1, (r - 1) * 7 + 2) > 0.15) mask |= NORTH;
|
|
2045
|
+
return mask;
|
|
2046
|
+
};
|
|
2047
|
+
ctx.clearRect(0, 0, width, height);
|
|
2048
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
2049
|
+
ctx.textBaseline = "top";
|
|
2050
|
+
const t = time * speed;
|
|
2051
|
+
for (let r = 0; r < rows; r++) {
|
|
2052
|
+
for (let c = 0; c < cols; c++) {
|
|
2053
|
+
const mask = getConnections(c, r);
|
|
2054
|
+
if (mask === 0) continue;
|
|
2055
|
+
const ch = DIR_CHARS[mask] ?? "\xB7";
|
|
2056
|
+
const pulsePosH = (t * 8 + hash2(c, r * 23) * 40) % cols;
|
|
2057
|
+
const pulsePosV = (t * 6 + hash2(r * 17, c * 11) * 40) % rows;
|
|
2058
|
+
const nearH = (mask & EAST || mask & WEST) && Math.abs(c - pulsePosH) < 1.5;
|
|
2059
|
+
const nearV = (mask & NORTH || mask & SOUTH) && Math.abs(r - pulsePosV) < 1.5;
|
|
2060
|
+
const isPulse = nearH || nearV;
|
|
2061
|
+
const baseAlpha = 0.25 + hash2(c * 3, r * 5) * 0.35;
|
|
2062
|
+
if (isPulse) {
|
|
2063
|
+
ctx.globalAlpha = 0.95;
|
|
2064
|
+
ctx.fillStyle = `rgb(${pp.r},${pp.g},${pp.b})`;
|
|
2065
|
+
} else {
|
|
2066
|
+
ctx.globalAlpha = baseAlpha;
|
|
2067
|
+
ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
|
|
2068
|
+
}
|
|
2069
|
+
ctx.fillText(ch, c * charW, r * lineH);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
ctx.globalAlpha = 1;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
1669
2075
|
// src/backgrounds/index.ts
|
|
1670
2076
|
function _parseColor(c) {
|
|
1671
2077
|
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
@@ -1770,6 +2176,24 @@ function asciiBackground(target, options = {}) {
|
|
|
1770
2176
|
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1771
2177
|
color: color ?? renderOpts.color
|
|
1772
2178
|
});
|
|
2179
|
+
const buildFireOpts = () => ({
|
|
2180
|
+
...renderOpts,
|
|
2181
|
+
color: color ?? renderOpts.color
|
|
2182
|
+
});
|
|
2183
|
+
const buildDnaOpts = () => ({
|
|
2184
|
+
...renderOpts,
|
|
2185
|
+
color: color ?? renderOpts.color
|
|
2186
|
+
});
|
|
2187
|
+
const buildTerrainOpts = () => ({
|
|
2188
|
+
...renderOpts,
|
|
2189
|
+
color: color ?? renderOpts.color,
|
|
2190
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
|
|
2191
|
+
});
|
|
2192
|
+
const buildCircuitOpts = () => ({
|
|
2193
|
+
...renderOpts,
|
|
2194
|
+
color: color ?? renderOpts.color,
|
|
2195
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
|
|
2196
|
+
});
|
|
1773
2197
|
const optsRef = { current: buildWaveOpts() };
|
|
1774
2198
|
const rebuildOpts = () => {
|
|
1775
2199
|
if (type === "rain") optsRef.current = buildRainOpts();
|
|
@@ -1781,6 +2205,10 @@ function asciiBackground(target, options = {}) {
|
|
|
1781
2205
|
else if (type === "silk") optsRef.current = buildSilkOpts();
|
|
1782
2206
|
else if (type === "void") optsRef.current = buildVoidOpts();
|
|
1783
2207
|
else if (type === "morph") optsRef.current = buildMorphOpts();
|
|
2208
|
+
else if (type === "fire") optsRef.current = buildFireOpts();
|
|
2209
|
+
else if (type === "dna") optsRef.current = buildDnaOpts();
|
|
2210
|
+
else if (type === "terrain") optsRef.current = buildTerrainOpts();
|
|
2211
|
+
else if (type === "circuit") optsRef.current = buildCircuitOpts();
|
|
1784
2212
|
else optsRef.current = buildWaveOpts();
|
|
1785
2213
|
};
|
|
1786
2214
|
rebuildOpts();
|
|
@@ -1827,6 +2255,14 @@ function asciiBackground(target, options = {}) {
|
|
|
1827
2255
|
renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1828
2256
|
} else if (type === "morph") {
|
|
1829
2257
|
renderMorphBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2258
|
+
} else if (type === "fire") {
|
|
2259
|
+
renderFireBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2260
|
+
} else if (type === "dna") {
|
|
2261
|
+
renderDnaBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2262
|
+
} else if (type === "terrain") {
|
|
2263
|
+
renderTerrainBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2264
|
+
} else if (type === "circuit") {
|
|
2265
|
+
renderCircuitBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
1830
2266
|
} else {
|
|
1831
2267
|
renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1832
2268
|
}
|
|
@@ -1847,6 +2283,178 @@ function asciiBackground(target, options = {}) {
|
|
|
1847
2283
|
}
|
|
1848
2284
|
var mountWaveBackground = asciiBackground;
|
|
1849
2285
|
|
|
2286
|
+
// src/core/ascii-text.ts
|
|
2287
|
+
function asciiText(source, options = {}, targetWidth, targetHeight) {
|
|
2288
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2289
|
+
const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
|
|
2290
|
+
if (!frame.length || cols === 0) return "";
|
|
2291
|
+
const lines = [];
|
|
2292
|
+
for (const row of frame) {
|
|
2293
|
+
lines.push(row.map((cell) => cell.char).join(""));
|
|
2294
|
+
}
|
|
2295
|
+
return lines.join("\n");
|
|
2296
|
+
}
|
|
2297
|
+
function asciiTextAnsi(source, options = {}, targetWidth, targetHeight) {
|
|
2298
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2299
|
+
const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
|
|
2300
|
+
if (!frame.length || cols === 0) return "";
|
|
2301
|
+
const RESET = "\x1B[0m";
|
|
2302
|
+
const lines = [];
|
|
2303
|
+
for (const row of frame) {
|
|
2304
|
+
let line = "";
|
|
2305
|
+
for (const cell of row) {
|
|
2306
|
+
if (cell.char === " " || cell.a < 10) {
|
|
2307
|
+
line += " ";
|
|
2308
|
+
} else {
|
|
2309
|
+
line += `\x1B[38;2;${cell.r};${cell.g};${cell.b}m${cell.char}${RESET}`;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
lines.push(line);
|
|
2313
|
+
}
|
|
2314
|
+
return lines.join("\n");
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// src/core/record.ts
|
|
2318
|
+
function createRecorder(canvas, options = {}) {
|
|
2319
|
+
const {
|
|
2320
|
+
fps = 15,
|
|
2321
|
+
maxFrames = 120,
|
|
2322
|
+
format = "gif",
|
|
2323
|
+
quality = 10,
|
|
2324
|
+
scale = 1
|
|
2325
|
+
} = options;
|
|
2326
|
+
const interval = 1e3 / fps;
|
|
2327
|
+
let recording = false;
|
|
2328
|
+
let timerId = -1;
|
|
2329
|
+
const blobs = [];
|
|
2330
|
+
const captureFrame = () => {
|
|
2331
|
+
if (!recording || blobs.length >= maxFrames) return;
|
|
2332
|
+
let src = canvas;
|
|
2333
|
+
if (scale !== 1) {
|
|
2334
|
+
const off = document.createElement("canvas");
|
|
2335
|
+
off.width = Math.round(canvas.width * scale);
|
|
2336
|
+
off.height = Math.round(canvas.height * scale);
|
|
2337
|
+
const offCtx = off.getContext("2d");
|
|
2338
|
+
offCtx.drawImage(canvas, 0, 0, off.width, off.height);
|
|
2339
|
+
src = off;
|
|
2340
|
+
}
|
|
2341
|
+
blobs.push(src.toDataURL("image/png"));
|
|
2342
|
+
};
|
|
2343
|
+
const encodeGif = async (frames) => {
|
|
2344
|
+
return new Promise((resolve, reject) => {
|
|
2345
|
+
if (typeof GIF === "undefined") {
|
|
2346
|
+
reject(new Error('[asciify recorder] gif.js not found. Add <script src="/gif.worker.js"> to your page.'));
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
const gif = new GIF({
|
|
2350
|
+
workers: 2,
|
|
2351
|
+
quality,
|
|
2352
|
+
workerScript: "/gif.worker.js"
|
|
2353
|
+
});
|
|
2354
|
+
let loaded = 0;
|
|
2355
|
+
const total = frames.length;
|
|
2356
|
+
frames.forEach((dataUrl) => {
|
|
2357
|
+
const img = new Image();
|
|
2358
|
+
img.onload = () => {
|
|
2359
|
+
gif.addFrame(img, { delay: interval, copy: true });
|
|
2360
|
+
loaded++;
|
|
2361
|
+
if (loaded === total) gif.render();
|
|
2362
|
+
};
|
|
2363
|
+
img.src = dataUrl;
|
|
2364
|
+
});
|
|
2365
|
+
gif.on("finished", (blob) => {
|
|
2366
|
+
const reader = new FileReader();
|
|
2367
|
+
reader.onload = () => resolve(reader.result);
|
|
2368
|
+
reader.readAsDataURL(blob);
|
|
2369
|
+
});
|
|
2370
|
+
gif.on("error", reject);
|
|
2371
|
+
});
|
|
2372
|
+
};
|
|
2373
|
+
const encodeWebP = async (frames, _fps) => {
|
|
2374
|
+
if (typeof MediaRecorder === "undefined") {
|
|
2375
|
+
throw new Error("[asciify recorder] MediaRecorder not available in this browser.");
|
|
2376
|
+
}
|
|
2377
|
+
const off = document.createElement("canvas");
|
|
2378
|
+
if (frames.length === 0) return "";
|
|
2379
|
+
const probe = new Image();
|
|
2380
|
+
await new Promise((res) => {
|
|
2381
|
+
probe.onload = () => res();
|
|
2382
|
+
probe.src = frames[0];
|
|
2383
|
+
});
|
|
2384
|
+
off.width = probe.naturalWidth;
|
|
2385
|
+
off.height = probe.naturalHeight;
|
|
2386
|
+
const offCtx = off.getContext("2d");
|
|
2387
|
+
const stream = off.captureStream(_fps);
|
|
2388
|
+
const recorder = new MediaRecorder(stream, { mimeType: "video/webm;codecs=vp9" });
|
|
2389
|
+
const chunks = [];
|
|
2390
|
+
recorder.ondataavailable = (e) => chunks.push(e.data);
|
|
2391
|
+
return new Promise((resolve, reject) => {
|
|
2392
|
+
recorder.onstop = () => {
|
|
2393
|
+
const blob = new Blob(chunks, { type: "video/webm" });
|
|
2394
|
+
const reader = new FileReader();
|
|
2395
|
+
reader.onload = () => resolve(reader.result);
|
|
2396
|
+
reader.readAsDataURL(blob);
|
|
2397
|
+
};
|
|
2398
|
+
recorder.onerror = reject;
|
|
2399
|
+
recorder.start();
|
|
2400
|
+
let idx = 0;
|
|
2401
|
+
const drawNext = () => {
|
|
2402
|
+
if (idx >= frames.length) {
|
|
2403
|
+
recorder.stop();
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
const img = new Image();
|
|
2407
|
+
img.onload = () => {
|
|
2408
|
+
offCtx.drawImage(img, 0, 0);
|
|
2409
|
+
idx++;
|
|
2410
|
+
setTimeout(drawNext, interval);
|
|
2411
|
+
};
|
|
2412
|
+
img.src = frames[idx];
|
|
2413
|
+
};
|
|
2414
|
+
drawNext();
|
|
2415
|
+
});
|
|
2416
|
+
};
|
|
2417
|
+
return {
|
|
2418
|
+
get isRecording() {
|
|
2419
|
+
return recording;
|
|
2420
|
+
},
|
|
2421
|
+
get frameCount() {
|
|
2422
|
+
return blobs.length;
|
|
2423
|
+
},
|
|
2424
|
+
start() {
|
|
2425
|
+
if (recording) return;
|
|
2426
|
+
blobs.length = 0;
|
|
2427
|
+
recording = true;
|
|
2428
|
+
timerId = window.setInterval(captureFrame, interval);
|
|
2429
|
+
},
|
|
2430
|
+
async stop() {
|
|
2431
|
+
if (!recording) return "";
|
|
2432
|
+
recording = false;
|
|
2433
|
+
clearInterval(timerId);
|
|
2434
|
+
const frames = blobs.slice();
|
|
2435
|
+
if (format === "png-sequence") {
|
|
2436
|
+
return JSON.stringify(frames);
|
|
2437
|
+
}
|
|
2438
|
+
if (format === "webp") {
|
|
2439
|
+
return encodeWebP(frames, fps);
|
|
2440
|
+
}
|
|
2441
|
+
return encodeGif(frames);
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
async function recordAndDownload(canvas, durationMs, options = {}) {
|
|
2446
|
+
const { filename = "asciify-recording", ...recOpts } = options;
|
|
2447
|
+
const recorder = createRecorder(canvas, recOpts);
|
|
2448
|
+
recorder.start();
|
|
2449
|
+
await new Promise((res) => setTimeout(res, durationMs));
|
|
2450
|
+
const dataUrl = await recorder.stop();
|
|
2451
|
+
const ext = options.format === "webp" ? "webm" : options.format === "png-sequence" ? "json" : "gif";
|
|
2452
|
+
const a = document.createElement("a");
|
|
2453
|
+
a.href = dataUrl;
|
|
2454
|
+
a.download = `${filename}.${ext}`;
|
|
2455
|
+
a.click();
|
|
2456
|
+
}
|
|
2457
|
+
|
|
1850
2458
|
// src/webgl-engine.ts
|
|
1851
2459
|
var VERT_SRC = (
|
|
1852
2460
|
/* glsl */
|
|
@@ -2109,8 +2717,8 @@ function makeTexture(gl, filter = gl.NEAREST) {
|
|
|
2109
2717
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2110
2718
|
return t;
|
|
2111
2719
|
}
|
|
2112
|
-
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField"];
|
|
2113
|
-
var HOVER_EFFECTS = ["spotlight", "magnify", "repel", "glow", "colorShift"];
|
|
2720
|
+
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField", "ripple", "melt", "orbit", "cellular"];
|
|
2721
|
+
var HOVER_EFFECTS = ["spotlight", "magnify", "repel", "glow", "colorShift", "attract", "shatter", "trail"];
|
|
2114
2722
|
var COLOR_MODES = ["grayscale", "fullcolor", "matrix", "accent"];
|
|
2115
2723
|
function tryCreateWebGLRenderer(canvas) {
|
|
2116
2724
|
const glOrNull = canvas.getContext("webgl2", {
|
|
@@ -2299,16 +2907,24 @@ exports.ART_STYLE_PRESETS = ART_STYLE_PRESETS;
|
|
|
2299
2907
|
exports.CHARSETS = CHARSETS;
|
|
2300
2908
|
exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
|
|
2301
2909
|
exports.HOVER_PRESETS = HOVER_PRESETS;
|
|
2910
|
+
exports.PALETTE_THEMES = PALETTE_THEMES;
|
|
2302
2911
|
exports.asciiBackground = asciiBackground;
|
|
2912
|
+
exports.asciiText = asciiText;
|
|
2913
|
+
exports.asciiTextAnsi = asciiTextAnsi;
|
|
2303
2914
|
exports.asciify = asciify;
|
|
2304
2915
|
exports.asciifyGif = asciifyGif;
|
|
2305
2916
|
exports.asciifyVideo = asciifyVideo;
|
|
2917
|
+
exports.createRecorder = createRecorder;
|
|
2306
2918
|
exports.generateAnimatedEmbedCode = generateAnimatedEmbedCode;
|
|
2307
2919
|
exports.generateEmbedCode = generateEmbedCode;
|
|
2308
2920
|
exports.gifToAsciiFrames = gifToAsciiFrames;
|
|
2309
2921
|
exports.imageToAsciiFrame = imageToAsciiFrame;
|
|
2310
2922
|
exports.mountWaveBackground = mountWaveBackground;
|
|
2923
|
+
exports.recordAndDownload = recordAndDownload;
|
|
2311
2924
|
exports.renderAuroraBackground = renderAuroraBackground;
|
|
2925
|
+
exports.renderCircuitBackground = renderCircuitBackground;
|
|
2926
|
+
exports.renderDnaBackground = renderDnaBackground;
|
|
2927
|
+
exports.renderFireBackground = renderFireBackground;
|
|
2312
2928
|
exports.renderFrameToCanvas = renderFrameToCanvas;
|
|
2313
2929
|
exports.renderGridBackground = renderGridBackground;
|
|
2314
2930
|
exports.renderMorphBackground = renderMorphBackground;
|
|
@@ -2317,6 +2933,7 @@ exports.renderPulseBackground = renderPulseBackground;
|
|
|
2317
2933
|
exports.renderRainBackground = renderRainBackground;
|
|
2318
2934
|
exports.renderSilkBackground = renderSilkBackground;
|
|
2319
2935
|
exports.renderStarsBackground = renderStarsBackground;
|
|
2936
|
+
exports.renderTerrainBackground = renderTerrainBackground;
|
|
2320
2937
|
exports.renderVoidBackground = renderVoidBackground;
|
|
2321
2938
|
exports.renderWaveBackground = renderWaveBackground;
|
|
2322
2939
|
exports.tryCreateWebGLRenderer = tryCreateWebGLRenderer;
|