asciify-engine 1.0.23 → 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 +618 -11
- 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 +610 -12
- 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;
|
|
@@ -755,17 +864,18 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
755
864
|
const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
|
|
756
865
|
const useFastRect = fontSize < 6;
|
|
757
866
|
if (!useFastRect) {
|
|
758
|
-
|
|
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`;
|
|
759
869
|
ctx.textAlign = "center";
|
|
760
870
|
ctx.textBaseline = "middle";
|
|
761
871
|
}
|
|
762
872
|
let charWeights = null;
|
|
763
873
|
if (useFastRect) {
|
|
764
874
|
charWeights = {};
|
|
765
|
-
const
|
|
766
|
-
const csLen =
|
|
875
|
+
const csChars = [...options.charset];
|
|
876
|
+
const csLen = csChars.length;
|
|
767
877
|
for (let i = 0; i < csLen; i++) {
|
|
768
|
-
charWeights[
|
|
878
|
+
charWeights[csChars[i]] = Math.max(0.1, (i + 0.3) / csLen);
|
|
769
879
|
}
|
|
770
880
|
}
|
|
771
881
|
const baseTransform = !useFastRect ? ctx.getTransform() : null;
|
|
@@ -1676,6 +1786,292 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1676
1786
|
}
|
|
1677
1787
|
}
|
|
1678
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
|
+
|
|
1679
2075
|
// src/backgrounds/index.ts
|
|
1680
2076
|
function _parseColor(c) {
|
|
1681
2077
|
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
@@ -1780,6 +2176,24 @@ function asciiBackground(target, options = {}) {
|
|
|
1780
2176
|
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1781
2177
|
color: color ?? renderOpts.color
|
|
1782
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
|
+
});
|
|
1783
2197
|
const optsRef = { current: buildWaveOpts() };
|
|
1784
2198
|
const rebuildOpts = () => {
|
|
1785
2199
|
if (type === "rain") optsRef.current = buildRainOpts();
|
|
@@ -1791,6 +2205,10 @@ function asciiBackground(target, options = {}) {
|
|
|
1791
2205
|
else if (type === "silk") optsRef.current = buildSilkOpts();
|
|
1792
2206
|
else if (type === "void") optsRef.current = buildVoidOpts();
|
|
1793
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();
|
|
1794
2212
|
else optsRef.current = buildWaveOpts();
|
|
1795
2213
|
};
|
|
1796
2214
|
rebuildOpts();
|
|
@@ -1837,6 +2255,14 @@ function asciiBackground(target, options = {}) {
|
|
|
1837
2255
|
renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1838
2256
|
} else if (type === "morph") {
|
|
1839
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);
|
|
1840
2266
|
} else {
|
|
1841
2267
|
renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1842
2268
|
}
|
|
@@ -1857,6 +2283,178 @@ function asciiBackground(target, options = {}) {
|
|
|
1857
2283
|
}
|
|
1858
2284
|
var mountWaveBackground = asciiBackground;
|
|
1859
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
|
+
|
|
1860
2458
|
// src/webgl-engine.ts
|
|
1861
2459
|
var VERT_SRC = (
|
|
1862
2460
|
/* glsl */
|
|
@@ -2119,8 +2717,8 @@ function makeTexture(gl, filter = gl.NEAREST) {
|
|
|
2119
2717
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2120
2718
|
return t;
|
|
2121
2719
|
}
|
|
2122
|
-
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField"];
|
|
2123
|
-
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"];
|
|
2124
2722
|
var COLOR_MODES = ["grayscale", "fullcolor", "matrix", "accent"];
|
|
2125
2723
|
function tryCreateWebGLRenderer(canvas) {
|
|
2126
2724
|
const glOrNull = canvas.getContext("webgl2", {
|
|
@@ -2309,16 +2907,24 @@ exports.ART_STYLE_PRESETS = ART_STYLE_PRESETS;
|
|
|
2309
2907
|
exports.CHARSETS = CHARSETS;
|
|
2310
2908
|
exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
|
|
2311
2909
|
exports.HOVER_PRESETS = HOVER_PRESETS;
|
|
2910
|
+
exports.PALETTE_THEMES = PALETTE_THEMES;
|
|
2312
2911
|
exports.asciiBackground = asciiBackground;
|
|
2912
|
+
exports.asciiText = asciiText;
|
|
2913
|
+
exports.asciiTextAnsi = asciiTextAnsi;
|
|
2313
2914
|
exports.asciify = asciify;
|
|
2314
2915
|
exports.asciifyGif = asciifyGif;
|
|
2315
2916
|
exports.asciifyVideo = asciifyVideo;
|
|
2917
|
+
exports.createRecorder = createRecorder;
|
|
2316
2918
|
exports.generateAnimatedEmbedCode = generateAnimatedEmbedCode;
|
|
2317
2919
|
exports.generateEmbedCode = generateEmbedCode;
|
|
2318
2920
|
exports.gifToAsciiFrames = gifToAsciiFrames;
|
|
2319
2921
|
exports.imageToAsciiFrame = imageToAsciiFrame;
|
|
2320
2922
|
exports.mountWaveBackground = mountWaveBackground;
|
|
2923
|
+
exports.recordAndDownload = recordAndDownload;
|
|
2321
2924
|
exports.renderAuroraBackground = renderAuroraBackground;
|
|
2925
|
+
exports.renderCircuitBackground = renderCircuitBackground;
|
|
2926
|
+
exports.renderDnaBackground = renderDnaBackground;
|
|
2927
|
+
exports.renderFireBackground = renderFireBackground;
|
|
2322
2928
|
exports.renderFrameToCanvas = renderFrameToCanvas;
|
|
2323
2929
|
exports.renderGridBackground = renderGridBackground;
|
|
2324
2930
|
exports.renderMorphBackground = renderMorphBackground;
|
|
@@ -2327,6 +2933,7 @@ exports.renderPulseBackground = renderPulseBackground;
|
|
|
2327
2933
|
exports.renderRainBackground = renderRainBackground;
|
|
2328
2934
|
exports.renderSilkBackground = renderSilkBackground;
|
|
2329
2935
|
exports.renderStarsBackground = renderStarsBackground;
|
|
2936
|
+
exports.renderTerrainBackground = renderTerrainBackground;
|
|
2330
2937
|
exports.renderVoidBackground = renderVoidBackground;
|
|
2331
2938
|
exports.renderWaveBackground = renderWaveBackground;
|
|
2332
2939
|
exports.tryCreateWebGLRenderer = tryCreateWebGLRenderer;
|