asciify-engine 1.0.0

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 ADDED
@@ -0,0 +1,876 @@
1
+ 'use strict';
2
+
3
+ var gifuctJs = require('gifuct-js');
4
+
5
+ // src/types.ts
6
+ var CHARSETS = {
7
+ standard: " .:-=+*#%@",
8
+ blocks: " \u2591\u2592\u2593\u2588",
9
+ minimal: " .:+",
10
+ dense: " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
11
+ binary: "01",
12
+ dots: " \u2801\u2803\u2807\u2847\u28C7\u28E7\u28F7\u28FF",
13
+ letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
14
+ claudeCode: " \u2554\u2557\u255A\u255D\u2551\u2550\u2560\u2563\u2566\u2569\u256C\u2591\u2592\u2593\u2588\u2502\u2500\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C"
15
+ };
16
+ var ART_STYLE_PRESETS = {
17
+ classic: {
18
+ renderMode: "ascii",
19
+ charset: CHARSETS.standard,
20
+ colorMode: "grayscale"
21
+ },
22
+ particles: {
23
+ renderMode: "dots",
24
+ colorMode: "fullcolor",
25
+ dotSizeRatio: 0.8
26
+ },
27
+ letters: {
28
+ renderMode: "ascii",
29
+ charset: CHARSETS.letters,
30
+ colorMode: "fullcolor"
31
+ },
32
+ claudeCode: {
33
+ renderMode: "ascii",
34
+ charset: CHARSETS.claudeCode,
35
+ colorMode: "accent",
36
+ accentColor: "#f97316"
37
+ },
38
+ art: {
39
+ renderMode: "ascii",
40
+ charset: CHARSETS.dense,
41
+ colorMode: "fullcolor"
42
+ },
43
+ terminal: {
44
+ renderMode: "ascii",
45
+ charset: CHARSETS.standard,
46
+ colorMode: "matrix"
47
+ }
48
+ };
49
+ var DEFAULT_OPTIONS = {
50
+ fontSize: 10,
51
+ charSpacing: 1,
52
+ brightness: 0,
53
+ contrast: 0,
54
+ charset: CHARSETS.standard,
55
+ colorMode: "grayscale",
56
+ accentColor: "#d4ff00",
57
+ invert: false,
58
+ renderMode: "ascii",
59
+ animationStyle: "none",
60
+ animationSpeed: 1,
61
+ dotSizeRatio: 0.8,
62
+ ditherStrength: 0,
63
+ hoverStrength: 0,
64
+ hoverRadius: 0.5,
65
+ hoverEffect: "spotlight",
66
+ hoverColor: "#ffffff",
67
+ artStyle: "classic"
68
+ };
69
+ var HOVER_PRESETS = {
70
+ none: {
71
+ label: "Off",
72
+ options: { hoverStrength: 0, hoverEffect: "spotlight", hoverRadius: 0.5, hoverColor: "#ffffff" }
73
+ },
74
+ subtle: {
75
+ label: "Subtle",
76
+ options: { hoverStrength: 0.8, hoverEffect: "glow", hoverRadius: 0.4, hoverColor: "#ffffff" }
77
+ },
78
+ flashlight: {
79
+ label: "Flashlight",
80
+ options: { hoverStrength: 2.5, hoverEffect: "spotlight", hoverRadius: 0.6, hoverColor: "#fffbe6" }
81
+ },
82
+ magnifier: {
83
+ label: "Magnifier",
84
+ options: { hoverStrength: 3, hoverEffect: "magnify", hoverRadius: 0.35, hoverColor: "#ffffff" }
85
+ },
86
+ forceField: {
87
+ label: "Force Field",
88
+ options: { hoverStrength: 3.5, hoverEffect: "repel", hoverRadius: 0.5, hoverColor: "#a0e8ff" }
89
+ },
90
+ neon: {
91
+ label: "Neon",
92
+ options: { hoverStrength: 2.5, hoverEffect: "colorShift", hoverRadius: 0.55, hoverColor: "#d946ef" }
93
+ },
94
+ fire: {
95
+ label: "Fire",
96
+ options: { hoverStrength: 3, hoverEffect: "spotlight", hoverRadius: 0.5, hoverColor: "#ff6b2b" }
97
+ },
98
+ ice: {
99
+ label: "Ice",
100
+ options: { hoverStrength: 2, hoverEffect: "glow", hoverRadius: 0.65, hoverColor: "#60d5f7" }
101
+ }
102
+ };
103
+ function createOffscreenCanvas(width, height) {
104
+ const canvas = document.createElement("canvas");
105
+ canvas.width = width;
106
+ canvas.height = height;
107
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
108
+ return { canvas, ctx };
109
+ }
110
+ function adjustLuminance(lum, brightness, contrast) {
111
+ let adjusted = lum + brightness * 255;
112
+ const factor = 259 * (contrast * 255 + 255) / (255 * (259 - contrast * 255));
113
+ adjusted = factor * (adjusted - 128) + 128;
114
+ return Math.max(0, Math.min(255, adjusted));
115
+ }
116
+ function luminanceToChar(lum, charset, invert) {
117
+ const normalized = invert ? 1 - lum / 255 : lum / 255;
118
+ const index = Math.floor(normalized * (charset.length - 1));
119
+ return charset[Math.max(0, Math.min(charset.length - 1, index))];
120
+ }
121
+ var BAYER_4X4 = [
122
+ [0, 8, 2, 10],
123
+ [12, 4, 14, 6],
124
+ [3, 11, 1, 9],
125
+ [15, 7, 13, 5]
126
+ ];
127
+ var _GRAY_LUT = new Array(256);
128
+ var _GREEN_LUT = new Array(256);
129
+ for (let _i = 0; _i < 256; _i++) {
130
+ _GRAY_LUT[_i] = `rgb(${_i},${_i},${_i})`;
131
+ _GREEN_LUT[_i] = `rgb(0,${_i},0)`;
132
+ }
133
+ function applyDither(lum, x, y, strength) {
134
+ if (strength <= 0) return lum;
135
+ const threshold = (BAYER_4X4[y % 4][x % 4] / 16 - 0.5) * strength * 128;
136
+ return Math.max(0, Math.min(255, lum + threshold));
137
+ }
138
+ function getCellColorStr(cell, colorMode, acR, acG, acB) {
139
+ switch (colorMode) {
140
+ case "fullcolor":
141
+ return `rgb(${cell.r},${cell.g},${cell.b})`;
142
+ case "matrix":
143
+ return _GREEN_LUT[0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0];
144
+ case "accent": {
145
+ const ab = (0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b) / 255;
146
+ return `rgb(${acR * ab | 0},${acG * ab | 0},${acB * ab | 0})`;
147
+ }
148
+ default:
149
+ return _GRAY_LUT[0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0];
150
+ }
151
+ }
152
+ var _colorRGB = [0, 0, 0];
153
+ function getCellColorRGB(cell, colorMode, acR, acG, acB) {
154
+ switch (colorMode) {
155
+ case "fullcolor":
156
+ _colorRGB[0] = cell.r;
157
+ _colorRGB[1] = cell.g;
158
+ _colorRGB[2] = cell.b;
159
+ break;
160
+ case "matrix": {
161
+ const mb = 0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0;
162
+ _colorRGB[0] = 0;
163
+ _colorRGB[1] = mb;
164
+ _colorRGB[2] = 0;
165
+ break;
166
+ }
167
+ case "accent": {
168
+ const ab = (0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b) / 255;
169
+ _colorRGB[0] = acR * ab | 0;
170
+ _colorRGB[1] = acG * ab | 0;
171
+ _colorRGB[2] = acB * ab | 0;
172
+ break;
173
+ }
174
+ default: {
175
+ const gray = 0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0;
176
+ _colorRGB[0] = gray;
177
+ _colorRGB[1] = gray;
178
+ _colorRGB[2] = gray;
179
+ break;
180
+ }
181
+ }
182
+ return _colorRGB;
183
+ }
184
+ function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
185
+ if (style === "none") return 1;
186
+ const t = time * speed;
187
+ switch (style) {
188
+ case "wave": {
189
+ const wave = Math.sin(x / cols * Math.PI * 4 + t * 3) * 0.5 + 0.5;
190
+ const wave2 = Math.sin(y / rows * Math.PI * 3 + t * 2) * 0.5 + 0.5;
191
+ return 0.3 + 0.7 * (wave * 0.6 + wave2 * 0.4);
192
+ }
193
+ case "pulse": {
194
+ const cx = cols / 2;
195
+ const cy = rows / 2;
196
+ const dist = Math.sqrt((x - cx) ** 2 + (y - cy) ** 2);
197
+ const maxDist = Math.sqrt(cx ** 2 + cy ** 2);
198
+ const ring = Math.sin(dist / maxDist * Math.PI * 6 - t * 4) * 0.5 + 0.5;
199
+ return 0.2 + 0.8 * ring;
200
+ }
201
+ case "rain": {
202
+ const drop = Math.sin(y / rows * Math.PI * 8 - t * 5 + x * 0.3) * 0.5 + 0.5;
203
+ const fade = Math.sin(x / cols * Math.PI * 2 + t) * 0.3 + 0.7;
204
+ return 0.1 + 0.9 * drop * fade;
205
+ }
206
+ case "breathe": {
207
+ const breathe = Math.sin(t * 2) * 0.3 + 0.7;
208
+ const subtle = Math.sin((x + y) * 0.1 + t) * 0.1;
209
+ return Math.max(0.1, Math.min(1, breathe + subtle));
210
+ }
211
+ case "sparkle": {
212
+ const hash = Math.sin(x * 127.1 + y * 311.7 + Math.floor(t * 8) * 43758.5453) * 43758.5453;
213
+ const sparkle = hash - Math.floor(hash);
214
+ return sparkle > 0.7 ? 1 : 0.15 + sparkle * 0.4;
215
+ }
216
+ case "glitch": {
217
+ const band = Math.floor(y / (rows * 0.05));
218
+ const glitchHash = Math.sin(band * 43.23 + Math.floor(t * 6) * 17.89) * 43758.5453;
219
+ const glitchVal = glitchHash - Math.floor(glitchHash);
220
+ if (glitchVal > 0.85) {
221
+ const flicker = Math.sin(t * 30 + band) * 0.5 + 0.5;
222
+ return flicker < 0.3 ? 0 : flicker;
223
+ }
224
+ return 1;
225
+ }
226
+ case "spiral": {
227
+ const cx = cols / 2;
228
+ const cy = rows / 2;
229
+ const dx = x - cx;
230
+ const dy = y - cy;
231
+ const angle = Math.atan2(dy, dx);
232
+ const dist = Math.sqrt(dx * dx + dy * dy);
233
+ const maxDist = Math.sqrt(cx * cx + cy * cy);
234
+ const spiral = Math.sin(angle * 3 + dist / maxDist * Math.PI * 8 - t * 3) * 0.5 + 0.5;
235
+ return 0.15 + 0.85 * spiral;
236
+ }
237
+ case "typewriter": {
238
+ const totalCells = cols * rows;
239
+ const cellIndex = y * cols + x;
240
+ const progress = t * 0.5 % 1;
241
+ const revealPoint = progress * totalCells * 1.3;
242
+ const dist = cellIndex - revealPoint;
243
+ if (dist > 0) return 0;
244
+ const fade = Math.max(0, 1 + dist / (totalCells * 0.15));
245
+ return Math.min(1, fade);
246
+ }
247
+ case "scatter": {
248
+ const scx = cols / 2;
249
+ const scy = rows / 2;
250
+ const sdx = x - scx;
251
+ const sdy = y - scy;
252
+ const sdist = Math.sqrt(sdx * sdx + sdy * sdy) / Math.sqrt(scx * scx + scy * scy);
253
+ const phase = Math.sin(t * 1.5) * 0.5 + 0.5;
254
+ const threshold = phase;
255
+ if (sdist > threshold) {
256
+ return Math.max(0, 1 - (sdist - threshold) * 3);
257
+ }
258
+ return 0.7 + 0.3 * Math.sin(sdist * 10 - t * 2);
259
+ }
260
+ default:
261
+ return 1;
262
+ }
263
+ }
264
+ function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
265
+ const srcWidth = source instanceof HTMLVideoElement ? source.videoWidth : source.width;
266
+ const srcHeight = source instanceof HTMLVideoElement ? source.videoHeight : source.height;
267
+ if (srcWidth === 0 || srcHeight === 0) {
268
+ return { frame: [], cols: 0, rows: 0 };
269
+ }
270
+ const charAspect = 0.55;
271
+ const cellW = options.fontSize * options.charSpacing;
272
+ const cellH = options.fontSize / charAspect * options.charSpacing;
273
+ const renderW = targetWidth || srcWidth;
274
+ const renderH = targetHeight || srcHeight;
275
+ const cols = Math.floor(renderW / cellW);
276
+ const rows = Math.floor(renderH / cellH);
277
+ if (cols <= 0 || rows <= 0) {
278
+ return { frame: [], cols: 0, rows: 0 };
279
+ }
280
+ const { ctx } = createOffscreenCanvas(cols, rows);
281
+ ctx.drawImage(source, 0, 0, cols, rows);
282
+ const imageData = ctx.getImageData(0, 0, cols, rows);
283
+ const pixels = imageData.data;
284
+ const frame = [];
285
+ for (let y = 0; y < rows; y++) {
286
+ const row = [];
287
+ for (let x = 0; x < cols; x++) {
288
+ const i = (y * cols + x) * 4;
289
+ const r = pixels[i];
290
+ const g = pixels[i + 1];
291
+ const b = pixels[i + 2];
292
+ const a = pixels[i + 3];
293
+ const lum = 0.299 * r + 0.587 * g + 0.114 * b;
294
+ const adjustedLum = adjustLuminance(lum, options.brightness, options.contrast);
295
+ const ditheredLum = applyDither(adjustedLum, x, y, options.ditherStrength);
296
+ const char = luminanceToChar(ditheredLum, options.charset, options.invert);
297
+ row.push({ char, r, g, b, a });
298
+ }
299
+ frame.push(row);
300
+ }
301
+ return { frame, cols, rows };
302
+ }
303
+ async function videoToAsciiFrames(video, options, targetWidth, targetHeight, targetFps = 12, maxDuration = 10, onProgress) {
304
+ const duration = Math.min(video.duration, maxDuration);
305
+ const totalFrames = Math.ceil(duration * targetFps);
306
+ const frames = [];
307
+ let cols = 0;
308
+ let rows = 0;
309
+ for (let i = 0; i < totalFrames; i++) {
310
+ const time = i / targetFps;
311
+ if (time > duration) break;
312
+ video.currentTime = time;
313
+ await new Promise((resolve) => {
314
+ const handler = () => {
315
+ video.removeEventListener("seeked", handler);
316
+ resolve();
317
+ };
318
+ video.addEventListener("seeked", handler);
319
+ });
320
+ const result = imageToAsciiFrame(video, options, targetWidth, targetHeight);
321
+ frames.push(result.frame);
322
+ cols = result.cols;
323
+ rows = result.rows;
324
+ onProgress?.((i + 1) / totalFrames);
325
+ }
326
+ return { frames, cols, rows, fps: targetFps };
327
+ }
328
+ async function gifToAsciiFrames(buffer, options, targetWidth, targetHeight, onProgress) {
329
+ const gif = gifuctJs.parseGIF(buffer);
330
+ const rawFrames = gifuctJs.decompressFrames(gif, true);
331
+ if (rawFrames.length === 0) {
332
+ return { frames: [], cols: 0, rows: 0, fps: 10 };
333
+ }
334
+ const gifW = rawFrames[0].dims.width;
335
+ const gifH = rawFrames[0].dims.height;
336
+ const logicalW = gif.lsd?.width || gifW;
337
+ const logicalH = gif.lsd?.height || gifH;
338
+ const compCanvas = document.createElement("canvas");
339
+ compCanvas.width = logicalW;
340
+ compCanvas.height = logicalH;
341
+ const compCtx = compCanvas.getContext("2d");
342
+ const prevCanvas = document.createElement("canvas");
343
+ prevCanvas.width = logicalW;
344
+ prevCanvas.height = logicalH;
345
+ const prevCtx = prevCanvas.getContext("2d");
346
+ const frames = [];
347
+ let cols = 0;
348
+ let rows = 0;
349
+ let totalDelay = 0;
350
+ for (const f of rawFrames) {
351
+ totalDelay += f.delay || 100;
352
+ }
353
+ const avgDelay = totalDelay / rawFrames.length;
354
+ const fps = Math.round(Math.min(30, Math.max(5, 1e3 / avgDelay)));
355
+ const maxFrames = Math.min(rawFrames.length, 300);
356
+ for (let i = 0; i < maxFrames; i++) {
357
+ const f = rawFrames[i];
358
+ const { dims, patch, disposalType } = f;
359
+ if (disposalType === 3) {
360
+ prevCtx.clearRect(0, 0, logicalW, logicalH);
361
+ prevCtx.drawImage(compCanvas, 0, 0);
362
+ }
363
+ const frameImageData = new ImageData(patch, dims.width, dims.height);
364
+ const tempCanvas = document.createElement("canvas");
365
+ tempCanvas.width = dims.width;
366
+ tempCanvas.height = dims.height;
367
+ const tempCtx = tempCanvas.getContext("2d");
368
+ tempCtx.putImageData(frameImageData, 0, 0);
369
+ compCtx.drawImage(tempCanvas, dims.left || 0, dims.top || 0);
370
+ const result = imageToAsciiFrame(compCanvas, options, targetWidth, targetHeight);
371
+ frames.push(result.frame);
372
+ cols = result.cols;
373
+ rows = result.rows;
374
+ if (disposalType === 2) {
375
+ compCtx.clearRect(dims.left || 0, dims.top || 0, dims.width, dims.height);
376
+ } else if (disposalType === 3) {
377
+ compCtx.clearRect(0, 0, logicalW, logicalH);
378
+ compCtx.drawImage(prevCanvas, 0, 0);
379
+ }
380
+ onProgress?.((i + 1) / maxFrames);
381
+ }
382
+ return { frames, cols, rows, fps };
383
+ }
384
+ function smoothstep(t) {
385
+ return t * t * (3 - 2 * t);
386
+ }
387
+ var _hoverResult = { scale: 1, offsetX: 0, offsetY: 0, glow: 0, colorBlend: 0, proximity: 0 };
388
+ function computeHoverEffect(nx, ny, hoverX, hoverY, hoverIntensity, strength, cellW, cellH, effect = "spotlight", radiusFactor = 0.5) {
389
+ const dx = nx - hoverX;
390
+ const dy = ny - hoverY;
391
+ const distSq = dx * dx + dy * dy;
392
+ const radius = 0.08 + radiusFactor * 0.35 + strength * 0.04;
393
+ if (distSq >= radius * radius) {
394
+ _hoverResult.scale = 1;
395
+ _hoverResult.offsetX = 0;
396
+ _hoverResult.offsetY = 0;
397
+ _hoverResult.glow = 0;
398
+ _hoverResult.colorBlend = 0;
399
+ _hoverResult.proximity = 0;
400
+ return _hoverResult;
401
+ }
402
+ const dist = Math.sqrt(distSq);
403
+ const t = 1 - dist / radius;
404
+ const eased = smoothstep(t) * hoverIntensity;
405
+ let scale = 1;
406
+ let offsetX = 0;
407
+ let offsetY = 0;
408
+ let glow = 0;
409
+ let colorBlend = 0;
410
+ switch (effect) {
411
+ case "spotlight": {
412
+ scale = 1 + eased * strength * 1.8;
413
+ const angle = Math.atan2(dy, dx);
414
+ const pushForce = eased * eased * strength * 0.6;
415
+ offsetX = Math.cos(angle) * pushForce * cellW;
416
+ offsetY = Math.sin(angle) * pushForce * cellH;
417
+ glow = eased * strength * 0.4;
418
+ colorBlend = eased * eased * strength * 0.25;
419
+ break;
420
+ }
421
+ case "magnify":
422
+ scale = 1 + eased * strength * 2.5;
423
+ glow = eased * strength * 0.15;
424
+ break;
425
+ case "repel": {
426
+ scale = 1 + eased * strength * 0.3;
427
+ const angle2 = Math.atan2(dy, dx);
428
+ const push = eased * eased * strength * 1.2;
429
+ offsetX = Math.cos(angle2) * push * cellW;
430
+ offsetY = Math.sin(angle2) * push * cellH;
431
+ break;
432
+ }
433
+ case "glow":
434
+ glow = eased * strength * 0.8;
435
+ colorBlend = eased * strength * 0.4;
436
+ break;
437
+ case "colorShift":
438
+ scale = 1 + eased * strength * 0.4;
439
+ glow = eased * strength * 0.2;
440
+ colorBlend = eased * strength * 0.7;
441
+ break;
442
+ }
443
+ _hoverResult.scale = scale;
444
+ _hoverResult.offsetX = offsetX;
445
+ _hoverResult.offsetY = offsetY;
446
+ _hoverResult.glow = glow;
447
+ _hoverResult.colorBlend = colorBlend;
448
+ _hoverResult.proximity = eased;
449
+ return _hoverResult;
450
+ }
451
+ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, time = 0, hoverPos) {
452
+ const rows = frame.length;
453
+ if (rows === 0) return;
454
+ const cols = frame[0].length;
455
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
456
+ let hasTransparency = false;
457
+ const sampleStepY = Math.max(1, rows >> 2);
458
+ const sampleStepX = Math.max(1, cols >> 2);
459
+ outer:
460
+ for (let sampleY = 0; sampleY < rows; sampleY += sampleStepY) {
461
+ const row = frame[sampleY];
462
+ for (let sampleX = 0; sampleX < cols; sampleX += sampleStepX) {
463
+ if (row[sampleX].a < 200) {
464
+ hasTransparency = true;
465
+ break outer;
466
+ }
467
+ }
468
+ }
469
+ if (!hasTransparency) {
470
+ ctx.fillStyle = "#0a0a0a";
471
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
472
+ }
473
+ const cellW = canvasWidth / cols;
474
+ const cellH = canvasHeight / rows;
475
+ const hoverIntensity = hoverPos?.intensity ?? 1;
476
+ const hoverActive = !!(hoverPos && options.hoverStrength > 0 && hoverIntensity > 5e-3);
477
+ const hc = options.hoverColor || "#ffffff";
478
+ const hcR = parseInt(hc.slice(1, 3), 16) || 255;
479
+ const hcG = parseInt(hc.slice(3, 5), 16) || 255;
480
+ const hcB = parseInt(hc.slice(5, 7), 16) || 255;
481
+ const acHex = (options.accentColor || "#ffffff").replace("#", "");
482
+ const acR = parseInt(acHex.substring(0, 2), 16) || 255;
483
+ const acG = parseInt(acHex.substring(2, 4), 16) || 255;
484
+ const acB = parseInt(acHex.substring(4, 6), 16) || 255;
485
+ let hoverMinCol = 0, hoverMaxCol = cols, hoverMinRow = 0, hoverMaxRow = rows;
486
+ let hoverPosX = 0, hoverPosY = 0;
487
+ if (hoverActive && hoverPos) {
488
+ hoverPosX = hoverPos.x;
489
+ hoverPosY = hoverPos.y;
490
+ const hoverNormRadius = 0.08 + options.hoverRadius * 0.35 + options.hoverStrength * 0.04;
491
+ hoverMinCol = Math.max(0, Math.floor((hoverPosX - hoverNormRadius) * cols) - 1);
492
+ hoverMaxCol = Math.min(cols, Math.ceil((hoverPosX + hoverNormRadius) * cols) + 1);
493
+ hoverMinRow = Math.max(0, Math.floor((hoverPosY - hoverNormRadius) * rows) - 1);
494
+ hoverMaxRow = Math.min(rows, Math.ceil((hoverPosY + hoverNormRadius) * rows) + 1);
495
+ }
496
+ const animStyle = options.animationStyle;
497
+ const animSpeed = options.animationSpeed;
498
+ const noAnimation = animStyle === "none";
499
+ const hoverStrength = options.hoverStrength;
500
+ const hoverEffect = options.hoverEffect;
501
+ const hoverRadiusFactor = options.hoverRadius;
502
+ const isInverted = options.invert;
503
+ const colorMode = options.colorMode;
504
+ const TWO_PI = Math.PI * 2;
505
+ const invCols = 1 / cols;
506
+ const invRows = 1 / rows;
507
+ let lastFillStyle = "";
508
+ let lastAlpha = -1;
509
+ if (options.renderMode === "dots") {
510
+ const maxRadius = Math.min(cellW, cellH) * 0.5 * options.dotSizeRatio;
511
+ for (let y = 0; y < rows; y++) {
512
+ const rowData = frame[y];
513
+ for (let x = 0; x < cols; x++) {
514
+ const cell = rowData[x];
515
+ if (cell.a < 10) continue;
516
+ const lum = (0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b) * 0.00392156863;
517
+ const intensity = isInverted ? 1 - lum : lum;
518
+ if (intensity < 0.02) continue;
519
+ const animMul = noAnimation ? 1 : getAnimationMultiplier(x, y, cols, rows, time, animStyle, animSpeed);
520
+ let hoverMul = 1;
521
+ let hoverOffX = 0;
522
+ let hoverOffY = 0;
523
+ let hoverGlow = 0;
524
+ let hoverBlend = 0;
525
+ if (hoverActive && x >= hoverMinCol && x <= hoverMaxCol && y >= hoverMinRow && y <= hoverMaxRow) {
526
+ const fx = computeHoverEffect(
527
+ x * invCols,
528
+ y * invRows,
529
+ hoverPosX,
530
+ hoverPosY,
531
+ hoverIntensity,
532
+ hoverStrength,
533
+ cellW,
534
+ cellH,
535
+ hoverEffect,
536
+ hoverRadiusFactor
537
+ );
538
+ hoverMul = fx.scale;
539
+ hoverOffX = fx.offsetX;
540
+ hoverOffY = fx.offsetY;
541
+ hoverGlow = fx.glow;
542
+ hoverBlend = fx.colorBlend;
543
+ }
544
+ const radius = maxRadius * intensity * animMul * hoverMul;
545
+ if (radius < 0.3) continue;
546
+ const px = x * cellW + cellW * 0.5 + hoverOffX;
547
+ const py = y * cellH + cellH * 0.5 + hoverOffY;
548
+ let color;
549
+ if (hoverBlend > 0) {
550
+ const rgb = getCellColorRGB(cell, colorMode, acR, acG, acB);
551
+ const cr = Math.min(255, rgb[0] + (hcR - rgb[0]) * hoverBlend | 0);
552
+ const cg = Math.min(255, rgb[1] + (hcG - rgb[1]) * hoverBlend | 0);
553
+ const cb = Math.min(255, rgb[2] + (hcB - rgb[2]) * hoverBlend | 0);
554
+ color = `rgb(${cr},${cg},${cb})`;
555
+ } else {
556
+ color = getCellColorStr(cell, colorMode, acR, acG, acB);
557
+ }
558
+ const alpha = Math.min(1, cell.a * 0.00392156863 * animMul * (1 + hoverGlow));
559
+ if (alpha !== lastAlpha) {
560
+ ctx.globalAlpha = alpha;
561
+ lastAlpha = alpha;
562
+ }
563
+ if (color !== lastFillStyle) {
564
+ ctx.fillStyle = color;
565
+ lastFillStyle = color;
566
+ }
567
+ if (radius <= 1.5) {
568
+ const d = radius * 2;
569
+ ctx.fillRect(px - radius, py - radius, d, d);
570
+ } else {
571
+ ctx.beginPath();
572
+ ctx.arc(px, py, radius, 0, TWO_PI);
573
+ ctx.fill();
574
+ }
575
+ }
576
+ }
577
+ } else {
578
+ const charAspect = 0.55;
579
+ const fontSize = Math.min(cellW / charAspect, cellH) * 0.9;
580
+ ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
581
+ ctx.textAlign = "center";
582
+ ctx.textBaseline = "middle";
583
+ const baseTransform = ctx.getTransform();
584
+ for (let y = 0; y < rows; y++) {
585
+ const rowData = frame[y];
586
+ for (let x = 0; x < cols; x++) {
587
+ const cell = rowData[x];
588
+ if (cell.char === " " || cell.a < 10) continue;
589
+ const animMul = noAnimation ? 1 : getAnimationMultiplier(x, y, cols, rows, time, animStyle, animSpeed);
590
+ if (animMul < 0.05) continue;
591
+ let hoverScale = 1;
592
+ let hoverOffX = 0;
593
+ let hoverOffY = 0;
594
+ let hoverGlow = 0;
595
+ let hoverBlend = 0;
596
+ if (hoverActive && x >= hoverMinCol && x <= hoverMaxCol && y >= hoverMinRow && y <= hoverMaxRow) {
597
+ const fx = computeHoverEffect(
598
+ x * invCols,
599
+ y * invRows,
600
+ hoverPosX,
601
+ hoverPosY,
602
+ hoverIntensity,
603
+ hoverStrength,
604
+ cellW,
605
+ cellH,
606
+ hoverEffect,
607
+ hoverRadiusFactor
608
+ );
609
+ hoverScale = fx.scale;
610
+ hoverOffX = fx.offsetX;
611
+ hoverOffY = fx.offsetY;
612
+ hoverGlow = fx.glow;
613
+ hoverBlend = fx.colorBlend;
614
+ }
615
+ const px = x * cellW + cellW * 0.5 + hoverOffX;
616
+ const py = y * cellH + cellH * 0.5 + hoverOffY;
617
+ const alpha = Math.min(1, cell.a * 0.00392156863 * animMul * (1 + hoverGlow));
618
+ if (alpha !== lastAlpha) {
619
+ ctx.globalAlpha = alpha;
620
+ lastAlpha = alpha;
621
+ }
622
+ let color;
623
+ if (hoverBlend > 0) {
624
+ const rgb = getCellColorRGB(cell, colorMode, acR, acG, acB);
625
+ const cr = Math.min(255, rgb[0] + (hcR - rgb[0]) * hoverBlend | 0);
626
+ const cg = Math.min(255, rgb[1] + (hcG - rgb[1]) * hoverBlend | 0);
627
+ const cb = Math.min(255, rgb[2] + (hcB - rgb[2]) * hoverBlend | 0);
628
+ color = `rgb(${cr},${cg},${cb})`;
629
+ } else {
630
+ color = getCellColorStr(cell, colorMode, acR, acG, acB);
631
+ }
632
+ if (color !== lastFillStyle) {
633
+ ctx.fillStyle = color;
634
+ lastFillStyle = color;
635
+ }
636
+ if (hoverScale !== 1) {
637
+ ctx.translate(px, py);
638
+ ctx.scale(hoverScale, hoverScale);
639
+ ctx.fillText(cell.char, 0, 0);
640
+ ctx.setTransform(baseTransform);
641
+ } else {
642
+ ctx.fillText(cell.char, px, py);
643
+ }
644
+ }
645
+ }
646
+ }
647
+ ctx.globalAlpha = 1;
648
+ }
649
+ function serializeFrame(frame, includeChars) {
650
+ return frame.map((row) => row.map((cell) => {
651
+ const obj = { r: cell.r, g: cell.g, b: cell.b, a: cell.a };
652
+ if (includeChars) obj.c = cell.char.charCodeAt(0);
653
+ else obj.l = Math.round(0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b);
654
+ return obj;
655
+ }));
656
+ }
657
+ function buildAnimFnJS(style, speed) {
658
+ if (style === "none") return "function(){return 1}";
659
+ return `function(x,y,cols,rows,t){
660
+ var s=${speed},T=t*s;
661
+ ${style === "wave" ? `var w=Math.sin(x/cols*Math.PI*4+T*3)*.5+.5,w2=Math.sin(y/rows*Math.PI*3+T*2)*.5+.5;return .3+.7*(w*.6+w2*.4);` : ""}${style === "pulse" ? `var cx=cols/2,cy=rows/2,d=Math.sqrt((x-cx)*(x-cx)+(y-cy)*(y-cy)),m=Math.sqrt(cx*cx+cy*cy);return .2+.8*(Math.sin(d/m*Math.PI*6-T*4)*.5+.5);` : ""}${style === "rain" ? `return .1+.9*(Math.sin(y/rows*Math.PI*8-T*5+x*.3)*.5+.5)*(Math.sin(x/cols*Math.PI*2+T)*.3+.7);` : ""}${style === "breathe" ? `return Math.max(.1,Math.min(1,Math.sin(T*2)*.3+.7+Math.sin((x+y)*.1+T)*.1));` : ""}${style === "sparkle" ? `var h=Math.sin(x*127.1+y*311.7+Math.floor(T*8)*43758.5453)*43758.5453;var sp=h-Math.floor(h);return sp>.7?1:.15+sp*.4;` : ""}${style === "glitch" ? `var b=Math.floor(y/(rows*.05)),gh=Math.sin(b*43.23+Math.floor(T*6)*17.89)*43758.5453,gv=gh-Math.floor(gh);if(gv>.85){var fl=Math.sin(T*30+b)*.5+.5;return fl<.3?0:fl}return 1;` : ""}${style === "spiral" ? `var cx=cols/2,cy=rows/2,dx=x-cx,dy=y-cy,a=Math.atan2(dy,dx),d=Math.sqrt(dx*dx+dy*dy),m=Math.sqrt(cx*cx+cy*cy);return .15+.85*(Math.sin(a*3+d/m*Math.PI*8-T*3)*.5+.5);` : ""}${style === "typewriter" ? `var tc=cols*rows,ci=y*cols+x,p=(T*.5)%1,rp=p*tc*1.3,dd=ci-rp;if(dd>0)return 0;return Math.min(1,Math.max(0,1+dd/(tc*.15)));` : ""}${style === "scatter" ? `var cx=cols/2,cy=rows/2,dx=x-cx,dy=y-cy,sd=Math.sqrt(dx*dx+dy*dy)/Math.sqrt(cx*cx+cy*cy),ph=Math.sin(T*1.5)*.5+.5;if(sd>ph)return Math.max(0,1-(sd-ph)*3);return .7+.3*Math.sin(sd*10-T*2);` : ""}
662
+ }`;
663
+ }
664
+ function buildColorFnJS(options) {
665
+ switch (options.colorMode) {
666
+ case "fullcolor":
667
+ return `function(c){return 'rgb('+c.r+','+c.g+','+c.b+')'}`;
668
+ case "matrix":
669
+ return `function(c){var b=Math.floor(.299*c.r+.587*c.g+.114*c.b);return 'rgb(0,'+b+',0)'}`;
670
+ case "accent": {
671
+ const hex = options.accentColor.replace("#", "");
672
+ const ar = parseInt(hex.substring(0, 2), 16);
673
+ const ag = parseInt(hex.substring(2, 4), 16);
674
+ const ab = parseInt(hex.substring(4, 6), 16);
675
+ return `function(c){var b=(.299*c.r+.587*c.g+.114*c.b)/255;return 'rgb('+Math.floor(${ar}*b)+','+Math.floor(${ag}*b)+','+Math.floor(${ab}*b)+')'}`;
676
+ }
677
+ default:
678
+ return `function(c){var g=Math.floor(.299*c.r+.587*c.g+.114*c.b);return 'rgb('+g+','+g+','+g+')'}`;
679
+ }
680
+ }
681
+ function generateEmbedCode(frame, options, width, height) {
682
+ const rows = frame.length;
683
+ if (rows === 0) return "";
684
+ const cols = frame[0].length;
685
+ const isDots = options.renderMode === "dots";
686
+ const data = serializeFrame(frame, !isDots);
687
+ const hasAnim = options.animationStyle !== "none";
688
+ const hasHover = options.hoverStrength > 0;
689
+ const charset = isDots ? "" : options.charset;
690
+ const hc = options.hoverColor || "#ffffff";
691
+ const hcR = parseInt(hc.slice(1, 3), 16) || 255;
692
+ const hcG = parseInt(hc.slice(3, 5), 16) || 255;
693
+ const hcB = parseInt(hc.slice(5, 7), 16) || 255;
694
+ const hasTransparency = frame.some((row) => row.some((cell) => cell.a < 200));
695
+ return `<!-- Asciify Embed -->
696
+ <canvas id="ar-embed" width="${width}" height="${height}" style="${hasTransparency ? "" : "background:#0a0a0a;"}display:block;"></canvas>
697
+ <script>
698
+ (function(){
699
+ var D=${JSON.stringify(data)};
700
+ var R=${rows},C=${cols},W=${width},H=${height};
701
+ var cW=W/C,cH=H/R;
702
+ var isDots=${isDots};
703
+ var invert=${options.invert};
704
+ var dotR=${options.dotSizeRatio};
705
+ var hStr=${options.hoverStrength},hRad=${options.hoverRadius};
706
+ var hcR=${hcR},hcG=${hcG},hcB=${hcB};
707
+ var hEff='${options.hoverEffect}';
708
+ var charset=${JSON.stringify(charset)};
709
+ var hasTr=${hasTransparency};
710
+ var cv=document.getElementById('ar-embed'),ctx=cv.getContext('2d');
711
+ var mx=-1,my=-1,mIn=0;
712
+ var getColor=${buildColorFnJS(options)};
713
+ var getAnim=${buildAnimFnJS(options.animationStyle, options.animationSpeed)};
714
+ ${hasHover ? `
715
+ cv.addEventListener('mousemove',function(e){var r=cv.getBoundingClientRect();mx=(e.clientX-r.left)/r.width;my=(e.clientY-r.top)/r.height});
716
+ cv.addEventListener('mouseleave',function(){mx=-1;my=-1});
717
+ function ss(t){return t*t*(3-2*t)}
718
+ ` : ""}
719
+ var smX=.5,smY=.5;
720
+ function draw(t){
721
+ var time=t/1000;
722
+ ctx.clearRect(0,0,W,H);
723
+ if(!hasTr){ctx.fillStyle='#0a0a0a';ctx.fillRect(0,0,W,H)};
724
+ ${hasHover ? `
725
+ if(mx>=0){smX+=(mx-smX)*.1;smY+=(my-smY)*.1;mIn+=(1-mIn)*.12}
726
+ else{mIn*=.96;if(mIn<.003)mIn=0}
727
+ ` : ""}
728
+ for(var y=0;y<R;y++)for(var x=0;x<C;x++){
729
+ var p=D[y][x];if(p.a<10)continue;
730
+ var am=getAnim(x,y,C,R,time);if(am<.05)continue;
731
+ var sc=1,ox=0,oy=0,gl=0,cb=0;
732
+ ${hasHover ? `
733
+ if(mIn>.003&&hStr>0){
734
+ var nx=x/C,ny=y/R,dx=nx-smX,dy=ny-smY,dd=Math.sqrt(dx*dx+dy*dy);
735
+ var rad=(.08+hRad*.35)+hStr*.04;
736
+ if(dd<rad){
737
+ var tt=1-dd/rad,e=ss(tt)*mIn,ang=Math.atan2(dy,dx);
738
+ if(hEff==='spotlight'){sc=1+e*hStr*1.8;var pf=e*e*hStr*.6;ox=Math.cos(ang)*pf*cW;oy=Math.sin(ang)*pf*cH;gl=e*hStr*.4;cb=e*e*hStr*.25}
739
+ else if(hEff==='magnify'){sc=1+e*hStr*2.5;gl=e*hStr*.15}
740
+ else if(hEff==='repel'){sc=1+e*hStr*.3;var pf=e*e*hStr*1.2;ox=Math.cos(ang)*pf*cW;oy=Math.sin(ang)*pf*cH}
741
+ else if(hEff==='glow'){gl=e*hStr*.8;cb=e*hStr*.4}
742
+ else if(hEff==='colorShift'){sc=1+e*hStr*.4;gl=e*hStr*.2;cb=e*hStr*.7}
743
+ }
744
+ }
745
+ ` : ""}
746
+ var px=x*cW+cW/2+ox,py=y*cH+cH/2+oy;
747
+ var col=getColor(p);
748
+ if(cb>0){var m=col.match(/rgb\\((\\d+),(\\d+),(\\d+)\\)/);if(m){var rr=+m[1],gg=+m[2],bb=+m[3];col='rgb('+Math.min(255,Math.round(rr+(hcR-rr)*cb))+','+Math.min(255,Math.round(gg+(hcG-gg)*cb))+','+Math.min(255,Math.round(bb+(hcB-bb)*cb))+')'}}
749
+ if(isDots){
750
+ var lum=p.l/255;var int=invert?1-lum:lum;if(int<.02)continue;
751
+ var rad=Math.min(cW,cH)*.5*dotR*int*am*sc;if(rad<.3)continue;
752
+ ctx.globalAlpha=Math.min(1,(p.a/255)*am*(1+gl));
753
+ ctx.fillStyle=col;ctx.beginPath();ctx.arc(px,py,rad,0,Math.PI*2);ctx.fill()
754
+ }else{
755
+ var lum=(.299*p.r+.587*p.g+.114*p.b)/255;if(invert)lum=1-lum;
756
+ var ci=Math.floor(lum*(charset.length-1));
757
+ var ch=charset[Math.max(0,Math.min(charset.length-1,ci))];
758
+ if(ch===' ')continue;
759
+ ctx.globalAlpha=Math.min(1,(p.a/255)*am*(1+gl));
760
+ ctx.fillStyle=col;
761
+ var fs=Math.min(cW/.55,cH)*.9*sc;
762
+ ctx.font=fs+'px "JetBrains Mono",monospace';ctx.textAlign='center';ctx.textBaseline='middle';
763
+ ctx.fillText(ch,px,py)
764
+ }
765
+ }
766
+ ctx.globalAlpha=1;
767
+ ${hasAnim || hasHover ? "requestAnimationFrame(draw)" : ""};
768
+ }
769
+ ${hasAnim || hasHover ? "requestAnimationFrame(draw)" : "draw(0)"};
770
+ })();
771
+ </script>
772
+ <!-- /Asciify Embed -->`;
773
+ }
774
+ function generateAnimatedEmbedCode(frames, options, fps, width, height) {
775
+ const rows = frames[0].length;
776
+ const cols = frames[0][0].length;
777
+ const isDots = options.renderMode === "dots";
778
+ const allData = frames.map((f) => serializeFrame(f, !isDots));
779
+ const hasHover = options.hoverStrength > 0;
780
+ const hc = options.hoverColor || "#ffffff";
781
+ const hcR = parseInt(hc.slice(1, 3), 16) || 255;
782
+ const hcG = parseInt(hc.slice(3, 5), 16) || 255;
783
+ const hcB = parseInt(hc.slice(5, 7), 16) || 255;
784
+ const charset = isDots ? "" : options.charset;
785
+ const hasTransparency = frames.some((f) => f.some((row) => row.some((cell) => cell.a < 200)));
786
+ return `<!-- Asciify Animated Embed -->
787
+ <canvas id="ar-anim" width="${width}" height="${height}" style="${hasTransparency ? "" : "background:#0a0a0a;"}display:block;"></canvas>
788
+ <script>
789
+ (function(){
790
+ var F=${JSON.stringify(allData)};
791
+ var R=${rows},C=${cols},W=${width},H=${height},FPS=${fps};
792
+ var cW=W/C,cH=H/R;
793
+ var isDots=${isDots};
794
+ var invert=${options.invert};
795
+ var dotR=${options.dotSizeRatio};
796
+ var hStr=${options.hoverStrength},hRad=${options.hoverRadius};
797
+ var hcR=${hcR},hcG=${hcG},hcB=${hcB};
798
+ var hEff='${options.hoverEffect}';
799
+ var charset=${JSON.stringify(charset)};
800
+ var hasTr=${hasTransparency};
801
+ var cv=document.getElementById('ar-anim'),ctx=cv.getContext('2d');
802
+ var mx=-1,my=-1,mIn=0,smX=.5,smY=.5;
803
+ var fi=0,lastT=0;
804
+ var getColor=${buildColorFnJS(options)};
805
+ ${hasHover ? `
806
+ cv.addEventListener('mousemove',function(e){var r=cv.getBoundingClientRect();mx=(e.clientX-r.left)/r.width;my=(e.clientY-r.top)/r.height});
807
+ cv.addEventListener('mouseleave',function(){mx=-1;my=-1});
808
+ function ss(t){return t*t*(3-2*t)}
809
+ ` : ""}
810
+ function draw(t){
811
+ var time=t/1000;
812
+ if(t-lastT>=1000/FPS){fi=(fi+1)%F.length;lastT=t}
813
+ var D=F[fi];
814
+ ctx.clearRect(0,0,W,H);if(!hasTr){ctx.fillStyle='#0a0a0a';ctx.fillRect(0,0,W,H)};
815
+ ${hasHover ? `
816
+ if(mx>=0){smX+=(mx-smX)*.1;smY+=(my-smY)*.1;mIn+=(1-mIn)*.12}
817
+ else{mIn*=.96;if(mIn<.003)mIn=0}
818
+ ` : ""}
819
+ for(var y=0;y<R;y++)for(var x=0;x<C;x++){
820
+ var p=D[y][x];if(p.a<10)continue;
821
+ var sc=1,ox=0,oy=0,gl=0,cb=0;
822
+ ${hasHover ? `
823
+ if(mIn>.003&&hStr>0){
824
+ var nx=x/C,ny=y/R,dx=nx-smX,dy=ny-smY,dd=Math.sqrt(dx*dx+dy*dy);
825
+ var rad=(.08+hRad*.35)+hStr*.04;
826
+ if(dd<rad){
827
+ var tt=1-dd/rad,e=ss(tt)*mIn,ang=Math.atan2(dy,dx);
828
+ if(hEff==='spotlight'){sc=1+e*hStr*1.8;var pf=e*e*hStr*.6;ox=Math.cos(ang)*pf*cW;oy=Math.sin(ang)*pf*cH;gl=e*hStr*.4;cb=e*e*hStr*.25}
829
+ else if(hEff==='magnify'){sc=1+e*hStr*2.5;gl=e*hStr*.15}
830
+ else if(hEff==='repel'){sc=1+e*hStr*.3;var pf=e*e*hStr*1.2;ox=Math.cos(ang)*pf*cW;oy=Math.sin(ang)*pf*cH}
831
+ else if(hEff==='glow'){gl=e*hStr*.8;cb=e*hStr*.4}
832
+ else if(hEff==='colorShift'){sc=1+e*hStr*.4;gl=e*hStr*.2;cb=e*hStr*.7}
833
+ }
834
+ }
835
+ ` : ""}
836
+ var px=x*cW+cW/2+ox,py=y*cH+cH/2+oy;
837
+ var col=getColor(p);
838
+ if(cb>0){var m=col.match(/rgb\\((\\d+),(\\d+),(\\d+)\\)/);if(m){var rr=+m[1],gg=+m[2],bb=+m[3];col='rgb('+Math.min(255,Math.round(rr+(hcR-rr)*cb))+','+Math.min(255,Math.round(gg+(hcG-gg)*cb))+','+Math.min(255,Math.round(bb+(hcB-bb)*cb))+')'}}
839
+ if(isDots){
840
+ var lum=p.l/255;var int=invert?1-lum:lum;if(int<.02)continue;
841
+ var rad=Math.min(cW,cH)*.5*dotR*int*sc;if(rad<.3)continue;
842
+ ctx.globalAlpha=Math.min(1,(p.a/255)*(1+gl));
843
+ ctx.fillStyle=col;ctx.beginPath();ctx.arc(px,py,rad,0,Math.PI*2);ctx.fill()
844
+ }else{
845
+ var lum=(.299*p.r+.587*p.g+.114*p.b)/255;if(invert)lum=1-lum;
846
+ var ci=Math.floor(lum*(charset.length-1));
847
+ var ch=charset[Math.max(0,Math.min(charset.length-1,ci))];
848
+ if(ch===' ')continue;
849
+ ctx.globalAlpha=Math.min(1,(p.a/255)*(1+gl));
850
+ ctx.fillStyle=col;
851
+ var fs=Math.min(cW/.55,cH)*.9*sc;
852
+ ctx.font=fs+'px "JetBrains Mono",monospace';ctx.textAlign='center';ctx.textBaseline='middle';
853
+ ctx.fillText(ch,px,py)
854
+ }
855
+ }
856
+ ctx.globalAlpha=1;
857
+ requestAnimationFrame(draw);
858
+ }
859
+ requestAnimationFrame(draw);
860
+ })();
861
+ </script>
862
+ <!-- /Asciify Animated Embed -->`;
863
+ }
864
+
865
+ exports.ART_STYLE_PRESETS = ART_STYLE_PRESETS;
866
+ exports.CHARSETS = CHARSETS;
867
+ exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
868
+ exports.HOVER_PRESETS = HOVER_PRESETS;
869
+ exports.generateAnimatedEmbedCode = generateAnimatedEmbedCode;
870
+ exports.generateEmbedCode = generateEmbedCode;
871
+ exports.gifToAsciiFrames = gifToAsciiFrames;
872
+ exports.imageToAsciiFrame = imageToAsciiFrame;
873
+ exports.renderFrameToCanvas = renderFrameToCanvas;
874
+ exports.videoToAsciiFrames = videoToAsciiFrames;
875
+ //# sourceMappingURL=index.cjs.map
876
+ //# sourceMappingURL=index.cjs.map