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.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;
|
|
@@ -396,7 +505,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
396
505
|
lineHeightRatio = 1.4,
|
|
397
506
|
chars = " .:-=+*#%@",
|
|
398
507
|
baseColor = null,
|
|
399
|
-
accentColor =
|
|
508
|
+
accentColor = void 0,
|
|
400
509
|
accentThreshold = 0.52,
|
|
401
510
|
mouseInfluence = 0.55,
|
|
402
511
|
mouseFalloff = 2.8,
|
|
@@ -406,6 +515,7 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
406
515
|
breathe = true,
|
|
407
516
|
lightMode = false
|
|
408
517
|
} = options;
|
|
518
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
409
519
|
const charW = fontSize * charAspect;
|
|
410
520
|
const lineH = fontSize * lineHeightRatio;
|
|
411
521
|
const cols = Math.ceil(width / charW);
|
|
@@ -413,8 +523,8 @@ function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
413
523
|
const mx = mousePos.x;
|
|
414
524
|
const my = mousePos.y;
|
|
415
525
|
let acR = 212, acG = 255, acB = 0;
|
|
416
|
-
|
|
417
|
-
const hex =
|
|
526
|
+
{
|
|
527
|
+
const hex = resolvedAccent.replace("#", "");
|
|
418
528
|
if (hex.length === 6) {
|
|
419
529
|
acR = parseInt(hex.slice(0, 2), 16);
|
|
420
530
|
acG = parseInt(hex.slice(2, 4), 16);
|
|
@@ -752,17 +862,18 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
752
862
|
const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
|
|
753
863
|
const useFastRect = fontSize < 6;
|
|
754
864
|
if (!useFastRect) {
|
|
755
|
-
|
|
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`;
|
|
756
867
|
ctx.textAlign = "center";
|
|
757
868
|
ctx.textBaseline = "middle";
|
|
758
869
|
}
|
|
759
870
|
let charWeights = null;
|
|
760
871
|
if (useFastRect) {
|
|
761
872
|
charWeights = {};
|
|
762
|
-
const
|
|
763
|
-
const csLen =
|
|
873
|
+
const csChars = [...options.charset];
|
|
874
|
+
const csLen = csChars.length;
|
|
764
875
|
for (let i = 0; i < csLen; i++) {
|
|
765
|
-
charWeights[
|
|
876
|
+
charWeights[csChars[i]] = Math.max(0.1, (i + 0.3) / csLen);
|
|
766
877
|
}
|
|
767
878
|
}
|
|
768
879
|
const baseTransform = !useFastRect ? ctx.getTransform() : null;
|
|
@@ -1024,13 +1135,14 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1024
1135
|
const {
|
|
1025
1136
|
fontSize = 13,
|
|
1026
1137
|
chars = "0123456789ABCDEF@#$&*+=/<>",
|
|
1027
|
-
accentColor =
|
|
1138
|
+
accentColor = void 0,
|
|
1028
1139
|
color,
|
|
1029
1140
|
speed = 1,
|
|
1030
1141
|
density = 0.55,
|
|
1031
1142
|
tailLength = 14,
|
|
1032
1143
|
lightMode = false
|
|
1033
1144
|
} = options;
|
|
1145
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1034
1146
|
const charW = fontSize * 0.62;
|
|
1035
1147
|
const lineH = fontSize * 1.4;
|
|
1036
1148
|
const cols = Math.ceil(width / charW);
|
|
@@ -1053,7 +1165,7 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1053
1165
|
}
|
|
1054
1166
|
}
|
|
1055
1167
|
let acR = 212, acG = 255, acB = 0;
|
|
1056
|
-
const ap = parseColor(
|
|
1168
|
+
const ap = parseColor(resolvedAccent);
|
|
1057
1169
|
if (ap) {
|
|
1058
1170
|
acR = ap.r;
|
|
1059
1171
|
acG = ap.g;
|
|
@@ -1089,12 +1201,13 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1089
1201
|
const {
|
|
1090
1202
|
fontSize = 14,
|
|
1091
1203
|
chars = " . \xB7 * + \xB0 \u2605",
|
|
1092
|
-
accentColor =
|
|
1204
|
+
accentColor = void 0,
|
|
1093
1205
|
color,
|
|
1094
1206
|
speed = 1,
|
|
1095
1207
|
count = 180,
|
|
1096
1208
|
lightMode = false
|
|
1097
1209
|
} = options;
|
|
1210
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1098
1211
|
ctx.clearRect(0, 0, width, height);
|
|
1099
1212
|
ctx.textBaseline = "middle";
|
|
1100
1213
|
ctx.textAlign = "center";
|
|
@@ -1116,7 +1229,7 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1116
1229
|
}
|
|
1117
1230
|
}
|
|
1118
1231
|
let acR = 212, acG = 255, acB = 0;
|
|
1119
|
-
const ap = parseColor(
|
|
1232
|
+
const ap = parseColor(resolvedAccent);
|
|
1120
1233
|
if (ap) {
|
|
1121
1234
|
acR = ap.r;
|
|
1122
1235
|
acG = ap.g;
|
|
@@ -1149,13 +1262,14 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1149
1262
|
const {
|
|
1150
1263
|
fontSize = 14,
|
|
1151
1264
|
chars = ". \xB7 \u25CB \u25CE \u25CF",
|
|
1152
|
-
accentColor =
|
|
1265
|
+
accentColor = void 0,
|
|
1153
1266
|
color,
|
|
1154
1267
|
rings = 5,
|
|
1155
1268
|
speed = 1,
|
|
1156
1269
|
sharpness = 4,
|
|
1157
1270
|
lightMode = false
|
|
1158
1271
|
} = options;
|
|
1272
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#007a5e" : "#00ffcc");
|
|
1159
1273
|
ctx.clearRect(0, 0, width, height);
|
|
1160
1274
|
ctx.textBaseline = "middle";
|
|
1161
1275
|
ctx.textAlign = "center";
|
|
@@ -1177,7 +1291,7 @@ function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1177
1291
|
}
|
|
1178
1292
|
}
|
|
1179
1293
|
let acR = 0, acG = 255, acB = 204;
|
|
1180
|
-
const ap = parseColor(
|
|
1294
|
+
const ap = parseColor(resolvedAccent);
|
|
1181
1295
|
if (ap) {
|
|
1182
1296
|
acR = ap.r;
|
|
1183
1297
|
acG = ap.g;
|
|
@@ -1222,7 +1336,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1222
1336
|
const {
|
|
1223
1337
|
fontSize = 14,
|
|
1224
1338
|
chars = " .\xB7:;=+*#%@\u2591\u2592\u2593",
|
|
1225
|
-
accentColor =
|
|
1339
|
+
accentColor = void 0,
|
|
1226
1340
|
color,
|
|
1227
1341
|
octaves = 4,
|
|
1228
1342
|
speed = 1,
|
|
@@ -1231,6 +1345,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1231
1345
|
mouseWarp = 0.3,
|
|
1232
1346
|
lightMode = false
|
|
1233
1347
|
} = options;
|
|
1348
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1234
1349
|
const charW = fontSize * 0.62;
|
|
1235
1350
|
const lineH = fontSize * 1.4;
|
|
1236
1351
|
const cols = Math.ceil(width / charW);
|
|
@@ -1253,7 +1368,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1253
1368
|
}
|
|
1254
1369
|
}
|
|
1255
1370
|
let acR = 212, acG = 255, acB = 0;
|
|
1256
|
-
const ap = parseColor(
|
|
1371
|
+
const ap = parseColor(resolvedAccent);
|
|
1257
1372
|
if (ap) {
|
|
1258
1373
|
acR = ap.r;
|
|
1259
1374
|
acG = ap.g;
|
|
@@ -1301,7 +1416,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1301
1416
|
const {
|
|
1302
1417
|
fontSize = 12,
|
|
1303
1418
|
chars = "\xB7-=+|/",
|
|
1304
|
-
accentColor =
|
|
1419
|
+
accentColor = void 0,
|
|
1305
1420
|
color,
|
|
1306
1421
|
bands = 3,
|
|
1307
1422
|
speed = 1,
|
|
@@ -1309,6 +1424,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1309
1424
|
glitch = true,
|
|
1310
1425
|
lightMode = false
|
|
1311
1426
|
} = options;
|
|
1427
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1312
1428
|
const charW = fontSize * 0.62;
|
|
1313
1429
|
const lineH = fontSize * 1.4;
|
|
1314
1430
|
const cols = Math.ceil(width / charW);
|
|
@@ -1331,7 +1447,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1331
1447
|
}
|
|
1332
1448
|
}
|
|
1333
1449
|
let acR = 212, acG = 255, acB = 0;
|
|
1334
|
-
const ap = parseColor(
|
|
1450
|
+
const ap = parseColor(resolvedAccent);
|
|
1335
1451
|
if (ap) {
|
|
1336
1452
|
acR = ap.r;
|
|
1337
1453
|
acG = ap.g;
|
|
@@ -1373,13 +1489,14 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1373
1489
|
fontSize = 14,
|
|
1374
1490
|
chars = " \xB7\u2219\u2022:;+=\u2261\u2263#@",
|
|
1375
1491
|
color,
|
|
1376
|
-
accentColor =
|
|
1492
|
+
accentColor = void 0,
|
|
1377
1493
|
speed = 1,
|
|
1378
1494
|
layers = 5,
|
|
1379
1495
|
softness = 1.2,
|
|
1380
1496
|
mouseRipple = 0.2,
|
|
1381
1497
|
lightMode = false
|
|
1382
1498
|
} = options;
|
|
1499
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1383
1500
|
const charW = fontSize * 0.62;
|
|
1384
1501
|
const lineH = fontSize * 1.4;
|
|
1385
1502
|
const cols = Math.ceil(width / charW);
|
|
@@ -1402,7 +1519,7 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1402
1519
|
}
|
|
1403
1520
|
}
|
|
1404
1521
|
let acR = 212, acG = 255, acB = 0;
|
|
1405
|
-
const ap = parseColor(
|
|
1522
|
+
const ap = parseColor(resolvedAccent);
|
|
1406
1523
|
if (ap) {
|
|
1407
1524
|
acR = ap.r;
|
|
1408
1525
|
acG = ap.g;
|
|
@@ -1458,12 +1575,13 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
|
|
|
1458
1575
|
const {
|
|
1459
1576
|
fontSize = 13,
|
|
1460
1577
|
color,
|
|
1461
|
-
accentColor =
|
|
1578
|
+
accentColor = void 0,
|
|
1462
1579
|
speed = 0.4,
|
|
1463
1580
|
layers = 4,
|
|
1464
1581
|
turbulence = 0.8,
|
|
1465
1582
|
lightMode = false
|
|
1466
1583
|
} = options;
|
|
1584
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1467
1585
|
const charW = fontSize * 0.62;
|
|
1468
1586
|
const lineH = fontSize * 1.4;
|
|
1469
1587
|
const cols = Math.ceil(width / charW);
|
|
@@ -1486,7 +1604,7 @@ function renderSilkBackground(ctx, width, height, time, options = {}) {
|
|
|
1486
1604
|
}
|
|
1487
1605
|
}
|
|
1488
1606
|
let acR = 212, acG = 255, acB = 0;
|
|
1489
|
-
const ap = parseColor(
|
|
1607
|
+
const ap = parseColor(resolvedAccent);
|
|
1490
1608
|
if (ap) {
|
|
1491
1609
|
acR = ap.r;
|
|
1492
1610
|
acG = ap.g;
|
|
@@ -1533,12 +1651,13 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1533
1651
|
fontSize = 13,
|
|
1534
1652
|
chars = " \xB7:;=+*#%@",
|
|
1535
1653
|
color,
|
|
1536
|
-
accentColor =
|
|
1654
|
+
accentColor = void 0,
|
|
1537
1655
|
speed = 1,
|
|
1538
1656
|
radius = 0.38,
|
|
1539
1657
|
swirl = 3,
|
|
1540
1658
|
lightMode = false
|
|
1541
1659
|
} = options;
|
|
1660
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1542
1661
|
const charW = fontSize * 0.62;
|
|
1543
1662
|
const lineH = fontSize * 1.4;
|
|
1544
1663
|
const cols = Math.ceil(width / charW);
|
|
@@ -1562,7 +1681,7 @@ function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1562
1681
|
}
|
|
1563
1682
|
}
|
|
1564
1683
|
let acR = 212, acG = 255, acB = 0;
|
|
1565
|
-
const ap = parseColor(
|
|
1684
|
+
const ap = parseColor(resolvedAccent);
|
|
1566
1685
|
if (ap) {
|
|
1567
1686
|
acR = ap.r;
|
|
1568
1687
|
acG = ap.g;
|
|
@@ -1606,11 +1725,12 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1606
1725
|
fontSize = 14,
|
|
1607
1726
|
chars = " \xB7\u2219\u2022:-=+*#",
|
|
1608
1727
|
color,
|
|
1609
|
-
accentColor =
|
|
1728
|
+
accentColor = void 0,
|
|
1610
1729
|
speed = 0.5,
|
|
1611
1730
|
harmonics = 3,
|
|
1612
1731
|
lightMode = false
|
|
1613
1732
|
} = options;
|
|
1733
|
+
const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
|
|
1614
1734
|
const charW = fontSize * 0.62;
|
|
1615
1735
|
const lineH = fontSize * 1.4;
|
|
1616
1736
|
const cols = Math.ceil(width / charW);
|
|
@@ -1633,7 +1753,7 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1633
1753
|
}
|
|
1634
1754
|
}
|
|
1635
1755
|
let acR = 212, acG = 255, acB = 0;
|
|
1636
|
-
const ap = parseColor(
|
|
1756
|
+
const ap = parseColor(resolvedAccent);
|
|
1637
1757
|
if (ap) {
|
|
1638
1758
|
acR = ap.r;
|
|
1639
1759
|
acG = ap.g;
|
|
@@ -1664,6 +1784,292 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1664
1784
|
}
|
|
1665
1785
|
}
|
|
1666
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
|
+
|
|
1667
2073
|
// src/backgrounds/index.ts
|
|
1668
2074
|
function _parseColor(c) {
|
|
1669
2075
|
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
@@ -1768,6 +2174,24 @@ function asciiBackground(target, options = {}) {
|
|
|
1768
2174
|
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1769
2175
|
color: color ?? renderOpts.color
|
|
1770
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
|
+
});
|
|
1771
2195
|
const optsRef = { current: buildWaveOpts() };
|
|
1772
2196
|
const rebuildOpts = () => {
|
|
1773
2197
|
if (type === "rain") optsRef.current = buildRainOpts();
|
|
@@ -1779,6 +2203,10 @@ function asciiBackground(target, options = {}) {
|
|
|
1779
2203
|
else if (type === "silk") optsRef.current = buildSilkOpts();
|
|
1780
2204
|
else if (type === "void") optsRef.current = buildVoidOpts();
|
|
1781
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();
|
|
1782
2210
|
else optsRef.current = buildWaveOpts();
|
|
1783
2211
|
};
|
|
1784
2212
|
rebuildOpts();
|
|
@@ -1825,6 +2253,14 @@ function asciiBackground(target, options = {}) {
|
|
|
1825
2253
|
renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1826
2254
|
} else if (type === "morph") {
|
|
1827
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);
|
|
1828
2264
|
} else {
|
|
1829
2265
|
renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1830
2266
|
}
|
|
@@ -1845,6 +2281,178 @@ function asciiBackground(target, options = {}) {
|
|
|
1845
2281
|
}
|
|
1846
2282
|
var mountWaveBackground = asciiBackground;
|
|
1847
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
|
+
|
|
1848
2456
|
// src/webgl-engine.ts
|
|
1849
2457
|
var VERT_SRC = (
|
|
1850
2458
|
/* glsl */
|
|
@@ -2107,8 +2715,8 @@ function makeTexture(gl, filter = gl.NEAREST) {
|
|
|
2107
2715
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2108
2716
|
return t;
|
|
2109
2717
|
}
|
|
2110
|
-
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField"];
|
|
2111
|
-
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"];
|
|
2112
2720
|
var COLOR_MODES = ["grayscale", "fullcolor", "matrix", "accent"];
|
|
2113
2721
|
function tryCreateWebGLRenderer(canvas) {
|
|
2114
2722
|
const glOrNull = canvas.getContext("webgl2", {
|
|
@@ -2293,6 +2901,6 @@ function tryCreateWebGLRenderer(canvas) {
|
|
|
2293
2901
|
}
|
|
2294
2902
|
}
|
|
2295
2903
|
|
|
2296
|
-
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 };
|
|
2297
2905
|
//# sourceMappingURL=index.js.map
|
|
2298
2906
|
//# sourceMappingURL=index.js.map
|