asciify-engine 1.0.71 → 1.0.72

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 CHANGED
@@ -1,2859 +1 @@
1
- 'use strict';
2
-
3
- var gifuctJs = require('gifuct-js');
4
-
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
- };
14
- var CHARSETS = {
15
- standard: " .:-=+*#%@",
16
- blocks: " \u2591\u2592\u2593\u2588",
17
- minimal: " .:+",
18
- dense: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
19
- binary: "01",
20
- dots: " \u2801\u2803\u2807\u2847\u28C7\u28E7\u28F7\u28FF",
21
- letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
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",
23
- box: " \u25AA\u25FE\u25FC\u25A0\u2588",
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",
29
- circles: " .\xB7:\u2218\u25CB\u25E6\xB0\u2022\u2219",
30
- shadows: " \xB7\u2218\u25E6\u25CB\u25CE\u2299\u25CF\u25C9",
31
- starfield: " \u02D9\xB7\u2218\u2217\u2726\u2727\u2605\u25C6\u25CF",
32
- geometric: " \xB7\u25B3\u25B7\u25C7\u25C8\u25C6\u25A3\u25A0\u2588",
33
- pipes: " \u2576\u2500\u2510\u2514\u251C\u2524\u252C\u2534\u253C\u256C\u2592\u2593\u2588",
34
- waves: " \u02DC\u223C\u2248\u3030\u224B\u223F\u223E\u222D\u222B",
35
- shards: " \u2571\u2572\u2573\u25E4\u25E5\u25E3\u25E2\u25B3\u25B2\u25C6\u25FC\u2588",
36
- smoke: " \xB7\u02D9\u205A\u2056\u2236\u2237\u22EE\u22F0\u22F1\u2234\u2235"
37
- };
38
- var CHARSET_SEQUENCES = {
39
- /** Stars → softcircles → orbs — dreamy space feel */
40
- cosmic: [CHARSETS.starfield, CHARSETS.circles, CHARSETS.shadows],
41
- /** Katakana → braille dots → binary — hacker rain */
42
- rain: [CHARSETS.katakana, CHARSETS.braille, CHARSETS.binary],
43
- /** Box pipes → Claude glyphs → classic — terminal morph */
44
- terminal: [CHARSETS.pipes, CHARSETS.claudeCode, CHARSETS.standard],
45
- /** Shards → blocks → squares — shattering crystal */
46
- crystal: [CHARSETS.shards, CHARSETS.geometric, CHARSETS.blocks],
47
- /** Wave glyphs → smoke dots → circles — fluid / organic */
48
- fluid: [CHARSETS.waves, CHARSETS.smoke, CHARSETS.circles],
49
- /** Dense classic → art → blocks — maximum detail pulse */
50
- pulse: [CHARSETS.dense, CHARSETS.standard, CHARSETS.blocks],
51
- /** Braille → shadows → smoke — ethereal / dream-like */
52
- dream: [CHARSETS.braille, CHARSETS.shadows, CHARSETS.smoke],
53
- /** Geometric shapes → shards → starfield — sci-fi angular */
54
- angular: [CHARSETS.geometric, CHARSETS.shards, CHARSETS.starfield]
55
- };
56
- var ART_STYLE_PRESETS = {
57
- classic: {
58
- renderMode: "ascii",
59
- charset: CHARSETS.standard,
60
- colorMode: "grayscale"
61
- },
62
- particles: {
63
- renderMode: "dots",
64
- colorMode: "fullcolor",
65
- dotSizeRatio: 0.8
66
- },
67
- letters: {
68
- renderMode: "ascii",
69
- charset: CHARSETS.letters,
70
- colorMode: "fullcolor"
71
- },
72
- claudeCode: {
73
- renderMode: "ascii",
74
- charset: CHARSETS.claudeCode,
75
- colorMode: "accent",
76
- accentColor: "#f97316"
77
- },
78
- art: {
79
- renderMode: "ascii",
80
- charset: CHARSETS.dense,
81
- colorMode: "fullcolor"
82
- },
83
- terminal: {
84
- renderMode: "ascii",
85
- charset: CHARSETS.standard,
86
- colorMode: "matrix"
87
- },
88
- box: {
89
- renderMode: "ascii",
90
- charset: CHARSETS.box,
91
- colorMode: "grayscale"
92
- },
93
- lines: {
94
- renderMode: "ascii",
95
- charset: CHARSETS.lines,
96
- colorMode: "fullcolor"
97
- },
98
- braille: {
99
- renderMode: "ascii",
100
- charset: CHARSETS.braille,
101
- colorMode: "fullcolor"
102
- },
103
- katakana: {
104
- renderMode: "ascii",
105
- charset: CHARSETS.katakana,
106
- colorMode: "matrix"
107
- },
108
- musical: {
109
- renderMode: "ascii",
110
- charset: CHARSETS.musical,
111
- colorMode: "accent",
112
- accentColor: "#e040fb"
113
- },
114
- emoji: {
115
- renderMode: "ascii",
116
- charset: CHARSETS.emoji,
117
- colorMode: "fullcolor"
118
- },
119
- circles: {
120
- renderMode: "ascii",
121
- charset: CHARSETS.circles,
122
- colorMode: "accent",
123
- accentColor: "#d4ff00"
124
- },
125
- shadows: {
126
- renderMode: "ascii",
127
- charset: CHARSETS.shadows,
128
- colorMode: "accent",
129
- accentColor: "#50a0ff"
130
- },
131
- starfield: {
132
- renderMode: "ascii",
133
- charset: CHARSETS.starfield,
134
- colorMode: "fullcolor"
135
- },
136
- geometric: {
137
- renderMode: "ascii",
138
- charset: CHARSETS.geometric,
139
- colorMode: "grayscale"
140
- },
141
- pipes: {
142
- renderMode: "ascii",
143
- charset: CHARSETS.pipes,
144
- colorMode: "accent",
145
- accentColor: "#00ff88"
146
- },
147
- waves: {
148
- renderMode: "ascii",
149
- charset: CHARSETS.waves,
150
- colorMode: "fullcolor"
151
- },
152
- shards: {
153
- renderMode: "ascii",
154
- charset: CHARSETS.shards,
155
- colorMode: "grayscale"
156
- },
157
- smoke: {
158
- renderMode: "ascii",
159
- charset: CHARSETS.smoke,
160
- colorMode: "accent",
161
- accentColor: "#c850ff"
162
- }
163
- };
164
- var DEFAULT_OPTIONS = {
165
- fontSize: 10,
166
- charSpacing: 1,
167
- brightness: 0,
168
- contrast: 0,
169
- charset: CHARSETS.standard,
170
- colorMode: "grayscale",
171
- accentColor: "#d4ff00",
172
- invert: false,
173
- renderMode: "ascii",
174
- animationStyle: "none",
175
- animationSpeed: 1,
176
- dotSizeRatio: 0.8,
177
- ditherStrength: 0,
178
- charAspect: 0.55,
179
- normalize: false,
180
- hoverStrength: 0,
181
- hoverRadius: 0.2,
182
- hoverEffect: "spotlight",
183
- hoverColor: "#ffffff",
184
- artStyle: "classic",
185
- customText: "",
186
- chromaKey: null,
187
- chromaKeyTolerance: 60
188
- };
189
- var HOVER_PRESETS = {
190
- none: {
191
- label: "Off",
192
- options: { hoverStrength: 0, hoverEffect: "spotlight", hoverRadius: 0.2, hoverColor: "#ffffff" }
193
- },
194
- subtle: {
195
- label: "Subtle",
196
- options: { hoverStrength: 0.25, hoverEffect: "glow", hoverRadius: 0.12, hoverColor: "#ffffff" }
197
- },
198
- flashlight: {
199
- label: "Flashlight",
200
- options: { hoverStrength: 0.6, hoverEffect: "spotlight", hoverRadius: 0.15, hoverColor: "#fffbe6" }
201
- },
202
- magnifier: {
203
- label: "Magnifier",
204
- options: { hoverStrength: 0.7, hoverEffect: "magnify", hoverRadius: 0.12, hoverColor: "#ffffff" }
205
- },
206
- forceField: {
207
- label: "Force Field",
208
- options: { hoverStrength: 0.7, hoverEffect: "repel", hoverRadius: 0.15, hoverColor: "#a0e8ff" }
209
- },
210
- neon: {
211
- label: "Neon",
212
- options: { hoverStrength: 0.6, hoverEffect: "colorShift", hoverRadius: 0.15, hoverColor: "#d946ef" }
213
- },
214
- fire: {
215
- label: "Fire",
216
- options: { hoverStrength: 0.7, hoverEffect: "spotlight", hoverRadius: 0.15, hoverColor: "#ff6b2b" }
217
- },
218
- ice: {
219
- label: "Ice",
220
- options: { hoverStrength: 0.5, hoverEffect: "glow", hoverRadius: 0.15, hoverColor: "#60d5f7" }
221
- },
222
- gravity: {
223
- label: "Gravity",
224
- options: { hoverStrength: 0.7, hoverEffect: "attract", hoverRadius: 0.18, hoverColor: "#a5d6ff" }
225
- },
226
- shatter: {
227
- label: "Shatter",
228
- options: { hoverStrength: 0.8, hoverEffect: "shatter", hoverRadius: 0.14, hoverColor: "#ff6090" }
229
- },
230
- ghost: {
231
- label: "Ghost",
232
- options: { hoverStrength: 0.55, hoverEffect: "trail", hoverRadius: 0.2, hoverColor: "#b39ddb" }
233
- }
234
- };
235
-
236
- // src/core/utils.ts
237
- function createOffscreenCanvas(width, height) {
238
- const canvas = document.createElement("canvas");
239
- canvas.width = width;
240
- canvas.height = height;
241
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
242
- return { canvas, ctx };
243
- }
244
- function adjustLuminance(lum, brightness, contrast) {
245
- let adjusted = lum + brightness * 255;
246
- const factor = 259 * (contrast * 255 + 255) / (255 * (259 - contrast * 255));
247
- adjusted = factor * (adjusted - 128) + 128;
248
- return Math.max(0, Math.min(255, adjusted));
249
- }
250
- function luminanceToChar(lum, charset, invert) {
251
- const normalized = invert ? 1 - lum / 255 : lum / 255;
252
- const chars = [...charset];
253
- const index = Math.floor(normalized * (chars.length - 1));
254
- return chars[Math.max(0, Math.min(chars.length - 1, index))];
255
- }
256
- function customTextToChar(lum, text, x, y, cols, invert) {
257
- const normalized = invert ? 1 - lum / 255 : lum / 255;
258
- if (normalized < 0.12) return " ";
259
- const chars = [...text];
260
- const pos = y * cols + x;
261
- return chars[pos % chars.length];
262
- }
263
- var BAYER_4X4 = [
264
- [0, 8, 2, 10],
265
- [12, 4, 14, 6],
266
- [3, 11, 1, 9],
267
- [15, 7, 13, 5]
268
- ];
269
- function applyDither(lum, x, y, strength) {
270
- if (strength <= 0) return lum;
271
- const threshold = (BAYER_4X4[y % 4][x % 4] / 16 - 0.5) * strength * 128;
272
- return Math.max(0, Math.min(255, lum + threshold));
273
- }
274
- var GRAY_LUT = new Array(256);
275
- var GREEN_LUT = new Array(256);
276
- for (let _i = 0; _i < 256; _i++) {
277
- GRAY_LUT[_i] = `rgb(${_i},${_i},${_i})`;
278
- GREEN_LUT[_i] = `rgb(0,${_i},0)`;
279
- }
280
- function getCellColorStr(cell, colorMode, acR, acG, acB, _isInverted = false) {
281
- switch (colorMode) {
282
- case "fullcolor":
283
- return `rgb(${cell.r},${cell.g},${cell.b})`;
284
- case "matrix":
285
- return GREEN_LUT[0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0];
286
- case "accent": {
287
- return `rgb(${acR},${acG},${acB})`;
288
- }
289
- default: {
290
- const gray = 0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0;
291
- return GRAY_LUT[gray];
292
- }
293
- }
294
- }
295
- var _colorRGB = [0, 0, 0];
296
- function getCellColorRGB(cell, colorMode, acR, acG, acB, _isInverted = false) {
297
- switch (colorMode) {
298
- case "fullcolor":
299
- _colorRGB[0] = cell.r;
300
- _colorRGB[1] = cell.g;
301
- _colorRGB[2] = cell.b;
302
- break;
303
- case "matrix": {
304
- const mb = 0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0;
305
- _colorRGB[0] = 0;
306
- _colorRGB[1] = mb;
307
- _colorRGB[2] = 0;
308
- break;
309
- }
310
- case "accent": {
311
- _colorRGB[0] = acR;
312
- _colorRGB[1] = acG;
313
- _colorRGB[2] = acB;
314
- break;
315
- }
316
- default: {
317
- const gray = 0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0;
318
- _colorRGB[0] = gray;
319
- _colorRGB[1] = gray;
320
- _colorRGB[2] = gray;
321
- break;
322
- }
323
- }
324
- return _colorRGB;
325
- }
326
- function parseChromaKeyColor(color) {
327
- if (typeof color !== "string") return color;
328
- const canvas = document.createElement("canvas");
329
- canvas.width = 1;
330
- canvas.height = 1;
331
- const ctx = canvas.getContext("2d");
332
- ctx.fillStyle = color;
333
- ctx.fillRect(0, 0, 1, 1);
334
- const d = ctx.getImageData(0, 0, 1, 1).data;
335
- return { r: d[0], g: d[1], b: d[2] };
336
- }
337
-
338
- // src/core/animation.ts
339
- function smoothstep(t) {
340
- return t * t * (3 - 2 * t);
341
- }
342
- function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
343
- if (style === "none") return 1;
344
- const t = time * speed;
345
- switch (style) {
346
- case "wave": {
347
- const wave = Math.sin(x / cols * Math.PI * 4 + t * 3) * 0.5 + 0.5;
348
- const wave2 = Math.sin(y / rows * Math.PI * 3 + t * 2) * 0.5 + 0.5;
349
- return 0.3 + 0.7 * (wave * 0.6 + wave2 * 0.4);
350
- }
351
- case "pulse": {
352
- const cx = cols / 2;
353
- const cy = rows / 2;
354
- const dist = Math.sqrt((x - cx) ** 2 + (y - cy) ** 2);
355
- const maxDist = Math.sqrt(cx ** 2 + cy ** 2);
356
- const ring = Math.sin(dist / maxDist * Math.PI * 6 - t * 4) * 0.5 + 0.5;
357
- return 0.2 + 0.8 * ring;
358
- }
359
- case "rain": {
360
- const drop = Math.sin(y / rows * Math.PI * 8 - t * 5 + x * 0.3) * 0.5 + 0.5;
361
- const fade2 = Math.sin(x / cols * Math.PI * 2 + t) * 0.3 + 0.7;
362
- return 0.1 + 0.9 * drop * fade2;
363
- }
364
- case "breathe": {
365
- const breathe = Math.sin(t * 2) * 0.3 + 0.7;
366
- const subtle = Math.sin((x + y) * 0.1 + t) * 0.1;
367
- return Math.max(0.1, Math.min(1, breathe + subtle));
368
- }
369
- case "sparkle": {
370
- const hash = Math.sin(x * 127.1 + y * 311.7 + Math.floor(t * 8) * 43758.5453) * 43758.5453;
371
- const sparkle = hash - Math.floor(hash);
372
- return sparkle > 0.7 ? 1 : 0.15 + sparkle * 0.4;
373
- }
374
- case "glitch": {
375
- const band = Math.floor(y / (rows * 0.05));
376
- const glitchHash = Math.sin(band * 43.23 + Math.floor(t * 6) * 17.89) * 43758.5453;
377
- const glitchVal = glitchHash - Math.floor(glitchHash);
378
- if (glitchVal > 0.85) {
379
- const flicker = Math.sin(t * 30 + band) * 0.5 + 0.5;
380
- return flicker < 0.3 ? 0 : flicker;
381
- }
382
- return 1;
383
- }
384
- case "spiral": {
385
- const cx = cols / 2;
386
- const cy = rows / 2;
387
- const dx = x - cx;
388
- const dy = y - cy;
389
- const angle = Math.atan2(dy, dx);
390
- const dist = Math.sqrt(dx * dx + dy * dy);
391
- const maxDist = Math.sqrt(cx * cx + cy * cy);
392
- const spiral = Math.sin(angle * 3 + dist / maxDist * Math.PI * 8 - t * 3) * 0.5 + 0.5;
393
- return 0.15 + 0.85 * spiral;
394
- }
395
- case "typewriter": {
396
- const totalCells = cols * rows;
397
- const cellIndex = y * cols + x;
398
- const progress = t * 0.5 % 1;
399
- const revealPoint = progress * totalCells * 1.3;
400
- const dist = cellIndex - revealPoint;
401
- if (dist > 0) return 0;
402
- const fade2 = Math.max(0, 1 + dist / (totalCells * 0.15));
403
- return Math.min(1, fade2);
404
- }
405
- case "scatter": {
406
- const scx = cols / 2;
407
- const scy = rows / 2;
408
- const sdx = x - scx;
409
- const sdy = y - scy;
410
- const sdist = Math.sqrt(sdx * sdx + sdy * sdy) / Math.sqrt(scx * scx + scy * scy);
411
- const phase = Math.sin(t * 1.5) * 0.5 + 0.5;
412
- const threshold = phase;
413
- if (sdist > threshold) {
414
- return Math.max(0, 1 - (sdist - threshold) * 3);
415
- }
416
- return 0.7 + 0.3 * Math.sin(sdist * 10 - t * 2);
417
- }
418
- case "waveField":
419
- return 1;
420
- case "ripple": {
421
- const cx2 = cols / 2;
422
- const cy2 = rows / 2;
423
- const dx2 = x - cx2;
424
- const dy2 = y - cy2;
425
- const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
426
- const maxDist2 = Math.sqrt(cx2 * cx2 + cy2 * cy2);
427
- const ripple = Math.sin(dist2 / maxDist2 * Math.PI * 10 - t * 5) * 0.5 + 0.5;
428
- const fadeEdge = 1 - Math.min(1, dist2 / (maxDist2 * 1.1));
429
- return 0.1 + 0.9 * ripple * (0.4 + fadeEdge * 0.6);
430
- }
431
- case "melt": {
432
- const lagPhase = y / rows * Math.PI;
433
- const drip = Math.sin(lagPhase - t * 1.8 + x * 0.15) * 0.5 + 0.5;
434
- const gravity = Math.max(0, y / rows - 0.1) / 0.9;
435
- return 0.05 + 0.95 * (drip * (1 - gravity * 0.6));
436
- }
437
- case "orbit": {
438
- const ocx = cols / 2;
439
- const ocy = rows / 2;
440
- const odx = x - ocx;
441
- const ody = y - ocy;
442
- const oAngle = Math.atan2(ody, odx);
443
- const oDist = Math.sqrt(odx * odx + ody * ody) / Math.sqrt(ocx * ocx + ocy * ocy);
444
- const orbit = Math.sin(oAngle * 2 + oDist * 6 - t * 2.5) * 0.5 + 0.5;
445
- return 0.1 + 0.9 * orbit;
446
- }
447
- case "cellular": {
448
- const tick = Math.floor(t * 4);
449
- const alive = (cx, cy) => {
450
- const h = Math.sin(cx * 127.1 + cy * 311.7 + tick * 43758.5453) * 43758.5453;
451
- return h - Math.floor(h) > 0.38 ? 1 : 0;
452
- };
453
- const self = alive(x, y);
454
- 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);
455
- const nextAlive = self === 1 ? neighbours === 2 || neighbours === 3 ? 1 : 0 : neighbours === 3 ? 1 : 0;
456
- return nextAlive === 1 ? 1 : 0.05;
457
- }
458
- default:
459
- return 1;
460
- }
461
- }
462
- var _hoverResult = { scale: 1, offsetX: 0, offsetY: 0, glow: 0, colorBlend: 0, proximity: 0 };
463
- function computeHoverEffect(nx, ny, hoverX, hoverY, hoverIntensity, strength, cellW, cellH, effect = "spotlight", radiusFactor = 0.5) {
464
- const dx = nx - hoverX;
465
- const dy = ny - hoverY;
466
- const distSq = dx * dx + dy * dy;
467
- const radius = 0.08 + radiusFactor * 0.35 + strength * 0.04;
468
- if (distSq >= radius * radius) {
469
- _hoverResult.scale = 1;
470
- _hoverResult.offsetX = 0;
471
- _hoverResult.offsetY = 0;
472
- _hoverResult.glow = 0;
473
- _hoverResult.colorBlend = 0;
474
- _hoverResult.proximity = 0;
475
- return _hoverResult;
476
- }
477
- const dist = Math.sqrt(distSq);
478
- const t = 1 - dist / radius;
479
- const eased = smoothstep(t) * hoverIntensity;
480
- let scale = 1;
481
- let offsetX = 0;
482
- let offsetY = 0;
483
- let glow = 0;
484
- let colorBlend = 0;
485
- switch (effect) {
486
- case "spotlight": {
487
- scale = 1 + eased * strength * 1.8;
488
- const angle = Math.atan2(dy, dx);
489
- const pushForce = eased * eased * strength * 0.6;
490
- offsetX = Math.cos(angle) * pushForce * cellW;
491
- offsetY = Math.sin(angle) * pushForce * cellH;
492
- glow = eased * strength * 0.4;
493
- colorBlend = eased * eased * strength * 0.25;
494
- break;
495
- }
496
- case "magnify":
497
- scale = 1 + eased * strength * 2.5;
498
- glow = eased * strength * 0.15;
499
- break;
500
- case "repel": {
501
- scale = 1 + eased * strength * 0.3;
502
- const angle2 = Math.atan2(dy, dx);
503
- const push = eased * eased * strength * 1.2;
504
- offsetX = Math.cos(angle2) * push * cellW;
505
- offsetY = Math.sin(angle2) * push * cellH;
506
- break;
507
- }
508
- case "glow":
509
- glow = eased * strength * 0.8;
510
- colorBlend = eased * strength * 0.4;
511
- break;
512
- case "colorShift":
513
- scale = 1 + eased * strength * 0.4;
514
- glow = eased * strength * 0.2;
515
- colorBlend = eased * strength * 0.7;
516
- break;
517
- case "attract": {
518
- const angle3 = Math.atan2(dy, dx);
519
- const pull = eased * eased * strength * 1;
520
- offsetX = -Math.cos(angle3) * pull * cellW;
521
- offsetY = -Math.sin(angle3) * pull * cellH;
522
- glow = eased * strength * 0.3;
523
- break;
524
- }
525
- case "shatter": {
526
- const angle4 = Math.atan2(dy, dx);
527
- const jitter = Math.sin(dx * 43.7 + dy * 29.3) * 0.5;
528
- const scatter = eased * strength * 1.4 * (0.7 + jitter * 0.3);
529
- offsetX = Math.cos(angle4 + jitter) * scatter * cellW;
530
- offsetY = Math.sin(angle4 + jitter) * scatter * cellH;
531
- scale = Math.max(0.1, 1 - eased * strength * 0.6);
532
- glow = eased * strength * 0.25;
533
- break;
534
- }
535
- case "trail": {
536
- colorBlend = eased * strength * 0.9;
537
- glow = eased * strength * 0.6;
538
- scale = 1 + eased * strength * 0.15;
539
- break;
540
- }
541
- }
542
- _hoverResult.scale = scale;
543
- _hoverResult.offsetX = offsetX;
544
- _hoverResult.offsetY = offsetY;
545
- _hoverResult.glow = glow;
546
- _hoverResult.colorBlend = colorBlend;
547
- _hoverResult.proximity = eased;
548
- return _hoverResult;
549
- }
550
-
551
- // src/backgrounds/_shared.ts
552
- function parseColor(c) {
553
- const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
554
- if (hex) {
555
- const h = hex.length <= 4 ? hex.split("").map((x) => parseInt(x + x, 16)) : [parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16)];
556
- return { r: h[0], g: h[1], b: h[2] };
557
- }
558
- const rgb = c.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/i);
559
- if (rgb) return { r: +rgb[1], g: +rgb[2], b: +rgb[3] };
560
- return null;
561
- }
562
- function fade(t) {
563
- return t * t * t * (t * (t * 6 - 15) + 10);
564
- }
565
- function lerp(a, b, t) {
566
- return a + (b - a) * t;
567
- }
568
- function hash2(ix, iy) {
569
- let n = ix * 127 + iy * 311;
570
- n = n >> 13 ^ n;
571
- return (n * (n * n * 15731 + 789221) + 1376312589 & 2147483647) / 2147483647;
572
- }
573
- function vnoise(x, y) {
574
- const ix = Math.floor(x), iy = Math.floor(y);
575
- const fx = x - ix, fy = y - iy;
576
- const ux = fade(fx), uy = fade(fy);
577
- const v00 = hash2(ix, iy);
578
- const v10 = hash2(ix + 1, iy);
579
- const v01 = hash2(ix, iy + 1);
580
- const v11 = hash2(ix + 1, iy + 1);
581
- return lerp(lerp(v00, v10, ux), lerp(v01, v11, ux), uy);
582
- }
583
- function fbm(x, y) {
584
- return (vnoise(x, y) * 0.5 + vnoise(x * 2.1, y * 2.1) * 0.25 + vnoise(x * 4.3, y * 4.3) * 0.125) / 0.875;
585
- }
586
-
587
- // src/backgrounds/wave.ts
588
- function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
589
- const {
590
- fontSize = 13,
591
- charAspect = 0.62,
592
- lineHeightRatio = 1.4,
593
- chars = " .:-=+*#%@",
594
- baseColor = null,
595
- accentColor = void 0,
596
- accentThreshold = 0.52,
597
- mouseInfluence = 0.55,
598
- mouseFalloff = 2.8,
599
- speed = 1,
600
- vortex = true,
601
- sparkles = true,
602
- breathe = true,
603
- lightMode = false
604
- } = options;
605
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
606
- const charW = fontSize * charAspect;
607
- const lineH = fontSize * lineHeightRatio;
608
- const cols = Math.ceil(width / charW);
609
- const rows = Math.ceil(height / lineH);
610
- const mx = mousePos.x;
611
- const my = mousePos.y;
612
- let acR = 212, acG = 255, acB = 0;
613
- {
614
- const hex = resolvedAccent.replace("#", "");
615
- if (hex.length === 6) {
616
- acR = parseInt(hex.slice(0, 2), 16);
617
- acG = parseInt(hex.slice(2, 4), 16);
618
- acB = parseInt(hex.slice(4, 6), 16);
619
- }
620
- }
621
- ctx.clearRect(0, 0, width, height);
622
- ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
623
- ctx.textBaseline = "top";
624
- const t = time * speed;
625
- const breatheAmp = breathe ? (Math.sin(t * 0.22) * 0.5 + 0.5) * 0.12 : 0;
626
- for (let row = 0; row < rows; row++) {
627
- for (let col = 0; col < cols; col++) {
628
- const nx = col / cols;
629
- const ny = row / rows;
630
- const w1 = Math.sin(col * 0.08 + row * 0.05 + t * 0.6) * 0.5 + 0.5;
631
- const w2 = Math.sin(col * 0.03 - row * 0.07 + t * 0.4) * 0.5 + 0.5;
632
- const w3 = Math.sin(col * 0.05 + row * 0.03 + t * 0.8) * 0.5 + 0.5;
633
- const sinePart = (w1 + w2 + w3) / 3;
634
- const noiseScale = 0.045;
635
- const noiseShift = t * 0.08;
636
- const noisePart = fbm(col * noiseScale + noiseShift, row * noiseScale * 1.4 - noiseShift * 0.7) * 0.5 + 0.5;
637
- const driftFreq = 0.06;
638
- const driftPart = Math.sin((col + row * 0.65) * driftFreq + t * 1.1) * 0.5 + 0.5;
639
- const wavePart = sinePart * 0.45 + noisePart * 0.35 + driftPart * 0.2 + breatheAmp;
640
- const dxRaw = nx - mx;
641
- const dyRaw = ny - my;
642
- const distRaw = Math.sqrt(dxRaw * dxRaw + dyRaw * dyRaw);
643
- let vortexBump = 0;
644
- if (vortex && distRaw < 0.35) {
645
- const angle = Math.atan2(dyRaw, dxRaw);
646
- const swirl = Math.sin(angle * 4 + t * 2.2 - distRaw * 14);
647
- const falloff = Math.max(0, 1 - distRaw / 0.35);
648
- vortexBump = swirl * falloff * falloff * 0.22;
649
- }
650
- const proximity = Math.max(0, 1 - distRaw * mouseFalloff);
651
- const intensity = wavePart * (1 - mouseInfluence) + (proximity + vortexBump * 0.5) * mouseInfluence + vortexBump * 0.15;
652
- const clamped = Math.min(1, Math.max(0, intensity));
653
- let finalIntensity = clamped;
654
- if (sparkles && clamped > 0.72) {
655
- const bucket = Math.floor(t * 8);
656
- const sparkleSeed = hash2(col * 7 + bucket * 3, row * 11 + bucket);
657
- if (sparkleSeed > 0.88) {
658
- finalIntensity = Math.min(1, clamped + (sparkleSeed - 0.88) * 4);
659
- }
660
- }
661
- const charIdx = Math.floor(finalIntensity * (chars.length - 1));
662
- if (chars[charIdx] === " ") continue;
663
- const alpha = 0.015 + finalIntensity * 0.07;
664
- const isAccent = finalIntensity > accentThreshold;
665
- if (isAccent) {
666
- const accentAlpha = Math.min(lightMode ? 0.9 : 0.28, alpha * (lightMode ? 14 : 2.8));
667
- ctx.fillStyle = `rgba(${acR},${acG},${acB},${accentAlpha})`;
668
- } else if (baseColor) {
669
- ctx.fillStyle = baseColor.replace("{a}", String(alpha));
670
- } else if (lightMode) {
671
- ctx.fillStyle = `rgba(55,55,55,${alpha * 7})`;
672
- } else {
673
- ctx.fillStyle = `rgba(255,255,255,${alpha})`;
674
- }
675
- ctx.fillText(chars[charIdx], col * charW, row * lineH);
676
- }
677
- }
678
- }
679
-
680
- // src/core/renderer.ts
681
- function resolveInvert(invert) {
682
- if (invert !== "auto") return invert;
683
- return typeof window !== "undefined" && !window.matchMedia("(prefers-color-scheme: dark)").matches;
684
- }
685
- function cssValueToHex(val) {
686
- const hexMatch = val.match(/^#([0-9a-fA-F]{3,6})$/);
687
- if (hexMatch) {
688
- let h = hexMatch[1];
689
- if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
690
- if (h.length === 6) return h;
691
- }
692
- const rgbMatch = val.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
693
- if (rgbMatch) {
694
- return [rgbMatch[1], rgbMatch[2], rgbMatch[3]].map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
695
- }
696
- return null;
697
- }
698
- function resolveAccentHex(accentColor) {
699
- const v = accentColor || "auto";
700
- if (v !== "auto") return v.replace("#", "");
701
- if (typeof document !== "undefined") {
702
- const rootStyle = getComputedStyle(document.documentElement);
703
- for (const prop of ["--accent-color", "--color-accent", "--accent", "--color-primary", "--primary", "--brand-color"]) {
704
- const hex = cssValueToHex(rootStyle.getPropertyValue(prop).trim());
705
- if (hex) return hex;
706
- }
707
- const native = cssValueToHex(getComputedStyle(document.body).accentColor ?? "");
708
- if (native) return native;
709
- }
710
- const isDark = typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
711
- return isDark ? "faf9f7" : "0d0d0d";
712
- }
713
- function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
714
- const srcWidth = source instanceof HTMLVideoElement ? source.videoWidth : source.width;
715
- const srcHeight = source instanceof HTMLVideoElement ? source.videoHeight : source.height;
716
- if (srcWidth === 0 || srcHeight === 0) {
717
- return { frame: [], cols: 0, rows: 0 };
718
- }
719
- const charAspect = options.charAspect;
720
- const cellW = options.fontSize * options.charSpacing;
721
- const cellH = options.fontSize / charAspect * options.charSpacing;
722
- const renderW = targetWidth || srcWidth;
723
- const renderH = targetHeight || srcHeight;
724
- const cols = Math.floor(renderW / cellW);
725
- const rows = Math.floor(renderH / cellH);
726
- if (cols <= 0 || rows <= 0) {
727
- return { frame: [], cols: 0, rows: 0 };
728
- }
729
- const maxDim = 2048;
730
- const ssX = Math.max(1, Math.min(Math.floor(maxDim / cols), Math.floor(srcWidth / cols)));
731
- const ssY = Math.max(1, Math.min(Math.floor(maxDim / rows), Math.floor(srcHeight / rows)));
732
- const sampleW = cols * ssX;
733
- const sampleH = rows * ssY;
734
- const { ctx } = createOffscreenCanvas(sampleW, sampleH);
735
- ctx.drawImage(source, 0, 0, sampleW, sampleH);
736
- const imageData = ctx.getImageData(0, 0, sampleW, sampleH);
737
- const pixels = imageData.data;
738
- const ck = options.chromaKey;
739
- const ckEnabled = ck != null && ck !== false;
740
- const ckHeuristicGreen = ck === true;
741
- const ckHeuristicBlue = ck === "blue-screen";
742
- let ckRGB = null;
743
- let ckTolSq = 0;
744
- if (ckEnabled && !ckHeuristicGreen && !ckHeuristicBlue) {
745
- ckRGB = parseChromaKeyColor(ck);
746
- ckTolSq = (options.chromaKeyTolerance ?? 60) ** 2;
747
- }
748
- let normMin = 0;
749
- let normRange = 255;
750
- if (options.normalize) {
751
- let lo = 255, hi = 0;
752
- for (let k = 0; k < pixels.length; k += 4) {
753
- if (ckEnabled) {
754
- const pr = pixels[k], pg = pixels[k + 1], pb = pixels[k + 2];
755
- let keyed = false;
756
- if (ckHeuristicGreen) {
757
- keyed = pg > pr * 1.4 && pg > pb * 1.4 && pg > 80;
758
- } else if (ckHeuristicBlue) {
759
- keyed = pb > pr * 1.4 && pb > pg * 1.4 && pb > 80;
760
- } else if (ckRGB !== null) {
761
- const dr = pr - ckRGB.r, dg = pg - ckRGB.g, db = pb - ckRGB.b;
762
- keyed = dr * dr + dg * dg + db * db <= ckTolSq;
763
- }
764
- if (keyed) continue;
765
- }
766
- const l = 0.299 * pixels[k] + 0.587 * pixels[k + 1] + 0.114 * pixels[k + 2];
767
- if (l < lo) lo = l;
768
- if (l > hi) hi = l;
769
- }
770
- normMin = lo;
771
- normRange = hi > lo ? hi - lo : 255;
772
- }
773
- const frame = [];
774
- const invertVal = resolveInvert(options.invert);
775
- const effectiveCharset = ckEnabled ? options.charset.replace(/ /g, "") || options.charset : options.charset;
776
- const ssCount = ssX * ssY;
777
- for (let y = 0; y < rows; y++) {
778
- const row = [];
779
- for (let x = 0; x < cols; x++) {
780
- let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
781
- let keyedCount = 0;
782
- for (let sy = 0; sy < ssY; sy++) {
783
- const rowOff = (y * ssY + sy) * sampleW;
784
- for (let sx = 0; sx < ssX; sx++) {
785
- const i = (rowOff + x * ssX + sx) * 4;
786
- const pr = pixels[i], pg = pixels[i + 1], pb = pixels[i + 2], pa = pixels[i + 3];
787
- if (ckEnabled) {
788
- let keyed = false;
789
- if (ckHeuristicGreen) {
790
- keyed = pg > pr * 1.4 && pg > pb * 1.4 && pg > 80;
791
- } else if (ckHeuristicBlue) {
792
- keyed = pb > pr * 1.4 && pb > pg * 1.4 && pb > 80;
793
- } else if (ckRGB !== null) {
794
- const dr = pr - ckRGB.r, dg = pg - ckRGB.g, db = pb - ckRGB.b;
795
- keyed = dr * dr + dg * dg + db * db <= ckTolSq;
796
- }
797
- if (keyed) {
798
- keyedCount++;
799
- continue;
800
- }
801
- }
802
- sumR += pr;
803
- sumG += pg;
804
- sumB += pb;
805
- sumA += pa;
806
- }
807
- }
808
- if (ckEnabled && keyedCount > ssCount / 2) {
809
- row.push({ char: " ", r: 0, g: 0, b: 0, a: 0 });
810
- continue;
811
- }
812
- const nonKeyed = ssCount - keyedCount;
813
- const r = nonKeyed > 0 ? sumR / nonKeyed : 0;
814
- const g = nonKeyed > 0 ? sumG / nonKeyed : 0;
815
- const b = nonKeyed > 0 ? sumB / nonKeyed : 0;
816
- const a = nonKeyed > 0 ? sumA / nonKeyed : 0;
817
- const rawLum = 0.299 * r + 0.587 * g + 0.114 * b;
818
- const lum = options.normalize ? (rawLum - normMin) / normRange * 255 : rawLum;
819
- const adjustedLum = adjustLuminance(lum, options.brightness, options.contrast);
820
- const ditheredLum = applyDither(adjustedLum, x, y, options.ditherStrength);
821
- const char = options.customText ? customTextToChar(ditheredLum, options.customText, x, y, cols, invertVal) : luminanceToChar(ditheredLum, effectiveCharset, invertVal);
822
- row.push({ char, r, g, b, a, lum: ditheredLum });
823
- }
824
- frame.push(row);
825
- }
826
- return { frame, cols, rows };
827
- }
828
- async function videoToAsciiFrames(video, options, targetWidth, targetHeight, targetFps = 12, maxDuration = 10, onProgress, startTime = 0) {
829
- const duration = Math.min(video.duration - startTime, maxDuration);
830
- const totalFrames = Math.ceil(duration * targetFps);
831
- const frames = [];
832
- let cols = 0;
833
- let rows = 0;
834
- for (let i = 0; i < totalFrames; i++) {
835
- const time = startTime + i / targetFps;
836
- if (time > startTime + duration) break;
837
- video.currentTime = time;
838
- await new Promise((resolve) => {
839
- const handler = () => {
840
- video.removeEventListener("seeked", handler);
841
- resolve();
842
- };
843
- video.addEventListener("seeked", handler);
844
- });
845
- const result = imageToAsciiFrame(video, options, targetWidth, targetHeight);
846
- frames.push(result.frame);
847
- cols = result.cols;
848
- rows = result.rows;
849
- onProgress?.((i + 1) / totalFrames);
850
- }
851
- return { frames, cols, rows, fps: targetFps };
852
- }
853
- async function gifToAsciiFrames(buffer, options, targetWidth, targetHeight, onProgress) {
854
- const gif = gifuctJs.parseGIF(buffer);
855
- const rawFrames = gifuctJs.decompressFrames(gif, true);
856
- if (rawFrames.length === 0) {
857
- return { frames: [], cols: 0, rows: 0, fps: 10 };
858
- }
859
- const gifW = rawFrames[0].dims.width;
860
- const gifH = rawFrames[0].dims.height;
861
- const logicalW = gif.lsd?.width || gifW;
862
- const logicalH = gif.lsd?.height || gifH;
863
- const compCanvas = document.createElement("canvas");
864
- compCanvas.width = logicalW;
865
- compCanvas.height = logicalH;
866
- const compCtx = compCanvas.getContext("2d");
867
- const prevCanvas = document.createElement("canvas");
868
- prevCanvas.width = logicalW;
869
- prevCanvas.height = logicalH;
870
- const prevCtx = prevCanvas.getContext("2d");
871
- const frames = [];
872
- let cols = 0;
873
- let rows = 0;
874
- let totalDelay = 0;
875
- for (const f of rawFrames) {
876
- totalDelay += f.delay || 100;
877
- }
878
- const avgDelay = totalDelay / rawFrames.length;
879
- const fps = Math.round(Math.min(30, Math.max(5, 1e3 / avgDelay)));
880
- const maxFrames = Math.min(rawFrames.length, 300);
881
- for (let i = 0; i < maxFrames; i++) {
882
- const f = rawFrames[i];
883
- const { dims, patch, disposalType } = f;
884
- if (disposalType === 3) {
885
- prevCtx.clearRect(0, 0, logicalW, logicalH);
886
- prevCtx.drawImage(compCanvas, 0, 0);
887
- }
888
- const frameImageData = new ImageData(new Uint8ClampedArray(patch.buffer), dims.width, dims.height);
889
- const tempCanvas = document.createElement("canvas");
890
- tempCanvas.width = dims.width;
891
- tempCanvas.height = dims.height;
892
- const tempCtx = tempCanvas.getContext("2d");
893
- tempCtx.putImageData(frameImageData, 0, 0);
894
- compCtx.drawImage(tempCanvas, dims.left || 0, dims.top || 0);
895
- const result = imageToAsciiFrame(compCanvas, options, targetWidth, targetHeight);
896
- frames.push(result.frame);
897
- cols = result.cols;
898
- rows = result.rows;
899
- if (disposalType === 2) {
900
- compCtx.clearRect(dims.left || 0, dims.top || 0, dims.width, dims.height);
901
- } else if (disposalType === 3) {
902
- compCtx.clearRect(0, 0, logicalW, logicalH);
903
- compCtx.drawImage(prevCanvas, 0, 0);
904
- }
905
- onProgress?.((i + 1) / maxFrames);
906
- }
907
- return { frames, cols, rows, fps };
908
- }
909
- function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, time = 0, hoverPos) {
910
- if (options.animationStyle === "waveField") {
911
- const mouseNorm = hoverPos ? { x: hoverPos.x, y: hoverPos.y } : { x: 0.5, y: 0.5 };
912
- const acHexWF = options.accentColor ? resolveAccentHex(options.accentColor) : "d4ff00";
913
- renderWaveBackground(ctx, canvasWidth, canvasHeight, time, mouseNorm, {
914
- accentColor: `#${acHexWF}`,
915
- accentThreshold: 0.52,
916
- mouseInfluence: options.hoverStrength > 0 ? Math.min(1, 0.3 + options.hoverStrength * 0.5) : 0.55,
917
- mouseFalloff: 2.8,
918
- speed: options.animationSpeed,
919
- vortex: options.hoverStrength > 0,
920
- sparkles: true,
921
- breathe: true
922
- });
923
- return;
924
- }
925
- const rows = frame.length;
926
- if (rows === 0) return;
927
- const cols = frame[0].length;
928
- ctx.clearRect(0, 0, canvasWidth, canvasHeight);
929
- let hasTransparency = false;
930
- const sampleStepY = Math.max(1, rows >> 2);
931
- const sampleStepX = Math.max(1, cols >> 2);
932
- outer:
933
- for (let sampleY = 0; sampleY < rows; sampleY += sampleStepY) {
934
- const row = frame[sampleY];
935
- for (let sampleX = 0; sampleX < cols; sampleX += sampleStepX) {
936
- if (row[sampleX].a < 200) {
937
- hasTransparency = true;
938
- break outer;
939
- }
940
- }
941
- }
942
- if (!hasTransparency) {
943
- const isDarkScheme = typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
944
- ctx.fillStyle = isDarkScheme ? "#0a0a0a" : "#faf9f7";
945
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
946
- }
947
- const cellW = canvasWidth / cols;
948
- const cellH = canvasHeight / rows;
949
- const totalCells = rows * cols;
950
- const hoverIntensity = hoverPos?.intensity ?? 1;
951
- const animationActive = options.animationStyle !== "none";
952
- const suppressHover = animationActive && totalCells > 5e3;
953
- const hoverActive = !suppressHover && !!(hoverPos && options.hoverStrength > 0 && hoverIntensity > 5e-3);
954
- const hc = options.hoverColor || "#ffffff";
955
- const hcR = parseInt(hc.slice(1, 3), 16) || 255;
956
- const hcG = parseInt(hc.slice(3, 5), 16) || 255;
957
- const hcB = parseInt(hc.slice(5, 7), 16) || 255;
958
- const acHex = resolveAccentHex(options.accentColor);
959
- const acR = parseInt(acHex.substring(0, 2), 16) || 255;
960
- const acG = parseInt(acHex.substring(2, 4), 16) || 255;
961
- const acB = parseInt(acHex.substring(4, 6), 16) || 255;
962
- const radiusScale = totalCells > 3e4 ? 0.25 : totalCells > 15e3 ? 0.4 : totalCells > 5e3 ? 0.6 : 1;
963
- const effectiveHoverRadius = options.hoverRadius * radiusScale;
964
- let hoverMinCol = 0, hoverMaxCol = cols, hoverMinRow = 0, hoverMaxRow = rows;
965
- let hoverPosX = 0, hoverPosY = 0;
966
- if (hoverActive && hoverPos) {
967
- hoverPosX = hoverPos.x;
968
- hoverPosY = hoverPos.y;
969
- const hoverNormRadius = 0.08 + effectiveHoverRadius * 0.35 + options.hoverStrength * 0.04;
970
- hoverMinCol = Math.max(0, Math.floor((hoverPosX - hoverNormRadius) * cols) - 1);
971
- hoverMaxCol = Math.min(cols, Math.ceil((hoverPosX + hoverNormRadius) * cols) + 1);
972
- hoverMinRow = Math.max(0, Math.floor((hoverPosY - hoverNormRadius) * rows) - 1);
973
- hoverMaxRow = Math.min(rows, Math.ceil((hoverPosY + hoverNormRadius) * rows) + 1);
974
- }
975
- const animStyle = options.animationStyle;
976
- const animSpeed = options.animationSpeed;
977
- const noAnimation = animStyle === "none";
978
- const hoverStrength = options.hoverStrength;
979
- const hoverEffect = options.hoverEffect;
980
- const hoverRadiusFactor = effectiveHoverRadius;
981
- const isInverted = resolveInvert(options.invert);
982
- const colorMode = options.colorMode;
983
- const TWO_PI = Math.PI * 2;
984
- const invCols = 1 / cols;
985
- const invRows = 1 / rows;
986
- let lastFillStyle = "";
987
- let lastAlpha = -1;
988
- if (options.renderMode === "dots") {
989
- const maxRadius = Math.min(cellW, cellH) * 0.5 * options.dotSizeRatio;
990
- for (let y = 0; y < rows; y++) {
991
- const rowData = frame[y];
992
- for (let x = 0; x < cols; x++) {
993
- const cell = rowData[x];
994
- if (cell.a < 10) continue;
995
- const lum = (0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b) * 0.00392156863;
996
- const intensity = isInverted ? 1 - lum : lum;
997
- if (intensity < 0.02) continue;
998
- const animMul = noAnimation ? 1 : getAnimationMultiplier(x, y, cols, rows, time, animStyle, animSpeed);
999
- let hoverMul = 1;
1000
- let hoverOffX = 0;
1001
- let hoverOffY = 0;
1002
- let hoverGlow = 0;
1003
- let hoverBlend = 0;
1004
- if (hoverActive && x >= hoverMinCol && x <= hoverMaxCol && y >= hoverMinRow && y <= hoverMaxRow) {
1005
- const fx = computeHoverEffect(
1006
- x * invCols,
1007
- y * invRows,
1008
- hoverPosX,
1009
- hoverPosY,
1010
- hoverIntensity,
1011
- hoverStrength,
1012
- cellW,
1013
- cellH,
1014
- hoverEffect,
1015
- hoverRadiusFactor
1016
- );
1017
- hoverMul = fx.scale;
1018
- hoverOffX = fx.offsetX;
1019
- hoverOffY = fx.offsetY;
1020
- hoverGlow = fx.glow;
1021
- hoverBlend = fx.colorBlend;
1022
- }
1023
- const radius = maxRadius * intensity * animMul * hoverMul;
1024
- if (radius < 0.3) continue;
1025
- const px = x * cellW + cellW * 0.5 + hoverOffX;
1026
- const py = y * cellH + cellH * 0.5 + hoverOffY;
1027
- let color;
1028
- if (hoverBlend > 0) {
1029
- const rgb = getCellColorRGB(cell, colorMode, acR, acG, acB, isInverted);
1030
- const cr = Math.min(255, rgb[0] + (hcR - rgb[0]) * hoverBlend | 0);
1031
- const cg = Math.min(255, rgb[1] + (hcG - rgb[1]) * hoverBlend | 0);
1032
- const cb = Math.min(255, rgb[2] + (hcB - rgb[2]) * hoverBlend | 0);
1033
- color = `rgb(${cr},${cg},${cb})`;
1034
- } else {
1035
- color = getCellColorStr(cell, colorMode, acR, acG, acB, isInverted);
1036
- }
1037
- const alpha = Math.min(1, cell.a * 0.00392156863 * animMul * (1 + hoverGlow));
1038
- if (alpha !== lastAlpha) {
1039
- ctx.globalAlpha = alpha;
1040
- lastAlpha = alpha;
1041
- }
1042
- if (color !== lastFillStyle) {
1043
- ctx.fillStyle = color;
1044
- lastFillStyle = color;
1045
- }
1046
- if (radius <= 3) {
1047
- const d = radius * 2;
1048
- ctx.fillRect(px - radius, py - radius, d, d);
1049
- } else {
1050
- ctx.beginPath();
1051
- ctx.arc(px, py, radius, 0, TWO_PI);
1052
- ctx.fill();
1053
- }
1054
- }
1055
- }
1056
- } else {
1057
- const charAspect = 0.55;
1058
- const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
1059
- const useFastRect = fontSize < 6;
1060
- if (!useFastRect) {
1061
- const isEmoji = options.artStyle === "emoji";
1062
- ctx.font = isEmoji ? `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif` : `${fontSize}px "JetBrains Mono", monospace`;
1063
- ctx.textAlign = "center";
1064
- ctx.textBaseline = "middle";
1065
- }
1066
- let charWeights = null;
1067
- const dynFrms = options.charsetFrames;
1068
- const hasDyn = !!dynFrms?.length;
1069
- const dynCharset = hasDyn ? dynFrms[Math.floor(Math.max(0, time) * (options.charsetFps ?? 2)) % dynFrms.length] : options.charset;
1070
- if (useFastRect) {
1071
- charWeights = {};
1072
- const csChars = [...dynCharset];
1073
- const csLen = csChars.length;
1074
- for (let i = 0; i < csLen; i++) {
1075
- charWeights[csChars[i]] = Math.max(0.1, (i + 0.3) / csLen);
1076
- }
1077
- }
1078
- const baseTransform = !useFastRect ? ctx.getTransform() : null;
1079
- for (let y = 0; y < rows; y++) {
1080
- const rowData = frame[y];
1081
- for (let x = 0; x < cols; x++) {
1082
- const cell = rowData[x];
1083
- if (cell.a < 10) continue;
1084
- const drawChar = hasDyn && cell.lum != null ? luminanceToChar(cell.lum, dynCharset, isInverted) : cell.char;
1085
- if (drawChar === " ") continue;
1086
- const animMul = noAnimation ? 1 : getAnimationMultiplier(x, y, cols, rows, time, animStyle, animSpeed);
1087
- if (animMul < 0.05) continue;
1088
- let hoverScale = 1;
1089
- let hoverOffX = 0;
1090
- let hoverOffY = 0;
1091
- let hoverGlow = 0;
1092
- let hoverBlend = 0;
1093
- if (hoverActive && x >= hoverMinCol && x <= hoverMaxCol && y >= hoverMinRow && y <= hoverMaxRow) {
1094
- const fx = computeHoverEffect(
1095
- x * invCols,
1096
- y * invRows,
1097
- hoverPosX,
1098
- hoverPosY,
1099
- hoverIntensity,
1100
- hoverStrength,
1101
- cellW,
1102
- cellH,
1103
- hoverEffect,
1104
- hoverRadiusFactor
1105
- );
1106
- hoverScale = fx.scale;
1107
- hoverOffX = fx.offsetX;
1108
- hoverOffY = fx.offsetY;
1109
- hoverGlow = fx.glow;
1110
- hoverBlend = fx.colorBlend;
1111
- }
1112
- const px = x * cellW + cellW * 0.5 + hoverOffX;
1113
- const py = y * cellH + cellH * 0.5 + hoverOffY;
1114
- let color;
1115
- if (hoverBlend > 0) {
1116
- const rgb = getCellColorRGB(cell, colorMode, acR, acG, acB, isInverted);
1117
- const cr = Math.min(255, rgb[0] + (hcR - rgb[0]) * hoverBlend | 0);
1118
- const cg = Math.min(255, rgb[1] + (hcG - rgb[1]) * hoverBlend | 0);
1119
- const cb = Math.min(255, rgb[2] + (hcB - rgb[2]) * hoverBlend | 0);
1120
- color = `rgb(${cr},${cg},${cb})`;
1121
- } else {
1122
- color = getCellColorStr(cell, colorMode, acR, acG, acB, isInverted);
1123
- }
1124
- if (useFastRect) {
1125
- const weight = charWeights[drawChar] ?? 0.5;
1126
- const effAlpha = Math.min(1, cell.a * 0.00392156863 * animMul * (1 + hoverGlow)) * weight;
1127
- if (effAlpha < 0.02) continue;
1128
- if (effAlpha !== lastAlpha) {
1129
- ctx.globalAlpha = effAlpha;
1130
- lastAlpha = effAlpha;
1131
- }
1132
- if (color !== lastFillStyle) {
1133
- ctx.fillStyle = color;
1134
- lastFillStyle = color;
1135
- }
1136
- const rw = cellW * hoverScale;
1137
- const rh = cellH * hoverScale;
1138
- ctx.fillRect(px - rw * 0.5, py - rh * 0.5, rw, rh);
1139
- } else {
1140
- const alpha = Math.min(1, cell.a * 0.00392156863 * animMul * (1 + hoverGlow));
1141
- if (alpha !== lastAlpha) {
1142
- ctx.globalAlpha = alpha;
1143
- lastAlpha = alpha;
1144
- }
1145
- if (color !== lastFillStyle) {
1146
- ctx.fillStyle = color;
1147
- lastFillStyle = color;
1148
- }
1149
- if (hoverScale !== 1) {
1150
- ctx.translate(px, py);
1151
- ctx.scale(hoverScale, hoverScale);
1152
- ctx.fillText(drawChar, 0, 0);
1153
- ctx.setTransform(baseTransform);
1154
- } else {
1155
- ctx.fillText(drawChar, px, py);
1156
- }
1157
- }
1158
- }
1159
- }
1160
- }
1161
- ctx.globalAlpha = 1;
1162
- }
1163
-
1164
- // src/core/simple-api.ts
1165
- function getSourceDims(el) {
1166
- if (el instanceof HTMLVideoElement) return { w: el.videoWidth, h: el.videoHeight };
1167
- if (el instanceof HTMLImageElement) return { w: el.naturalWidth || el.width, h: el.naturalHeight || el.height };
1168
- return { w: el.width, h: el.height };
1169
- }
1170
- function computeRenderDims(srcW, srcH) {
1171
- const MAX = 2048;
1172
- const scale = Math.min(1, MAX / Math.max(srcW, srcH));
1173
- return { renderW: Math.round(srcW * scale), renderH: Math.round(srcH * scale) };
1174
- }
1175
- function sizeCanvasToContainer(canvas, container, aspect, srcW, srcH) {
1176
- const { width, height } = container.getBoundingClientRect();
1177
- if (!width || !height) return { renderW: 0, renderH: 0, dpr: 1 };
1178
- let cssW = width, cssH = cssW / aspect;
1179
- if (cssH > height) {
1180
- cssH = height;
1181
- cssW = cssH * aspect;
1182
- }
1183
- cssW = Math.round(cssW);
1184
- cssH = Math.round(cssH);
1185
- let renderW, renderH;
1186
- if (srcW && srcH) {
1187
- ({ renderW, renderH } = computeRenderDims(srcW, srcH));
1188
- } else {
1189
- renderW = cssW;
1190
- renderH = cssH;
1191
- }
1192
- const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1193
- const MAX_PX = 8e6;
1194
- const cappedDpr = renderW * dpr * renderH * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW * renderH)) : dpr;
1195
- canvas.width = Math.round(renderW * cappedDpr);
1196
- canvas.height = Math.round(renderH * cappedDpr);
1197
- canvas.style.width = cssW + "px";
1198
- canvas.style.height = cssH + "px";
1199
- return { renderW, renderH, dpr: cappedDpr };
1200
- }
1201
- async function asciify(source, canvas, { fontSize, artStyle = "classic", options = {} } = {}) {
1202
- let el;
1203
- if (typeof source === "string") {
1204
- const img = new Image();
1205
- img.crossOrigin = "anonymous";
1206
- await new Promise((resolve, reject) => {
1207
- img.onload = () => resolve();
1208
- img.onerror = () => reject(new Error(`Failed to load image: ${source}`));
1209
- img.src = source;
1210
- });
1211
- el = img;
1212
- } else if (source instanceof HTMLImageElement && !source.complete) {
1213
- await new Promise((resolve, reject) => {
1214
- source.onload = () => resolve();
1215
- source.onerror = () => reject(new Error("Image failed to load"));
1216
- });
1217
- el = source;
1218
- } else {
1219
- el = source;
1220
- }
1221
- const preset = ART_STYLE_PRESETS[artStyle];
1222
- const resolvedFontSize = fontSize ?? options.fontSize ?? 10;
1223
- const merged = { ...DEFAULT_OPTIONS, ...preset, ...options, fontSize: resolvedFontSize };
1224
- const ctx = canvas.getContext("2d");
1225
- if (!ctx) throw new Error("Could not get 2d context from canvas");
1226
- const { w: srcW, h: srcH } = getSourceDims(el);
1227
- const { renderW, renderH } = computeRenderDims(srcW, srcH);
1228
- const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1229
- const MAX_PX = 8e6;
1230
- const cappedDpr = renderW * dpr * renderH * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW * renderH)) : dpr;
1231
- if (canvas.width < renderW || canvas.height < renderH) {
1232
- canvas.width = Math.round(renderW * cappedDpr);
1233
- canvas.height = Math.round(renderH * cappedDpr);
1234
- }
1235
- const { frame } = imageToAsciiFrame(el, merged, renderW, renderH);
1236
- ctx.save();
1237
- ctx.setTransform(cappedDpr, 0, 0, cappedDpr, 0, 0);
1238
- renderFrameToCanvas(ctx, frame, merged, renderW, renderH);
1239
- ctx.restore();
1240
- }
1241
- async function asciifyGif(source, canvas, { fontSize, artStyle = "classic", options = {} } = {}) {
1242
- const buffer = typeof source === "string" ? await fetch(source).then((r) => r.arrayBuffer()) : source;
1243
- const resolvedFontSize = fontSize ?? options.fontSize ?? 10;
1244
- const merged = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[artStyle], ...options, fontSize: resolvedFontSize };
1245
- const ctx = canvas.getContext("2d");
1246
- if (!ctx) throw new Error("Could not get 2d context from canvas");
1247
- const { frames, fps } = await gifToAsciiFrames(buffer, merged, canvas.width, canvas.height);
1248
- let cancelled = false;
1249
- let animId;
1250
- let i = 0;
1251
- let last = performance.now();
1252
- const interval = 1e3 / fps;
1253
- const tick = (now) => {
1254
- if (cancelled) return;
1255
- if (now - last >= interval) {
1256
- renderFrameToCanvas(ctx, frames[i], merged, canvas.width, canvas.height);
1257
- i = (i + 1) % frames.length;
1258
- last = now;
1259
- }
1260
- animId = requestAnimationFrame(tick);
1261
- };
1262
- animId = requestAnimationFrame(tick);
1263
- return () => {
1264
- cancelled = true;
1265
- cancelAnimationFrame(animId);
1266
- };
1267
- }
1268
- async function asciifyVideo(source, canvas, { fontSize, artStyle = "classic", options = {}, fitTo, preExtract = false, trim, onReady, onFrame } = {}) {
1269
- const trimStart = trim?.start ?? 0;
1270
- const trimEnd = trim?.end;
1271
- const resolvedFontSize = fontSize ?? options.fontSize ?? 10;
1272
- const merged = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[artStyle], ...options, fontSize: resolvedFontSize };
1273
- const ctx = canvas.getContext("2d");
1274
- if (!ctx) throw new Error("asciifyVideo: could not get 2d context from canvas.");
1275
- const container = typeof fitTo === "string" ? document.querySelector(fitTo) : fitTo instanceof HTMLElement ? fitTo : null;
1276
- if (preExtract) {
1277
- let video2;
1278
- if (typeof source === "string") {
1279
- video2 = document.createElement("video");
1280
- video2.crossOrigin = "anonymous";
1281
- video2.src = source;
1282
- if (video2.readyState < 2) {
1283
- await new Promise((resolve, reject) => {
1284
- video2.onloadeddata = () => resolve();
1285
- video2.onerror = () => reject(new Error(`asciifyVideo: failed to load "${source}"`));
1286
- });
1287
- }
1288
- } else {
1289
- video2 = source;
1290
- }
1291
- if (container) sizeCanvasToContainer(canvas, container, video2.videoWidth / video2.videoHeight, video2.videoWidth, video2.videoHeight);
1292
- const { renderW: renderW2, renderH: renderH2 } = computeRenderDims(video2.videoWidth, video2.videoHeight);
1293
- const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1294
- const MAX_PX = 8e6;
1295
- const cappedDpr = renderW2 * dpr * renderH2 * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW2 * renderH2)) : dpr;
1296
- if (canvas.width < Math.round(renderW2 * cappedDpr)) {
1297
- canvas.width = Math.round(renderW2 * cappedDpr);
1298
- canvas.height = Math.round(renderH2 * cappedDpr);
1299
- }
1300
- const maxDur = trimEnd !== void 0 ? trimEnd - trimStart : 10;
1301
- const { frames, fps } = await videoToAsciiFrames(video2, merged, renderW2, renderH2, void 0, maxDur, void 0, trimStart);
1302
- let cancelled2 = false, animId2, i = 0, last = performance.now();
1303
- let firstFrame2 = true;
1304
- const interval = 1e3 / fps;
1305
- const tick2 = (now) => {
1306
- if (cancelled2) return;
1307
- if (now - last >= interval) {
1308
- ctx.save();
1309
- ctx.setTransform(cappedDpr, 0, 0, cappedDpr, 0, 0);
1310
- renderFrameToCanvas(ctx, frames[i], merged, renderW2, renderH2);
1311
- ctx.restore();
1312
- i = (i + 1) % frames.length;
1313
- last = now;
1314
- if (firstFrame2) {
1315
- firstFrame2 = false;
1316
- onReady?.(video2);
1317
- }
1318
- onFrame?.();
1319
- }
1320
- animId2 = requestAnimationFrame(tick2);
1321
- };
1322
- animId2 = requestAnimationFrame(tick2);
1323
- return () => {
1324
- cancelled2 = true;
1325
- cancelAnimationFrame(animId2);
1326
- };
1327
- }
1328
- let video;
1329
- let ownedVideo = false;
1330
- if (typeof source === "string") {
1331
- video = document.createElement("video");
1332
- video.src = source;
1333
- video.muted = true;
1334
- video.loop = true;
1335
- video.playsInline = true;
1336
- video.setAttribute("playsinline", "");
1337
- Object.assign(video.style, {
1338
- position: "fixed",
1339
- top: "0",
1340
- left: "0",
1341
- width: "1px",
1342
- height: "1px",
1343
- opacity: "0",
1344
- pointerEvents: "none",
1345
- zIndex: "-1"
1346
- });
1347
- document.body.appendChild(video);
1348
- ownedVideo = true;
1349
- await new Promise((resolve, reject) => {
1350
- video.onloadedmetadata = () => resolve();
1351
- video.onerror = () => reject(new Error(`asciifyVideo: failed to load "${source}"`));
1352
- });
1353
- await video.play().catch(() => {
1354
- });
1355
- } else {
1356
- video = source;
1357
- if (video.paused) await video.play().catch(() => {
1358
- });
1359
- }
1360
- if (trimStart > 0) {
1361
- video.currentTime = trimStart;
1362
- await new Promise((resolve) => {
1363
- const h = () => {
1364
- video.removeEventListener("seeked", h);
1365
- resolve();
1366
- };
1367
- video.addEventListener("seeked", h);
1368
- });
1369
- }
1370
- let timeupdateHandler = null;
1371
- if (trimStart > 0 || trimEnd !== void 0) {
1372
- timeupdateHandler = () => {
1373
- if (trimEnd !== void 0 && video.currentTime >= trimEnd) {
1374
- video.currentTime = trimStart;
1375
- } else if (trimStart > 0 && video.currentTime < trimStart) {
1376
- video.currentTime = trimStart;
1377
- }
1378
- };
1379
- video.addEventListener("timeupdate", timeupdateHandler);
1380
- }
1381
- let ro = null;
1382
- const { renderW, renderH } = computeRenderDims(video.videoWidth, video.videoHeight);
1383
- if (container) {
1384
- const aspect = video.videoWidth / video.videoHeight;
1385
- const vw = video.videoWidth, vh = video.videoHeight;
1386
- const sizing = sizeCanvasToContainer(canvas, container, aspect, vw, vh);
1387
- const sCtx = canvas.getContext("2d");
1388
- if (sCtx) sCtx.setTransform(sizing.dpr, 0, 0, sizing.dpr, 0, 0);
1389
- ro = new ResizeObserver(() => {
1390
- const s = sizeCanvasToContainer(canvas, container, aspect, vw, vh);
1391
- const rCtx = canvas.getContext("2d");
1392
- if (rCtx) rCtx.setTransform(s.dpr, 0, 0, s.dpr, 0, 0);
1393
- });
1394
- ro.observe(container);
1395
- } else {
1396
- const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1397
- const MAX_PX = 8e6;
1398
- const cappedDpr = renderW * dpr * renderH * dpr > MAX_PX ? Math.sqrt(MAX_PX / (renderW * renderH)) : dpr;
1399
- if (canvas.width < Math.round(renderW * cappedDpr)) {
1400
- canvas.width = Math.round(renderW * cappedDpr);
1401
- canvas.height = Math.round(renderH * cappedDpr);
1402
- }
1403
- ctx.setTransform(cappedDpr, 0, 0, cappedDpr, 0, 0);
1404
- }
1405
- let cancelled = false;
1406
- let animId;
1407
- let firstFrame = true;
1408
- const tick = () => {
1409
- if (cancelled) return;
1410
- animId = requestAnimationFrame(tick);
1411
- if (video.readyState < 2 || canvas.width === 0 || canvas.height === 0) return;
1412
- if (trimStart > 0 && video.currentTime < trimStart) return;
1413
- if (trimEnd !== void 0 && video.currentTime >= trimEnd) return;
1414
- const { frame } = imageToAsciiFrame(video, merged, renderW, renderH);
1415
- if (frame.length > 0) {
1416
- renderFrameToCanvas(ctx, frame, merged, renderW, renderH, 0, null);
1417
- if (firstFrame) {
1418
- firstFrame = false;
1419
- onReady?.(video);
1420
- }
1421
- onFrame?.();
1422
- }
1423
- };
1424
- animId = requestAnimationFrame(tick);
1425
- return () => {
1426
- cancelled = true;
1427
- cancelAnimationFrame(animId);
1428
- ro?.disconnect();
1429
- if (timeupdateHandler) video.removeEventListener("timeupdate", timeupdateHandler);
1430
- if (ownedVideo) {
1431
- video.pause();
1432
- video.src = "";
1433
- document.body.removeChild(video);
1434
- }
1435
- };
1436
- }
1437
- function asciifyLiveVideo(source, canvas, opts) {
1438
- return asciifyVideo(source, canvas, opts);
1439
- }
1440
-
1441
- // src/backgrounds/rain.ts
1442
- function renderRainBackground(ctx, width, height, time, options = {}) {
1443
- const {
1444
- fontSize = 13,
1445
- chars = "0123456789ABCDEF@#$&*+=/<>",
1446
- accentColor = void 0,
1447
- color,
1448
- speed = 1,
1449
- density = 0.55,
1450
- tailLength = 14,
1451
- lightMode = false
1452
- } = options;
1453
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1454
- const charW = fontSize * 0.62;
1455
- const lineH = fontSize * 1.4;
1456
- const cols = Math.ceil(width / charW);
1457
- const rows = Math.ceil(height / lineH);
1458
- ctx.clearRect(0, 0, width, height);
1459
- ctx.font = `${fontSize}px monospace`;
1460
- ctx.textBaseline = "top";
1461
- let br = 255, bg = 255, bb = 255;
1462
- if (lightMode) {
1463
- br = 55;
1464
- bg = 55;
1465
- bb = 55;
1466
- }
1467
- if (color) {
1468
- const p = parseColor(color);
1469
- if (p) {
1470
- br = p.r;
1471
- bg = p.g;
1472
- bb = p.b;
1473
- }
1474
- }
1475
- let acR = 212, acG = 255, acB = 0;
1476
- const ap = parseColor(resolvedAccent);
1477
- if (ap) {
1478
- acR = ap.r;
1479
- acG = ap.g;
1480
- acB = ap.b;
1481
- }
1482
- const period = rows + tailLength;
1483
- for (let c = 0; c < cols; c++) {
1484
- if (hash2(c * 17, 3) > density) continue;
1485
- const colSpeed = (0.5 + hash2(c * 31, 7) * 1.5) * speed;
1486
- const phase = hash2(c * 13, 11) * period;
1487
- const headRow = Math.floor((time * colSpeed * 7 + phase) % period);
1488
- const x = c * charW;
1489
- for (let k = 0; k <= tailLength; k++) {
1490
- const row = headRow - (tailLength - k);
1491
- if (row < 0 || row >= rows) continue;
1492
- const y = row * lineH;
1493
- const charSeed = hash2(c * 53 + Math.floor(time * 5 + k), row * 7);
1494
- const ch = chars[Math.floor(charSeed * chars.length)];
1495
- const tRatio = k / tailLength;
1496
- if (k === tailLength) {
1497
- ctx.fillStyle = `rgba(${acR},${acG},${acB},${lightMode ? 0.7 : 0.85})`;
1498
- } else {
1499
- const alpha = lightMode ? tRatio * 0.85 : tRatio * 0.15;
1500
- ctx.fillStyle = `rgba(${br},${bg},${bb},${alpha})`;
1501
- }
1502
- ctx.fillText(ch, x, y);
1503
- }
1504
- }
1505
- }
1506
-
1507
- // src/backgrounds/stars.ts
1508
- function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
1509
- const {
1510
- fontSize = 14,
1511
- chars = " . \xB7 * + \xB0 \u2605",
1512
- accentColor = void 0,
1513
- color,
1514
- speed = 1,
1515
- count = 180,
1516
- lightMode = false
1517
- } = options;
1518
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1519
- ctx.clearRect(0, 0, width, height);
1520
- ctx.textBaseline = "middle";
1521
- ctx.textAlign = "center";
1522
- const cx = width * (0.2 + mousePos.x * 0.6);
1523
- const cy = height * (0.2 + mousePos.y * 0.6);
1524
- const maxR = Math.sqrt(width * width + height * height) * 0.65;
1525
- let br = 255, bg = 255, bb = 255;
1526
- if (lightMode) {
1527
- br = 55;
1528
- bg = 55;
1529
- bb = 55;
1530
- }
1531
- if (color) {
1532
- const p = parseColor(color);
1533
- if (p) {
1534
- br = p.r;
1535
- bg = p.g;
1536
- bb = p.b;
1537
- }
1538
- }
1539
- let acR = 212, acG = 255, acB = 0;
1540
- const ap = parseColor(resolvedAccent);
1541
- if (ap) {
1542
- acR = ap.r;
1543
- acG = ap.g;
1544
- acB = ap.b;
1545
- }
1546
- const charArr = chars.replace(/ /g, "").split("");
1547
- if (charArr.length === 0) return;
1548
- for (let i = 0; i < count; i++) {
1549
- const angle = hash2(i * 17, 3) * Math.PI * 2;
1550
- const baseSpd = 0.15 + hash2(i * 31, 7) * 0.85;
1551
- const phase = hash2(i * 13, 11);
1552
- const r = (time * baseSpd * speed * 0.22 + phase) % 1;
1553
- const x = cx + Math.cos(angle) * r * maxR;
1554
- const y = cy + Math.sin(angle) * r * maxR;
1555
- if (x < -20 || x > width + 20 || y < -20 || y > height + 20) continue;
1556
- const sz = Math.max(6, fontSize * (0.4 + r * 0.9));
1557
- ctx.font = `${sz}px monospace`;
1558
- const charIdx = Math.min(charArr.length - 1, Math.floor(r * charArr.length));
1559
- const ch = charArr[charIdx];
1560
- const isAccent = r > 0.72;
1561
- const alpha = lightMode ? r * 0.85 : r * 0.2;
1562
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${Math.min(lightMode ? 0.92 : 0.32, alpha * 2.2)})` : `rgba(${br},${bg},${bb},${alpha})`;
1563
- ctx.fillText(ch, x, y);
1564
- }
1565
- ctx.textAlign = "left";
1566
- }
1567
-
1568
- // src/backgrounds/pulse.ts
1569
- function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
1570
- const {
1571
- fontSize = 14,
1572
- chars = ". \xB7 \u25CB \u25CE \u25CF",
1573
- accentColor = void 0,
1574
- color,
1575
- rings = 5,
1576
- speed = 1,
1577
- sharpness = 4,
1578
- lightMode = false
1579
- } = options;
1580
- const resolvedAccent = accentColor ?? (lightMode ? "#007a5e" : "#00ffcc");
1581
- ctx.clearRect(0, 0, width, height);
1582
- ctx.textBaseline = "middle";
1583
- ctx.textAlign = "center";
1584
- const cx = width * mousePos.x;
1585
- const cy = height * mousePos.y;
1586
- const maxDist = Math.sqrt(cx * cx + cy * cy) * 1.6 + Math.sqrt(width * width + height * height) * 0.2;
1587
- let br = 255, bg = 255, bb = 255;
1588
- if (lightMode) {
1589
- br = 55;
1590
- bg = 55;
1591
- bb = 55;
1592
- }
1593
- if (color) {
1594
- const p = parseColor(color);
1595
- if (p) {
1596
- br = p.r;
1597
- bg = p.g;
1598
- bb = p.b;
1599
- }
1600
- }
1601
- let acR = 0, acG = 255, acB = 204;
1602
- const ap = parseColor(resolvedAccent);
1603
- if (ap) {
1604
- acR = ap.r;
1605
- acG = ap.g;
1606
- acB = ap.b;
1607
- }
1608
- const charArr = chars.replace(/ /g, "").split("");
1609
- if (charArr.length === 0) return;
1610
- const cols = Math.ceil(width / fontSize);
1611
- const rows = Math.ceil(height / fontSize);
1612
- for (let row = 0; row < rows; row++) {
1613
- for (let col = 0; col < cols; col++) {
1614
- const px = col * fontSize + fontSize * 0.5;
1615
- const py = row * fontSize + fontSize * 0.5;
1616
- const dx = px - cx;
1617
- const dy = py - cy;
1618
- const dist = Math.sqrt(dx * dx + dy * dy);
1619
- const norm = dist / maxDist;
1620
- let totalIntensity = 0;
1621
- for (let r = 0; r < rings; r++) {
1622
- const phase = r / rings;
1623
- const t = (time * speed * 0.38 + phase) % 1;
1624
- const ringDist = Math.abs(norm - t);
1625
- const ringNorm = Math.max(0, 1 - ringDist * maxDist / (fontSize * (12 - sharpness)));
1626
- totalIntensity += Math.cos(ringNorm * Math.PI * 0.5) * ringNorm;
1627
- }
1628
- totalIntensity = Math.min(1, totalIntensity);
1629
- if (totalIntensity < 0.02) continue;
1630
- const isAccent = totalIntensity > 0.6;
1631
- ctx.font = `${fontSize}px monospace`;
1632
- const charIdx = Math.floor(totalIntensity * (charArr.length - 1));
1633
- const ch = charArr[Math.min(charIdx, charArr.length - 1)];
1634
- const alpha = lightMode ? totalIntensity * 0.88 : totalIntensity * 0.22;
1635
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${Math.min(lightMode ? 0.95 : 0.4, totalIntensity * 0.55)})` : `rgba(${br},${bg},${bb},${alpha})`;
1636
- ctx.fillText(ch, px, py);
1637
- }
1638
- }
1639
- ctx.textAlign = "left";
1640
- }
1641
-
1642
- // src/backgrounds/noise.ts
1643
- function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
1644
- const {
1645
- fontSize = 14,
1646
- chars = " .\xB7:;=+*#%@\u2591\u2592\u2593",
1647
- accentColor = void 0,
1648
- color,
1649
- octaves = 4,
1650
- speed = 1,
1651
- scale = 1,
1652
- accentThreshold = 0.78,
1653
- mouseWarp = 0.3,
1654
- lightMode = false
1655
- } = options;
1656
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1657
- const charW = fontSize * 0.62;
1658
- const lineH = fontSize * 1.4;
1659
- const cols = Math.ceil(width / charW);
1660
- const rows = Math.ceil(height / lineH);
1661
- ctx.clearRect(0, 0, width, height);
1662
- ctx.font = `${fontSize}px monospace`;
1663
- ctx.textBaseline = "top";
1664
- let br = 255, bgc = 255, bb = 255;
1665
- if (lightMode) {
1666
- br = 55;
1667
- bgc = 55;
1668
- bb = 55;
1669
- }
1670
- if (color) {
1671
- const p = parseColor(color);
1672
- if (p) {
1673
- br = p.r;
1674
- bgc = p.g;
1675
- bb = p.b;
1676
- }
1677
- }
1678
- let acR = 212, acG = 255, acB = 0;
1679
- const ap = parseColor(resolvedAccent);
1680
- if (ap) {
1681
- acR = ap.r;
1682
- acG = ap.g;
1683
- acB = ap.b;
1684
- }
1685
- const noiseScale = 0.035 * scale;
1686
- const t = time * speed;
1687
- const oct = Math.min(6, Math.max(1, octaves));
1688
- const fbmN = (x, y) => {
1689
- let v = 0, amp = 0.5, freq = 1, norm = 0;
1690
- for (let o = 0; o < oct; o++) {
1691
- v += vnoise(x * freq, y * freq) * amp;
1692
- norm += amp;
1693
- amp *= 0.5;
1694
- freq *= 2.1;
1695
- }
1696
- return v / norm;
1697
- };
1698
- for (let row = 0; row < rows; row++) {
1699
- for (let col = 0; col < cols; col++) {
1700
- const nx = col * noiseScale + t * 0.06;
1701
- const ny = row * noiseScale * 1.3 - t * 0.04;
1702
- const dx = col / cols - mousePos.x;
1703
- const dy = row / rows - mousePos.y;
1704
- const dist = Math.sqrt(dx * dx + dy * dy);
1705
- const warp = mouseWarp > 0 ? Math.max(0, 1 - dist / mouseWarp) * 0.12 : 0;
1706
- const wx = nx + warp * Math.sin(t * 1.3 + dy * 8);
1707
- const wy = ny + warp * Math.cos(t * 0.9 + dx * 8);
1708
- const raw = fbmN(wx, wy);
1709
- const norm2 = raw * 0.5 + 0.5;
1710
- if (norm2 < 0.12) continue;
1711
- const charIdx = Math.floor(norm2 * (chars.length - 1));
1712
- const ch = chars[charIdx];
1713
- if (ch === " ") continue;
1714
- const isAccent = norm2 > accentThreshold;
1715
- const alpha = lightMode ? norm2 * 0.82 : norm2 * 0.13;
1716
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.92 : 0.28})` : `rgba(${br},${bgc},${bb},${alpha})`;
1717
- ctx.fillText(ch, col * charW, row * lineH);
1718
- }
1719
- }
1720
- }
1721
-
1722
- // src/backgrounds/grid.ts
1723
- function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
1724
- const {
1725
- fontSize = 12,
1726
- chars = "\xB7-=+|/",
1727
- accentColor = void 0,
1728
- color,
1729
- bands = 3,
1730
- speed = 1,
1731
- bandWidth = 0.12,
1732
- glitch = true,
1733
- lightMode = false
1734
- } = options;
1735
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1736
- const charW = fontSize * 0.62;
1737
- const lineH = fontSize * 1.4;
1738
- const cols = Math.ceil(width / charW);
1739
- const rows = Math.ceil(height / lineH);
1740
- ctx.clearRect(0, 0, width, height);
1741
- ctx.font = `${fontSize}px monospace`;
1742
- ctx.textBaseline = "top";
1743
- let br = 255, bgv = 255, bb = 255;
1744
- if (lightMode) {
1745
- br = 55;
1746
- bgv = 55;
1747
- bb = 55;
1748
- }
1749
- if (color) {
1750
- const p = parseColor(color);
1751
- if (p) {
1752
- br = p.r;
1753
- bgv = p.g;
1754
- bb = p.b;
1755
- }
1756
- }
1757
- let acR = 212, acG = 255, acB = 0;
1758
- const ap = parseColor(resolvedAccent);
1759
- if (ap) {
1760
- acR = ap.r;
1761
- acG = ap.g;
1762
- acB = ap.b;
1763
- }
1764
- const t = time * speed;
1765
- for (let row = 0; row < rows; row++) {
1766
- for (let col = 0; col < cols; col++) {
1767
- const ny = row / rows;
1768
- const scanPhase = ((ny * bands - t * 0.5) % 1 + 1) % 1;
1769
- const bandIntensity = Math.max(0, 1 - scanPhase / bandWidth);
1770
- const gridSeed = hash2(col * 3, row * 7);
1771
- const gridBase = (gridSeed * 0.5 + 0.5) * 0.35;
1772
- let glitchBump = 0;
1773
- if (glitch) {
1774
- const dx = col / cols - mousePos.x;
1775
- const dy = ny - mousePos.y;
1776
- const d = Math.sqrt(dx * dx + dy * dy);
1777
- if (d < 0.18) {
1778
- const g = hash2(col * 11 + Math.floor(t * 12), row * 5);
1779
- glitchBump = Math.max(0, 1 - d / 0.18) * (g > 0.5 ? g - 0.3 : 0);
1780
- }
1781
- }
1782
- const intensity = Math.min(1, gridBase + bandIntensity * 0.8 + glitchBump * 0.6);
1783
- if (intensity < 0.04) continue;
1784
- const charIdx = Math.floor(intensity * (chars.length - 1));
1785
- const ch = chars[charIdx];
1786
- const isAccent = bandIntensity > 0.55;
1787
- const alpha = lightMode ? intensity * 0.82 : intensity * 0.12;
1788
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.92 : 0.28})` : `rgba(${br},${bgv},${bb},${alpha})`;
1789
- ctx.fillText(ch, col * charW, row * lineH);
1790
- }
1791
- }
1792
- }
1793
-
1794
- // src/backgrounds/aurora.ts
1795
- function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
1796
- const {
1797
- fontSize = 14,
1798
- chars = " \xB7\u2219\u2022:;+=\u2261\u2263#@",
1799
- color,
1800
- accentColor = void 0,
1801
- speed = 1,
1802
- layers = 5,
1803
- softness = 1.2,
1804
- mouseRipple = 0.2,
1805
- lightMode = false
1806
- } = options;
1807
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1808
- const charW = fontSize * 0.62;
1809
- const lineH = fontSize * 1.4;
1810
- const cols = Math.ceil(width / charW);
1811
- const rows = Math.ceil(height / lineH);
1812
- ctx.clearRect(0, 0, width, height);
1813
- ctx.font = `${fontSize}px monospace`;
1814
- ctx.textBaseline = "top";
1815
- let cr = 255, cg = 255, cb = 255;
1816
- if (lightMode) {
1817
- cr = 55;
1818
- cg = 55;
1819
- cb = 55;
1820
- }
1821
- if (color) {
1822
- const p = parseColor(color);
1823
- if (p) {
1824
- cr = p.r;
1825
- cg = p.g;
1826
- cb = p.b;
1827
- }
1828
- }
1829
- let acR = 212, acG = 255, acB = 0;
1830
- const ap = parseColor(resolvedAccent);
1831
- if (ap) {
1832
- acR = ap.r;
1833
- acG = ap.g;
1834
- acB = ap.b;
1835
- }
1836
- const t = time * speed;
1837
- const layerParams = [];
1838
- for (let l = 0; l < layers; l++) {
1839
- const seed = hash2(l * 17, l * 31 + 7);
1840
- const seed2 = hash2(l * 23 + 5, l * 11);
1841
- layerParams.push({
1842
- fx: 0.8 + seed * 2.2,
1843
- fy: 1.2 + seed2 * 1.8,
1844
- phase: seed * Math.PI * 4,
1845
- dt: (0.3 + hash2(l * 7, l * 13 + 3) * 0.5) * (l % 2 === 0 ? 1 : -1),
1846
- amp: 0.55 + hash2(l * 29, l * 3) * 0.45
1847
- });
1848
- }
1849
- for (let row = 0; row < rows; row++) {
1850
- const ny = row / rows;
1851
- for (let col = 0; col < cols; col++) {
1852
- const nx = col / cols;
1853
- const mdx = nx - mousePos.x;
1854
- const mdy = ny - mousePos.y;
1855
- const md = Math.sqrt(mdx * mdx + mdy * mdy);
1856
- const warp = mouseRipple * Math.exp(-md * md / 0.06);
1857
- const wx = nx + mdx * warp;
1858
- const wy = ny + mdy * warp;
1859
- let sum = 0;
1860
- let totalAmp = 0;
1861
- for (let l = 0; l < layers; l++) {
1862
- const { fx, fy, phase, dt, amp } = layerParams[l];
1863
- const wave = Math.sin(wx * fx * Math.PI * 2 + t * dt + phase) * Math.cos(wy * fy * Math.PI * 2 + t * dt * 0.7 + phase * 1.3);
1864
- sum += wave * amp;
1865
- totalAmp += amp;
1866
- }
1867
- const rawVal = sum / totalAmp;
1868
- const curved = 0.5 + 0.5 * Math.tanh(rawVal * softness * 2.2);
1869
- if (curved < 0.12) continue;
1870
- const normalized = (curved - 0.12) / 0.88;
1871
- const charIdx = Math.min(chars.length - 1, Math.floor(normalized * chars.length));
1872
- const ch = chars[charIdx];
1873
- const isAccent = curved > 0.82;
1874
- const alpha = lightMode ? curved * 0.82 : curved * 0.14;
1875
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.92 : 0.32})` : `rgba(${cr},${cg},${cb},${alpha})`;
1876
- ctx.fillText(ch, col * charW, row * lineH);
1877
- }
1878
- }
1879
- }
1880
-
1881
- // src/backgrounds/silk.ts
1882
- function renderSilkBackground(ctx, width, height, time, options = {}) {
1883
- const {
1884
- fontSize = 13,
1885
- color,
1886
- accentColor = void 0,
1887
- speed = 0.4,
1888
- layers = 4,
1889
- turbulence = 0.8,
1890
- lightMode = false
1891
- } = options;
1892
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1893
- const charW = fontSize * 0.62;
1894
- const lineH = fontSize * 1.4;
1895
- const cols = Math.ceil(width / charW);
1896
- const rows = Math.ceil(height / lineH);
1897
- ctx.clearRect(0, 0, width, height);
1898
- ctx.font = `${fontSize}px monospace`;
1899
- ctx.textBaseline = "top";
1900
- let cr = 255, cg = 255, cb = 255;
1901
- if (lightMode) {
1902
- cr = 55;
1903
- cg = 55;
1904
- cb = 55;
1905
- }
1906
- if (color) {
1907
- const p = parseColor(color);
1908
- if (p) {
1909
- cr = p.r;
1910
- cg = p.g;
1911
- cb = p.b;
1912
- }
1913
- }
1914
- let acR = 212, acG = 255, acB = 0;
1915
- const ap = parseColor(resolvedAccent);
1916
- if (ap) {
1917
- acR = ap.r;
1918
- acG = ap.g;
1919
- acB = ap.b;
1920
- }
1921
- const t = time * speed;
1922
- const dirChars = ["\u2500", "\u2500", "\u254C", "\xB7", "\u254C", "\u2500", "\u2500", "\u254C", "\xB7"];
1923
- for (let row = 0; row < rows; row++) {
1924
- const ny = row / rows;
1925
- for (let col = 0; col < cols; col++) {
1926
- const nx = col / cols;
1927
- let angleSum = 0;
1928
- let intensitySum = 0;
1929
- for (let l = 0; l < layers; l++) {
1930
- const ls = hash2(l * 13, l * 7 + 3);
1931
- const ls2 = hash2(l * 29, l * 11 + 1);
1932
- const fx = 1.1 + ls * 2.4;
1933
- const fy = 0.9 + ls2 * 2;
1934
- const ph = ls * Math.PI * 6;
1935
- const dr = (0.2 + hash2(l * 41, l * 17) * 0.5) * (l % 2 === 0 ? 1 : -1.3);
1936
- const u = Math.sin(nx * fx * Math.PI * 2 + t * dr + ph);
1937
- const v = Math.cos(ny * fy * Math.PI * 2 + t * dr * 0.6 + ph * 1.7);
1938
- const cross = Math.sin(nx * fy * Math.PI * turbulence + ny * fx * Math.PI * turbulence + t * dr * 0.4);
1939
- angleSum += Math.atan2(v + cross * 0.3, u);
1940
- intensitySum += (u * v + 1) * 0.5;
1941
- }
1942
- const angle = angleSum / layers;
1943
- const intensity = Math.min(1, intensitySum / layers);
1944
- if (intensity < 0.1) continue;
1945
- const angleNorm = (angle + Math.PI) / (Math.PI * 2);
1946
- const charIdx = Math.floor(angleNorm * dirChars.length) % dirChars.length;
1947
- const ch = dirChars[charIdx];
1948
- const isAccent = intensity > 0.8;
1949
- const alpha = lightMode ? intensity * 0.8 : intensity * 0.13;
1950
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.9 : 0.26})` : `rgba(${cr},${cg},${cb},${alpha})`;
1951
- ctx.fillText(ch, col * charW, row * lineH);
1952
- }
1953
- }
1954
- }
1955
-
1956
- // src/backgrounds/void.ts
1957
- function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
1958
- const {
1959
- fontSize = 13,
1960
- chars = " \xB7:;=+*#%@",
1961
- color,
1962
- accentColor = void 0,
1963
- speed = 1,
1964
- radius = 0.38,
1965
- swirl = 3,
1966
- lightMode = false
1967
- } = options;
1968
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
1969
- const charW = fontSize * 0.62;
1970
- const lineH = fontSize * 1.4;
1971
- const cols = Math.ceil(width / charW);
1972
- const rows = Math.ceil(height / lineH);
1973
- const aspect = width / height;
1974
- ctx.clearRect(0, 0, width, height);
1975
- ctx.font = `${fontSize}px monospace`;
1976
- ctx.textBaseline = "top";
1977
- let cr = 255, cg = 255, cb = 255;
1978
- if (lightMode) {
1979
- cr = 55;
1980
- cg = 55;
1981
- cb = 55;
1982
- }
1983
- if (color) {
1984
- const p = parseColor(color);
1985
- if (p) {
1986
- cr = p.r;
1987
- cg = p.g;
1988
- cb = p.b;
1989
- }
1990
- }
1991
- let acR = 212, acG = 255, acB = 0;
1992
- const ap = parseColor(resolvedAccent);
1993
- if (ap) {
1994
- acR = ap.r;
1995
- acG = ap.g;
1996
- acB = ap.b;
1997
- }
1998
- const t = time * speed;
1999
- for (let row = 0; row < rows; row++) {
2000
- const ny = row / rows;
2001
- for (let col = 0; col < cols; col++) {
2002
- const nx = col / cols;
2003
- const dx = (nx - mousePos.x) * aspect;
2004
- const dy = ny - mousePos.y;
2005
- const dist = Math.sqrt(dx * dx + dy * dy);
2006
- const r = dist / radius;
2007
- if (r > 1) {
2008
- const outerNoise = hash2(col * 3, row * 7) * Math.max(0, 1 - (r - 1) * 3);
2009
- if (outerNoise < 0.62) continue;
2010
- const alpha2 = outerNoise * (lightMode ? 0.28 : 0.04);
2011
- ctx.fillStyle = `rgba(${cr},${cg},${cb},${alpha2})`;
2012
- ctx.fillText(chars[1], col * charW, row * lineH);
2013
- continue;
2014
- }
2015
- const pulseRing = Math.max(0, 1 - Math.abs(r - (0.15 + 0.12 * Math.sin(t * 1.1))) / 0.07);
2016
- const gravity = Math.pow(1 - r, 2.2);
2017
- const intensity = Math.min(1, gravity + pulseRing * 0.6);
2018
- if (intensity < 0.06) continue;
2019
- const densityI = Math.floor(intensity * (chars.length - 1));
2020
- const charIdx = Math.min(chars.length - 1, densityI);
2021
- const ch = chars[charIdx];
2022
- const isAccent = pulseRing > 0.35 || r < 0.08;
2023
- const alpha = lightMode ? intensity * 0.85 : intensity * 0.18;
2024
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.95 : 0.38})` : `rgba(${cr},${cg},${cb},${alpha})`;
2025
- ctx.fillText(ch, col * charW, row * lineH);
2026
- }
2027
- }
2028
- }
2029
-
2030
- // src/backgrounds/morph.ts
2031
- function renderMorphBackground(ctx, width, height, time, options = {}) {
2032
- const {
2033
- fontSize = 14,
2034
- chars = " \xB7\u2219\u2022:-=+*#",
2035
- color,
2036
- accentColor = void 0,
2037
- speed = 0.5,
2038
- harmonics = 3,
2039
- lightMode = false
2040
- } = options;
2041
- const resolvedAccent = accentColor ?? (lightMode ? "#6b8700" : "#d4ff00");
2042
- const charW = fontSize * 0.62;
2043
- const lineH = fontSize * 1.4;
2044
- const cols = Math.ceil(width / charW);
2045
- const rows = Math.ceil(height / lineH);
2046
- ctx.clearRect(0, 0, width, height);
2047
- ctx.font = `${fontSize}px monospace`;
2048
- ctx.textBaseline = "top";
2049
- let cr = 255, cg = 255, cb = 255;
2050
- if (lightMode) {
2051
- cr = 55;
2052
- cg = 55;
2053
- cb = 55;
2054
- }
2055
- if (color) {
2056
- const p = parseColor(color);
2057
- if (p) {
2058
- cr = p.r;
2059
- cg = p.g;
2060
- cb = p.b;
2061
- }
2062
- }
2063
- let acR = 212, acG = 255, acB = 0;
2064
- const ap = parseColor(resolvedAccent);
2065
- if (ap) {
2066
- acR = ap.r;
2067
- acG = ap.g;
2068
- acB = ap.b;
2069
- }
2070
- const t = time * speed;
2071
- const maxV = Array.from({ length: harmonics }, (_, h) => 1 / (h + 1)).reduce((a, b) => a + b, 0);
2072
- for (let row = 0; row < rows; row++) {
2073
- for (let col = 0; col < cols; col++) {
2074
- let v = 0;
2075
- for (let h = 0; h < harmonics; h++) {
2076
- const fBase = hash2(col * (h + 3) + 7, row * (h + 5) + 11);
2077
- const fineF = 0.18 + fBase * 1.4;
2078
- const phase = hash2(col * (h + 7), row * (h + 9) + 3) * Math.PI * 2;
2079
- const weight = 1 / (h + 1);
2080
- v += Math.sin(t * fineF + phase) * weight;
2081
- }
2082
- const norm = (v / maxV + 1) * 0.5;
2083
- if (norm < 0.28) continue;
2084
- const remapped = (norm - 0.28) / 0.72;
2085
- const charIdx = Math.min(chars.length - 1, Math.floor(remapped * chars.length));
2086
- const ch = chars[charIdx];
2087
- const isAccent = norm > 0.88;
2088
- const alpha = lightMode ? remapped * 0.82 : remapped * 0.13;
2089
- ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.92 : 0.28})` : `rgba(${cr},${cg},${cb},${alpha})`;
2090
- ctx.fillText(ch, col * charW, row * lineH);
2091
- }
2092
- }
2093
- }
2094
-
2095
- // src/backgrounds/fire.ts
2096
- function renderFireBackground(ctx, width, height, time, options = {}) {
2097
- const {
2098
- fontSize = 13,
2099
- chars = " .,:;i+xX#&@",
2100
- color = "#ff4500",
2101
- hotColor = "#ffe066",
2102
- intensity = 0.85,
2103
- wind = 0,
2104
- speed = 1,
2105
- lightMode = false
2106
- } = options;
2107
- const charW = fontSize * 0.62;
2108
- const lineH = fontSize * 1.4;
2109
- const cols = Math.ceil(width / charW);
2110
- const rows = Math.ceil(height / lineH);
2111
- const len = cols * rows;
2112
- const key = "__fire_heat__";
2113
- const canvasAny = ctx.canvas;
2114
- let heat = canvasAny[key];
2115
- if (!heat || heat.length !== len) {
2116
- heat = new Float32Array(len);
2117
- canvasAny[key] = heat;
2118
- }
2119
- const dt = 0.016 * speed;
2120
- const coolingRate = 0.18 * dt;
2121
- const windShift = wind * speed * 0.8;
2122
- const baseRow = rows - 1;
2123
- const t = time * speed;
2124
- for (let c = 0; c < cols; c++) {
2125
- const flicker = Math.sin(c * 0.31 + t * 4.1) * 0.5 + 0.5;
2126
- const flicker2 = Math.sin(c * 0.73 - t * 2.7) * 0.5 + 0.5;
2127
- const seed = (flicker * 0.6 + flicker2 * 0.4) * intensity;
2128
- heat[baseRow * cols + c] = Math.min(1, seed + Math.random() * 0.15 * intensity);
2129
- if (baseRow > 0) heat[(baseRow - 1) * cols + c] = Math.min(1, seed * 0.85 + Math.random() * 0.1 * intensity);
2130
- }
2131
- const newHeat = new Float32Array(len);
2132
- for (let r = 0; r < rows - 2; r++) {
2133
- for (let c = 0; c < cols; c++) {
2134
- const below = heat[(r + 1) * cols + c];
2135
- const below2 = heat[(r + 2) * cols + Math.max(0, Math.min(cols - 1, c + Math.round(windShift)))];
2136
- const left = heat[(r + 1) * cols + Math.max(0, c - 1)];
2137
- const right = heat[(r + 1) * cols + Math.min(cols - 1, c + 1)];
2138
- const avg = below * 0.4 + below2 * 0.25 + left * 0.175 + right * 0.175;
2139
- newHeat[r * cols + c] = Math.max(0, avg - coolingRate - Math.random() * 0.02 * speed);
2140
- }
2141
- }
2142
- for (let c = 0; c < cols; c++) {
2143
- newHeat[(rows - 1) * cols + c] = heat[(rows - 1) * cols + c];
2144
- if (rows > 1) newHeat[(rows - 2) * cols + c] = heat[(rows - 2) * cols + c];
2145
- }
2146
- canvasAny[key] = newHeat;
2147
- const cp = parseColor(color) ?? { r: 255, g: 69, b: 0 };
2148
- const hp = parseColor(hotColor) ?? { r: 255, g: 224, b: 102 };
2149
- ctx.clearRect(0, 0, width, height);
2150
- ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
2151
- ctx.textBaseline = "top";
2152
- for (let r = 0; r < rows; r++) {
2153
- for (let c = 0; c < cols; c++) {
2154
- const v = newHeat[r * cols + c];
2155
- if (v < 0.03) continue;
2156
- const charIdx = Math.min(chars.length - 1, Math.floor(v * chars.length));
2157
- const ch = chars[charIdx];
2158
- if (ch === " ") continue;
2159
- const blend = Math.min(1, v * 1.2);
2160
- const r2 = cp.r + (hp.r - cp.r) * blend | 0;
2161
- const g2 = cp.g + (hp.g - cp.g) * blend | 0;
2162
- const b2 = cp.b + (hp.b - cp.b) * blend | 0;
2163
- const alpha = lightMode ? 1 - v * 0.3 : Math.min(1, v + 0.15);
2164
- ctx.globalAlpha = alpha;
2165
- ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
2166
- ctx.fillText(ch, c * charW, r * lineH);
2167
- }
2168
- }
2169
- ctx.globalAlpha = 1;
2170
- }
2171
-
2172
- // src/backgrounds/dna.ts
2173
- function renderDnaBackground(ctx, width, height, time, options = {}) {
2174
- const {
2175
- fontSize = 13,
2176
- baseChars = "ATCG",
2177
- bridgeChars = "-=\u2261",
2178
- color = "#00e5ff",
2179
- color2 = "#ff4081",
2180
- bridgeColor = "#88ffcc",
2181
- speed = 1,
2182
- helixCount,
2183
- lightMode = false
2184
- } = options;
2185
- const charW = fontSize * 0.62;
2186
- const lineH = fontSize * 1.4;
2187
- const cols = Math.ceil(width / charW);
2188
- const rows = Math.ceil(height / lineH);
2189
- const numHelix = helixCount ?? Math.max(1, Math.floor(width / 80));
2190
- const sectionW = cols / numHelix;
2191
- const cp = parseColor(color) ?? { r: 0, g: 229, b: 255 };
2192
- const cp2 = parseColor(color2) ?? { r: 255, g: 64, b: 129 };
2193
- const bp = parseColor(bridgeColor) ?? { r: 136, g: 255, b: 204 };
2194
- ctx.clearRect(0, 0, width, height);
2195
- ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
2196
- ctx.textBaseline = "top";
2197
- const t = time * speed;
2198
- const amplitude = sectionW * 0.35;
2199
- for (let h = 0; h < numHelix; h++) {
2200
- const centerCol = sectionW * (h + 0.5);
2201
- for (let r = 0; r < rows; r++) {
2202
- const phase = r / rows * Math.PI * 6 - t * 1.8;
2203
- const strand1ColF = centerCol + Math.sin(phase) * amplitude;
2204
- const strand2ColF = centerCol + Math.sin(phase + Math.PI) * amplitude;
2205
- const strand1Col = Math.round(strand1ColF);
2206
- const strand2Col = Math.round(strand2ColF);
2207
- if (strand1Col < 0 || strand1Col >= cols) continue;
2208
- const baseSeed1 = hash2(h * 31 + r * 7, 3);
2209
- const ch1 = baseChars[Math.floor(baseSeed1 * baseChars.length)];
2210
- const depth1 = (Math.sin(phase) + 1) * 0.5;
2211
- const depth2 = (Math.sin(phase + Math.PI) + 1) * 0.5;
2212
- ctx.globalAlpha = 0.35 + depth1 * 0.65;
2213
- ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
2214
- ctx.fillText(ch1, strand1Col * charW, r * lineH);
2215
- if (strand2Col >= 0 && strand2Col < cols) {
2216
- const baseSeed2 = hash2(h * 53 + r * 11, 7);
2217
- const ch2 = baseChars[Math.floor(baseSeed2 * baseChars.length)];
2218
- ctx.globalAlpha = 0.35 + depth2 * 0.65;
2219
- ctx.fillStyle = `rgb(${cp2.r},${cp2.g},${cp2.b})`;
2220
- ctx.fillText(ch2, strand2Col * charW, r * lineH);
2221
- }
2222
- const bridgeInterval = 3;
2223
- if (r % bridgeInterval === 0) {
2224
- const minC = Math.min(strand1Col, strand2Col);
2225
- const maxC = Math.max(strand1Col, strand2Col);
2226
- const bridgeLen = maxC - minC;
2227
- if (bridgeLen > 1) {
2228
- const bSeed = hash2(r * 17 + h * 43, 5);
2229
- const bCh = bridgeChars[Math.floor(bSeed * bridgeChars.length)];
2230
- const midBridgeAlpha = (depth1 + depth2) * 0.25 + 0.2;
2231
- ctx.globalAlpha = midBridgeAlpha;
2232
- ctx.fillStyle = `rgb(${bp.r},${bp.g},${bp.b})`;
2233
- for (let bc = minC + 1; bc < maxC; bc++) {
2234
- ctx.fillText(bCh, bc * charW, r * lineH);
2235
- }
2236
- }
2237
- }
2238
- }
2239
- }
2240
- ctx.globalAlpha = 1;
2241
- }
2242
-
2243
- // src/backgrounds/terrain.ts
2244
- function renderTerrainBackground(ctx, width, height, time, options = {}) {
2245
- const {
2246
- fontSize = 13,
2247
- chars = " .,:;+*#@",
2248
- color = "#4caf50",
2249
- skyColor = "#1a237e",
2250
- peakColor = "#e0e0e0",
2251
- speed = 1,
2252
- roughness = 0.55,
2253
- heightScale = 0.55,
2254
- stars = true,
2255
- lightMode = false
2256
- } = options;
2257
- const charW = fontSize * 0.62;
2258
- const lineH = fontSize * 1.4;
2259
- const cols = Math.ceil(width / charW);
2260
- const rows = Math.ceil(height / lineH);
2261
- const cp = parseColor(color) ?? { r: 76, g: 175, b: 80 };
2262
- const sky = parseColor(skyColor) ?? { r: 26, g: 35, b: 126 };
2263
- const peak = parseColor(peakColor) ?? { r: 224, g: 224, b: 224 };
2264
- ctx.clearRect(0, 0, width, height);
2265
- ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
2266
- ctx.textBaseline = "top";
2267
- const scroll = time * speed * 0.4;
2268
- const terrainRow = new Array(cols);
2269
- for (let c = 0; c < cols; c++) {
2270
- const nx = (c / cols + scroll) * roughness * 3;
2271
- const h = (fbm(nx, 0.5) * 0.5 + 0.5) * heightScale;
2272
- terrainRow[c] = Math.floor(h * rows);
2273
- }
2274
- for (let r = 0; r < rows; r++) {
2275
- for (let c = 0; c < cols; c++) {
2276
- const terrainStart = rows - 1 - terrainRow[c];
2277
- const isGround = r >= terrainStart;
2278
- const isNearPeak = r === terrainStart;
2279
- if (!isGround) {
2280
- if (stars) {
2281
- const starSeed = hash2(c * 7 + Math.floor(scroll * 0.3), r * 13);
2282
- if (starSeed > 0.97) {
2283
- const twinkle = Math.sin(time * 2 + starSeed * 100) * 0.3 + 0.7;
2284
- ctx.globalAlpha = twinkle * 0.5;
2285
- ctx.fillStyle = `rgb(${sky.r + 60},${sky.g + 60},${sky.b + 80})`;
2286
- ctx.fillText("\xB7", c * charW, r * lineH);
2287
- }
2288
- }
2289
- continue;
2290
- }
2291
- const depth = (r - terrainStart) / Math.max(1, terrainRow[c]);
2292
- const charIdx = Math.min(chars.length - 1, Math.floor(depth * chars.length));
2293
- const ch = chars[charIdx];
2294
- if (ch === " " && !isNearPeak) continue;
2295
- const blendPeak = isNearPeak ? 1 : Math.max(0, 1 - depth * 4);
2296
- const r2 = cp.r + (peak.r - cp.r) * blendPeak | 0;
2297
- const g2 = cp.g + (peak.g - cp.g) * blendPeak | 0;
2298
- const b2 = cp.b + (peak.b - cp.b) * blendPeak | 0;
2299
- ctx.globalAlpha = 0.5 + depth * 0.5;
2300
- ctx.fillStyle = `rgb(${r2},${g2},${b2})`;
2301
- ctx.fillText(isNearPeak ? chars[chars.length - 1] : ch, c * charW, r * lineH);
2302
- }
2303
- }
2304
- ctx.globalAlpha = 1;
2305
- }
2306
-
2307
- // src/backgrounds/circuit.ts
2308
- var EAST = 1;
2309
- var WEST = 2;
2310
- var NORTH = 4;
2311
- var SOUTH = 8;
2312
- var DIR_CHARS = {
2313
- [EAST | WEST]: "\u2500",
2314
- [NORTH | SOUTH]: "\u2502",
2315
- [EAST | SOUTH]: "\u250C",
2316
- [WEST | SOUTH]: "\u2510",
2317
- [EAST | NORTH]: "\u2514",
2318
- [WEST | NORTH]: "\u2518",
2319
- [EAST | WEST | SOUTH]: "\u252C",
2320
- [EAST | WEST | NORTH]: "\u2534",
2321
- [NORTH | SOUTH | EAST]: "\u251C",
2322
- [NORTH | SOUTH | WEST]: "\u2524",
2323
- [EAST | WEST | NORTH | SOUTH]: "\u253C",
2324
- [EAST]: "\u2576",
2325
- [WEST]: "\u2574",
2326
- [NORTH]: "\u2575",
2327
- [SOUTH]: "\u2577"
2328
- };
2329
- function renderCircuitBackground(ctx, width, height, time, options = {}) {
2330
- const {
2331
- fontSize = 13,
2332
- pulseColor = "#ffffff",
2333
- color = "#00ff88",
2334
- density = 0.38,
2335
- speed = 1,
2336
- lightMode = false
2337
- } = options;
2338
- const charW = fontSize * 0.62;
2339
- const lineH = fontSize * 1.4;
2340
- const cols = Math.ceil(width / charW);
2341
- const rows = Math.ceil(height / lineH);
2342
- const cp = parseColor(color) ?? { r: 0, g: 255, b: 136 };
2343
- const pp = parseColor(pulseColor) ?? { r: 255, g: 255, b: 255 };
2344
- const getConnections = (c, r) => {
2345
- if (hash2(c * 17 + 1, r * 7 + 2) > density) return 0;
2346
- let mask = 0;
2347
- if (c + 1 < cols && hash2(c * 17 + 1, r * 7 + 2) > 0.15) mask |= EAST;
2348
- if (c - 1 >= 0 && hash2((c - 1) * 17 + 1, r * 7 + 2) > 0.15) mask |= WEST;
2349
- if (r + 1 < rows && hash2(c * 17 + 1, (r + 1) * 7 + 2) > 0.15) mask |= SOUTH;
2350
- if (r - 1 >= 0 && hash2(c * 17 + 1, (r - 1) * 7 + 2) > 0.15) mask |= NORTH;
2351
- return mask;
2352
- };
2353
- ctx.clearRect(0, 0, width, height);
2354
- ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
2355
- ctx.textBaseline = "top";
2356
- const t = time * speed;
2357
- for (let r = 0; r < rows; r++) {
2358
- for (let c = 0; c < cols; c++) {
2359
- const mask = getConnections(c, r);
2360
- if (mask === 0) continue;
2361
- const ch = DIR_CHARS[mask] ?? "\xB7";
2362
- const pulsePosH = (t * 8 + hash2(c, r * 23) * 40) % cols;
2363
- const pulsePosV = (t * 6 + hash2(r * 17, c * 11) * 40) % rows;
2364
- const nearH = (mask & EAST || mask & WEST) && Math.abs(c - pulsePosH) < 1.5;
2365
- const nearV = (mask & NORTH || mask & SOUTH) && Math.abs(r - pulsePosV) < 1.5;
2366
- const isPulse = nearH || nearV;
2367
- const baseAlpha = 0.25 + hash2(c * 3, r * 5) * 0.35;
2368
- if (isPulse) {
2369
- ctx.globalAlpha = 0.95;
2370
- ctx.fillStyle = `rgb(${pp.r},${pp.g},${pp.b})`;
2371
- } else {
2372
- ctx.globalAlpha = baseAlpha;
2373
- ctx.fillStyle = `rgb(${cp.r},${cp.g},${cp.b})`;
2374
- }
2375
- ctx.fillText(ch, c * charW, r * lineH);
2376
- }
2377
- }
2378
- ctx.globalAlpha = 1;
2379
- }
2380
-
2381
- // src/backgrounds/index.ts
2382
- function _parseColor(c) {
2383
- const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
2384
- if (hex) {
2385
- const h = hex.length <= 4 ? hex.split("").map((x) => parseInt(x + x, 16)) : [parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16)];
2386
- return { r: h[0], g: h[1], b: h[2] };
2387
- }
2388
- const rgb = c.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/i);
2389
- if (rgb) return { r: +rgb[1], g: +rgb[2], b: +rgb[3] };
2390
- return null;
2391
- }
2392
- var BACKGROUND_TYPES = [
2393
- "wave",
2394
- "rain",
2395
- "stars",
2396
- "pulse",
2397
- "noise",
2398
- "grid",
2399
- "aurora",
2400
- "silk",
2401
- "void",
2402
- "morph",
2403
- "fire",
2404
- "dna",
2405
- "terrain",
2406
- "circuit"
2407
- ];
2408
- function asciiBackground(target, options = {}) {
2409
- const {
2410
- type = "wave",
2411
- opacity = 0.2,
2412
- className,
2413
- zIndex = 0,
2414
- colorScheme = "auto",
2415
- color,
2416
- ...renderOpts
2417
- } = options;
2418
- const container = typeof target === "string" ? document.querySelector(target) : target;
2419
- if (!container) {
2420
- console.warn("[asciify] asciiBackground: target not found", target);
2421
- return { destroy: () => {
2422
- } };
2423
- }
2424
- const prevPosition = container.style.position;
2425
- if (getComputedStyle(container).position === "static") {
2426
- container.style.position = "relative";
2427
- }
2428
- const canvas = document.createElement("canvas");
2429
- canvas.style.cssText = [
2430
- "position:absolute",
2431
- "inset:0",
2432
- "width:100%",
2433
- "height:100%",
2434
- `opacity:${opacity}`,
2435
- "pointer-events:none",
2436
- `z-index:${zIndex}`
2437
- ].join(";");
2438
- if (className) canvas.className = className;
2439
- container.prepend(canvas);
2440
- const ctx = canvas.getContext("2d");
2441
- const dpr = window.devicePixelRatio || 1;
2442
- const mouse = { x: 0.5, y: 0.5 };
2443
- const smoothMouse = { x: 0.5, y: 0.5 };
2444
- const mq = window.matchMedia("(prefers-color-scheme: light)");
2445
- const isLight = () => {
2446
- if (colorScheme === "light") return true;
2447
- if (colorScheme === "dark") return false;
2448
- return mq.matches;
2449
- };
2450
- const parsedColor = color ? _parseColor(color) : null;
2451
- const buildWaveOpts = () => ({
2452
- ...renderOpts,
2453
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2454
- baseColor: parsedColor ? `rgba(${parsedColor.r},${parsedColor.g},${parsedColor.b},{a})` : renderOpts.baseColor
2455
- });
2456
- const buildRainOpts = () => ({
2457
- ...renderOpts,
2458
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2459
- color: color ?? renderOpts.color
2460
- });
2461
- const buildStarsOpts = () => ({
2462
- ...renderOpts,
2463
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2464
- color: color ?? renderOpts.color
2465
- });
2466
- const buildPulseOpts = () => ({
2467
- ...renderOpts,
2468
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2469
- color: color ?? renderOpts.color
2470
- });
2471
- const buildNoiseOpts = () => ({
2472
- ...renderOpts,
2473
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2474
- color: color ?? renderOpts.color
2475
- });
2476
- const buildGridOpts = () => ({
2477
- ...renderOpts,
2478
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2479
- color: color ?? renderOpts.color
2480
- });
2481
- const buildAuroraOpts = () => ({
2482
- ...renderOpts,
2483
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2484
- color: color ?? renderOpts.color
2485
- });
2486
- const buildSilkOpts = () => ({
2487
- ...renderOpts,
2488
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2489
- color: color ?? renderOpts.color
2490
- });
2491
- const buildVoidOpts = () => ({
2492
- ...renderOpts,
2493
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2494
- color: color ?? renderOpts.color
2495
- });
2496
- const buildMorphOpts = () => ({
2497
- ...renderOpts,
2498
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
2499
- color: color ?? renderOpts.color
2500
- });
2501
- const buildFireOpts = () => ({
2502
- ...renderOpts,
2503
- color: color ?? renderOpts.color
2504
- });
2505
- const buildDnaOpts = () => ({
2506
- ...renderOpts,
2507
- color: color ?? renderOpts.color
2508
- });
2509
- const buildTerrainOpts = () => ({
2510
- ...renderOpts,
2511
- color: color ?? renderOpts.color,
2512
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
2513
- });
2514
- const buildCircuitOpts = () => ({
2515
- ...renderOpts,
2516
- color: color ?? renderOpts.color,
2517
- lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight()
2518
- });
2519
- const optsRef = { current: buildWaveOpts() };
2520
- const rebuildOpts = () => {
2521
- if (type === "rain") optsRef.current = buildRainOpts();
2522
- else if (type === "stars") optsRef.current = buildStarsOpts();
2523
- else if (type === "pulse") optsRef.current = buildPulseOpts();
2524
- else if (type === "noise") optsRef.current = buildNoiseOpts();
2525
- else if (type === "grid") optsRef.current = buildGridOpts();
2526
- else if (type === "aurora") optsRef.current = buildAuroraOpts();
2527
- else if (type === "silk") optsRef.current = buildSilkOpts();
2528
- else if (type === "void") optsRef.current = buildVoidOpts();
2529
- else if (type === "morph") optsRef.current = buildMorphOpts();
2530
- else if (type === "fire") optsRef.current = buildFireOpts();
2531
- else if (type === "dna") optsRef.current = buildDnaOpts();
2532
- else if (type === "terrain") optsRef.current = buildTerrainOpts();
2533
- else if (type === "circuit") optsRef.current = buildCircuitOpts();
2534
- else optsRef.current = buildWaveOpts();
2535
- };
2536
- rebuildOpts();
2537
- const onSchemeChange = () => {
2538
- rebuildOpts();
2539
- };
2540
- if (colorScheme === "auto") mq.addEventListener("change", onSchemeChange);
2541
- const resize = () => {
2542
- const r = container.getBoundingClientRect();
2543
- canvas.width = r.width * dpr;
2544
- canvas.height = r.height * dpr;
2545
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
2546
- };
2547
- resize();
2548
- const onMouseMove = (e) => {
2549
- const r = container.getBoundingClientRect();
2550
- mouse.x = (e.clientX - r.left) / r.width;
2551
- mouse.y = (e.clientY - r.top) / r.height;
2552
- };
2553
- const ro = new ResizeObserver(resize);
2554
- ro.observe(container);
2555
- window.addEventListener("mousemove", onMouseMove);
2556
- let time = 0;
2557
- let raf = 0;
2558
- const tick = () => {
2559
- smoothMouse.x += (mouse.x - smoothMouse.x) * 0.07;
2560
- smoothMouse.y += (mouse.y - smoothMouse.y) * 0.07;
2561
- const r = container.getBoundingClientRect();
2562
- if (type === "rain") {
2563
- renderRainBackground(ctx, r.width, r.height, time, optsRef.current);
2564
- } else if (type === "stars") {
2565
- renderStarsBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2566
- } else if (type === "pulse") {
2567
- renderPulseBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2568
- } else if (type === "noise") {
2569
- renderNoiseBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2570
- } else if (type === "grid") {
2571
- renderGridBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2572
- } else if (type === "aurora") {
2573
- renderAuroraBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2574
- } else if (type === "silk") {
2575
- renderSilkBackground(ctx, r.width, r.height, time, optsRef.current);
2576
- } else if (type === "void") {
2577
- renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2578
- } else if (type === "morph") {
2579
- renderMorphBackground(ctx, r.width, r.height, time, optsRef.current);
2580
- } else if (type === "fire") {
2581
- renderFireBackground(ctx, r.width, r.height, time, optsRef.current);
2582
- } else if (type === "dna") {
2583
- renderDnaBackground(ctx, r.width, r.height, time, optsRef.current);
2584
- } else if (type === "terrain") {
2585
- renderTerrainBackground(ctx, r.width, r.height, time, optsRef.current);
2586
- } else if (type === "circuit") {
2587
- renderCircuitBackground(ctx, r.width, r.height, time, optsRef.current);
2588
- } else {
2589
- renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
2590
- }
2591
- time += 0.016;
2592
- raf = requestAnimationFrame(tick);
2593
- };
2594
- raf = requestAnimationFrame(tick);
2595
- return {
2596
- destroy: () => {
2597
- cancelAnimationFrame(raf);
2598
- ro.disconnect();
2599
- if (colorScheme === "auto") mq.removeEventListener("change", onSchemeChange);
2600
- window.removeEventListener("mousemove", onMouseMove);
2601
- canvas.remove();
2602
- container.style.position = prevPosition;
2603
- }
2604
- };
2605
- }
2606
- var mountWaveBackground = asciiBackground;
2607
-
2608
- // src/core/ascii-text.ts
2609
- function asciiText(source, options = {}, targetWidth, targetHeight) {
2610
- const opts = { ...DEFAULT_OPTIONS, ...options };
2611
- const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
2612
- if (!frame.length || cols === 0) return "";
2613
- const lines = [];
2614
- for (const row of frame) {
2615
- lines.push(row.map((cell) => cell.char).join(""));
2616
- }
2617
- return lines.join("\n");
2618
- }
2619
- function asciiTextAnsi(source, options = {}, targetWidth, targetHeight) {
2620
- const opts = { ...DEFAULT_OPTIONS, ...options };
2621
- const { frame, cols } = imageToAsciiFrame(source, opts, targetWidth, targetHeight);
2622
- if (!frame.length || cols === 0) return "";
2623
- const RESET = "\x1B[0m";
2624
- const lines = [];
2625
- for (const row of frame) {
2626
- let line = "";
2627
- for (const cell of row) {
2628
- if (cell.char === " " || cell.a < 10) {
2629
- line += " ";
2630
- } else {
2631
- line += `\x1B[38;2;${cell.r};${cell.g};${cell.b}m${cell.char}${RESET}`;
2632
- }
2633
- }
2634
- lines.push(line);
2635
- }
2636
- return lines.join("\n");
2637
- }
2638
-
2639
- // src/core/text-frame.ts
2640
- function buildTextFrame(text, cols, rows, color = "#505050", opacity = 100) {
2641
- if (!text || cols <= 0 || rows <= 0) return [];
2642
- const parsed = parseColor(color) ?? { r: 80, g: 80, b: 80 };
2643
- const pattern = text;
2644
- const len = pattern.length;
2645
- return Array.from(
2646
- { length: rows },
2647
- (_, row) => Array.from({ length: cols }, (_2, col) => ({
2648
- char: pattern[(row * cols + col) % len],
2649
- r: parsed.r,
2650
- g: parsed.g,
2651
- b: parsed.b,
2652
- a: opacity
2653
- }))
2654
- );
2655
- }
2656
- function renderTextBackground(ctx, width, height, text, options = {}, hoverPos) {
2657
- const {
2658
- fontSize = 10,
2659
- lineHeight = 1.6,
2660
- color = "#505050",
2661
- opacity = 100,
2662
- hoverEffect = "spotlight",
2663
- hoverStrength = 0.85,
2664
- hoverRadius = 0.18,
2665
- hoverColor = "#d4ff00"
2666
- } = options;
2667
- const cols = Math.max(1, Math.floor(width / fontSize));
2668
- const rows = Math.max(1, Math.floor(height / (fontSize * lineHeight)));
2669
- const frame = buildTextFrame(text, cols, rows, color, opacity);
2670
- const renderOpts = {
2671
- ...DEFAULT_OPTIONS,
2672
- hoverEffect,
2673
- hoverStrength,
2674
- hoverRadius,
2675
- hoverColor
2676
- };
2677
- renderFrameToCanvas(ctx, frame, renderOpts, width, height, 0, hoverPos ?? null);
2678
- }
2679
-
2680
- // src/core/record.ts
2681
- function captureSnapshot(canvas, { format = "png", quality = 0.92, scale = 1 } = {}) {
2682
- return new Promise((resolve, reject) => {
2683
- let src = canvas;
2684
- if (scale !== 1) {
2685
- const off = document.createElement("canvas");
2686
- off.width = Math.round(canvas.width * scale);
2687
- off.height = Math.round(canvas.height * scale);
2688
- const offCtx = off.getContext("2d");
2689
- if (!offCtx) {
2690
- reject(new Error("captureSnapshot: could not get 2d context"));
2691
- return;
2692
- }
2693
- offCtx.drawImage(canvas, 0, 0, off.width, off.height);
2694
- src = off;
2695
- }
2696
- src.toBlob(
2697
- (blob) => blob ? resolve(blob) : reject(new Error("captureSnapshot: toBlob returned null")),
2698
- `image/${format}`,
2699
- quality
2700
- );
2701
- });
2702
- }
2703
- async function snapshotAndDownload(canvas, options = {}) {
2704
- const { filename = "asciify-snapshot", format = "png", ...snapOpts } = options;
2705
- const blob = await captureSnapshot(canvas, { format, ...snapOpts });
2706
- const ext = format === "jpeg" ? "jpg" : format;
2707
- const a = document.createElement("a");
2708
- a.href = URL.createObjectURL(blob);
2709
- a.download = `${filename}.${ext}`;
2710
- a.click();
2711
- setTimeout(() => URL.revokeObjectURL(a.href), 1e4);
2712
- }
2713
-
2714
- // src/core/webcam.ts
2715
- async function asciifyWebcam(canvas, {
2716
- fontSize = 10,
2717
- style = "classic",
2718
- options = {},
2719
- liveOptions,
2720
- mirror = true,
2721
- constraints = { facingMode: "user" },
2722
- dpr: dprOverride
2723
- } = {}) {
2724
- if (!navigator.mediaDevices?.getUserMedia) {
2725
- throw new Error("asciifyWebcam: getUserMedia is not supported in this browser.");
2726
- }
2727
- const stream = await navigator.mediaDevices.getUserMedia({ video: constraints });
2728
- const video = document.createElement("video");
2729
- video.srcObject = stream;
2730
- video.muted = true;
2731
- video.playsInline = true;
2732
- await new Promise((resolve, reject) => {
2733
- video.onloadedmetadata = () => resolve();
2734
- video.onerror = () => reject(new Error("asciifyWebcam: video stream failed to load."));
2735
- video.play().catch(reject);
2736
- });
2737
- const merged = {
2738
- ...DEFAULT_OPTIONS,
2739
- ...ART_STYLE_PRESETS[style],
2740
- ...options,
2741
- fontSize
2742
- };
2743
- const ctx = canvas.getContext("2d");
2744
- if (!ctx) throw new Error("asciifyWebcam: could not get 2d context from canvas.");
2745
- const deviceRatio = dprOverride ?? (typeof window !== "undefined" ? window.devicePixelRatio : 1) ?? 1;
2746
- if (deviceRatio !== 1) {
2747
- ctx.scale(deviceRatio, deviceRatio);
2748
- }
2749
- let hoverPos = null;
2750
- const smoothHover = { x: 0.5, y: 0.5, intensity: 0 };
2751
- const onMouseMove = (e) => {
2752
- const rect = canvas.getBoundingClientRect();
2753
- hoverPos = {
2754
- x: (e.clientX - rect.left) / rect.width,
2755
- y: (e.clientY - rect.top) / rect.height
2756
- };
2757
- };
2758
- const onMouseLeave = () => {
2759
- hoverPos = null;
2760
- };
2761
- if (merged.hoverStrength > 0) {
2762
- canvas.addEventListener("mousemove", onMouseMove);
2763
- canvas.addEventListener("mouseleave", onMouseLeave);
2764
- }
2765
- let cancelled = false;
2766
- let animId;
2767
- const startTime = performance.now();
2768
- const tick = (timestamp) => {
2769
- if (cancelled) return;
2770
- if (video.readyState >= video.HAVE_CURRENT_DATA) {
2771
- const displayW = canvas.width / deviceRatio;
2772
- const displayH = canvas.height / deviceRatio;
2773
- const elapsed = (timestamp - startTime) / 1e3;
2774
- const frameOptions = liveOptions ? { ...merged, ...liveOptions() } : merged;
2775
- const wantsHover = frameOptions.hoverStrength > 0;
2776
- if (wantsHover) {
2777
- canvas.addEventListener("mousemove", onMouseMove);
2778
- canvas.addEventListener("mouseleave", onMouseLeave);
2779
- } else {
2780
- canvas.removeEventListener("mousemove", onMouseMove);
2781
- canvas.removeEventListener("mouseleave", onMouseLeave);
2782
- }
2783
- const { frame } = imageToAsciiFrame(video, frameOptions, displayW, displayH);
2784
- if (hoverPos) {
2785
- const dx = hoverPos.x - smoothHover.x;
2786
- const dy = hoverPos.y - smoothHover.y;
2787
- const dist = Math.sqrt(dx * dx + dy * dy);
2788
- const speed = Math.min(0.25, 0.06 + dist * 0.8);
2789
- smoothHover.x += dx * speed;
2790
- smoothHover.y += dy * speed;
2791
- smoothHover.intensity += (1 - smoothHover.intensity) * 0.12;
2792
- } else {
2793
- smoothHover.intensity *= 0.965;
2794
- if (smoothHover.intensity < 3e-3) smoothHover.intensity = 0;
2795
- }
2796
- const hoverArg = smoothHover.intensity > 3e-3 ? { x: smoothHover.x, y: smoothHover.y, intensity: smoothHover.intensity } : null;
2797
- if (mirror) {
2798
- ctx.save();
2799
- ctx.scale(-1, 1);
2800
- ctx.translate(-displayW, 0);
2801
- renderFrameToCanvas(ctx, frame, frameOptions, displayW, displayH, elapsed, hoverArg);
2802
- ctx.restore();
2803
- } else {
2804
- renderFrameToCanvas(ctx, frame, frameOptions, displayW, displayH, elapsed, hoverArg);
2805
- }
2806
- }
2807
- animId = requestAnimationFrame(tick);
2808
- };
2809
- animId = requestAnimationFrame(tick);
2810
- return () => {
2811
- cancelled = true;
2812
- cancelAnimationFrame(animId);
2813
- canvas.removeEventListener("mousemove", onMouseMove);
2814
- canvas.removeEventListener("mouseleave", onMouseLeave);
2815
- stream.getTracks().forEach((t) => t.stop());
2816
- video.srcObject = null;
2817
- };
2818
- }
2819
-
2820
- exports.ART_STYLE_PRESETS = ART_STYLE_PRESETS;
2821
- exports.BACKGROUND_TYPES = BACKGROUND_TYPES;
2822
- exports.CHARSETS = CHARSETS;
2823
- exports.CHARSET_SEQUENCES = CHARSET_SEQUENCES;
2824
- exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
2825
- exports.HOVER_PRESETS = HOVER_PRESETS;
2826
- exports.PALETTE_THEMES = PALETTE_THEMES;
2827
- exports.asciiBackground = asciiBackground;
2828
- exports.asciiText = asciiText;
2829
- exports.asciiTextAnsi = asciiTextAnsi;
2830
- exports.asciify = asciify;
2831
- exports.asciifyGif = asciifyGif;
2832
- exports.asciifyLiveVideo = asciifyLiveVideo;
2833
- exports.asciifyVideo = asciifyVideo;
2834
- exports.asciifyWebcam = asciifyWebcam;
2835
- exports.buildTextFrame = buildTextFrame;
2836
- exports.captureSnapshot = captureSnapshot;
2837
- exports.gifToAsciiFrames = gifToAsciiFrames;
2838
- exports.imageToAsciiFrame = imageToAsciiFrame;
2839
- exports.mountWaveBackground = mountWaveBackground;
2840
- exports.renderAuroraBackground = renderAuroraBackground;
2841
- exports.renderCircuitBackground = renderCircuitBackground;
2842
- exports.renderDnaBackground = renderDnaBackground;
2843
- exports.renderFireBackground = renderFireBackground;
2844
- exports.renderFrameToCanvas = renderFrameToCanvas;
2845
- exports.renderGridBackground = renderGridBackground;
2846
- exports.renderMorphBackground = renderMorphBackground;
2847
- exports.renderNoiseBackground = renderNoiseBackground;
2848
- exports.renderPulseBackground = renderPulseBackground;
2849
- exports.renderRainBackground = renderRainBackground;
2850
- exports.renderSilkBackground = renderSilkBackground;
2851
- exports.renderStarsBackground = renderStarsBackground;
2852
- exports.renderTerrainBackground = renderTerrainBackground;
2853
- exports.renderTextBackground = renderTextBackground;
2854
- exports.renderVoidBackground = renderVoidBackground;
2855
- exports.renderWaveBackground = renderWaveBackground;
2856
- exports.snapshotAndDownload = snapshotAndDownload;
2857
- exports.videoToAsciiFrames = videoToAsciiFrames;
2858
- //# sourceMappingURL=index.cjs.map
2859
- //# sourceMappingURL=index.cjs.map
1
+ "use strict";var t=require("gifuct-js"),e={standard:" .:-=+*#%@",blocks:" ░▒▓█",minimal:" .:+",dense:" .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",binary:"01",dots:" ⠁⠃⠇⡇⣇⣧⣷⣿",letters:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",claudeCode:" ╔╗╚╝║═╠╣╦╩╬░▒▓█│─┌┐└┘├┤┬┴┼",box:" ▪◾◼■█",lines:" ˗‐–—―━",braille:" ⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿",katakana:" ヲァィゥェォャュョッアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン",musical:" ♩♪♫♬♭♮♯",emoji:" ⬛🟫🟥🟧🟨🟩🟦🟪⬜",circles:" .·:∘○◦°•∙",shadows:" ·∘◦○◎⊙●◉",starfield:" ˙·∘∗✦✧★◆●",geometric:" ·△▷◇◈◆▣■█",pipes:" ╶─┐└├┤┬┴┼╬▒▓█",waves:" ˜∼≈〰≋∿∾∭∫",shards:" ╱╲╳◤◥◣◢△▲◆◼█",smoke:" ·˙⁚⁖∶∷⋮⋰⋱∴∵"},o={cosmic:[e.starfield,e.circles,e.shadows],rain:[e.katakana,e.braille,e.binary],terminal:[e.pipes,e.claudeCode,e.standard],crystal:[e.shards,e.geometric,e.blocks],fluid:[e.waves,e.smoke,e.circles],pulse:[e.dense,e.standard,e.blocks],dream:[e.braille,e.shadows,e.smoke],angular:[e.geometric,e.shards,e.starfield]},n={classic:{renderMode:"ascii",charset:e.standard,colorMode:"grayscale"},particles:{renderMode:"dots",colorMode:"fullcolor",dotSizeRatio:.8},letters:{renderMode:"ascii",charset:e.letters,colorMode:"fullcolor"},claudeCode:{renderMode:"ascii",charset:e.claudeCode,colorMode:"accent",accentColor:"#f97316"},art:{renderMode:"ascii",charset:e.dense,colorMode:"fullcolor"},terminal:{renderMode:"ascii",charset:e.standard,colorMode:"matrix"},box:{renderMode:"ascii",charset:e.box,colorMode:"grayscale"},lines:{renderMode:"ascii",charset:e.lines,colorMode:"fullcolor"},braille:{renderMode:"ascii",charset:e.braille,colorMode:"fullcolor"},katakana:{renderMode:"ascii",charset:e.katakana,colorMode:"matrix"},musical:{renderMode:"ascii",charset:e.musical,colorMode:"accent",accentColor:"#e040fb"},emoji:{renderMode:"ascii",charset:e.emoji,colorMode:"fullcolor"},circles:{renderMode:"ascii",charset:e.circles,colorMode:"accent",accentColor:"#d4ff00"},shadows:{renderMode:"ascii",charset:e.shadows,colorMode:"accent",accentColor:"#50a0ff"},starfield:{renderMode:"ascii",charset:e.starfield,colorMode:"fullcolor"},geometric:{renderMode:"ascii",charset:e.geometric,colorMode:"grayscale"},pipes:{renderMode:"ascii",charset:e.pipes,colorMode:"accent",accentColor:"#00ff88"},waves:{renderMode:"ascii",charset:e.waves,colorMode:"fullcolor"},shards:{renderMode:"ascii",charset:e.shards,colorMode:"grayscale"},smoke:{renderMode:"ascii",charset:e.smoke,colorMode:"accent",accentColor:"#c850ff"}},r={fontSize:10,charSpacing:1,brightness:0,contrast:0,charset:e.standard,colorMode:"grayscale",accentColor:"#d4ff00",invert:!1,renderMode:"ascii",animationStyle:"none",animationSpeed:1,dotSizeRatio:.8,ditherStrength:0,charAspect:.55,normalize:!1,hoverStrength:0,hoverRadius:.2,hoverEffect:"spotlight",hoverColor:"#ffffff",artStyle:"classic",customText:"",chromaKey:null,chromaKeyTolerance:60};function a(t,e,o){let n=t+255*e;return n=259*(255*o+255)/(255*(259-255*o))*(n-128)+128,Math.max(0,Math.min(255,n))}function i(t,e,o){const n=o?1-t/255:t/255,r=[...e],a=Math.floor(n*(r.length-1));return r[Math.max(0,Math.min(r.length-1,a))]}function s(t,e,o,n,r,a){if((a?1-t/255:t/255)<.12)return" ";const i=[...e];return i[(n*r+o)%i.length]}var c=[[0,8,2,10],[12,4,14,6],[3,11,1,9],[15,7,13,5]];function l(t,e,o,n){if(n<=0)return t;const r=(c[o%4][e%4]/16-.5)*n*128;return Math.max(0,Math.min(255,t+r))}var h=new Array(256),f=new Array(256);for(let t=0;t<256;t++)h[t]=`rgb(${t},${t},${t})`,f[t]=`rgb(0,${t},0)`;function d(t,e,o,n,r,a=!1){switch(e){case"fullcolor":return`rgb(${t.r},${t.g},${t.b})`;case"matrix":return f[.299*t.r+.587*t.g+.114*t.b|0];case"accent":return`rgb(${o},${n},${r})`;default:{const e=.299*t.r+.587*t.g+.114*t.b|0;return h[e]}}}var g=[0,0,0];function u(t,e,o,n,r,a=!1){switch(e){case"fullcolor":g[0]=t.r,g[1]=t.g,g[2]=t.b;break;case"matrix":{const e=.299*t.r+.587*t.g+.114*t.b|0;g[0]=0,g[1]=e,g[2]=0;break}case"accent":g[0]=o,g[1]=n,g[2]=r;break;default:{const e=.299*t.r+.587*t.g+.114*t.b|0;g[0]=e,g[1]=e,g[2]=e;break}}return g}function m(t,e,o,n,r,a,i){if("none"===a)return 1;const s=r*i;switch(a){case"wave":return.3+.7*(.6*(.5*Math.sin(t/o*Math.PI*4+3*s)+.5)+.4*(.5*Math.sin(e/n*Math.PI*3+2*s)+.5));case"pulse":{const r=o/2,a=n/2,i=Math.sqrt((t-r)**2+(e-a)**2),c=Math.sqrt(r**2+a**2);return.2+.8*(.5*Math.sin(i/c*Math.PI*6-4*s)+.5)}case"rain":return.1+.9*(.5*Math.sin(e/n*Math.PI*8-5*s+.3*t)+.5)*(.3*Math.sin(t/o*Math.PI*2+s)+.7);case"breathe":{const o=.3*Math.sin(2*s)+.7,n=.1*Math.sin(.1*(t+e)+s);return Math.max(.1,Math.min(1,o+n))}case"sparkle":{const o=43758.5453*Math.sin(127.1*t+311.7*e+43758.5453*Math.floor(8*s)),n=o-Math.floor(o);return n>.7?1:.15+.4*n}case"glitch":{const t=Math.floor(e/(.05*n)),o=43758.5453*Math.sin(43.23*t+17.89*Math.floor(6*s));if(o-Math.floor(o)>.85){const e=.5*Math.sin(30*s+t)+.5;return e<.3?0:e}return 1}case"spiral":{const r=o/2,a=n/2,i=t-r,c=e-a,l=Math.atan2(c,i),h=Math.sqrt(i*i+c*c),f=Math.sqrt(r*r+a*a);return.15+.85*(.5*Math.sin(3*l+h/f*Math.PI*8-3*s)+.5)}case"typewriter":{const r=o*n,a=e*o+t-.5*s%1*r*1.3;if(a>0)return 0;const i=Math.max(0,1+a/(.15*r));return Math.min(1,i)}case"scatter":{const r=o/2,a=n/2,i=t-r,c=e-a,l=Math.sqrt(i*i+c*c)/Math.sqrt(r*r+a*a),h=.5*Math.sin(1.5*s)+.5;return l>h?Math.max(0,1-3*(l-h)):.7+.3*Math.sin(10*l-2*s)}case"waveField":default:return 1;case"ripple":{const r=o/2,a=n/2,i=t-r,c=e-a,l=Math.sqrt(i*i+c*c),h=Math.sqrt(r*r+a*a);return.1+.9*(.5*Math.sin(l/h*Math.PI*10-5*s)+.5)*(.4+.6*(1-Math.min(1,l/(1.1*h))))}case"melt":{const o=e/n*Math.PI;return.05+(.5*Math.sin(o-1.8*s+.15*t)+.5)*(1-.6*(Math.max(0,e/n-.1)/.9))*.95}case"orbit":{const r=o/2,a=n/2,i=t-r,c=e-a,l=Math.atan2(c,i),h=Math.sqrt(i*i+c*c)/Math.sqrt(r*r+a*a);return.1+.9*(.5*Math.sin(2*l+6*h-2.5*s)+.5)}case"cellular":{const o=Math.floor(4*s),n=(t,e)=>{const n=43758.5453*Math.sin(127.1*t+311.7*e+43758.5453*o);return n-Math.floor(n)>.38?1:0},r=n(t,e),a=n(t-1,e)+n(t+1,e)+n(t,e-1)+n(t,e+1)+n(t-1,e-1)+n(t+1,e-1)+n(t-1,e+1)+n(t+1,e+1);return 1===(1===r?2===a||3===a?1:0:3===a?1:0)?1:.05}}}var M={scale:1,offsetX:0,offsetY:0,glow:0,colorBlend:0,proximity:0};function p(t,e,o,n,r,a,i,s,c="spotlight",l=.5){const h=t-o,f=e-n,d=h*h+f*f,g=.08+.35*l+.04*a;if(d>=g*g)return M.scale=1,M.offsetX=0,M.offsetY=0,M.glow=0,M.colorBlend=0,M.proximity=0,M;const u=function(t){return t*t*(3-2*t)}(1-Math.sqrt(d)/g)*r;let m=1,p=0,b=0,x=0,v=0;switch(c){case"spotlight":{m=1+u*a*1.8;const t=Math.atan2(f,h),e=u*u*a*.6;p=Math.cos(t)*e*i,b=Math.sin(t)*e*s,x=u*a*.4,v=u*u*a*.25;break}case"magnify":m=1+u*a*2.5,x=u*a*.15;break;case"repel":{m=1+u*a*.3;const t=Math.atan2(f,h),e=u*u*a*1.2;p=Math.cos(t)*e*i,b=Math.sin(t)*e*s;break}case"glow":x=u*a*.8,v=u*a*.4;break;case"colorShift":m=1+u*a*.4,x=u*a*.2,v=u*a*.7;break;case"attract":{const t=Math.atan2(f,h),e=u*u*a*1;p=-Math.cos(t)*e*i,b=-Math.sin(t)*e*s,x=u*a*.3;break}case"shatter":{const t=Math.atan2(f,h),e=.5*Math.sin(43.7*h+29.3*f),o=u*a*1.4*(.7+.3*e);p=Math.cos(t+e)*o*i,b=Math.sin(t+e)*o*s,m=Math.max(.1,1-u*a*.6),x=u*a*.25;break}case"trail":v=u*a*.9,x=u*a*.6,m=1+u*a*.15}return M.scale=m,M.offsetX=p,M.offsetY=b,M.glow=x,M.colorBlend=v,M.proximity=u,M}function b(t){const e=t.match(/^#([0-9a-f]{3,8})$/i)?.[1];if(e){const t=e.length<=4?e.split("").map(t=>parseInt(t+t,16)):[parseInt(e.slice(0,2),16),parseInt(e.slice(2,4),16),parseInt(e.slice(4,6),16)];return{r:t[0],g:t[1],b:t[2]}}const o=t.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/i);return o?{r:+o[1],g:+o[2],b:+o[3]}:null}function x(t){return t*t*t*(t*(6*t-15)+10)}function v(t,e,o){return t+(e-t)*o}function w(t,e){let o=127*t+311*e;return o^=o>>13,(o*(o*o*15731+789221)+1376312589&2147483647)/2147483647}function y(t,e){const o=Math.floor(t),n=Math.floor(e),r=e-n,a=x(t-o),i=x(r),s=w(o,n),c=w(o+1,n),l=w(o,n+1),h=w(o+1,n+1);return v(v(s,c,a),v(l,h,a),i)}function $(t,e){return(.5*y(t,e)+.25*y(2.1*t,2.1*e)+.125*y(4.3*t,4.3*e))/.875}function S(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=13,charAspect:s=.62,lineHeightRatio:c=1.4,chars:l=" .:-=+*#%@",baseColor:h=null,accentColor:f,accentThreshold:d=.52,mouseInfluence:g=.55,mouseFalloff:u=2.8,speed:m=1,vortex:M=!0,sparkles:p=!0,breathe:b=!0,lightMode:x=!1}=a,v=f??(x?"#6b8700":"#d4ff00"),y=i*s,S=i*c,C=Math.ceil(e/y),E=Math.ceil(o/S),T=r.x,R=r.y;let k=212,A=255,I=0;{const t=v.replace("#","");6===t.length&&(k=parseInt(t.slice(0,2),16),A=parseInt(t.slice(2,4),16),I=parseInt(t.slice(4,6),16))}t.clearRect(0,0,e,o),t.font=`${i}px "JetBrains Mono", monospace`,t.textBaseline="top";const B=n*m,P=b?.12*(.5*Math.sin(.22*B)+.5):0;for(let e=0;e<E;e++)for(let o=0;o<C;o++){const n=o/C,r=e/E,a=.045,i=.08*B,s=.06,c=.45*((.5*Math.sin(.08*o+.05*e+.6*B)+.5+(.5*Math.sin(.03*o-.07*e+.4*B)+.5)+(.5*Math.sin(.05*o+.03*e+.8*B)+.5))/3)+.35*(.5*$(o*a+i,e*a*1.4-.7*i)+.5)+.2*(.5*Math.sin((o+.65*e)*s+1.1*B)+.5)+P,f=n-T,m=r-R,b=Math.sqrt(f*f+m*m);let v=0;if(M&&b<.35){const t=Math.atan2(m,f),e=Math.sin(4*t+2.2*B-14*b),o=Math.max(0,1-b/.35);v=e*o*o*.22}const z=c*(1-g)+(Math.max(0,1-b*u)+.5*v)*g+.15*v,F=Math.min(1,Math.max(0,z));let q=F;if(p&&F>.72){const t=Math.floor(8*B),n=w(7*o+3*t,11*e+t);n>.88&&(q=Math.min(1,F+4*(n-.88)))}const L=Math.floor(q*(l.length-1));if(" "===l[L])continue;const H=.015+.07*q;if(q>d){const e=Math.min(x?.9:.28,H*(x?14:2.8));t.fillStyle=`rgba(${k},${A},${I},${e})`}else t.fillStyle=h?h.replace("{a}",String(H)):x?`rgba(55,55,55,${7*H})`:`rgba(255,255,255,${H})`;t.fillText(l[L],o*y,e*S)}}function C(t){return"auto"!==t?t:"undefined"!=typeof window&&!window.matchMedia("(prefers-color-scheme: dark)").matches}function E(t){const e=t.match(/^#([0-9a-fA-F]{3,6})$/);if(e){let t=e[1];if(3===t.length&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),6===t.length)return t}const o=t.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);return o?[o[1],o[2],o[3]].map(t=>parseInt(t).toString(16).padStart(2,"0")).join(""):null}function T(t){const e=t||"auto";if("auto"!==e)return e.replace("#","");if("undefined"!=typeof document){const t=getComputedStyle(document.documentElement);for(const e of["--accent-color","--color-accent","--accent","--color-primary","--primary","--brand-color"]){const o=E(t.getPropertyValue(e).trim());if(o)return o}const e=E(getComputedStyle(document.body).accentColor??"");if(e)return e}return"undefined"!=typeof window&&window.matchMedia("(prefers-color-scheme: dark)").matches?"faf9f7":"0d0d0d"}function R(t,e,o,n){const r=t instanceof HTMLVideoElement?t.videoWidth:t.width,c=t instanceof HTMLVideoElement?t.videoHeight:t.height;if(0===r||0===c)return{frame:[],cols:0,rows:0};const h=e.charAspect,f=e.fontSize*e.charSpacing,d=e.fontSize/h*e.charSpacing,g=o||r,u=n||c,m=Math.floor(g/f),M=Math.floor(u/d);if(m<=0||M<=0)return{frame:[],cols:0,rows:0};const p=Math.max(1,Math.min(Math.floor(2048/m),Math.floor(r/m))),b=Math.max(1,Math.min(Math.floor(2048/M),Math.floor(c/M))),x=m*p,v=M*b,{ctx:w}=function(t,e){const o=document.createElement("canvas");o.width=t,o.height=e;const n=o.getContext("2d",{willReadFrequently:!0});return{canvas:o,ctx:n}}(x,v);w.drawImage(t,0,0,x,v);const y=w.getImageData(0,0,x,v).data,$=e.chromaKey,S=null!=$&&!1!==$,E=!0===$,T="blue-screen"===$;let R=null,k=0;!S||E||T||(R=function(t){if("string"!=typeof t)return t;const e=document.createElement("canvas");e.width=1,e.height=1;const o=e.getContext("2d");o.fillStyle=t,o.fillRect(0,0,1,1);const n=o.getImageData(0,0,1,1).data;return{r:n[0],g:n[1],b:n[2]}}($),k=(e.chromaKeyTolerance??60)**2);let A=0,I=255;if(e.normalize){let t=255,e=0;for(let o=0;o<y.length;o+=4){if(S){const t=y[o],e=y[o+1],n=y[o+2];let r=!1;if(E)r=e>1.4*t&&e>1.4*n&&e>80;else if(T)r=n>1.4*t&&n>1.4*e&&n>80;else if(null!==R){const o=t-R.r,a=e-R.g,i=n-R.b;r=o*o+a*a+i*i<=k}if(r)continue}const n=.299*y[o]+.587*y[o+1]+.114*y[o+2];n<t&&(t=n),n>e&&(e=n)}A=t,I=e>t?e-t:255}const B=[],P=C(e.invert),z=S&&e.charset.replace(/ /g,"")||e.charset,F=p*b;for(let t=0;t<M;t++){const o=[];for(let n=0;n<m;n++){let r=0,c=0,h=0,f=0,d=0;for(let e=0;e<b;e++){const o=(t*b+e)*x;for(let t=0;t<p;t++){const e=4*(o+n*p+t),a=y[e],i=y[e+1],s=y[e+2],l=y[e+3];if(S){let t=!1;if(E)t=i>1.4*a&&i>1.4*s&&i>80;else if(T)t=s>1.4*a&&s>1.4*i&&s>80;else if(null!==R){const e=a-R.r,o=i-R.g,n=s-R.b;t=e*e+o*o+n*n<=k}if(t){d++;continue}}r+=a,c+=i,h+=s,f+=l}}if(S&&d>F/2){o.push({char:" ",r:0,g:0,b:0,a:0});continue}const g=F-d,u=g>0?r/g:0,M=g>0?c/g:0,v=g>0?h/g:0,w=g>0?f/g:0,$=.299*u+.587*M+.114*v,C=l(a(e.normalize?($-A)/I*255:$,e.brightness,e.contrast),n,t,e.ditherStrength),B=e.customText?s(C,e.customText,n,t,m,P):i(C,z,P);o.push({char:B,r:u,g:M,b:v,a:w,lum:C})}B.push(o)}return{frame:B,cols:m,rows:M}}async function k(t,e,o,n,r=12,a=10,i,s=0){const c=Math.min(t.duration-s,a),l=Math.ceil(c*r),h=[];let f=0,d=0;for(let a=0;a<l;a++){const g=s+a/r;if(g>s+c)break;t.currentTime=g,await new Promise(e=>{const o=()=>{t.removeEventListener("seeked",o),e()};t.addEventListener("seeked",o)});const u=R(t,e,o,n);h.push(u.frame),f=u.cols,d=u.rows,i?.((a+1)/l)}return{frames:h,cols:f,rows:d,fps:r}}async function A(e,o,n,r,a){const i=t.parseGIF(e),s=t.decompressFrames(i,!0);if(0===s.length)return{frames:[],cols:0,rows:0,fps:10};const c=s[0].dims.width,l=s[0].dims.height,h=i.lsd?.width||c,f=i.lsd?.height||l,d=document.createElement("canvas");d.width=h,d.height=f;const g=d.getContext("2d"),u=document.createElement("canvas");u.width=h,u.height=f;const m=u.getContext("2d"),M=[];let p=0,b=0,x=0;for(const t of s)x+=t.delay||100;const v=x/s.length,w=Math.round(Math.min(30,Math.max(5,1e3/v))),y=Math.min(s.length,300);for(let t=0;t<y;t++){const e=s[t],{dims:i,patch:c,disposalType:l}=e;3===l&&(m.clearRect(0,0,h,f),m.drawImage(d,0,0));const x=new ImageData(new Uint8ClampedArray(c.buffer),i.width,i.height),v=document.createElement("canvas");v.width=i.width,v.height=i.height;v.getContext("2d").putImageData(x,0,0),g.drawImage(v,i.left||0,i.top||0);const w=R(d,o,n,r);M.push(w.frame),p=w.cols,b=w.rows,2===l?g.clearRect(i.left||0,i.top||0,i.width,i.height):3===l&&(g.clearRect(0,0,h,f),g.drawImage(u,0,0)),a?.((t+1)/y)}return{frames:M,cols:p,rows:b,fps:w}}function I(t,e,o,n,r,a=0,s){if("waveField"===o.animationStyle){return void S(t,n,r,a,s?{x:s.x,y:s.y}:{x:.5,y:.5},{accentColor:`#${o.accentColor?T(o.accentColor):"d4ff00"}`,accentThreshold:.52,mouseInfluence:o.hoverStrength>0?Math.min(1,.3+.5*o.hoverStrength):.55,mouseFalloff:2.8,speed:o.animationSpeed,vortex:o.hoverStrength>0,sparkles:!0,breathe:!0})}const c=e.length;if(0===c)return;const l=e[0].length;t.clearRect(0,0,n,r);let h=!1;const f=Math.max(1,c>>2),g=Math.max(1,l>>2);t:for(let t=0;t<c;t+=f){const o=e[t];for(let t=0;t<l;t+=g)if(o[t].a<200){h=!0;break t}}if(!h){const e="undefined"!=typeof window&&window.matchMedia("(prefers-color-scheme: dark)").matches;t.fillStyle=e?"#0a0a0a":"#faf9f7",t.fillRect(0,0,n,r)}const M=n/l,b=r/c,x=c*l,v=s?.intensity??1,w=!("none"!==o.animationStyle&&x>5e3)&&!!(s&&o.hoverStrength>0&&v>.005),y=o.hoverColor||"#ffffff",$=parseInt(y.slice(1,3),16)||255,E=parseInt(y.slice(3,5),16)||255,R=parseInt(y.slice(5,7),16)||255,k=T(o.accentColor),A=parseInt(k.substring(0,2),16)||255,I=parseInt(k.substring(2,4),16)||255,B=parseInt(k.substring(4,6),16)||255,P=x>3e4?.25:x>15e3?.4:x>5e3?.6:1,z=o.hoverRadius*P;let F=0,q=l,L=0,H=c,W=0,j=0;if(w&&s){W=s.x,j=s.y;const t=.08+.35*z+.04*o.hoverStrength;F=Math.max(0,Math.floor((W-t)*l)-1),q=Math.min(l,Math.ceil((W+t)*l)+1),L=Math.max(0,Math.floor((j-t)*c)-1),H=Math.min(c,Math.ceil((j+t)*c)+1)}const O=o.animationStyle,U=o.animationSpeed,_="none"===O,D=o.hoverStrength,V=o.hoverEffect,N=z,Y=C(o.invert),X=o.colorMode,G=2*Math.PI,J=1/l,K=1/c;let Q="",Z=-1;if("dots"===o.renderMode){const n=.5*Math.min(M,b)*o.dotSizeRatio;for(let o=0;o<c;o++){const r=e[o];for(let e=0;e<l;e++){const i=r[e];if(i.a<10)continue;const s=.00392156863*(.299*i.r+.587*i.g+.114*i.b),h=Y?1-s:s;if(h<.02)continue;const f=_?1:m(e,o,l,c,a,O,U);let g=1,x=0,y=0,S=0,C=0;if(w&&e>=F&&e<=q&&o>=L&&o<=H){const t=p(e*J,o*K,W,j,v,D,M,b,V,N);g=t.scale,x=t.offsetX,y=t.offsetY,S=t.glow,C=t.colorBlend}const T=n*h*f*g;if(T<.3)continue;const k=e*M+.5*M+x,P=o*b+.5*b+y;let z;if(C>0){const t=u(i,X,A,I,B,Y);z=`rgb(${Math.min(255,t[0]+($-t[0])*C|0)},${Math.min(255,t[1]+(E-t[1])*C|0)},${Math.min(255,t[2]+(R-t[2])*C|0)})`}else z=d(i,X,A,I,B,Y);const tt=Math.min(1,.00392156863*i.a*f*(1+S));if(tt!==Z&&(t.globalAlpha=tt,Z=tt),z!==Q&&(t.fillStyle=z,Q=z),T<=3){const e=2*T;t.fillRect(k-T,P-T,e,e)}else t.beginPath(),t.arc(k,P,T,0,G),t.fill()}}}else{const n=.55,r=.9*Math.min(M/n,b),s=r<6;if(!s){const e="emoji"===o.artStyle;t.font=e?`${r}px "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif`:`${r}px "JetBrains Mono", monospace`,t.textAlign="center",t.textBaseline="middle"}let h=null;const f=o.charsetFrames,g=!!f?.length,x=g?f[Math.floor(Math.max(0,a)*(o.charsetFps??2))%f.length]:o.charset;if(s){h={};const t=[...x],e=t.length;for(let o=0;o<e;o++)h[t[o]]=Math.max(.1,(o+.3)/e)}const y=s?null:t.getTransform();for(let o=0;o<c;o++){const n=e[o];for(let e=0;e<l;e++){const r=n[e];if(r.a<10)continue;const f=g&&null!=r.lum?i(r.lum,x,Y):r.char;if(" "===f)continue;const S=_?1:m(e,o,l,c,a,O,U);if(S<.05)continue;let C=1,T=0,k=0,P=0,z=0;if(w&&e>=F&&e<=q&&o>=L&&o<=H){const t=p(e*J,o*K,W,j,v,D,M,b,V,N);C=t.scale,T=t.offsetX,k=t.offsetY,P=t.glow,z=t.colorBlend}const G=e*M+.5*M+T,tt=o*b+.5*b+k;let et;if(z>0){const t=u(r,X,A,I,B,Y);et=`rgb(${Math.min(255,t[0]+($-t[0])*z|0)},${Math.min(255,t[1]+(E-t[1])*z|0)},${Math.min(255,t[2]+(R-t[2])*z|0)})`}else et=d(r,X,A,I,B,Y);if(s){const e=h[f]??.5,o=Math.min(1,.00392156863*r.a*S*(1+P))*e;if(o<.02)continue;o!==Z&&(t.globalAlpha=o,Z=o),et!==Q&&(t.fillStyle=et,Q=et);const n=M*C,a=b*C;t.fillRect(G-.5*n,tt-.5*a,n,a)}else{const e=Math.min(1,.00392156863*r.a*S*(1+P));e!==Z&&(t.globalAlpha=e,Z=e),et!==Q&&(t.fillStyle=et,Q=et),1!==C?(t.translate(G,tt),t.scale(C,C),t.fillText(f,0,0),t.setTransform(y)):t.fillText(f,G,tt)}}}}t.globalAlpha=1}function B(t,e){const o=Math.min(1,2048/Math.max(t,e));return{renderW:Math.round(t*o),renderH:Math.round(e*o)}}function P(t,e,o,n,r){const{width:a,height:i}=e.getBoundingClientRect();if(!a||!i)return{renderW:0,renderH:0,dpr:1};let s,c,l=a,h=l/o;h>i&&(h=i,l=h*o),l=Math.round(l),h=Math.round(h),n&&r?({renderW:s,renderH:c}=B(n,r)):(s=l,c=h);const f="undefined"!=typeof window&&window.devicePixelRatio||1,d=8e6,g=s*f*c*f>d?Math.sqrt(d/(s*c)):f;return t.width=Math.round(s*g),t.height=Math.round(c*g),t.style.width=l+"px",t.style.height=h+"px",{renderW:s,renderH:c,dpr:g}}async function z(t,e,{fontSize:o,artStyle:a="classic",options:i={},fitTo:s,preExtract:c=!1,trim:l,onReady:h,onFrame:f}={}){const d=l?.start??0,g=l?.end,u=o??i.fontSize??10,m={...r,...n[a],...i,fontSize:u},M=e.getContext("2d");if(!M)throw new Error("asciifyVideo: could not get 2d context from canvas.");const p="string"==typeof s?document.querySelector(s):s instanceof HTMLElement?s:null;if(c){let o;"string"==typeof t?(o=document.createElement("video"),o.crossOrigin="anonymous",o.src=t,o.readyState<2&&await new Promise((e,n)=>{o.onloadeddata=()=>e(),o.onerror=()=>n(new Error(`asciifyVideo: failed to load "${t}"`))})):o=t,p&&P(e,p,o.videoWidth/o.videoHeight,o.videoWidth,o.videoHeight);const{renderW:n,renderH:r}=B(o.videoWidth,o.videoHeight),a="undefined"!=typeof window&&window.devicePixelRatio||1,i=8e6,s=n*a*r*a>i?Math.sqrt(i/(n*r)):a;e.width<Math.round(n*s)&&(e.width=Math.round(n*s),e.height=Math.round(r*s));const c=void 0!==g?g-d:10,{frames:l,fps:u}=await k(o,m,n,r,void 0,c,void 0,d);let b,x=!1,v=0,w=performance.now(),y=!0;const $=1e3/u,S=t=>{x||(t-w>=$&&(M.save(),M.setTransform(s,0,0,s,0,0),I(M,l[v],m,n,r),M.restore(),v=(v+1)%l.length,w=t,y&&(y=!1,h?.(o)),f?.()),b=requestAnimationFrame(S))};return b=requestAnimationFrame(S),()=>{x=!0,cancelAnimationFrame(b)}}let b,x=!1;"string"==typeof t?(b=document.createElement("video"),b.src=t,b.muted=!0,b.loop=!0,b.playsInline=!0,b.setAttribute("playsinline",""),Object.assign(b.style,{position:"fixed",top:"0",left:"0",width:"1px",height:"1px",opacity:"0",pointerEvents:"none",zIndex:"-1"}),document.body.appendChild(b),x=!0,await new Promise((e,o)=>{b.onloadedmetadata=()=>e(),b.onerror=()=>o(new Error(`asciifyVideo: failed to load "${t}"`))}),await b.play().catch(()=>{})):(b=t,b.paused&&await b.play().catch(()=>{})),d>0&&(b.currentTime=d,await new Promise(t=>{const e=()=>{b.removeEventListener("seeked",e),t()};b.addEventListener("seeked",e)}));let v=null;(d>0||void 0!==g)&&(v=()=>{(void 0!==g&&b.currentTime>=g||d>0&&b.currentTime<d)&&(b.currentTime=d)},b.addEventListener("timeupdate",v));let w=null;const{renderW:y,renderH:$}=B(b.videoWidth,b.videoHeight);if(p){const t=b.videoWidth/b.videoHeight,o=b.videoWidth,n=b.videoHeight,r=P(e,p,t,o,n),a=e.getContext("2d");a&&a.setTransform(r.dpr,0,0,r.dpr,0,0),w=new ResizeObserver(()=>{const r=P(e,p,t,o,n),a=e.getContext("2d");a&&a.setTransform(r.dpr,0,0,r.dpr,0,0)}),w.observe(p)}else{const t="undefined"!=typeof window&&window.devicePixelRatio||1,o=8e6,n=y*t*$*t>o?Math.sqrt(o/(y*$)):t;e.width<Math.round(y*n)&&(e.width=Math.round(y*n),e.height=Math.round($*n)),M.setTransform(n,0,0,n,0,0)}let S,C=!1,E=!0;const T=()=>{if(C)return;if(S=requestAnimationFrame(T),b.readyState<2||0===e.width||0===e.height)return;if(d>0&&b.currentTime<d)return;if(void 0!==g&&b.currentTime>=g)return;const{frame:t}=R(b,m,y,$);t.length>0&&(I(M,t,m,y,$,0,null),E&&(E=!1,h?.(b)),f?.())};return S=requestAnimationFrame(T),()=>{C=!0,cancelAnimationFrame(S),w?.disconnect(),v&&b.removeEventListener("timeupdate",v),x&&(b.pause(),b.src="",document.body.removeChild(b))}}function F(t,e,o,n,r={}){const{fontSize:a=13,chars:i="0123456789ABCDEF@#$&*+=/<>",accentColor:s,color:c,speed:l=1,density:h=.55,tailLength:f=14,lightMode:d=!1}=r,g=s??(d?"#6b8700":"#d4ff00"),u=.62*a,m=1.4*a,M=Math.ceil(e/u),p=Math.ceil(o/m);t.clearRect(0,0,e,o),t.font=`${a}px monospace`,t.textBaseline="top";let x=255,v=255,y=255;if(d&&(x=55,v=55,y=55),c){const t=b(c);t&&(x=t.r,v=t.g,y=t.b)}let $=212,S=255,C=0;const E=b(g);E&&($=E.r,S=E.g,C=E.b);const T=p+f;for(let e=0;e<M;e++){if(w(17*e,3)>h)continue;const o=(.5+1.5*w(31*e,7))*l,r=w(13*e,11)*T,a=Math.floor((n*o*7+r)%T),s=e*u;for(let o=0;o<=f;o++){const r=a-(f-o);if(r<0||r>=p)continue;const c=r*m,l=w(53*e+Math.floor(5*n+o),7*r),h=i[Math.floor(l*i.length)],g=o/f;if(o===f)t.fillStyle=`rgba(${$},${S},${C},${d?.7:.85})`;else{const e=d?.85*g:.15*g;t.fillStyle=`rgba(${x},${v},${y},${e})`}t.fillText(h,s,c)}}}function q(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=14,chars:s=" . · * + ° ★",accentColor:c,color:l,speed:h=1,count:f=180,lightMode:d=!1}=a,g=c??(d?"#6b8700":"#d4ff00");t.clearRect(0,0,e,o),t.textBaseline="middle",t.textAlign="center";const u=e*(.2+.6*r.x),m=o*(.2+.6*r.y),M=.65*Math.sqrt(e*e+o*o);let p=255,x=255,v=255;if(d&&(p=55,x=55,v=55),l){const t=b(l);t&&(p=t.r,x=t.g,v=t.b)}let y=212,$=255,S=0;const C=b(g);C&&(y=C.r,$=C.g,S=C.b);const E=s.replace(/ /g,"").split("");if(0!==E.length){for(let r=0;r<f;r++){const a=w(17*r,3)*Math.PI*2,s=(n*(.15+.85*w(31*r,7))*h*.22+w(13*r,11))%1,c=u+Math.cos(a)*s*M,l=m+Math.sin(a)*s*M;if(c<-20||c>e+20||l<-20||l>o+20)continue;const f=Math.max(6,i*(.4+.9*s));t.font=`${f}px monospace`;const g=E[Math.min(E.length-1,Math.floor(s*E.length))],b=s>.72,C=d?.85*s:.2*s;t.fillStyle=b?`rgba(${y},${$},${S},${Math.min(d?.92:.32,2.2*C)})`:`rgba(${p},${x},${v},${C})`,t.fillText(g,c,l)}t.textAlign="left"}}function L(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=14,chars:s=". · ○ ◎ ●",accentColor:c,color:l,rings:h=5,speed:f=1,sharpness:d=4,lightMode:g=!1}=a,u=c??(g?"#007a5e":"#00ffcc");t.clearRect(0,0,e,o),t.textBaseline="middle",t.textAlign="center";const m=e*r.x,M=o*r.y,p=1.6*Math.sqrt(m*m+M*M)+.2*Math.sqrt(e*e+o*o);let x=255,v=255,w=255;if(g&&(x=55,v=55,w=55),l){const t=b(l);t&&(x=t.r,v=t.g,w=t.b)}let y=0,$=255,S=204;const C=b(u);C&&(y=C.r,$=C.g,S=C.b);const E=s.replace(/ /g,"").split("");if(0===E.length)return;const T=Math.ceil(e/i),R=Math.ceil(o/i);for(let e=0;e<R;e++)for(let o=0;o<T;o++){const r=o*i+.5*i,a=e*i+.5*i,s=r-m,c=a-M,l=Math.sqrt(s*s+c*c)/p;let u=0;for(let t=0;t<h;t++){const e=(n*f*.38+t/h)%1,o=Math.abs(l-e),r=Math.max(0,1-o*p/(i*(12-d)));u+=Math.cos(r*Math.PI*.5)*r}if(u=Math.min(1,u),u<.02)continue;const b=u>.6;t.font=`${i}px monospace`;const C=Math.floor(u*(E.length-1)),T=E[Math.min(C,E.length-1)],R=g?.88*u:.22*u;t.fillStyle=b?`rgba(${y},${$},${S},${Math.min(g?.95:.4,.55*u)})`:`rgba(${x},${v},${w},${R})`,t.fillText(T,r,a)}t.textAlign="left"}function H(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=14,chars:s=" .·:;=+*#%@░▒▓",accentColor:c,color:l,octaves:h=4,speed:f=1,scale:d=1,accentThreshold:g=.78,mouseWarp:u=.3,lightMode:m=!1}=a,M=c??(m?"#6b8700":"#d4ff00"),p=.62*i,x=1.4*i,v=Math.ceil(e/p),w=Math.ceil(o/x);t.clearRect(0,0,e,o),t.font=`${i}px monospace`,t.textBaseline="top";let $=255,S=255,C=255;if(m&&($=55,S=55,C=55),l){const t=b(l);t&&($=t.r,S=t.g,C=t.b)}let E=212,T=255,R=0;const k=b(M);k&&(E=k.r,T=k.g,R=k.b);const A=.035*d,I=n*f,B=Math.min(6,Math.max(1,h)),P=(t,e)=>{let o=0,n=.5,r=1,a=0;for(let i=0;i<B;i++)o+=y(t*r,e*r)*n,a+=n,n*=.5,r*=2.1;return o/a};for(let e=0;e<w;e++)for(let o=0;o<v;o++){const n=o*A+.06*I,a=e*A*1.3-.04*I,i=o/v-r.x,c=e/w-r.y,l=Math.sqrt(i*i+c*c),h=u>0?.12*Math.max(0,1-l/u):0,f=.5*P(n+h*Math.sin(1.3*I+8*c),a+h*Math.cos(.9*I+8*i))+.5;if(f<.12)continue;const d=s[Math.floor(f*(s.length-1))];if(" "===d)continue;const M=f>g,b=m?.82*f:.13*f;t.fillStyle=M?`rgba(${E},${T},${R},${m?.92:.28})`:`rgba(${$},${S},${C},${b})`,t.fillText(d,o*p,e*x)}}function W(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=12,chars:s="·-=+|/",accentColor:c,color:l,bands:h=3,speed:f=1,bandWidth:d=.12,glitch:g=!0,lightMode:u=!1}=a,m=c??(u?"#6b8700":"#d4ff00"),M=.62*i,p=1.4*i,x=Math.ceil(e/M),v=Math.ceil(o/p);t.clearRect(0,0,e,o),t.font=`${i}px monospace`,t.textBaseline="top";let y=255,$=255,S=255;if(u&&(y=55,$=55,S=55),l){const t=b(l);t&&(y=t.r,$=t.g,S=t.b)}let C=212,E=255,T=0;const R=b(m);R&&(C=R.r,E=R.g,T=R.b);const k=n*f;for(let e=0;e<v;e++)for(let o=0;o<x;o++){const n=e/v,a=((n*h-.5*k)%1+1)%1,i=Math.max(0,1-a/d),c=.35*(.5*w(3*o,7*e)+.5);let l=0;if(g){const t=o/x-r.x,a=n-r.y,i=Math.sqrt(t*t+a*a);if(i<.18){const t=w(11*o+Math.floor(12*k),5*e);l=Math.max(0,1-i/.18)*(t>.5?t-.3:0)}}const f=Math.min(1,c+.8*i+.6*l);if(f<.04)continue;const m=s[Math.floor(f*(s.length-1))],b=i>.55,R=u?.82*f:.12*f;t.fillStyle=b?`rgba(${C},${E},${T},${u?.92:.28})`:`rgba(${y},${$},${S},${R})`,t.fillText(m,o*M,e*p)}}function j(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=14,chars:s=" ·∙•:;+=≡≣#@",color:c,accentColor:l,speed:h=1,layers:f=5,softness:d=1.2,mouseRipple:g=.2,lightMode:u=!1}=a,m=l??(u?"#6b8700":"#d4ff00"),M=.62*i,p=1.4*i,x=Math.ceil(e/M),v=Math.ceil(o/p);t.clearRect(0,0,e,o),t.font=`${i}px monospace`,t.textBaseline="top";let y=255,$=255,S=255;if(u&&(y=55,$=55,S=55),c){const t=b(c);t&&(y=t.r,$=t.g,S=t.b)}let C=212,E=255,T=0;const R=b(m);R&&(C=R.r,E=R.g,T=R.b);const k=n*h,A=[];for(let t=0;t<f;t++){const e=w(17*t,31*t+7),o=w(23*t+5,11*t);A.push({fx:.8+2.2*e,fy:1.2+1.8*o,phase:e*Math.PI*4,dt:(.3+.5*w(7*t,13*t+3))*(t%2==0?1:-1),amp:.55+.45*w(29*t,3*t)})}for(let e=0;e<v;e++){const o=e/v;for(let n=0;n<x;n++){const a=n/x,i=a-r.x,c=o-r.y,l=Math.sqrt(i*i+c*c),h=g*Math.exp(-l*l/.06),m=a+i*h,b=o+c*h;let v=0,w=0;for(let t=0;t<f;t++){const{fx:e,fy:o,phase:n,dt:r,amp:a}=A[t];v+=Math.sin(m*e*Math.PI*2+k*r+n)*Math.cos(b*o*Math.PI*2+k*r*.7+1.3*n)*a,w+=a}const R=v/w,I=.5+.5*Math.tanh(R*d*2.2);if(I<.12)continue;const B=(I-.12)/.88,P=s[Math.min(s.length-1,Math.floor(B*s.length))],z=I>.82,F=u?.82*I:.14*I;t.fillStyle=z?`rgba(${C},${E},${T},${u?.92:.32})`:`rgba(${y},${$},${S},${F})`,t.fillText(P,n*M,e*p)}}}function O(t,e,o,n,r={}){const{fontSize:a=13,color:i,accentColor:s,speed:c=.4,layers:l=4,turbulence:h=.8,lightMode:f=!1}=r,d=s??(f?"#6b8700":"#d4ff00"),g=.62*a,u=1.4*a,m=Math.ceil(e/g),M=Math.ceil(o/u);t.clearRect(0,0,e,o),t.font=`${a}px monospace`,t.textBaseline="top";let p=255,x=255,v=255;if(f&&(p=55,x=55,v=55),i){const t=b(i);t&&(p=t.r,x=t.g,v=t.b)}let y=212,$=255,S=0;const C=b(d);C&&(y=C.r,$=C.g,S=C.b);const E=n*c,T=["─","─","╌","·","╌","─","─","╌","·"];for(let e=0;e<M;e++){const o=e/M;for(let n=0;n<m;n++){const r=n/m;let a=0,i=0;for(let t=0;t<l;t++){const e=w(13*t,7*t+3),n=1.1+2.4*e,s=.9+2*w(29*t,11*t+1),c=e*Math.PI*6,l=(.2+.5*w(41*t,17*t))*(t%2==0?1:-1.3),f=Math.sin(r*n*Math.PI*2+E*l+c),d=Math.cos(o*s*Math.PI*2+E*l*.6+1.7*c),g=Math.sin(r*s*Math.PI*h+o*n*Math.PI*h+E*l*.4);a+=Math.atan2(d+.3*g,f),i+=.5*(f*d+1)}const s=a/l,c=Math.min(1,i/l);if(c<.1)continue;const d=(s+Math.PI)/(2*Math.PI),M=T[Math.floor(d*T.length)%T.length],b=c>.8,C=f?.8*c:.13*c;t.fillStyle=b?`rgba(${y},${$},${S},${f?.9:.26})`:`rgba(${p},${x},${v},${C})`,t.fillText(M,n*g,e*u)}}}function U(t,e,o,n,r={x:.5,y:.5},a={}){const{fontSize:i=13,chars:s=" ·:;=+*#%@",color:c,accentColor:l,speed:h=1,radius:f=.38,swirl:d=3,lightMode:g=!1}=a,u=l??(g?"#6b8700":"#d4ff00"),m=.62*i,M=1.4*i,p=Math.ceil(e/m),x=Math.ceil(o/M),v=e/o;t.clearRect(0,0,e,o),t.font=`${i}px monospace`,t.textBaseline="top";let y=255,$=255,S=255;if(g&&(y=55,$=55,S=55),c){const t=b(c);t&&(y=t.r,$=t.g,S=t.b)}let C=212,E=255,T=0;const R=b(u);R&&(C=R.r,E=R.g,T=R.b);const k=n*h;for(let e=0;e<x;e++){const o=e/x;for(let n=0;n<p;n++){const a=(n/p-r.x)*v,i=o-r.y,c=Math.sqrt(a*a+i*i)/f;if(c>1){const o=w(3*n,7*e)*Math.max(0,1-3*(c-1));if(o<.62)continue;const r=o*(g?.28:.04);t.fillStyle=`rgba(${y},${$},${S},${r})`,t.fillText(s[1],n*m,e*M);continue}const l=Math.max(0,1-Math.abs(c-(.15+.12*Math.sin(1.1*k)))/.07),h=Math.pow(1-c,2.2),d=Math.min(1,h+.6*l);if(d<.06)continue;const u=Math.floor(d*(s.length-1)),b=s[Math.min(s.length-1,u)],x=l>.35||c<.08,R=g?.85*d:.18*d;t.fillStyle=x?`rgba(${C},${E},${T},${g?.95:.38})`:`rgba(${y},${$},${S},${R})`,t.fillText(b,n*m,e*M)}}}function _(t,e,o,n,r={}){const{fontSize:a=14,chars:i=" ·∙•:-=+*#",color:s,accentColor:c,speed:l=.5,harmonics:h=3,lightMode:f=!1}=r,d=c??(f?"#6b8700":"#d4ff00"),g=.62*a,u=1.4*a,m=Math.ceil(e/g),M=Math.ceil(o/u);t.clearRect(0,0,e,o),t.font=`${a}px monospace`,t.textBaseline="top";let p=255,x=255,v=255;if(f&&(p=55,x=55,v=55),s){const t=b(s);t&&(p=t.r,x=t.g,v=t.b)}let y=212,$=255,S=0;const C=b(d);C&&(y=C.r,$=C.g,S=C.b);const E=n*l,T=Array.from({length:h},(t,e)=>1/(e+1)).reduce((t,e)=>t+e,0);for(let e=0;e<M;e++)for(let o=0;o<m;o++){let n=0;for(let t=0;t<h;t++){const r=.18+1.4*w(o*(t+3)+7,e*(t+5)+11),a=w(o*(t+7),e*(t+9)+3)*Math.PI*2,i=1/(t+1);n+=Math.sin(E*r+a)*i}const r=.5*(n/T+1);if(r<.28)continue;const a=(r-.28)/.72,s=i[Math.min(i.length-1,Math.floor(a*i.length))],c=r>.88,l=f?.82*a:.13*a;t.fillStyle=c?`rgba(${y},${$},${S},${f?.92:.28})`:`rgba(${p},${x},${v},${l})`,t.fillText(s,o*g,e*u)}}function D(t,e,o,n,r={}){const{fontSize:a=13,chars:i=" .,:;i+xX#&@",color:s="#ff4500",hotColor:c="#ffe066",intensity:l=.85,wind:h=0,speed:f=1,lightMode:d=!1}=r,g=.62*a,u=1.4*a,m=Math.ceil(e/g),M=Math.ceil(o/u),p=m*M,x="__fire_heat__",v=t.canvas;let w=v[x];w&&w.length===p||(w=new Float32Array(p),v[x]=w);const y=.18*(.016*f),$=h*f*.8,S=M-1,C=n*f;for(let t=0;t<m;t++){const e=(.6*(.5*Math.sin(.31*t+4.1*C)+.5)+.4*(.5*Math.sin(.73*t-2.7*C)+.5))*l;w[S*m+t]=Math.min(1,e+.15*Math.random()*l),S>0&&(w[(S-1)*m+t]=Math.min(1,.85*e+.1*Math.random()*l))}const E=new Float32Array(p);for(let t=0;t<M-2;t++)for(let e=0;e<m;e++){const o=.4*w[(t+1)*m+e]+.25*w[(t+2)*m+Math.max(0,Math.min(m-1,e+Math.round($)))]+.175*w[(t+1)*m+Math.max(0,e-1)]+.175*w[(t+1)*m+Math.min(m-1,e+1)];E[t*m+e]=Math.max(0,o-y-.02*Math.random()*f)}for(let t=0;t<m;t++)E[(M-1)*m+t]=w[(M-1)*m+t],M>1&&(E[(M-2)*m+t]=w[(M-2)*m+t]);v[x]=E;const T=b(s)??{r:255,g:69,b:0},R=b(c)??{r:255,g:224,b:102};t.clearRect(0,0,e,o),t.font=`${a}px "JetBrains Mono", monospace`,t.textBaseline="top";for(let e=0;e<M;e++)for(let o=0;o<m;o++){const n=E[e*m+o];if(n<.03)continue;const r=i[Math.min(i.length-1,Math.floor(n*i.length))];if(" "===r)continue;const a=Math.min(1,1.2*n),s=T.r+(R.r-T.r)*a|0,c=T.g+(R.g-T.g)*a|0,l=T.b+(R.b-T.b)*a|0,h=d?1-.3*n:Math.min(1,n+.15);t.globalAlpha=h,t.fillStyle=`rgb(${s},${c},${l})`,t.fillText(r,o*g,e*u)}t.globalAlpha=1}function V(t,e,o,n,r={}){const{fontSize:a=13,baseChars:i="ATCG",bridgeChars:s="-=≡",color:c="#00e5ff",color2:l="#ff4081",bridgeColor:h="#88ffcc",speed:f=1,helixCount:d,lightMode:g=!1}=r,u=.62*a,m=1.4*a,M=Math.ceil(e/u),p=Math.ceil(o/m),x=d??Math.max(1,Math.floor(e/80)),v=M/x,y=b(c)??{r:0,g:229,b:255},$=b(l)??{r:255,g:64,b:129},S=b(h)??{r:136,g:255,b:204};t.clearRect(0,0,e,o),t.font=`${a}px "JetBrains Mono", monospace`,t.textBaseline="top";const C=n*f,E=.35*v;for(let e=0;e<x;e++){const o=v*(e+.5);for(let n=0;n<p;n++){const r=n/p*Math.PI*6-1.8*C,a=o+Math.sin(r)*E,c=o+Math.sin(r+Math.PI)*E,l=Math.round(a),h=Math.round(c);if(l<0||l>=M)continue;const f=w(31*e+7*n,3),d=i[Math.floor(f*i.length)],g=.5*(Math.sin(r)+1),b=.5*(Math.sin(r+Math.PI)+1);if(t.globalAlpha=.35+.65*g,t.fillStyle=`rgb(${y.r},${y.g},${y.b})`,t.fillText(d,l*u,n*m),h>=0&&h<M){const o=w(53*e+11*n,7),r=i[Math.floor(o*i.length)];t.globalAlpha=.35+.65*b,t.fillStyle=`rgb(${$.r},${$.g},${$.b})`,t.fillText(r,h*u,n*m)}if(n%3===0){const o=Math.min(l,h),r=Math.max(l,h);if(r-o>1){const a=w(17*n+43*e,5),i=s[Math.floor(a*s.length)],c=.25*(g+b)+.2;t.globalAlpha=c,t.fillStyle=`rgb(${S.r},${S.g},${S.b})`;for(let e=o+1;e<r;e++)t.fillText(i,e*u,n*m)}}}}t.globalAlpha=1}function N(t,e,o,n,r={}){const{fontSize:a=13,chars:i=" .,:;+*#@",color:s="#4caf50",skyColor:c="#1a237e",peakColor:l="#e0e0e0",speed:h=1,roughness:f=.55,heightScale:d=.55,stars:g=!0,lightMode:u=!1}=r,m=.62*a,M=1.4*a,p=Math.ceil(e/m),x=Math.ceil(o/M),v=b(s)??{r:76,g:175,b:80},y=b(c)??{r:26,g:35,b:126},S=b(l)??{r:224,g:224,b:224};t.clearRect(0,0,e,o),t.font=`${a}px "JetBrains Mono", monospace`,t.textBaseline="top";const C=n*h*.4,E=new Array(p);for(let t=0;t<p;t++){const e=(.5*$((t/p+C)*f*3,.5)+.5)*d;E[t]=Math.floor(e*x)}for(let e=0;e<x;e++)for(let o=0;o<p;o++){const r=x-1-E[o],a=e===r;if(!(e>=r)){if(g){const r=w(7*o+Math.floor(.3*C),13*e);if(r>.97){const a=.3*Math.sin(2*n+100*r)+.7;t.globalAlpha=.5*a,t.fillStyle=`rgb(${y.r+60},${y.g+60},${y.b+80})`,t.fillText("·",o*m,e*M)}}continue}const s=(e-r)/Math.max(1,E[o]),c=i[Math.min(i.length-1,Math.floor(s*i.length))];if(" "===c&&!a)continue;const l=a?1:Math.max(0,1-4*s),h=v.r+(S.r-v.r)*l|0,f=v.g+(S.g-v.g)*l|0,d=v.b+(S.b-v.b)*l|0;t.globalAlpha=.5+.5*s,t.fillStyle=`rgb(${h},${f},${d})`,t.fillText(a?i[i.length-1]:c,o*m,e*M)}t.globalAlpha=1}var Y=1,X=2,G=4,J=8,K={3:"─",12:"│",9:"┌",10:"┐",5:"└",6:"┘",11:"┬",7:"┴",13:"├",14:"┤",15:"┼",[Y]:"╶",[X]:"╴",[G]:"╵",[J]:"╷"};function Q(t,e,o,n,r={}){const{fontSize:a=13,pulseColor:i="#ffffff",color:s="#00ff88",density:c=.38,speed:l=1,lightMode:h=!1}=r,f=.62*a,d=1.4*a,g=Math.ceil(e/f),u=Math.ceil(o/d),m=b(s)??{r:0,g:255,b:136},M=b(i)??{r:255,g:255,b:255},p=(t,e)=>{if(w(17*t+1,7*e+2)>c)return 0;let o=0;return t+1<g&&w(17*t+1,7*e+2)>.15&&(o|=1),t-1>=0&&w(17*(t-1)+1,7*e+2)>.15&&(o|=2),e+1<u&&w(17*t+1,7*(e+1)+2)>.15&&(o|=8),e-1>=0&&w(17*t+1,7*(e-1)+2)>.15&&(o|=4),o};t.clearRect(0,0,e,o),t.font=`${a}px "JetBrains Mono", monospace`,t.textBaseline="top";const x=n*l;for(let e=0;e<u;e++)for(let o=0;o<g;o++){const n=p(o,e);if(0===n)continue;const r=K[n]??"·",a=(8*x+40*w(o,23*e))%g,i=(6*x+40*w(17*e,11*o))%u,s=(1&n||2&n)&&Math.abs(o-a)<1.5,c=(4&n||8&n)&&Math.abs(e-i)<1.5,l=s||c,h=.25+.35*w(3*o,5*e);l?(t.globalAlpha=.95,t.fillStyle=`rgb(${M.r},${M.g},${M.b})`):(t.globalAlpha=h,t.fillStyle=`rgb(${m.r},${m.g},${m.b})`),t.fillText(r,o*f,e*d)}t.globalAlpha=1}function Z(t,e={}){const{type:o="wave",opacity:n=.2,className:r,zIndex:a=0,colorScheme:i="auto",color:s,...c}=e,l="string"==typeof t?document.querySelector(t):t;if(!l)return console.warn("[asciify] asciiBackground: target not found",t),{destroy:()=>{}};const h=l.style.position;"static"===getComputedStyle(l).position&&(l.style.position="relative");const f=document.createElement("canvas");f.style.cssText=["position:absolute","inset:0","width:100%","height:100%",`opacity:${n}`,"pointer-events:none",`z-index:${a}`].join(";"),r&&(f.className=r),l.prepend(f);const d=f.getContext("2d"),g=window.devicePixelRatio||1,u={x:.5,y:.5},m={x:.5,y:.5},M=window.matchMedia("(prefers-color-scheme: light)"),p=()=>"light"===i||"dark"!==i&&M.matches,b=s?function(t){const e=t.match(/^#([0-9a-f]{3,8})$/i)?.[1];if(e){const t=e.length<=4?e.split("").map(t=>parseInt(t+t,16)):[parseInt(e.slice(0,2),16),parseInt(e.slice(2,4),16),parseInt(e.slice(4,6),16)];return{r:t[0],g:t[1],b:t[2]}}const o=t.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/i);return o?{r:+o[1],g:+o[2],b:+o[3]}:null}(s):null,x=()=>({...c,lightMode:void 0!==c.lightMode?c.lightMode:p(),baseColor:b?`rgba(${b.r},${b.g},${b.b},{a})`:c.baseColor}),v={current:x()},w=()=>{v.current="rain"===o||"stars"===o||"pulse"===o||"noise"===o||"grid"===o||"aurora"===o||"silk"===o||"void"===o||"morph"===o?{...c,lightMode:void 0!==c.lightMode?c.lightMode:p(),color:s??c.color}:"fire"===o||"dna"===o?{...c,color:s??c.color}:"terrain"===o||"circuit"===o?{...c,color:s??c.color,lightMode:void 0!==c.lightMode?c.lightMode:p()}:x()};w();const y=()=>{w()};"auto"===i&&M.addEventListener("change",y);const $=()=>{const t=l.getBoundingClientRect();f.width=t.width*g,f.height=t.height*g,d.setTransform(g,0,0,g,0,0)};$();const C=t=>{const e=l.getBoundingClientRect();u.x=(t.clientX-e.left)/e.width,u.y=(t.clientY-e.top)/e.height},E=new ResizeObserver($);E.observe(l),window.addEventListener("mousemove",C);let T=0,R=0;const k=()=>{m.x+=.07*(u.x-m.x),m.y+=.07*(u.y-m.y);const t=l.getBoundingClientRect();"rain"===o?F(d,t.width,t.height,T,v.current):"stars"===o?q(d,t.width,t.height,T,m,v.current):"pulse"===o?L(d,t.width,t.height,T,m,v.current):"noise"===o?H(d,t.width,t.height,T,m,v.current):"grid"===o?W(d,t.width,t.height,T,m,v.current):"aurora"===o?j(d,t.width,t.height,T,m,v.current):"silk"===o?O(d,t.width,t.height,T,v.current):"void"===o?U(d,t.width,t.height,T,m,v.current):"morph"===o?_(d,t.width,t.height,T,v.current):"fire"===o?D(d,t.width,t.height,T,v.current):"dna"===o?V(d,t.width,t.height,T,v.current):"terrain"===o?N(d,t.width,t.height,T,v.current):"circuit"===o?Q(d,t.width,t.height,T,v.current):S(d,t.width,t.height,T,m,v.current),T+=.016,R=requestAnimationFrame(k)};return R=requestAnimationFrame(k),{destroy:()=>{cancelAnimationFrame(R),E.disconnect(),"auto"===i&&M.removeEventListener("change",y),window.removeEventListener("mousemove",C),f.remove(),l.style.position=h}}}var tt=Z;function et(t,e,o,n="#505050",r=100){if(!t||e<=0||o<=0)return[];const a=b(n)??{r:80,g:80,b:80},i=t,s=i.length;return Array.from({length:o},(t,o)=>Array.from({length:e},(t,n)=>({char:i[(o*e+n)%s],r:a.r,g:a.g,b:a.b,a:r})))}function ot(t,{format:e="png",quality:o=.92,scale:n=1}={}){return new Promise((r,a)=>{let i=t;if(1!==n){const e=document.createElement("canvas");e.width=Math.round(t.width*n),e.height=Math.round(t.height*n);const o=e.getContext("2d");if(!o)return void a(new Error("captureSnapshot: could not get 2d context"));o.drawImage(t,0,0,e.width,e.height),i=e}i.toBlob(t=>t?r(t):a(new Error("captureSnapshot: toBlob returned null")),`image/${e}`,o)})}exports.ART_STYLE_PRESETS=n,exports.BACKGROUND_TYPES=["wave","rain","stars","pulse","noise","grid","aurora","silk","void","morph","fire","dna","terrain","circuit"],exports.CHARSETS=e,exports.CHARSET_SEQUENCES=o,exports.DEFAULT_OPTIONS=r,exports.HOVER_PRESETS={none:{label:"Off",options:{hoverStrength:0,hoverEffect:"spotlight",hoverRadius:.2,hoverColor:"#ffffff"}},subtle:{label:"Subtle",options:{hoverStrength:.25,hoverEffect:"glow",hoverRadius:.12,hoverColor:"#ffffff"}},flashlight:{label:"Flashlight",options:{hoverStrength:.6,hoverEffect:"spotlight",hoverRadius:.15,hoverColor:"#fffbe6"}},magnifier:{label:"Magnifier",options:{hoverStrength:.7,hoverEffect:"magnify",hoverRadius:.12,hoverColor:"#ffffff"}},forceField:{label:"Force Field",options:{hoverStrength:.7,hoverEffect:"repel",hoverRadius:.15,hoverColor:"#a0e8ff"}},neon:{label:"Neon",options:{hoverStrength:.6,hoverEffect:"colorShift",hoverRadius:.15,hoverColor:"#d946ef"}},fire:{label:"Fire",options:{hoverStrength:.7,hoverEffect:"spotlight",hoverRadius:.15,hoverColor:"#ff6b2b"}},ice:{label:"Ice",options:{hoverStrength:.5,hoverEffect:"glow",hoverRadius:.15,hoverColor:"#60d5f7"}},gravity:{label:"Gravity",options:{hoverStrength:.7,hoverEffect:"attract",hoverRadius:.18,hoverColor:"#a5d6ff"}},shatter:{label:"Shatter",options:{hoverStrength:.8,hoverEffect:"shatter",hoverRadius:.14,hoverColor:"#ff6090"}},ghost:{label:"Ghost",options:{hoverStrength:.55,hoverEffect:"trail",hoverRadius:.2,hoverColor:"#b39ddb"}}},exports.PALETTE_THEMES={dracula:{name:"Dracula",accent:"#bd93f9",bg:"#282a36",fg:"#f8f8f2"},monokai:{name:"Monokai",accent:"#a6e22e",bg:"#272822",fg:"#f8f8f2"},nord:{name:"Nord",accent:"#88c0d0",bg:"#2e3440",fg:"#eceff4"},catppuccin:{name:"Catppuccin",accent:"#cba6f7",bg:"#1e1e2e",fg:"#cdd6f4"},solarized:{name:"Solarized",accent:"#268bd2",bg:"#002b36",fg:"#839496"},gruvbox:{name:"Gruvbox",accent:"#b8bb26",bg:"#282828",fg:"#ebdbb2"}},exports.asciiBackground=Z,exports.asciiText=function(t,e={},o,n){const a={...r,...e},{frame:i,cols:s}=R(t,a,o,n);if(!i.length||0===s)return"";const c=[];for(const t of i)c.push(t.map(t=>t.char).join(""));return c.join("\n")},exports.asciiTextAnsi=function(t,e={},o,n){const a={...r,...e},{frame:i,cols:s}=R(t,a,o,n);if(!i.length||0===s)return"";const c=[];for(const t of i){let e="";for(const o of t)" "===o.char||o.a<10?e+=" ":e+=`[38;2;${o.r};${o.g};${o.b}m${o.char}`;c.push(e)}return c.join("\n")},exports.asciify=async function(t,e,{fontSize:o,artStyle:a="classic",options:i={}}={}){let s;if("string"==typeof t){const e=new Image;e.crossOrigin="anonymous",await new Promise((o,n)=>{e.onload=()=>o(),e.onerror=()=>n(new Error(`Failed to load image: ${t}`)),e.src=t}),s=e}else t instanceof HTMLImageElement&&!t.complete?(await new Promise((e,o)=>{t.onload=()=>e(),t.onerror=()=>o(new Error("Image failed to load"))}),s=t):s=t;const c=n[a],l=o??i.fontSize??10,h={...r,...c,...i,fontSize:l},f=e.getContext("2d");if(!f)throw new Error("Could not get 2d context from canvas");const{w:d,h:g}=function(t){return t instanceof HTMLVideoElement?{w:t.videoWidth,h:t.videoHeight}:t instanceof HTMLImageElement?{w:t.naturalWidth||t.width,h:t.naturalHeight||t.height}:{w:t.width,h:t.height}}(s),{renderW:u,renderH:m}=B(d,g),M="undefined"!=typeof window&&window.devicePixelRatio||1,p=8e6,b=u*M*m*M>p?Math.sqrt(p/(u*m)):M;(e.width<u||e.height<m)&&(e.width=Math.round(u*b),e.height=Math.round(m*b));const{frame:x}=R(s,h,u,m);f.save(),f.setTransform(b,0,0,b,0,0),I(f,x,h,u,m),f.restore()},exports.asciifyGif=async function(t,e,{fontSize:o,artStyle:a="classic",options:i={}}={}){const s="string"==typeof t?await fetch(t).then(t=>t.arrayBuffer()):t,c=o??i.fontSize??10,l={...r,...n[a],...i,fontSize:c},h=e.getContext("2d");if(!h)throw new Error("Could not get 2d context from canvas");const{frames:f,fps:d}=await A(s,l,e.width,e.height);let g,u=!1,m=0,M=performance.now();const p=1e3/d,b=t=>{u||(t-M>=p&&(I(h,f[m],l,e.width,e.height),m=(m+1)%f.length,M=t),g=requestAnimationFrame(b))};return g=requestAnimationFrame(b),()=>{u=!0,cancelAnimationFrame(g)}},exports.asciifyLiveVideo=function(t,e,o){return z(t,e,o)},exports.asciifyVideo=z,exports.asciifyWebcam=async function(t,{fontSize:e=10,style:o="classic",options:a={},liveOptions:i,mirror:s=!0,constraints:c={facingMode:"user"},dpr:l}={}){if(!navigator.mediaDevices?.getUserMedia)throw new Error("asciifyWebcam: getUserMedia is not supported in this browser.");const h=await navigator.mediaDevices.getUserMedia({video:c}),f=document.createElement("video");f.srcObject=h,f.muted=!0,f.playsInline=!0,await new Promise((t,e)=>{f.onloadedmetadata=()=>t(),f.onerror=()=>e(new Error("asciifyWebcam: video stream failed to load.")),f.play().catch(e)});const d={...r,...n[o],...a,fontSize:e},g=t.getContext("2d");if(!g)throw new Error("asciifyWebcam: could not get 2d context from canvas.");const u=l??("undefined"!=typeof window?window.devicePixelRatio:1)??1;1!==u&&g.scale(u,u);let m=null;const M={x:.5,y:.5,intensity:0},p=e=>{const o=t.getBoundingClientRect();m={x:(e.clientX-o.left)/o.width,y:(e.clientY-o.top)/o.height}},b=()=>{m=null};d.hoverStrength>0&&(t.addEventListener("mousemove",p),t.addEventListener("mouseleave",b));let x,v=!1;const w=performance.now(),y=e=>{if(!v){if(f.readyState>=f.HAVE_CURRENT_DATA){const o=t.width/u,n=t.height/u,r=(e-w)/1e3,a=i?{...d,...i()}:d;a.hoverStrength>0?(t.addEventListener("mousemove",p),t.addEventListener("mouseleave",b)):(t.removeEventListener("mousemove",p),t.removeEventListener("mouseleave",b));const{frame:c}=R(f,a,o,n);if(m){const t=m.x-M.x,e=m.y-M.y,o=Math.sqrt(t*t+e*e),n=Math.min(.25,.06+.8*o);M.x+=t*n,M.y+=e*n,M.intensity+=.12*(1-M.intensity)}else M.intensity*=.965,M.intensity<.003&&(M.intensity=0);const l=M.intensity>.003?{x:M.x,y:M.y,intensity:M.intensity}:null;s?(g.save(),g.scale(-1,1),g.translate(-o,0),I(g,c,a,o,n,r,l),g.restore()):I(g,c,a,o,n,r,l)}x=requestAnimationFrame(y)}};return x=requestAnimationFrame(y),()=>{v=!0,cancelAnimationFrame(x),t.removeEventListener("mousemove",p),t.removeEventListener("mouseleave",b),h.getTracks().forEach(t=>t.stop()),f.srcObject=null}},exports.buildTextFrame=et,exports.captureSnapshot=ot,exports.gifToAsciiFrames=A,exports.imageToAsciiFrame=R,exports.mountWaveBackground=tt,exports.renderAuroraBackground=j,exports.renderCircuitBackground=Q,exports.renderDnaBackground=V,exports.renderFireBackground=D,exports.renderFrameToCanvas=I,exports.renderGridBackground=W,exports.renderMorphBackground=_,exports.renderNoiseBackground=H,exports.renderPulseBackground=L,exports.renderRainBackground=F,exports.renderSilkBackground=O,exports.renderStarsBackground=q,exports.renderTerrainBackground=N,exports.renderTextBackground=function(t,e,o,n,a={},i){const{fontSize:s=10,lineHeight:c=1.6,color:l="#505050",opacity:h=100,hoverEffect:f="spotlight",hoverStrength:d=.85,hoverRadius:g=.18,hoverColor:u="#d4ff00"}=a;I(t,et(n,Math.max(1,Math.floor(e/s)),Math.max(1,Math.floor(o/(s*c))),l,h),{...r,hoverEffect:f,hoverStrength:d,hoverRadius:g,hoverColor:u},e,o,0,i??null)},exports.renderVoidBackground=U,exports.renderWaveBackground=S,exports.snapshotAndDownload=async function(t,e={}){const{filename:o="asciify-snapshot",format:n="png",...r}=e,a=await ot(t,{format:n,...r}),i="jpeg"===n?"jpg":n,s=document.createElement("a");s.href=URL.createObjectURL(a),s.download=`${o}.${i}`,s.click(),setTimeout(()=>URL.revokeObjectURL(s.href),1e4)},exports.videoToAsciiFrames=k;//# sourceMappingURL=index.cjs.map