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.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;
|
|
@@ -753,17 +862,18 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
753
862
|
const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
|
|
754
863
|
const useFastRect = fontSize < 6;
|
|
755
864
|
if (!useFastRect) {
|
|
756
|
-
|
|
865
|
+
const isEmoji = options.artStyle === "emoji";
|
|
866
|
+
ctx.font = isEmoji ? `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif` : `${fontSize}px "JetBrains Mono", monospace`;
|
|
757
867
|
ctx.textAlign = "center";
|
|
758
868
|
ctx.textBaseline = "middle";
|
|
759
869
|
}
|
|
760
870
|
let charWeights = null;
|
|
761
871
|
if (useFastRect) {
|
|
762
872
|
charWeights = {};
|
|
763
|
-
const
|
|
764
|
-
const csLen =
|
|
873
|
+
const csChars = [...options.charset];
|
|
874
|
+
const csLen = csChars.length;
|
|
765
875
|
for (let i = 0; i < csLen; i++) {
|
|
766
|
-
charWeights[
|
|
876
|
+
charWeights[csChars[i]] = Math.max(0.1, (i + 0.3) / csLen);
|
|
767
877
|
}
|
|
768
878
|
}
|
|
769
879
|
const baseTransform = !useFastRect ? ctx.getTransform() : null;
|
|
@@ -1674,6 +1784,292 @@ function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
|
1674
1784
|
}
|
|
1675
1785
|
}
|
|
1676
1786
|
|
|
1787
|
+
// src/backgrounds/fire.ts
|
|
1788
|
+
function renderFireBackground(ctx, width, height, time, options = {}) {
|
|
1789
|
+
const {
|
|
1790
|
+
fontSize = 13,
|
|
1791
|
+
chars = " .,:;i+xX#&@",
|
|
1792
|
+
color = "#ff4500",
|
|
1793
|
+
hotColor = "#ffe066",
|
|
1794
|
+
intensity = 0.85,
|
|
1795
|
+
wind = 0,
|
|
1796
|
+
speed = 1,
|
|
1797
|
+
lightMode = false
|
|
1798
|
+
} = options;
|
|
1799
|
+
const charW = fontSize * 0.62;
|
|
1800
|
+
const lineH = fontSize * 1.4;
|
|
1801
|
+
const cols = Math.ceil(width / charW);
|
|
1802
|
+
const rows = Math.ceil(height / lineH);
|
|
1803
|
+
const len = cols * rows;
|
|
1804
|
+
const key = "__fire_heat__";
|
|
1805
|
+
const canvasAny = ctx.canvas;
|
|
1806
|
+
let heat = canvasAny[key];
|
|
1807
|
+
if (!heat || heat.length !== len) {
|
|
1808
|
+
heat = new Float32Array(len);
|
|
1809
|
+
canvasAny[key] = heat;
|
|
1810
|
+
}
|
|
1811
|
+
const dt = 0.016 * speed;
|
|
1812
|
+
const coolingRate = 0.18 * dt;
|
|
1813
|
+
const windShift = wind * speed * 0.8;
|
|
1814
|
+
const baseRow = rows - 1;
|
|
1815
|
+
const t = time * speed;
|
|
1816
|
+
for (let c = 0; c < cols; c++) {
|
|
1817
|
+
const flicker = Math.sin(c * 0.31 + t * 4.1) * 0.5 + 0.5;
|
|
1818
|
+
const flicker2 = Math.sin(c * 0.73 - t * 2.7) * 0.5 + 0.5;
|
|
1819
|
+
const seed = (flicker * 0.6 + flicker2 * 0.4) * intensity;
|
|
1820
|
+
heat[baseRow * cols + c] = Math.min(1, seed + Math.random() * 0.15 * intensity);
|
|
1821
|
+
if (baseRow > 0) heat[(baseRow - 1) * cols + c] = Math.min(1, seed * 0.85 + Math.random() * 0.1 * intensity);
|
|
1822
|
+
}
|
|
1823
|
+
const newHeat = new Float32Array(len);
|
|
1824
|
+
for (let r = 0; r < rows - 2; r++) {
|
|
1825
|
+
for (let c = 0; c < cols; c++) {
|
|
1826
|
+
const below = heat[(r + 1) * cols + c];
|
|
1827
|
+
const below2 = heat[(r + 2) * cols + Math.max(0, Math.min(cols - 1, c + Math.round(windShift)))];
|
|
1828
|
+
const left = heat[(r + 1) * cols + Math.max(0, c - 1)];
|
|
1829
|
+
const right = heat[(r + 1) * cols + Math.min(cols - 1, c + 1)];
|
|
1830
|
+
const avg = below * 0.4 + below2 * 0.25 + left * 0.175 + right * 0.175;
|
|
1831
|
+
newHeat[r * cols + c] = Math.max(0, avg - coolingRate - Math.random() * 0.02 * speed);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
for (let c = 0; c < cols; c++) {
|
|
1835
|
+
newHeat[(rows - 1) * cols + c] = heat[(rows - 1) * cols + c];
|
|
1836
|
+
if (rows > 1) newHeat[(rows - 2) * cols + c] = heat[(rows - 2) * cols + c];
|
|
1837
|
+
}
|
|
1838
|
+
canvasAny[key] = newHeat;
|
|
1839
|
+
const cp = parseColor(color) ?? { r: 255, g: 69, b: 0 };
|
|
1840
|
+
const hp = parseColor(hotColor) ?? { r: 255, g: 224, b: 102 };
|
|
1841
|
+
ctx.clearRect(0, 0, width, height);
|
|
1842
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1843
|
+
ctx.textBaseline = "top";
|
|
1844
|
+
for (let r = 0; r < rows; r++) {
|
|
1845
|
+
for (let c = 0; c < cols; c++) {
|
|
1846
|
+
const v = newHeat[r * cols + c];
|
|
1847
|
+
if (v < 0.03) continue;
|
|
1848
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(v * chars.length));
|
|
1849
|
+
const ch = chars[charIdx];
|
|
1850
|
+
if (ch === " ") continue;
|
|
1851
|
+
const blend = Math.min(1, v * 1.2);
|
|
1852
|
+
const r2 = cp.r + (hp.r - cp.r) * blend | 0;
|
|
1853
|
+
const g2 = cp.g + (hp.g - cp.g) * blend | 0;
|
|
1854
|
+
const b2 = cp.b + (hp.b - cp.b) * blend | 0;
|
|
1855
|
+
const alpha = lightMode ? 1 - v * 0.3 : Math.min(1, v + 0.15);
|
|
1856
|
+
ctx.globalAlpha = alpha;
|
|
1857
|
+
ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
|
|
1858
|
+
ctx.fillText(ch, c * charW, r * lineH);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
ctx.globalAlpha = 1;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// src/backgrounds/dna.ts
|
|
1865
|
+
function renderDnaBackground(ctx, width, height, time, options = {}) {
|
|
1866
|
+
const {
|
|
1867
|
+
fontSize = 13,
|
|
1868
|
+
baseChars = "ATCG",
|
|
1869
|
+
bridgeChars = "-=\u2261",
|
|
1870
|
+
color = "#00e5ff",
|
|
1871
|
+
color2 = "#ff4081",
|
|
1872
|
+
bridgeColor = "#88ffcc",
|
|
1873
|
+
speed = 1,
|
|
1874
|
+
helixCount,
|
|
1875
|
+
lightMode = false
|
|
1876
|
+
} = options;
|
|
1877
|
+
const charW = fontSize * 0.62;
|
|
1878
|
+
const lineH = fontSize * 1.4;
|
|
1879
|
+
const cols = Math.ceil(width / charW);
|
|
1880
|
+
const rows = Math.ceil(height / lineH);
|
|
1881
|
+
const numHelix = helixCount ?? Math.max(1, Math.floor(width / 80));
|
|
1882
|
+
const sectionW = cols / numHelix;
|
|
1883
|
+
const cp = parseColor(color) ?? { r: 0, g: 229, b: 255 };
|
|
1884
|
+
const cp2 = parseColor(color2) ?? { r: 255, g: 64, b: 129 };
|
|
1885
|
+
const bp = parseColor(bridgeColor) ?? { r: 136, g: 255, b: 204 };
|
|
1886
|
+
ctx.clearRect(0, 0, width, height);
|
|
1887
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1888
|
+
ctx.textBaseline = "top";
|
|
1889
|
+
const t = time * speed;
|
|
1890
|
+
const amplitude = sectionW * 0.35;
|
|
1891
|
+
for (let h = 0; h < numHelix; h++) {
|
|
1892
|
+
const centerCol = sectionW * (h + 0.5);
|
|
1893
|
+
for (let r = 0; r < rows; r++) {
|
|
1894
|
+
const phase = r / rows * Math.PI * 6 - t * 1.8;
|
|
1895
|
+
const strand1ColF = centerCol + Math.sin(phase) * amplitude;
|
|
1896
|
+
const strand2ColF = centerCol + Math.sin(phase + Math.PI) * amplitude;
|
|
1897
|
+
const strand1Col = Math.round(strand1ColF);
|
|
1898
|
+
const strand2Col = Math.round(strand2ColF);
|
|
1899
|
+
if (strand1Col < 0 || strand1Col >= cols) continue;
|
|
1900
|
+
const baseSeed1 = hash2(h * 31 + r * 7, 3);
|
|
1901
|
+
const ch1 = baseChars[Math.floor(baseSeed1 * baseChars.length)];
|
|
1902
|
+
const depth1 = (Math.sin(phase) + 1) * 0.5;
|
|
1903
|
+
const depth2 = (Math.sin(phase + Math.PI) + 1) * 0.5;
|
|
1904
|
+
ctx.globalAlpha = 0.35 + depth1 * 0.65;
|
|
1905
|
+
ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
|
|
1906
|
+
ctx.fillText(ch1, strand1Col * charW, r * lineH);
|
|
1907
|
+
if (strand2Col >= 0 && strand2Col < cols) {
|
|
1908
|
+
const baseSeed2 = hash2(h * 53 + r * 11, 7);
|
|
1909
|
+
const ch2 = baseChars[Math.floor(baseSeed2 * baseChars.length)];
|
|
1910
|
+
ctx.globalAlpha = 0.35 + depth2 * 0.65;
|
|
1911
|
+
ctx.fillStyle = `rgb(${cp2.r},${cp2.g},${cp2.b})`;
|
|
1912
|
+
ctx.fillText(ch2, strand2Col * charW, r * lineH);
|
|
1913
|
+
}
|
|
1914
|
+
const bridgeInterval = 3;
|
|
1915
|
+
if (r % bridgeInterval === 0) {
|
|
1916
|
+
const minC = Math.min(strand1Col, strand2Col);
|
|
1917
|
+
const maxC = Math.max(strand1Col, strand2Col);
|
|
1918
|
+
const bridgeLen = maxC - minC;
|
|
1919
|
+
if (bridgeLen > 1) {
|
|
1920
|
+
const bSeed = hash2(r * 17 + h * 43, 5);
|
|
1921
|
+
const bCh = bridgeChars[Math.floor(bSeed * bridgeChars.length)];
|
|
1922
|
+
const midBridgeAlpha = (depth1 + depth2) * 0.25 + 0.2;
|
|
1923
|
+
ctx.globalAlpha = midBridgeAlpha;
|
|
1924
|
+
ctx.fillStyle = `rgb(${bp.r},${bp.g},${bp.b})`;
|
|
1925
|
+
for (let bc = minC + 1; bc < maxC; bc++) {
|
|
1926
|
+
ctx.fillText(bCh, bc * charW, r * lineH);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
ctx.globalAlpha = 1;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// src/backgrounds/terrain.ts
|
|
1936
|
+
function renderTerrainBackground(ctx, width, height, time, options = {}) {
|
|
1937
|
+
const {
|
|
1938
|
+
fontSize = 13,
|
|
1939
|
+
chars = " .,:;+*#@",
|
|
1940
|
+
color = "#4caf50",
|
|
1941
|
+
skyColor = "#1a237e",
|
|
1942
|
+
peakColor = "#e0e0e0",
|
|
1943
|
+
speed = 1,
|
|
1944
|
+
roughness = 0.55,
|
|
1945
|
+
heightScale = 0.55,
|
|
1946
|
+
stars = true,
|
|
1947
|
+
lightMode = false
|
|
1948
|
+
} = options;
|
|
1949
|
+
const charW = fontSize * 0.62;
|
|
1950
|
+
const lineH = fontSize * 1.4;
|
|
1951
|
+
const cols = Math.ceil(width / charW);
|
|
1952
|
+
const rows = Math.ceil(height / lineH);
|
|
1953
|
+
const cp = parseColor(color) ?? { r: 76, g: 175, b: 80 };
|
|
1954
|
+
const sky = parseColor(skyColor) ?? { r: 26, g: 35, b: 126 };
|
|
1955
|
+
const peak = parseColor(peakColor) ?? { r: 224, g: 224, b: 224 };
|
|
1956
|
+
ctx.clearRect(0, 0, width, height);
|
|
1957
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
1958
|
+
ctx.textBaseline = "top";
|
|
1959
|
+
const scroll = time * speed * 0.4;
|
|
1960
|
+
const terrainRow = new Array(cols);
|
|
1961
|
+
for (let c = 0; c < cols; c++) {
|
|
1962
|
+
const nx = (c / cols + scroll) * roughness * 3;
|
|
1963
|
+
const h = (fbm(nx, 0.5) * 0.5 + 0.5) * heightScale;
|
|
1964
|
+
terrainRow[c] = Math.floor(h * rows);
|
|
1965
|
+
}
|
|
1966
|
+
for (let r = 0; r < rows; r++) {
|
|
1967
|
+
for (let c = 0; c < cols; c++) {
|
|
1968
|
+
const terrainStart = rows - 1 - terrainRow[c];
|
|
1969
|
+
const isGround = r >= terrainStart;
|
|
1970
|
+
const isNearPeak = r === terrainStart;
|
|
1971
|
+
if (!isGround) {
|
|
1972
|
+
if (stars) {
|
|
1973
|
+
const starSeed = hash2(c * 7 + Math.floor(scroll * 0.3), r * 13);
|
|
1974
|
+
if (starSeed > 0.97) {
|
|
1975
|
+
const twinkle = Math.sin(time * 2 + starSeed * 100) * 0.3 + 0.7;
|
|
1976
|
+
ctx.globalAlpha = twinkle * 0.5;
|
|
1977
|
+
ctx.fillStyle = `rgb(${sky.r + 60},${sky.g + 60},${sky.b + 80})`;
|
|
1978
|
+
ctx.fillText("\xB7", c * charW, r * lineH);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
const depth = (r - terrainStart) / Math.max(1, terrainRow[c]);
|
|
1984
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(depth * chars.length));
|
|
1985
|
+
const ch = chars[charIdx];
|
|
1986
|
+
if (ch === " " && !isNearPeak) continue;
|
|
1987
|
+
const blendPeak = isNearPeak ? 1 : Math.max(0, 1 - depth * 4);
|
|
1988
|
+
const r2 = cp.r + (peak.r - cp.r) * blendPeak | 0;
|
|
1989
|
+
const g2 = cp.g + (peak.g - cp.g) * blendPeak | 0;
|
|
1990
|
+
const b2 = cp.b + (peak.b - cp.b) * blendPeak | 0;
|
|
1991
|
+
ctx.globalAlpha = 0.5 + depth * 0.5;
|
|
1992
|
+
ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
|
|
1993
|
+
ctx.fillText(isNearPeak ? chars[chars.length - 1] : ch, c * charW, r * lineH);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
ctx.globalAlpha = 1;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/backgrounds/circuit.ts
|
|
2000
|
+
var EAST = 1;
|
|
2001
|
+
var WEST = 2;
|
|
2002
|
+
var NORTH = 4;
|
|
2003
|
+
var SOUTH = 8;
|
|
2004
|
+
var DIR_CHARS = {
|
|
2005
|
+
[EAST | WEST]: "\u2500",
|
|
2006
|
+
[NORTH | SOUTH]: "\u2502",
|
|
2007
|
+
[EAST | SOUTH]: "\u250C",
|
|
2008
|
+
[WEST | SOUTH]: "\u2510",
|
|
2009
|
+
[EAST | NORTH]: "\u2514",
|
|
2010
|
+
[WEST | NORTH]: "\u2518",
|
|
2011
|
+
[EAST | WEST | SOUTH]: "\u252C",
|
|
2012
|
+
[EAST | WEST | NORTH]: "\u2534",
|
|
2013
|
+
[NORTH | SOUTH | EAST]: "\u251C",
|
|
2014
|
+
[NORTH | SOUTH | WEST]: "\u2524",
|
|
2015
|
+
[EAST | WEST | NORTH | SOUTH]: "\u253C",
|
|
2016
|
+
[EAST]: "\u2576",
|
|
2017
|
+
[WEST]: "\u2574",
|
|
2018
|
+
[NORTH]: "\u2575",
|
|
2019
|
+
[SOUTH]: "\u2577"
|
|
2020
|
+
};
|
|
2021
|
+
function renderCircuitBackground(ctx, width, height, time, options = {}) {
|
|
2022
|
+
const {
|
|
2023
|
+
fontSize = 13,
|
|
2024
|
+
pulseColor = "#ffffff",
|
|
2025
|
+
color = "#00ff88",
|
|
2026
|
+
density = 0.38,
|
|
2027
|
+
speed = 1,
|
|
2028
|
+
lightMode = false
|
|
2029
|
+
} = options;
|
|
2030
|
+
const charW = fontSize * 0.62;
|
|
2031
|
+
const lineH = fontSize * 1.4;
|
|
2032
|
+
const cols = Math.ceil(width / charW);
|
|
2033
|
+
const rows = Math.ceil(height / lineH);
|
|
2034
|
+
const cp = parseColor(color) ?? { r: 0, g: 255, b: 136 };
|
|
2035
|
+
const pp = parseColor(pulseColor) ?? { r: 255, g: 255, b: 255 };
|
|
2036
|
+
const getConnections = (c, r) => {
|
|
2037
|
+
if (hash2(c * 17 + 1, r * 7 + 2) > density) return 0;
|
|
2038
|
+
let mask = 0;
|
|
2039
|
+
if (c + 1 < cols && hash2(c * 17 + 1, r * 7 + 2) > 0.15) mask |= EAST;
|
|
2040
|
+
if (c - 1 >= 0 && hash2((c - 1) * 17 + 1, r * 7 + 2) > 0.15) mask |= WEST;
|
|
2041
|
+
if (r + 1 < rows && hash2(c * 17 + 1, (r + 1) * 7 + 2) > 0.15) mask |= SOUTH;
|
|
2042
|
+
if (r - 1 >= 0 && hash2(c * 17 + 1, (r - 1) * 7 + 2) > 0.15) mask |= NORTH;
|
|
2043
|
+
return mask;
|
|
2044
|
+
};
|
|
2045
|
+
ctx.clearRect(0, 0, width, height);
|
|
2046
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
2047
|
+
ctx.textBaseline = "top";
|
|
2048
|
+
const t = time * speed;
|
|
2049
|
+
for (let r = 0; r < rows; r++) {
|
|
2050
|
+
for (let c = 0; c < cols; c++) {
|
|
2051
|
+
const mask = getConnections(c, r);
|
|
2052
|
+
if (mask === 0) continue;
|
|
2053
|
+
const ch = DIR_CHARS[mask] ?? "\xB7";
|
|
2054
|
+
const pulsePosH = (t * 8 + hash2(c, r * 23) * 40) % cols;
|
|
2055
|
+
const pulsePosV = (t * 6 + hash2(r * 17, c * 11) * 40) % rows;
|
|
2056
|
+
const nearH = (mask & EAST || mask & WEST) && Math.abs(c - pulsePosH) < 1.5;
|
|
2057
|
+
const nearV = (mask & NORTH || mask & SOUTH) && Math.abs(r - pulsePosV) < 1.5;
|
|
2058
|
+
const isPulse = nearH || nearV;
|
|
2059
|
+
const baseAlpha = 0.25 + hash2(c * 3, r * 5) * 0.35;
|
|
2060
|
+
if (isPulse) {
|
|
2061
|
+
ctx.globalAlpha = 0.95;
|
|
2062
|
+
ctx.fillStyle = `rgb(${pp.r},${pp.g},${pp.b})`;
|
|
2063
|
+
} else {
|
|
2064
|
+
ctx.globalAlpha = baseAlpha;
|
|
2065
|
+
ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
|
|
2066
|
+
}
|
|
2067
|
+
ctx.fillText(ch, c * charW, r * lineH);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
ctx.globalAlpha = 1;
|
|
2071
|
+
}
|
|
2072
|
+
|
|
1677
2073
|
// src/backgrounds/index.ts
|
|
1678
2074
|
function _parseColor(c) {
|
|
1679
2075
|
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
@@ -1778,6 +2174,24 @@ function asciiBackground(target, options = {}) {
|
|
|
1778
2174
|
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1779
2175
|
color: color ?? renderOpts.color
|
|
1780
2176
|
});
|
|
2177
|
+
const buildFireOpts = () => ({
|
|
2178
|
+
...renderOpts,
|
|
2179
|
+
color: color ?? renderOpts.color
|
|
2180
|
+
});
|
|
2181
|
+
const buildDnaOpts = () => ({
|
|
2182
|
+
...renderOpts,
|
|
2183
|
+
color: color ?? renderOpts.color
|
|
2184
|
+
});
|
|
2185
|
+
const buildTerrainOpts = () => ({
|
|
2186
|
+
...renderOpts,
|
|
2187
|
+
color: color ?? renderOpts.color,
|
|
2188
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
|
|
2189
|
+
});
|
|
2190
|
+
const buildCircuitOpts = () => ({
|
|
2191
|
+
...renderOpts,
|
|
2192
|
+
color: color ?? renderOpts.color,
|
|
2193
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
|
|
2194
|
+
});
|
|
1781
2195
|
const optsRef = { current: buildWaveOpts() };
|
|
1782
2196
|
const rebuildOpts = () => {
|
|
1783
2197
|
if (type === "rain") optsRef.current = buildRainOpts();
|
|
@@ -1789,6 +2203,10 @@ function asciiBackground(target, options = {}) {
|
|
|
1789
2203
|
else if (type === "silk") optsRef.current = buildSilkOpts();
|
|
1790
2204
|
else if (type === "void") optsRef.current = buildVoidOpts();
|
|
1791
2205
|
else if (type === "morph") optsRef.current = buildMorphOpts();
|
|
2206
|
+
else if (type === "fire") optsRef.current = buildFireOpts();
|
|
2207
|
+
else if (type === "dna") optsRef.current = buildDnaOpts();
|
|
2208
|
+
else if (type === "terrain") optsRef.current = buildTerrainOpts();
|
|
2209
|
+
else if (type === "circuit") optsRef.current = buildCircuitOpts();
|
|
1792
2210
|
else optsRef.current = buildWaveOpts();
|
|
1793
2211
|
};
|
|
1794
2212
|
rebuildOpts();
|
|
@@ -1835,6 +2253,14 @@ function asciiBackground(target, options = {}) {
|
|
|
1835
2253
|
renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1836
2254
|
} else if (type === "morph") {
|
|
1837
2255
|
renderMorphBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2256
|
+
} else if (type === "fire") {
|
|
2257
|
+
renderFireBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2258
|
+
} else if (type === "dna") {
|
|
2259
|
+
renderDnaBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2260
|
+
} else if (type === "terrain") {
|
|
2261
|
+
renderTerrainBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
2262
|
+
} else if (type === "circuit") {
|
|
2263
|
+
renderCircuitBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
1838
2264
|
} else {
|
|
1839
2265
|
renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1840
2266
|
}
|
|
@@ -1855,6 +2281,178 @@ function asciiBackground(target, options = {}) {
|
|
|
1855
2281
|
}
|
|
1856
2282
|
var mountWaveBackground = asciiBackground;
|
|
1857
2283
|
|
|
2284
|
+
// src/core/ascii-text.ts
|
|
2285
|
+
function asciiText(source, options = {}, targetWidth, targetHeight) {
|
|
2286
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2287
|
+
const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
|
|
2288
|
+
if (!frame.length || cols === 0) return "";
|
|
2289
|
+
const lines = [];
|
|
2290
|
+
for (const row of frame) {
|
|
2291
|
+
lines.push(row.map((cell) => cell.char).join(""));
|
|
2292
|
+
}
|
|
2293
|
+
return lines.join("\n");
|
|
2294
|
+
}
|
|
2295
|
+
function asciiTextAnsi(source, options = {}, targetWidth, targetHeight) {
|
|
2296
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2297
|
+
const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
|
|
2298
|
+
if (!frame.length || cols === 0) return "";
|
|
2299
|
+
const RESET = "\x1B[0m";
|
|
2300
|
+
const lines = [];
|
|
2301
|
+
for (const row of frame) {
|
|
2302
|
+
let line = "";
|
|
2303
|
+
for (const cell of row) {
|
|
2304
|
+
if (cell.char === " " || cell.a < 10) {
|
|
2305
|
+
line += " ";
|
|
2306
|
+
} else {
|
|
2307
|
+
line += `\x1B[38;2;${cell.r};${cell.g};${cell.b}m${cell.char}${RESET}`;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
lines.push(line);
|
|
2311
|
+
}
|
|
2312
|
+
return lines.join("\n");
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// src/core/record.ts
|
|
2316
|
+
function createRecorder(canvas, options = {}) {
|
|
2317
|
+
const {
|
|
2318
|
+
fps = 15,
|
|
2319
|
+
maxFrames = 120,
|
|
2320
|
+
format = "gif",
|
|
2321
|
+
quality = 10,
|
|
2322
|
+
scale = 1
|
|
2323
|
+
} = options;
|
|
2324
|
+
const interval = 1e3 / fps;
|
|
2325
|
+
let recording = false;
|
|
2326
|
+
let timerId = -1;
|
|
2327
|
+
const blobs = [];
|
|
2328
|
+
const captureFrame = () => {
|
|
2329
|
+
if (!recording || blobs.length >= maxFrames) return;
|
|
2330
|
+
let src = canvas;
|
|
2331
|
+
if (scale !== 1) {
|
|
2332
|
+
const off = document.createElement("canvas");
|
|
2333
|
+
off.width = Math.round(canvas.width * scale);
|
|
2334
|
+
off.height = Math.round(canvas.height * scale);
|
|
2335
|
+
const offCtx = off.getContext("2d");
|
|
2336
|
+
offCtx.drawImage(canvas, 0, 0, off.width, off.height);
|
|
2337
|
+
src = off;
|
|
2338
|
+
}
|
|
2339
|
+
blobs.push(src.toDataURL("image/png"));
|
|
2340
|
+
};
|
|
2341
|
+
const encodeGif = async (frames) => {
|
|
2342
|
+
return new Promise((resolve, reject) => {
|
|
2343
|
+
if (typeof GIF === "undefined") {
|
|
2344
|
+
reject(new Error('[asciify recorder] gif.js not found. Add <script src="/gif.worker.js"> to your page.'));
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
const gif = new GIF({
|
|
2348
|
+
workers: 2,
|
|
2349
|
+
quality,
|
|
2350
|
+
workerScript: "/gif.worker.js"
|
|
2351
|
+
});
|
|
2352
|
+
let loaded = 0;
|
|
2353
|
+
const total = frames.length;
|
|
2354
|
+
frames.forEach((dataUrl) => {
|
|
2355
|
+
const img = new Image();
|
|
2356
|
+
img.onload = () => {
|
|
2357
|
+
gif.addFrame(img, { delay: interval, copy: true });
|
|
2358
|
+
loaded++;
|
|
2359
|
+
if (loaded === total) gif.render();
|
|
2360
|
+
};
|
|
2361
|
+
img.src = dataUrl;
|
|
2362
|
+
});
|
|
2363
|
+
gif.on("finished", (blob) => {
|
|
2364
|
+
const reader = new FileReader();
|
|
2365
|
+
reader.onload = () => resolve(reader.result);
|
|
2366
|
+
reader.readAsDataURL(blob);
|
|
2367
|
+
});
|
|
2368
|
+
gif.on("error", reject);
|
|
2369
|
+
});
|
|
2370
|
+
};
|
|
2371
|
+
const encodeWebP = async (frames, _fps) => {
|
|
2372
|
+
if (typeof MediaRecorder === "undefined") {
|
|
2373
|
+
throw new Error("[asciify recorder] MediaRecorder not available in this browser.");
|
|
2374
|
+
}
|
|
2375
|
+
const off = document.createElement("canvas");
|
|
2376
|
+
if (frames.length === 0) return "";
|
|
2377
|
+
const probe = new Image();
|
|
2378
|
+
await new Promise((res) => {
|
|
2379
|
+
probe.onload = () => res();
|
|
2380
|
+
probe.src = frames[0];
|
|
2381
|
+
});
|
|
2382
|
+
off.width = probe.naturalWidth;
|
|
2383
|
+
off.height = probe.naturalHeight;
|
|
2384
|
+
const offCtx = off.getContext("2d");
|
|
2385
|
+
const stream = off.captureStream(_fps);
|
|
2386
|
+
const recorder = new MediaRecorder(stream, { mimeType: "video/webm;codecs=vp9" });
|
|
2387
|
+
const chunks = [];
|
|
2388
|
+
recorder.ondataavailable = (e) => chunks.push(e.data);
|
|
2389
|
+
return new Promise((resolve, reject) => {
|
|
2390
|
+
recorder.onstop = () => {
|
|
2391
|
+
const blob = new Blob(chunks, { type: "video/webm" });
|
|
2392
|
+
const reader = new FileReader();
|
|
2393
|
+
reader.onload = () => resolve(reader.result);
|
|
2394
|
+
reader.readAsDataURL(blob);
|
|
2395
|
+
};
|
|
2396
|
+
recorder.onerror = reject;
|
|
2397
|
+
recorder.start();
|
|
2398
|
+
let idx = 0;
|
|
2399
|
+
const drawNext = () => {
|
|
2400
|
+
if (idx >= frames.length) {
|
|
2401
|
+
recorder.stop();
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
const img = new Image();
|
|
2405
|
+
img.onload = () => {
|
|
2406
|
+
offCtx.drawImage(img, 0, 0);
|
|
2407
|
+
idx++;
|
|
2408
|
+
setTimeout(drawNext, interval);
|
|
2409
|
+
};
|
|
2410
|
+
img.src = frames[idx];
|
|
2411
|
+
};
|
|
2412
|
+
drawNext();
|
|
2413
|
+
});
|
|
2414
|
+
};
|
|
2415
|
+
return {
|
|
2416
|
+
get isRecording() {
|
|
2417
|
+
return recording;
|
|
2418
|
+
},
|
|
2419
|
+
get frameCount() {
|
|
2420
|
+
return blobs.length;
|
|
2421
|
+
},
|
|
2422
|
+
start() {
|
|
2423
|
+
if (recording) return;
|
|
2424
|
+
blobs.length = 0;
|
|
2425
|
+
recording = true;
|
|
2426
|
+
timerId = window.setInterval(captureFrame, interval);
|
|
2427
|
+
},
|
|
2428
|
+
async stop() {
|
|
2429
|
+
if (!recording) return "";
|
|
2430
|
+
recording = false;
|
|
2431
|
+
clearInterval(timerId);
|
|
2432
|
+
const frames = blobs.slice();
|
|
2433
|
+
if (format === "png-sequence") {
|
|
2434
|
+
return JSON.stringify(frames);
|
|
2435
|
+
}
|
|
2436
|
+
if (format === "webp") {
|
|
2437
|
+
return encodeWebP(frames, fps);
|
|
2438
|
+
}
|
|
2439
|
+
return encodeGif(frames);
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
async function recordAndDownload(canvas, durationMs, options = {}) {
|
|
2444
|
+
const { filename = "asciify-recording", ...recOpts } = options;
|
|
2445
|
+
const recorder = createRecorder(canvas, recOpts);
|
|
2446
|
+
recorder.start();
|
|
2447
|
+
await new Promise((res) => setTimeout(res, durationMs));
|
|
2448
|
+
const dataUrl = await recorder.stop();
|
|
2449
|
+
const ext = options.format === "webp" ? "webm" : options.format === "png-sequence" ? "json" : "gif";
|
|
2450
|
+
const a = document.createElement("a");
|
|
2451
|
+
a.href = dataUrl;
|
|
2452
|
+
a.download = `${filename}.${ext}`;
|
|
2453
|
+
a.click();
|
|
2454
|
+
}
|
|
2455
|
+
|
|
1858
2456
|
// src/webgl-engine.ts
|
|
1859
2457
|
var VERT_SRC = (
|
|
1860
2458
|
/* glsl */
|
|
@@ -2117,8 +2715,8 @@ function makeTexture(gl, filter = gl.NEAREST) {
|
|
|
2117
2715
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2118
2716
|
return t;
|
|
2119
2717
|
}
|
|
2120
|
-
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField"];
|
|
2121
|
-
var HOVER_EFFECTS = ["spotlight", "magnify", "repel", "glow", "colorShift"];
|
|
2718
|
+
var ANIM_STYLES = ["none", "wave", "pulse", "rain", "breathe", "sparkle", "glitch", "spiral", "typewriter", "scatter", "waveField", "ripple", "melt", "orbit", "cellular"];
|
|
2719
|
+
var HOVER_EFFECTS = ["spotlight", "magnify", "repel", "glow", "colorShift", "attract", "shatter", "trail"];
|
|
2122
2720
|
var COLOR_MODES = ["grayscale", "fullcolor", "matrix", "accent"];
|
|
2123
2721
|
function tryCreateWebGLRenderer(canvas) {
|
|
2124
2722
|
const glOrNull = canvas.getContext("webgl2", {
|
|
@@ -2303,6 +2901,6 @@ function tryCreateWebGLRenderer(canvas) {
|
|
|
2303
2901
|
}
|
|
2304
2902
|
}
|
|
2305
2903
|
|
|
2306
|
-
export { ART_STYLE_PRESETS, CHARSETS, DEFAULT_OPTIONS, HOVER_PRESETS, asciiBackground, asciify, asciifyGif, asciifyVideo, generateAnimatedEmbedCode, generateEmbedCode, gifToAsciiFrames, imageToAsciiFrame, mountWaveBackground, renderAuroraBackground, renderFrameToCanvas, renderGridBackground, renderMorphBackground, renderNoiseBackground, renderPulseBackground, renderRainBackground, renderSilkBackground, renderStarsBackground, renderVoidBackground, renderWaveBackground, tryCreateWebGLRenderer, videoToAsciiFrames };
|
|
2904
|
+
export { ART_STYLE_PRESETS, CHARSETS, DEFAULT_OPTIONS, HOVER_PRESETS, PALETTE_THEMES, asciiBackground, asciiText, asciiTextAnsi, asciify, asciifyGif, asciifyVideo, createRecorder, generateAnimatedEmbedCode, generateEmbedCode, gifToAsciiFrames, imageToAsciiFrame, mountWaveBackground, recordAndDownload, renderAuroraBackground, renderCircuitBackground, renderDnaBackground, renderFireBackground, renderFrameToCanvas, renderGridBackground, renderMorphBackground, renderNoiseBackground, renderPulseBackground, renderRainBackground, renderSilkBackground, renderStarsBackground, renderTerrainBackground, renderVoidBackground, renderWaveBackground, tryCreateWebGLRenderer, videoToAsciiFrames };
|
|
2307
2905
|
//# sourceMappingURL=index.js.map
|
|
2308
2906
|
//# sourceMappingURL=index.js.map
|