asciify-engine 1.0.117 → 1.1.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.
@@ -0,0 +1,1265 @@
1
+ type ColorMode = 'grayscale' | 'fullcolor' | 'matrix' | 'accent';
2
+ type RenderMode = 'ascii' | 'dots';
3
+ type AnimationStyle = 'none' | 'wave' | 'pulse' | 'rain' | 'breathe' | 'sparkle' | 'glitch' | 'spiral' | 'typewriter' | 'scatter' | 'waveField' | 'ripple' | 'melt' | 'orbit' | 'cellular';
4
+ type ArtStyle = 'classic' | 'particles' | 'letters' | 'claudeCode' | 'art' | 'terminal' | 'box' | 'lines' | 'braille' | 'katakana' | 'musical' | 'emoji' | 'circles' | 'shadows' | 'starfield' | 'geometric' | 'pipes' | 'waves' | 'shards' | 'smoke' | 'ascii' | 'interface' | 'prompt' | 'data' | 'humanist' | 'mesh';
5
+ type HoverEffect = 'spotlight' | 'magnify' | 'repel' | 'glow' | 'colorShift' | 'attract' | 'shatter' | 'trail' | 'glitchText';
6
+ type HoverShape = 'circle' | 'box';
7
+ type HoverPreset = 'none' | 'subtle' | 'flashlight' | 'magnifier' | 'forceField' | 'neon' | 'fire' | 'ice' | 'gravity' | 'shatter' | 'ghost' | 'glitchReveal';
8
+ type SourceCropUnit = 'percent' | 'pixel';
9
+ type SourceCropAnchor = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
10
+ interface SourceCrop {
11
+ /** Legacy crop origin on the source media. Percent values use 0–1. Default: centered when cropped */
12
+ x?: number;
13
+ /** Legacy crop origin on the source media. Percent values use 0–1. Default: centered when cropped */
14
+ y?: number;
15
+ /** Legacy crop width on the source media. Percent values use 0–1. Default: mirrors `height` to preserve proportions */
16
+ width?: number;
17
+ /** Legacy crop height on the source media. Percent values use 0–1. Default: mirrors `width` to preserve proportions */
18
+ height?: number;
19
+ /** CSS-like crop inset from the top edge. Percent values use 0–1. */
20
+ top?: number;
21
+ /** CSS-like crop inset from the right edge. Percent values use 0–1. */
22
+ right?: number;
23
+ /** CSS-like crop inset from the bottom edge. Percent values use 0–1. */
24
+ bottom?: number;
25
+ /** CSS-like crop inset from the left edge. Percent values use 0–1. */
26
+ left?: number;
27
+ /**
28
+ * Keep the source aspect ratio when deriving a crop from a single `width` or
29
+ * `height`. CSS-like side insets (`top`, `right`, `bottom`, `left`) already
30
+ * define an exact source view box, and `asciifyVideo` sizes from that crop
31
+ * aspect so the output does not stretch. Default: `true`.
32
+ */
33
+ preserveAspect?: boolean;
34
+ /** Bias auto-centered crops toward an edge or corner. Default: `'center'` */
35
+ anchor?: SourceCropAnchor;
36
+ /** Interpret values as normalized percentages or source pixels. Default: `'percent'` */
37
+ unit?: SourceCropUnit;
38
+ }
39
+ /**
40
+ * Named colour palette presets — pass as `colorMode` for themed rendering.
41
+ * Applied as a post-process colour remap over the standard grayscale output.
42
+ */
43
+ type PaletteTheme = 'dracula' | 'monokai' | 'nord' | 'catppuccin' | 'solarized' | 'gruvbox';
44
+ declare const PALETTE_THEMES: Record<PaletteTheme, {
45
+ name: string;
46
+ accent: string;
47
+ bg: string;
48
+ fg: string;
49
+ }>;
50
+ type SourceType = 'image' | 'video' | 'gif' | null;
51
+ interface AsciiOptions {
52
+ /** Character cell width in pixels. Smaller = more detail, more cells. Default: `7` */
53
+ fontSize: number;
54
+ /** Extra horizontal spacing between characters (pixels). Default: `1` */
55
+ charSpacing: number;
56
+ /**
57
+ * Brightness adjustment applied before luminance mapping.
58
+ * Range −1 (black) → 0 (unchanged) → 1 (white). Default: `0`
59
+ */
60
+ brightness: number;
61
+ /**
62
+ * Contrast boost applied before luminance mapping.
63
+ * `0` = unchanged, positive values increase contrast, negative decrease it.
64
+ * Default: `0`
65
+ */
66
+ contrast: number;
67
+ /**
68
+ * Character density ramp — ordered from lightest to darkest.
69
+ * Use `CHARSETS` for pre-built ramps or supply your own string.
70
+ * Default: `' .:-=+*#%@'`
71
+ */
72
+ charset: string;
73
+ /**
74
+ * Colour output mode.
75
+ * - `'grayscale'` — white-on-black monochrome
76
+ * - `'fullcolor'` — samples pixel colours from the source
77
+ * - `'matrix'` — green phosphor terminal look
78
+ * - `'accent'` — single accent colour with intensity-driven brightness
79
+ * Default: `'grayscale'`
80
+ */
81
+ colorMode: ColorMode;
82
+ /**
83
+ * The accent colour used when `colorMode` is `'accent'` or `'matrix'`.
84
+ * Any CSS colour string. Default: `'#d4ff00'`
85
+ */
86
+ /**
87
+ * Hex color string used when `colorMode` is `'accent'`.
88
+ * Set to `'auto'` to let the engine pick contrasting ink automatically:
89
+ * dark ink (`#0d0d0d`) in light mode, light ink (`#faf9f7`) in dark mode.
90
+ */
91
+ accentColor: string;
92
+ /**
93
+ * Invert luminance mapping (light pixels → dense chars). Default: `false`
94
+ *
95
+ * Set to `'auto'` to let the engine detect the OS color scheme:
96
+ * - Light mode → inverts (bright pixel = dark char = visible on white background)
97
+ * - Dark mode → normal (bright pixel = bright char = visible on dark background)
98
+ */
99
+ invert: boolean | 'auto';
100
+ /**
101
+ * Render mode.
102
+ * - `'ascii'` — characters drawn as text
103
+ * - `'dots'` — each cell rendered as a filled circle (particle look)
104
+ * Default: `'ascii'`
105
+ */
106
+ renderMode: RenderMode;
107
+ /**
108
+ * Per-character animation driven over time.
109
+ * Applies wave, glitch, rain-drop, spiral, and other effects to the rendered text.
110
+ * Default: `'none'`
111
+ */
112
+ animationStyle: AnimationStyle;
113
+ /** Speed multiplier for `animationStyle` effects. Default: `1` */
114
+ animationSpeed: number;
115
+ /**
116
+ * Size of each dot relative to the cell when `renderMode === 'dots'`.
117
+ * `1` fills the whole cell, `0.5` draws half-size circles. Default: `0.8`
118
+ */
119
+ dotSizeRatio: number;
120
+ /**
121
+ * Ordered Bayer (4×4) dither strength applied to the luminance map.
122
+ * `0` = no dithering, `1` = full dithering. Default: `0`
123
+ */
124
+ ditherStrength: number;
125
+ /**
126
+ * Assumed aspect ratio (width ÷ height) of a single output character.
127
+ * Controls how many rows vs columns are generated — must match your rendering
128
+ * environment to preserve the source image's proportions.
129
+ * - `0.55` — browser monospace at `line-height: 1.09` (default)
130
+ * - `0.52` — browser monospace at `line-height: 1.15`
131
+ * - `0.5` — most terminal emulators
132
+ * Default: `0.55`
133
+ */
134
+ charAspect: number;
135
+ /**
136
+ * Auto-stretch the luminance range before charset mapping.
137
+ * When `true`, the darkest pixel in the frame maps to the first charset character
138
+ * and the brightest to the last, maximising perceived detail and contrast.
139
+ * Particularly useful for images with low inherent contrast or muted tones.
140
+ * Default: `false`
141
+ */
142
+ normalize: boolean;
143
+ /**
144
+ * Overall intensity of the hover / cursor interaction effect.
145
+ * `0` disables the effect. Default: `0`
146
+ */
147
+ hoverStrength: number;
148
+ /**
149
+ * Radius of the hover interaction zone as a fraction of the canvas size.
150
+ * `0.1` = 10% of canvas width. Default: `0.2`
151
+ */
152
+ hoverRadius: number;
153
+ /**
154
+ * Which cursor interaction style to apply when `hoverStrength > 0`.
155
+ * See `HoverPreset` or `HOVER_PRESETS` for ready-made configurations.
156
+ * Default: `'spotlight'`
157
+ */
158
+ hoverEffect: HoverEffect;
159
+ /**
160
+ * Tint colour used by hover effects such as spotlight, glow, and colorShift.
161
+ * Any CSS colour string. Default: `'#ffffff'`
162
+ */
163
+ hoverColor: string;
164
+ /**
165
+ * Shape of the hover interaction zone.
166
+ * - `'circle'` — radial falloff (default)
167
+ * - `'box'` — rectangular zone, text fills a block around the cursor
168
+ * Default: `'circle'`
169
+ */
170
+ hoverShape: HoverShape;
171
+ /**
172
+ * Text string revealed by the `'glitchText'` hover effect.
173
+ * Characters around the cursor scramble and resolve into this text.
174
+ * Supports `\n` for multi-line reveals.
175
+ *
176
+ * Pass an **array** of strings to cycle through different words based on
177
+ * the cursor's grid region — the engine picks one via a spatial hash so
178
+ * each area of the canvas shows a different word.
179
+ *
180
+ * Default: `'ASCIIFY'`
181
+ *
182
+ * @example
183
+ * // Single word:
184
+ * options: { hoverEffect: 'glitchText', hoverText: 'HELLO' }
185
+ *
186
+ * // Word pool — different text at different cursor positions:
187
+ * options: { hoverEffect: 'glitchText', hoverText: ['DEPLOY', 'SCALE', 'SHIP', 'BUILD'] }
188
+ */
189
+ hoverText: string | string[];
190
+ /**
191
+ * Art style preset applied at render time.
192
+ * Shorthand for a specific combination of `charset`, `renderMode`, and `colorMode`.
193
+ * See `ART_STYLE_PRESETS` for the full list. Default: `'classic'`
194
+ */
195
+ artStyle: ArtStyle;
196
+ /**
197
+ * Custom repeating text used when `artStyle` is `'letters'` or when you set
198
+ * a custom charset. Leave empty to use the selected `charset`. Default: `''`
199
+ */
200
+ customText: string;
201
+ /**
202
+ * Chroma-key colour to remove from the source (green screen / blue screen).
203
+ * Pixels whose RGB distance from this colour is within `chromaKeyTolerance`
204
+ * are keyed out: their character becomes a space and alpha is set to 0,
205
+ * letting the canvas background show through.
206
+ *
207
+ * - `true` — **smart green screen**: heuristic detection (`g > r*1.4 && g > b*1.4`)
208
+ * catches every shade of green screen (lime, broadcast, chroma green) with no
209
+ * config. Works on virtually all studio/streaming footage.
210
+ * - `'blue-screen'` — **smart blue screen**: same heuristic for blue.
211
+ * - `{ r, g, b }` or CSS colour string — custom key colour + `chromaKeyTolerance`
212
+ * - `null` / `false` — disabled (default)
213
+ *
214
+ * @example
215
+ * // Standard green screen — zero config:
216
+ * options: { chromaKey: true }
217
+ *
218
+ * // Blue screen — zero config:
219
+ * options: { chromaKey: 'blue-screen' }
220
+ *
221
+ * // Custom CSS colour:
222
+ * options: { chromaKey: '#00b140', chromaKeyTolerance: 70 }
223
+ */
224
+ chromaKey: {
225
+ r: number;
226
+ g: number;
227
+ b: number;
228
+ } | string | boolean | null;
229
+ /**
230
+ * Euclidean RGB tolerance radius for chroma-key detection.
231
+ * `0` = exact match only, `441` ≈ key out everything.
232
+ * Higher values remove more pixels. Default: `60`
233
+ */
234
+ chromaKeyTolerance: number;
235
+ /**
236
+ * Extra normalized padding kept around the detected chroma-key foreground when
237
+ * `asciifyVideo` auto-trims keyed video content inside `sourceCrop`.
238
+ * `0` hugs the visible foreground. `0.01` keeps 1% safety padding.
239
+ * Default: `0.002`
240
+ */
241
+ chromaKeyTrimPadding: number;
242
+ /**
243
+ * Minimum source-pixel luminance for chroma-key foreground auto-trim.
244
+ * Raise this when faint edge noise is counted as content and prevents a
245
+ * keyed subject from filling a preserved-ratio layout. Range `0–255`.
246
+ * Default: `0`
247
+ */
248
+ chromaKeyTrimLuminanceThreshold: number;
249
+ /**
250
+ * How keyed video foreground trimming is applied when `chromaKey` and
251
+ * `sourceCrop` are enabled.
252
+ *
253
+ * - `'range'` samples the video/scroll range once and keeps a stable union
254
+ * crop. Best default for consistent framing.
255
+ * - `'frame'` remeasures each rendered frame and expands the crop to the
256
+ * render aspect. Best for scroll-scrubbed hero footage where foreground
257
+ * position changes and pure contain leaves empty edges.
258
+ * - `'off'` disables automatic keyed foreground trimming.
259
+ *
260
+ * Default: `'range'`
261
+ */
262
+ chromaKeyTrimMode: 'range' | 'frame' | 'off';
263
+ /**
264
+ * Optional source crop applied before ASCII sampling.
265
+ *
266
+ * Use this when the media has empty edges or a subject that should be framed
267
+ * tighter without changing the destination canvas size.
268
+ *
269
+ * @example
270
+ * // Remove 15% from the top and 15% from the bottom, preserving proportions:
271
+ * options: { sourceCrop: { top: 0.15, bottom: 0.15 } }
272
+ *
273
+ * @example
274
+ * // Keep 70% of the source, preserve proportions, and center it:
275
+ * options: { sourceCrop: { height: 0.7 } }
276
+ *
277
+ * @example
278
+ * // Use an exact source window when manual framing should override proportions:
279
+ * options: { sourceCrop: { x: 0.05, y: 0.08, width: 0.9, height: 0.9, preserveAspect: false } }
280
+ */
281
+ sourceCrop?: SourceCrop | null;
282
+ /**
283
+ * Array of charset strings to cycle through over time.
284
+ * The engine picks `charsetFrames[Math.floor(time * charsetFps) % length]` on each
285
+ * render tick, re-mapping characters from stored cell luminance.
286
+ * Pairs beautifully with `animationStyle` and `asciiBackground`.
287
+ *
288
+ * Use `CHARSET_SEQUENCES` for curated combinations or build your own:
289
+ * @example
290
+ * options: { charsetFrames: CHARSET_SEQUENCES.cosmic }
291
+ * options: { charsetFrames: [CHARSETS.standard, CHARSETS.blocks, CHARSETS.circles] }
292
+ */
293
+ charsetFrames?: string[];
294
+ /** Cycle rate for `charsetFrames` in frames-per-second. Default: `2` */
295
+ charsetFps?: number;
296
+ }
297
+ interface AsciiCell {
298
+ char: string;
299
+ r: number;
300
+ g: number;
301
+ b: number;
302
+ a: number;
303
+ /** Stored dithered luminance (0–255). Present when the frame was generated by the engine. Used for dynamic charset re-selection at render time via `charsetFrames`. */
304
+ lum?: number;
305
+ }
306
+ type AsciiFrame = AsciiCell[][];
307
+ interface AsciiResult {
308
+ frames: AsciiFrame[];
309
+ cols: number;
310
+ rows: number;
311
+ fps: number;
312
+ }
313
+ declare const CHARSETS: {
314
+ readonly standard: " .:-=+*#%@";
315
+ readonly blocks: " ░▒▓█";
316
+ readonly minimal: " .:+";
317
+ readonly dense: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
318
+ readonly binary: "01";
319
+ readonly dots: " ⠁⠃⠇⡇⣇⣧⣷⣿";
320
+ readonly letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
321
+ readonly claudeCode: " ╔╗╚╝║═╠╣╦╩╬░▒▓█│─┌┐└┘├┤┬┴┼";
322
+ readonly box: " ▪◾◼■█";
323
+ readonly lines: " ˗‐–—―━";
324
+ readonly braille: " ⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿";
325
+ readonly katakana: " ヲァィゥェォャュョッアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン";
326
+ readonly musical: " ♩♪♫♬♭♮♯";
327
+ readonly emoji: " ⬛🟫🟥🟧🟨🟩🟦🟪⬜";
328
+ readonly circles: " .·:∘○◦°•∙";
329
+ readonly shadows: " ·∘◦○◎⊙●◉";
330
+ readonly starfield: " ˙·∘∗✦✧★◆●";
331
+ readonly geometric: " ·△▷◇◈◆▣■█";
332
+ readonly pipes: " ╶─┐└├┤┬┴┼╬▒▓█";
333
+ readonly waves: " ˜∼≈〰≋∿∾∭∫";
334
+ readonly shards: " ╱╲╳◤◥◣◢△▲◆◼█";
335
+ readonly smoke: " ·˙⁚⁖∶∷⋮⋰⋱∴∵";
336
+ /** All printable ASCII characters ordered from airy punctuation to dense symbols. */
337
+ readonly ascii: " .'`^\",:;~-_+<>i!lI?/\\|()[]{}1tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
338
+ /** Clean product UI glyphs inspired by command palettes, panes, controls, and focus rings. */
339
+ readonly interface: " ·•-–—+=:;<>[]{}()⌘⌥⇧⌃↵↗↘◇◆◈□■";
340
+ /** Prompt, shell, and assistant transcript glyphs for modern AI/dev surfaces. */
341
+ readonly prompt: " .:;>_`/\\|{}[]()=+*#@$";
342
+ /** Analytical/data-viz glyphs: sparse dots into nodes, diamonds, and dense blocks. */
343
+ readonly data: " ·∙•◦○●◇◆◈▱▰▣█";
344
+ /** Warm editorial/math symbols for quieter Claude-like and research-oriented pages. */
345
+ readonly humanist: " ·˙,:;∴∵∷∶⁚⋮⋯∼≈≋∞";
346
+ /** Dense network/mesh characters for agent maps, model graphs, and infrastructure visuals. */
347
+ readonly mesh: " ·╶╴╷╵─│┌┐└┘├┤┬┴┼╬░▒▓█";
348
+ };
349
+ /**
350
+ * Curated charset sequences for use with `charsetFrames`.
351
+ * Each sequence morphs between 2–3 complementary charsets over time,
352
+ * creating a living texture effect.
353
+ *
354
+ * @example
355
+ * asciiBackground('#hero', { charsetFrames: CHARSET_SEQUENCES.cosmic })
356
+ */
357
+ declare const CHARSET_SEQUENCES: {
358
+ /** Stars → softcircles → orbs — dreamy space feel */
359
+ readonly cosmic: string[];
360
+ /** Katakana → braille dots → binary — hacker rain */
361
+ readonly rain: string[];
362
+ /** Box pipes → Claude glyphs → classic — terminal morph */
363
+ readonly terminal: string[];
364
+ /** Shards → blocks → squares — shattering crystal */
365
+ readonly crystal: string[];
366
+ /** Wave glyphs → smoke dots → circles — fluid / organic */
367
+ readonly fluid: string[];
368
+ /** Dense classic → art → blocks — maximum detail pulse */
369
+ readonly pulse: string[];
370
+ /** Braille → shadows → smoke — ethereal / dream-like */
371
+ readonly dream: string[];
372
+ /** Geometric shapes → shards → starfield — sci-fi angular */
373
+ readonly angular: string[];
374
+ /** Interface controls → prompts → data nodes — clean assistant/product motion */
375
+ readonly assistant: string[];
376
+ /** Mesh → data → ASCII — alive network signal with readable texture */
377
+ readonly signal: string[];
378
+ /** Humanist symbols → waves → smoke — soft research/editorial motion */
379
+ readonly editorial: string[];
380
+ };
381
+ type CharsetSequenceKey = keyof typeof CHARSET_SEQUENCES;
382
+ type CharsetKey = keyof typeof CHARSETS;
383
+ /**
384
+ * Art Style presets — each one sets render mode, charset, color mode, etc.
385
+ */
386
+ declare const ART_STYLE_PRESETS: Record<ArtStyle, Partial<AsciiOptions>>;
387
+ /**
388
+ * Higher-level living presets for output that should feel active immediately.
389
+ * They intentionally compose existing primitives so renderers and integrations
390
+ * can consume them as a normal `Partial<AsciiOptions>`.
391
+ */
392
+ declare const LIVING_STYLE_PRESETS: {
393
+ readonly agentField: {
394
+ readonly charsetFrames: string[];
395
+ readonly charsetFps: 1.6;
396
+ readonly animationStyle: "breathe";
397
+ readonly animationSpeed: 0.75;
398
+ readonly ditherStrength: 0.12;
399
+ readonly hoverEffect: "glow";
400
+ readonly hoverStrength: 0.42;
401
+ readonly hoverRadius: 0.18;
402
+ readonly hoverColor: "#f4f1ea";
403
+ readonly normalize: true;
404
+ readonly fontSize?: number | undefined;
405
+ readonly charSpacing?: number | undefined;
406
+ readonly brightness?: number | undefined;
407
+ readonly contrast?: number | undefined;
408
+ readonly charset?: string | undefined;
409
+ readonly colorMode?: ColorMode | undefined;
410
+ readonly accentColor?: string | undefined;
411
+ readonly invert?: boolean | "auto" | undefined;
412
+ readonly renderMode?: RenderMode | undefined;
413
+ readonly dotSizeRatio?: number | undefined;
414
+ readonly charAspect?: number | undefined;
415
+ readonly hoverShape?: HoverShape | undefined;
416
+ readonly hoverText?: string | string[] | undefined;
417
+ readonly artStyle?: ArtStyle | undefined;
418
+ readonly customText?: string | undefined;
419
+ readonly chromaKey?: string | boolean | {
420
+ r: number;
421
+ g: number;
422
+ b: number;
423
+ } | null | undefined;
424
+ readonly chromaKeyTolerance?: number | undefined;
425
+ readonly chromaKeyTrimPadding?: number | undefined;
426
+ readonly chromaKeyTrimLuminanceThreshold?: number | undefined;
427
+ readonly chromaKeyTrimMode?: "range" | "frame" | "off" | undefined;
428
+ readonly sourceCrop?: SourceCrop | null;
429
+ };
430
+ readonly liquidSignal: {
431
+ readonly charsetFrames: string[];
432
+ readonly charsetFps: 2.4;
433
+ readonly animationStyle: "melt";
434
+ readonly animationSpeed: 0.65;
435
+ readonly ditherStrength: 0.45;
436
+ readonly hoverEffect: "repel";
437
+ readonly hoverStrength: 0.55;
438
+ readonly hoverRadius: 0.22;
439
+ readonly hoverColor: "#8fd3ff";
440
+ readonly normalize: true;
441
+ readonly fontSize?: number | undefined;
442
+ readonly charSpacing?: number | undefined;
443
+ readonly brightness?: number | undefined;
444
+ readonly contrast?: number | undefined;
445
+ readonly charset?: string | undefined;
446
+ readonly colorMode?: ColorMode | undefined;
447
+ readonly accentColor?: string | undefined;
448
+ readonly invert?: boolean | "auto" | undefined;
449
+ readonly renderMode?: RenderMode | undefined;
450
+ readonly dotSizeRatio?: number | undefined;
451
+ readonly charAspect?: number | undefined;
452
+ readonly hoverShape?: HoverShape | undefined;
453
+ readonly hoverText?: string | string[] | undefined;
454
+ readonly artStyle?: ArtStyle | undefined;
455
+ readonly customText?: string | undefined;
456
+ readonly chromaKey?: string | boolean | {
457
+ r: number;
458
+ g: number;
459
+ b: number;
460
+ } | null | undefined;
461
+ readonly chromaKeyTolerance?: number | undefined;
462
+ readonly chromaKeyTrimPadding?: number | undefined;
463
+ readonly chromaKeyTrimLuminanceThreshold?: number | undefined;
464
+ readonly chromaKeyTrimMode?: "range" | "frame" | "off" | undefined;
465
+ readonly sourceCrop?: SourceCrop | null;
466
+ };
467
+ readonly cursorGravity: {
468
+ readonly charsetFrames: string[];
469
+ readonly charsetFps: 1.2;
470
+ readonly animationStyle: "orbit";
471
+ readonly animationSpeed: 0.9;
472
+ readonly ditherStrength: 0.2;
473
+ readonly hoverEffect: "attract";
474
+ readonly hoverStrength: 0.82;
475
+ readonly hoverRadius: 0.24;
476
+ readonly hoverColor: "#ffffff";
477
+ readonly normalize: true;
478
+ readonly fontSize?: number | undefined;
479
+ readonly charSpacing?: number | undefined;
480
+ readonly brightness?: number | undefined;
481
+ readonly contrast?: number | undefined;
482
+ readonly charset?: string | undefined;
483
+ readonly colorMode?: ColorMode | undefined;
484
+ readonly accentColor?: string | undefined;
485
+ readonly invert?: boolean | "auto" | undefined;
486
+ readonly renderMode?: RenderMode | undefined;
487
+ readonly dotSizeRatio?: number | undefined;
488
+ readonly charAspect?: number | undefined;
489
+ readonly hoverShape?: HoverShape | undefined;
490
+ readonly hoverText?: string | string[] | undefined;
491
+ readonly artStyle?: ArtStyle | undefined;
492
+ readonly customText?: string | undefined;
493
+ readonly chromaKey?: string | boolean | {
494
+ r: number;
495
+ g: number;
496
+ b: number;
497
+ } | null | undefined;
498
+ readonly chromaKeyTolerance?: number | undefined;
499
+ readonly chromaKeyTrimPadding?: number | undefined;
500
+ readonly chromaKeyTrimLuminanceThreshold?: number | undefined;
501
+ readonly chromaKeyTrimMode?: "range" | "frame" | "off" | undefined;
502
+ readonly sourceCrop?: SourceCrop | null;
503
+ };
504
+ readonly editorialPulse: {
505
+ readonly charsetFrames: string[];
506
+ readonly charsetFps: 0.9;
507
+ readonly animationStyle: "ripple";
508
+ readonly animationSpeed: 0.55;
509
+ readonly ditherStrength: 0.18;
510
+ readonly hoverEffect: "spotlight";
511
+ readonly hoverStrength: 0.35;
512
+ readonly hoverRadius: 0.2;
513
+ readonly hoverColor: "#fff8ea";
514
+ readonly normalize: true;
515
+ readonly fontSize?: number | undefined;
516
+ readonly charSpacing?: number | undefined;
517
+ readonly brightness?: number | undefined;
518
+ readonly contrast?: number | undefined;
519
+ readonly charset?: string | undefined;
520
+ readonly colorMode?: ColorMode | undefined;
521
+ readonly accentColor?: string | undefined;
522
+ readonly invert?: boolean | "auto" | undefined;
523
+ readonly renderMode?: RenderMode | undefined;
524
+ readonly dotSizeRatio?: number | undefined;
525
+ readonly charAspect?: number | undefined;
526
+ readonly hoverShape?: HoverShape | undefined;
527
+ readonly hoverText?: string | string[] | undefined;
528
+ readonly artStyle?: ArtStyle | undefined;
529
+ readonly customText?: string | undefined;
530
+ readonly chromaKey?: string | boolean | {
531
+ r: number;
532
+ g: number;
533
+ b: number;
534
+ } | null | undefined;
535
+ readonly chromaKeyTolerance?: number | undefined;
536
+ readonly chromaKeyTrimPadding?: number | undefined;
537
+ readonly chromaKeyTrimLuminanceThreshold?: number | undefined;
538
+ readonly chromaKeyTrimMode?: "range" | "frame" | "off" | undefined;
539
+ readonly sourceCrop?: SourceCrop | null;
540
+ };
541
+ readonly terminalFlow: {
542
+ readonly charsetFrames: string[];
543
+ readonly charsetFps: 3;
544
+ readonly animationStyle: "rain";
545
+ readonly animationSpeed: 1.1;
546
+ readonly ditherStrength: 0.22;
547
+ readonly hoverEffect: "glitchText";
548
+ readonly hoverStrength: 0.65;
549
+ readonly hoverRadius: 0.16;
550
+ readonly hoverColor: "#d4ff00";
551
+ readonly hoverText: ["BUILD", "SHIP", "RUN", "ASK", "MODEL"];
552
+ readonly normalize: true;
553
+ readonly fontSize?: number | undefined;
554
+ readonly charSpacing?: number | undefined;
555
+ readonly brightness?: number | undefined;
556
+ readonly contrast?: number | undefined;
557
+ readonly charset?: string | undefined;
558
+ readonly colorMode?: ColorMode | undefined;
559
+ readonly accentColor?: string | undefined;
560
+ readonly invert?: boolean | "auto" | undefined;
561
+ readonly renderMode?: RenderMode | undefined;
562
+ readonly dotSizeRatio?: number | undefined;
563
+ readonly charAspect?: number | undefined;
564
+ readonly hoverShape?: HoverShape | undefined;
565
+ readonly artStyle?: ArtStyle | undefined;
566
+ readonly customText?: string | undefined;
567
+ readonly chromaKey?: string | boolean | {
568
+ r: number;
569
+ g: number;
570
+ b: number;
571
+ } | null | undefined;
572
+ readonly chromaKeyTolerance?: number | undefined;
573
+ readonly chromaKeyTrimPadding?: number | undefined;
574
+ readonly chromaKeyTrimLuminanceThreshold?: number | undefined;
575
+ readonly chromaKeyTrimMode?: "range" | "frame" | "off" | undefined;
576
+ readonly sourceCrop?: SourceCrop | null;
577
+ };
578
+ };
579
+ type LivingStylePresetKey = keyof typeof LIVING_STYLE_PRESETS;
580
+ declare const DEFAULT_OPTIONS: AsciiOptions;
581
+ /**
582
+ * Hover presets — one-click configurations for the hover system.
583
+ */
584
+ declare const HOVER_PRESETS: Record<HoverPreset, {
585
+ label: string;
586
+ options: Partial<AsciiOptions>;
587
+ }>;
588
+
589
+ /**
590
+ * Core frame-to-canvas renderer and source-to-frame converters.
591
+ */
592
+
593
+ interface AsciiTextFrame {
594
+ rows: string[];
595
+ cols: number;
596
+ rowCount: number;
597
+ colors?: Uint8ClampedArray;
598
+ }
599
+ /** Clear shared renderer caches. Useful for long-lived editors that cycle many fonts/charsets. */
600
+ declare function clearAsciifyCaches(): void;
601
+ /**
602
+ * Convert an image element or canvas to a single ASCII frame.
603
+ */
604
+ declare function imageToAsciiFrame(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options: AsciiOptions, targetWidth?: number, targetHeight?: number): {
605
+ frame: AsciiFrame;
606
+ cols: number;
607
+ rows: number;
608
+ };
609
+ /**
610
+ * Convert a source into pre-shaped text rows for fast monochrome/accent ASCII.
611
+ *
612
+ * This is the high-performance path for dense video scrubbers. It keeps the
613
+ * expensive pixel sampling work, but avoids allocating one object per cell and
614
+ * lets the renderer draw one string per row instead of one `fillText` per cell.
615
+ */
616
+ declare function imageToAsciiTextFrame(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options: AsciiOptions, targetWidth?: number, targetHeight?: number): AsciiTextFrame;
617
+ declare function renderTextFrameToCanvas(ctx: CanvasRenderingContext2D, textFrame: AsciiTextFrame, options: AsciiOptions, canvasWidth: number, canvasHeight: number): void;
618
+ /**
619
+ * Extract frames from a video element for ASCII animation.
620
+ */
621
+ declare function videoToAsciiFrames(video: HTMLVideoElement, options: AsciiOptions, targetWidth: number, targetHeight: number, targetFps?: number, maxDuration?: number, onProgress?: (progress: number) => void, startTime?: number): Promise<{
622
+ frames: AsciiFrame[];
623
+ cols: number;
624
+ rows: number;
625
+ fps: number;
626
+ }>;
627
+ /**
628
+ * Extract video frames into compact text rows for high-density monochrome/accent
629
+ * animations. This mirrors videoToAsciiFrames but avoids object-per-cell frames.
630
+ */
631
+ declare function videoToAsciiTextFrames(video: HTMLVideoElement, options: AsciiOptions, targetWidth: number, targetHeight: number, targetFps?: number, maxDuration?: number, onProgress?: (progress: number) => void, startTime?: number): Promise<{
632
+ frames: AsciiTextFrame[];
633
+ cols: number;
634
+ rows: number;
635
+ fps: number;
636
+ }>;
637
+ /**
638
+ * Extract frames from an animated GIF file buffer.
639
+ */
640
+ declare function gifToAsciiFrames(buffer: ArrayBuffer, options: AsciiOptions, targetWidth: number, targetHeight: number, onProgress?: (progress: number) => void): Promise<{
641
+ frames: AsciiFrame[];
642
+ cols: number;
643
+ rows: number;
644
+ fps: number;
645
+ }>;
646
+ /**
647
+ * Extract animated GIF frames into compact text rows for high-density
648
+ * monochrome/accent playback. Keeps the legacy object-frame extractor intact
649
+ * for full-color and interactive modes.
650
+ */
651
+ declare function gifToAsciiTextFrames(buffer: ArrayBuffer, options: AsciiOptions, targetWidth: number, targetHeight: number, onProgress?: (progress: number) => void): Promise<{
652
+ frames: AsciiTextFrame[];
653
+ cols: number;
654
+ rows: number;
655
+ fps: number;
656
+ }>;
657
+ /** Frames are immutable: replace a frame after editing cells to invalidate its cached drawing. */
658
+ declare function renderFrameToCanvas(ctx: CanvasRenderingContext2D, frame: AsciiFrame, options: AsciiOptions, width: number, height: number, time?: number, pointer?: {
659
+ x: number;
660
+ y: number;
661
+ intensity?: number;
662
+ } | null): void;
663
+
664
+ /**
665
+ * Simple one-call asciify API.
666
+ * Wraps imageToAsciiFrame + renderFrameToCanvas behind easy-to-use helpers.
667
+ */
668
+
669
+ interface AsciifySimpleOptions {
670
+ /** Character size in pixels. Default: 10 */
671
+ fontSize?: number;
672
+ /**
673
+ * Art style preset — controls charset, render mode, and color mode together.
674
+ * Shorthand for spreading `ART_STYLE_PRESETS[artStyle]` into options.
675
+ * Default: `'classic'`
676
+ */
677
+ artStyle?: ArtStyle;
678
+ /** Extra options to merge on top of the preset */
679
+ options?: Partial<AsciiOptions>;
680
+ }
681
+ type ScrollTriggerInstance = {
682
+ kill?: () => void;
683
+ };
684
+ type ScrollTriggerLike = {
685
+ create: (vars: Record<string, unknown>) => ScrollTriggerInstance;
686
+ };
687
+ type GsapLike = {
688
+ registerPlugin?: (...plugins: unknown[]) => void;
689
+ ScrollTrigger?: ScrollTriggerLike;
690
+ };
691
+ interface VideoScrollScrubOptions {
692
+ /**
693
+ * Element that controls scroll progress. Accepts an HTMLElement or selector.
694
+ * When used through `asciifyVideo`, defaults to `fitTo` or the canvas.
695
+ */
696
+ trigger?: HTMLElement | string | null;
697
+ /**
698
+ * GSAP instance. Pass this with ScrollTrigger for native GSAP scrub support.
699
+ *
700
+ * @example
701
+ * asciifyVideo('/hero.mp4', canvas, {
702
+ * fitTo: '#hero',
703
+ * scroll: { gsap, ScrollTrigger }
704
+ * });
705
+ */
706
+ gsap?: GsapLike;
707
+ /** GSAP ScrollTrigger plugin. Optional when available as `gsap.ScrollTrigger`. */
708
+ ScrollTrigger?: ScrollTriggerLike;
709
+ /** GSAP start value. Native fallback ignores this and uses viewport progress. */
710
+ start?: string;
711
+ /** GSAP end value. Native fallback ignores this and uses viewport progress. */
712
+ end?: string;
713
+ /** GSAP scrub value. Default: `true`. Native fallback always scrubs. */
714
+ scrub?: boolean | number;
715
+ /**
716
+ * Multiply scroll progress before mapping it to video time.
717
+ * `2` reaches the end of the clip in half the scroll distance, `0.5` makes it
718
+ * take twice as much scroll. Default: `1`.
719
+ */
720
+ speed?: number;
721
+ /** Video time to map from. Defaults to trim start or `0`. */
722
+ from?: number;
723
+ /** Video time to map to. Defaults to trim end or video duration. */
724
+ to?: number;
725
+ /** Optional progress transform before mapping to video time. */
726
+ ease?: (progress: number) => number;
727
+ /** Called whenever scroll progress updates. */
728
+ onUpdate?: (progress: number, video: HTMLVideoElement) => void;
729
+ }
730
+ type CanvasObjectFit = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
731
+ type CanvasBleed = number | string | {
732
+ x?: number | string;
733
+ y?: number | string;
734
+ };
735
+ interface AsciifyVideoOptions extends AsciifySimpleOptions {
736
+ /**
737
+ * Fit the canvas to a container element, maintaining the video's aspect ratio.
738
+ * Accepts an HTMLElement or a CSS selector string. The canvas is resized on
739
+ * load and again whenever the container resizes (via ResizeObserver).
740
+ * `stop()` automatically disconnects the observer.
741
+ *
742
+ * @example
743
+ * // Fill the hero div, re-size on window resize automatically:
744
+ * asciifyVideo('/clip.mp4', canvas, { fitTo: '#hero' });
745
+ */
746
+ fitTo?: HTMLElement | string | null;
747
+ /**
748
+ * CSS object-fit behavior for the visible canvas when `fitTo` or explicit
749
+ * `width`/`height` is used. This controls visual framing only; ASCII sampling
750
+ * resolution remains independent. When `options.sourceCrop` is present, the
751
+ * engine sizes from the resolved crop aspect to avoid stretch.
752
+ *
753
+ * Use `'cover'` for full-bleed heroes and `'contain'` for previews.
754
+ * Default: `'contain'`.
755
+ */
756
+ objectFit?: CanvasObjectFit;
757
+ /**
758
+ * CSS object-position for the visible canvas, e.g. `'center bottom'` or
759
+ * `'50% 62%'`. Useful with `objectFit: 'cover'`.
760
+ */
761
+ objectPosition?: string;
762
+ /**
763
+ * Visual scale applied with the CSS individual `scale` property. This is a
764
+ * layout-safe way to overfill a container without changing source proportions
765
+ * or overriding an app's `transform` styles.
766
+ */
767
+ scale?: number;
768
+ /**
769
+ * Visible CSS width for the canvas. Numbers are pixels; strings are used as
770
+ * provided (`'100%'`, `'100vw'`, `min(100vw, 1200px)`, etc.).
771
+ */
772
+ width?: number | string;
773
+ /**
774
+ * Visible CSS height for the canvas. Numbers are pixels; strings are used as
775
+ * provided.
776
+ */
777
+ height?: number | string;
778
+ /**
779
+ * Extra visible overfill added around the fitted canvas box before
780
+ * object-fit sizing. Useful for full-bleed ASCII heroes where glyph side
781
+ * bearings or character-cell quantization can leave a small edge gap even
782
+ * when the source media and canvas are full width.
783
+ *
784
+ * Numbers are pixels. Strings support `%`, `px`, `vw`, and `vh`. Pass an
785
+ * object to control axes independently, e.g. `{ x: '2vw', y: 0 }`.
786
+ */
787
+ bleed?: CanvasBleed;
788
+ /**
789
+ * Pre-extract all video frames into memory before starting playback.
790
+ * Useful for short clips where you need frame-perfect control.
791
+ *
792
+ * Default: `false` — streams live directly from the playing video (instant
793
+ * start, constant memory, unlimited duration).
794
+ *
795
+ * ⚠️ Memory-intensive. Capped at 10 s / 300 frames.
796
+ */
797
+ preExtract?: boolean;
798
+ /**
799
+ * Target render FPS.
800
+ * - In pre-extracted mode, this controls how many frames are decoded into memory.
801
+ * - In live mode, this throttles expensive ASCII frame rendering while the
802
+ * backing video continues decoding normally.
803
+ *
804
+ * Lower values use less CPU/memory and make scroll scrubbing cheaper.
805
+ * Defaults to `18` for pre-extracted scroll scrub and unthrottled rendering
806
+ * for normal live playback.
807
+ */
808
+ fps?: number;
809
+ /**
810
+ * Maximum long-edge render dimension for video frame extraction.
811
+ * Raise this for full-width or 4K heroes so the canvas is not upscaled by CSS.
812
+ * Default: `2048`.
813
+ */
814
+ maxRenderDimension?: number;
815
+ /**
816
+ * Trim the video to a specific time range (in seconds).
817
+ * - `start` — seek to this time before playback begins. Default: `0`
818
+ * - `end` — loop back to `start` when this time is reached.
819
+ * In `preExtract` mode, only frames up to `end` are extracted.
820
+ *
821
+ * @example
822
+ * // Play only seconds 2–8, looping:
823
+ * asciifyVideo('/clip.mp4', canvas, { trim: { start: 2, end: 8 } });
824
+ */
825
+ trim?: {
826
+ start?: number;
827
+ end?: number;
828
+ };
829
+ /**
830
+ * Sync video time to scroll progress.
831
+ *
832
+ * - `true` uses native scroll scrubbing with `fitTo`/canvas as the trigger.
833
+ * - Passing `gsap` + `ScrollTrigger` uses GSAP ScrollTrigger.
834
+ *
835
+ * @example
836
+ * await asciifyVideo('/hero.mp4', canvas, {
837
+ * fitTo: '#hero',
838
+ * scroll: true
839
+ * });
840
+ *
841
+ * @example
842
+ * await asciifyVideo('/hero.mp4', canvas, {
843
+ * fitTo: '#hero',
844
+ * scroll: { gsap, ScrollTrigger, start: 'top bottom', end: 'bottom top', scrub: 1 }
845
+ * });
846
+ */
847
+ scroll?: boolean | VideoScrollScrubOptions;
848
+ /**
849
+ * Called once when the video metadata is loaded and playback has started.
850
+ * Receives the backing video element.
851
+ */
852
+ onReady?: (video: HTMLVideoElement) => void;
853
+ /**
854
+ * Maximum number of decoded text frames kept in memory for live scroll scrub.
855
+ * The cache is nearest-frame and bounded; lower values reduce memory for long
856
+ * clips while preserving smooth scrubbing around the current scroll position.
857
+ * Default: `90`.
858
+ */
859
+ maxCachedFrames?: number;
860
+ /** Called after every rendered frame. */
861
+ onFrame?: () => void;
862
+ }
863
+ /** @deprecated Use {@link AsciifyVideoOptions} */
864
+ type AsciifyLiveVideoOptions = AsciifyVideoOptions;
865
+ /**
866
+ * Sync an HTMLVideoElement's currentTime to scroll progress.
867
+ *
868
+ * If `gsap` and `ScrollTrigger` are supplied, the helper uses GSAP. Otherwise
869
+ * it falls back to a tiny native scroll listener. Returns a cleanup function.
870
+ */
871
+ declare function createVideoScrollScrub(video: HTMLVideoElement, opts?: VideoScrollScrubOptions): () => void;
872
+ interface CanvasRenderSizeInput {
873
+ sourceWidth: number;
874
+ sourceHeight: number;
875
+ cssWidth?: number;
876
+ cssHeight?: number;
877
+ /** Accepted for callers that pass complete display context; DPR is applied separately by the canvas backing buffer. */
878
+ dpr?: number;
879
+ maxRenderDimension?: number;
880
+ }
881
+ declare function computeCanvasRenderSize({ sourceWidth, sourceHeight, cssWidth, cssHeight, maxRenderDimension, }: CanvasRenderSizeInput): {
882
+ renderW: number;
883
+ renderH: number;
884
+ };
885
+ /**
886
+ * Convert an image/video/canvas element to ASCII art and render it onto a canvas.
887
+ *
888
+ * When hover options are active (`hoverStrength > 0` in `options`), the engine
889
+ * automatically sets up mouse tracking and a `requestAnimationFrame` loop so
890
+ * the hover effect works out of the box. In that case, a `stop()` function is
891
+ * returned to tear down the loop and listeners.
892
+ *
893
+ * @example
894
+ * // Static (no hover):
895
+ * await asciify('/photo.jpg', canvas);
896
+ *
897
+ * // With hover — returns a cleanup handle:
898
+ * const stop = await asciify('/photo.jpg', canvas, {
899
+ * options: { hoverEffect: 'glitchText', hoverStrength: 0.8, hoverText: 'HI' }
900
+ * });
901
+ * // later: stop?.();
902
+ */
903
+ declare function asciify(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | string, canvas: HTMLCanvasElement, { fontSize, artStyle, options }?: AsciifySimpleOptions): Promise<() => void>;
904
+ /**
905
+ * Fetch a GIF, convert it to ASCII, and start an animation loop on a canvas.
906
+ * Returns a `stop()` function that cancels the loop.
907
+ *
908
+ * @example
909
+ * const stop = await asciifyGif('animation.gif', canvas);
910
+ * // later: stop();
911
+ */
912
+ declare function asciifyGif(source: string | ArrayBuffer, canvas: HTMLCanvasElement, { fontSize, artStyle, options }?: AsciifySimpleOptions): Promise<() => void>;
913
+ /**
914
+ * Render a video as ASCII art on a canvas. Defaults to live streaming —
915
+ * instant start, constant memory, unlimited duration.
916
+ *
917
+ * Pass `{ preExtract: true }` to pre-decode all frames before playback starts
918
+ * (useful for short clips that need frame-perfect looping).
919
+ *
920
+ * Pass `{ fitTo: '#container' }` to automatically size and re-size the canvas
921
+ * to fill a container element, maintaining the video's aspect ratio.
922
+ *
923
+ * Returns a `stop()` function that cancels the loop and cleans up.
924
+ *
925
+ * ⚠️ Never set the backing `<video>` to `display: none` — browsers skip GPU
926
+ * frame decoding for hidden elements. When given a URL string, this function
927
+ * handles that automatically.
928
+ *
929
+ * @example
930
+ * // Minimal
931
+ * const stop = await asciifyVideo('/clip.mp4', canvas);
932
+ *
933
+ * // Fit to container, re-size on window resize:
934
+ * const stop = await asciifyVideo('/clip.mp4', canvas, { fitTo: '#hero' });
935
+ *
936
+ * // Pre-extract frames (old behavior):
937
+ * const stop = await asciifyVideo('/clip.mp4', canvas, { preExtract: true });
938
+ */
939
+ declare function asciifyVideo(source: HTMLVideoElement | string, canvas: HTMLCanvasElement, { fontSize, artStyle, options, fitTo, objectFit, objectPosition, scale, width, height, bleed, preExtract, fps, maxRenderDimension, trim, scroll, maxCachedFrames, onReady, onFrame, }?: AsciifyVideoOptions): Promise<() => void>;
940
+ /**
941
+ * @deprecated Use {@link asciifyVideo} instead — it now defaults to live streaming
942
+ * and accepts the same options including `fitTo` and `preExtract`.
943
+ */
944
+ declare function asciifyLiveVideo(source: HTMLVideoElement | string, canvas: HTMLCanvasElement, opts?: AsciifyVideoOptions): Promise<() => void>;
945
+
946
+ /**
947
+ * asciiText — pure string output (no canvas required).
948
+ * Converts an image/video/canvas element to a plain-text ASCII string
949
+ * suitable for server-side rendering, clipboard export, or terminal output.
950
+ */
951
+
952
+ /**
953
+ * Convert any image/video/canvas source to a plain multi-line string of ASCII.
954
+ *
955
+ * @example
956
+ * ```ts
957
+ * const text = asciiText(imgEl, { fontSize: 8, colorMode: 'grayscale' });
958
+ * console.log(text);
959
+ * ```
960
+ *
961
+ * @example SSR (Node/JSDOM)
962
+ * ```ts
963
+ * const { createCanvas, loadImage } = require('@napi-rs/canvas');
964
+ * const img = await loadImage('./photo.jpg');
965
+ * const canvas = createCanvas(img.width, img.height);
966
+ * const ctx = canvas.getContext('2d');
967
+ * ctx.drawImage(img, 0, 0);
968
+ * const text = asciiText(canvas as unknown as HTMLCanvasElement, { fontSize: 10 });
969
+ * ```
970
+ */
971
+ declare function asciiText(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options?: Partial<AsciiOptions>, targetWidth?: number, targetHeight?: number): string;
972
+ /**
973
+ * Convert any source to an ASCII string with ANSI 256-colour escape codes.
974
+ * Works in any terminal that supports ANSI colours.
975
+ *
976
+ * @example
977
+ * ```ts
978
+ * process.stdout.write(asciiTextAnsi(imgEl, { fontSize: 8 }));
979
+ * ```
980
+ */
981
+ declare function asciiTextAnsi(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options?: Partial<AsciiOptions>, targetWidth?: number, targetHeight?: number): string;
982
+
983
+ /**
984
+ * text-frame.ts — Build and render ASCII frames from tiling text patterns.
985
+ *
986
+ * Enables interactive text backgrounds with the full hover-effect system
987
+ * (spotlight, magnify, repel, glow, colorShift, …) without needing an image
988
+ * source.
989
+ *
990
+ * @example — basic usage
991
+ * ```ts
992
+ * import { renderTextBackground } from 'asciify-engine';
993
+ *
994
+ * function tick(ctx, w, h, mousePos) {
995
+ * renderTextBackground(ctx, w, h, 'asciify·engine·v1·', {
996
+ * hoverEffect: 'spotlight',
997
+ * hoverColor: '#d4ff00',
998
+ * }, mousePos);
999
+ * requestAnimationFrame(() => tick(ctx, w, h, mousePos));
1000
+ * }
1001
+ * ```
1002
+ *
1003
+ * @example — build the frame yourself and pass it to renderFrameToCanvas
1004
+ * ```ts
1005
+ * import { buildTextFrame, renderFrameToCanvas, DEFAULT_OPTIONS } from 'asciify-engine';
1006
+ *
1007
+ * const frame = buildTextFrame('hello world · ', 80, 24);
1008
+ * renderFrameToCanvas(ctx, frame, {
1009
+ * ...DEFAULT_OPTIONS,
1010
+ * hoverEffect: 'repel',
1011
+ * hoverStrength: 0.9,
1012
+ * hoverColor: '#a0e8ff',
1013
+ * }, width, height, 0, mousePos);
1014
+ * ```
1015
+ */
1016
+
1017
+ interface TextBackgroundOptions {
1018
+ /**
1019
+ * Character size in pixels.
1020
+ * Controls the grid density: smaller → more cells, finer text.
1021
+ * @default 10
1022
+ */
1023
+ fontSize?: number;
1024
+ /**
1025
+ * Line-height multiplier applied on top of `fontSize` to derive row height.
1026
+ * Matches the default monospace aspect ratio used by the renderer.
1027
+ * @default 1.6
1028
+ */
1029
+ lineHeight?: number;
1030
+ /**
1031
+ * Base colour for the text cells (inactive state).
1032
+ * Accepts any CSS hex or rgb string.
1033
+ * @default '#505050'
1034
+ */
1035
+ color?: string;
1036
+ /**
1037
+ * Base alpha for the text cells, 0–255.
1038
+ * @default 100
1039
+ */
1040
+ opacity?: number;
1041
+ /**
1042
+ * Which hover effect to activate when `hoverPos` is supplied.
1043
+ * @default 'spotlight'
1044
+ */
1045
+ hoverEffect?: HoverEffect;
1046
+ /**
1047
+ * Intensity of the hover distortion / glow, 0–1.
1048
+ * @default 0.85
1049
+ */
1050
+ hoverStrength?: number;
1051
+ /**
1052
+ * Normalised radius of the hover influence zone, 0–1.
1053
+ * @default 0.18
1054
+ */
1055
+ hoverRadius?: number;
1056
+ /**
1057
+ * Accent colour applied by the hover effect.
1058
+ * @default '#d4ff00'
1059
+ */
1060
+ hoverColor?: string;
1061
+ }
1062
+ /**
1063
+ * Build an `AsciiFrame` filled with a tiling text pattern.
1064
+ *
1065
+ * The `text` string is repeated (wrapping at column boundaries) across every
1066
+ * cell of the `cols × rows` grid. Each cell carries the supplied base colour
1067
+ * and opacity as `{r, g, b, a}` values.
1068
+ *
1069
+ * The returned frame can be passed directly to `renderFrameToCanvas` with any
1070
+ * `AsciiOptions` including hover effects.
1071
+ *
1072
+ * @param text - Pattern string to tile across the grid (e.g. `'hello · '`)
1073
+ * @param cols - Number of character columns
1074
+ * @param rows - Number of character rows
1075
+ * @param color - Base RGB colour string for the cells (default: `'#505050'`)
1076
+ * @param opacity - Base alpha 0–255 (default: `100`)
1077
+ */
1078
+ declare function buildTextFrame(text: string, cols: number, rows: number, color?: string, opacity?: number): AsciiFrame;
1079
+ /**
1080
+ * All-in-one: build a tiling text grid and render it to a canvas with the
1081
+ * full hover-effect system (spotlight, magnify, repel, glow, colorShift, …).
1082
+ *
1083
+ * Call this inside a `requestAnimationFrame` loop, passing updated `hoverPos`
1084
+ * each frame to get smooth interactive effects.
1085
+ *
1086
+ * @param ctx - 2D rendering context of the target canvas
1087
+ * @param width - Canvas logical width in pixels
1088
+ * @param height - Canvas logical height in pixels
1089
+ * @param text - Pattern string to tile (e.g. `'asciify·engine·v1·'`)
1090
+ * @param options - {@link TextBackgroundOptions}
1091
+ * @param hoverPos - Normalised mouse position `{x, y}` in 0–1 range,
1092
+ * optionally with an `intensity` multiplier (0–1).
1093
+ * Pass `null` or omit to render without hover.
1094
+ */
1095
+ declare function renderTextBackground(ctx: CanvasRenderingContext2D, width: number, height: number, text: string, options?: TextBackgroundOptions, hoverPos?: {
1096
+ x: number;
1097
+ y: number;
1098
+ intensity?: number;
1099
+ } | null): void;
1100
+
1101
+ interface SnapshotOptions {
1102
+ /**
1103
+ * Image format.
1104
+ * - 'png' — lossless (default)
1105
+ * - 'jpeg' — smaller, no transparency
1106
+ * - 'webp' — best compression with transparency
1107
+ */
1108
+ format?: 'png' | 'jpeg' | 'webp';
1109
+ /** 0–1 quality for jpeg/webp. Default: 0.92 */
1110
+ quality?: number;
1111
+ /**
1112
+ * Scale factor applied before capture. Default: 1.
1113
+ * Use 2 for a high-resolution export at 2× the canvas size.
1114
+ */
1115
+ scale?: number;
1116
+ }
1117
+ /**
1118
+ * Capture a single frame from a canvas as a Blob.
1119
+ *
1120
+ * @example
1121
+ * ```ts
1122
+ * const blob = await captureSnapshot(canvas);
1123
+ * const url = URL.createObjectURL(blob);
1124
+ * ```
1125
+ */
1126
+ declare function captureSnapshot(canvas: HTMLCanvasElement, { format, quality, scale }?: SnapshotOptions): Promise<Blob>;
1127
+ /**
1128
+ * Capture a single frame and immediately trigger a browser download.
1129
+ *
1130
+ * @example
1131
+ * ```ts
1132
+ * await snapshotAndDownload(canvas);
1133
+ * await snapshotAndDownload(canvas, { format: 'jpeg', filename: 'my-art' });
1134
+ * ```
1135
+ */
1136
+ declare function snapshotAndDownload(canvas: HTMLCanvasElement, options?: SnapshotOptions & {
1137
+ filename?: string;
1138
+ }): Promise<void>;
1139
+
1140
+ /**
1141
+ * asciifyWebcam — live webcam → ASCII art on canvas.
1142
+ *
1143
+ * Requests camera access, attaches the stream to a hidden video element, and
1144
+ * runs a rAF loop that converts each frame to ASCII and renders it onto a
1145
+ * supplied canvas.
1146
+ *
1147
+ * @example
1148
+ * const stop = await asciifyWebcam(canvas);
1149
+ * // later: stop();
1150
+ *
1151
+ * @example
1152
+ * const stop = await asciifyWebcam(canvas, {
1153
+ * fontSize: 8,
1154
+ * style: 'terminal',
1155
+ * mirror: true, // horizontal flip (selfie mode)
1156
+ * constraints: { facingMode: 'user' },
1157
+ * });
1158
+ */
1159
+
1160
+ interface WebcamOptions {
1161
+ /** Character size in pixels. Default: 10 */
1162
+ fontSize?: number;
1163
+ /** Art style preset. Default: 'classic' */
1164
+ style?: ArtStyle;
1165
+ /** Extra AsciiOptions merged on top of the preset */
1166
+ options?: Partial<AsciiOptions>;
1167
+ /**
1168
+ * Called every frame to get the latest options. Takes priority over `options`.
1169
+ * Use this to keep the rendering in sync with live UI controls without
1170
+ * restarting the camera.
1171
+ *
1172
+ * @example
1173
+ * const optionsRef = useRef(currentOptions);
1174
+ * optionsRef.current = currentOptions;
1175
+ * asciifyWebcam(canvas, { liveOptions: () => optionsRef.current });
1176
+ */
1177
+ liveOptions?: () => Partial<AsciiOptions>;
1178
+ /**
1179
+ * Flip the output horizontally so it reads like a mirror / selfie camera.
1180
+ * Default: true
1181
+ */
1182
+ mirror?: boolean;
1183
+ /**
1184
+ * Passed directly to `getUserMedia({ video: constraints })`.
1185
+ * Defaults to `{ facingMode: 'user' }`.
1186
+ */
1187
+ constraints?: MediaTrackConstraints;
1188
+ /**
1189
+ * Device pixel ratio used to compute logical render dimensions from the
1190
+ * canvas's physical pixel size. Defaults to `window.devicePixelRatio ?? 1`.
1191
+ * Set this when you size the canvas at physical resolution (e.g. width × dpr)
1192
+ * so that ASCII column/row counts are based on CSS pixels, not physical ones.
1193
+ */
1194
+ dpr?: number;
1195
+ }
1196
+ /**
1197
+ * Start a live webcam ASCII-art loop and render it onto `canvas`.
1198
+ * Returns a `stop()` function that cancels the loop and releases the camera.
1199
+ *
1200
+ * Throws if the browser doesn't support `getUserMedia` or the user denies
1201
+ * camera permission.
1202
+ */
1203
+ declare function asciifyWebcam(canvas: HTMLCanvasElement, { fontSize, style, options, liveOptions, mirror, constraints, dpr: dprOverride, }?: WebcamOptions): Promise<() => void>;
1204
+
1205
+ /**
1206
+ * big-text.ts — ASCII text rendering via an embedded 7×7 bitmap font.
1207
+ *
1208
+ * Each character is defined as 7 rows × 7 pixels stored as compact bit-patterns.
1209
+ * No canvas, no font files, no pixel-sampling — output is always crisp and readable.
1210
+ *
1211
+ * @example
1212
+ * ```ts
1213
+ * import { asciifyText } from 'asciify-engine';
1214
+ * console.log(asciifyText('Hello!'));
1215
+ * console.log(asciifyText('v2', { scale: 2, char: '#' }));
1216
+ * ```
1217
+ */
1218
+ interface BigTextOptions {
1219
+ /**
1220
+ * Character used for ink pixels. Any single grapheme — emoji work too.
1221
+ * @default '█'
1222
+ */
1223
+ char?: string;
1224
+ /**
1225
+ * Pixel scale factor.
1226
+ * `1` = each font pixel → 1 char (output is 7 rows tall).
1227
+ * `2` = each pixel → 2×2 chars (14 rows tall), etc.
1228
+ * @default 1
1229
+ */
1230
+ scale?: number;
1231
+ /** Foreground colour (canvas output only). @default '#d4ff00' */
1232
+ color?: string;
1233
+ /** Background colour (canvas output only). @default transparent */
1234
+ bgColor?: string;
1235
+ /** Physical character cell size in px (canvas output only). @default 10 */
1236
+ fontSize?: number;
1237
+ }
1238
+ /**
1239
+ * Convert a string to multi-line ASCII text art.
1240
+ *
1241
+ * Uses an embedded 7×7 pixel bitmap font — no font files, no install step,
1242
+ * works in every JS environment (browser, Node, Deno, Bun).
1243
+ *
1244
+ * @example
1245
+ * ```ts
1246
+ * import { asciifyText } from 'asciify-engine';
1247
+ * console.log(asciifyText('Hello!'));
1248
+ * console.log(asciifyText('v2.0', { char: '#' }));
1249
+ * console.log(asciifyText('WOW', { scale: 2, char: '▓' }));
1250
+ * ```
1251
+ */
1252
+ declare function asciifyText(text: string, options?: BigTextOptions): string;
1253
+ /**
1254
+ * Render ASCII text directly onto an HTMLCanvasElement.
1255
+ * The canvas is automatically resized to fit the output.
1256
+ *
1257
+ * @example
1258
+ * ```ts
1259
+ * import { renderTextToCanvas } from 'asciify-engine';
1260
+ * renderTextToCanvas(canvas, 'Hello!', { color: '#d4ff00', scale: 2 });
1261
+ * ```
1262
+ */
1263
+ declare function renderTextToCanvas(canvas: HTMLCanvasElement, text: string, options?: BigTextOptions): void;
1264
+
1265
+ export { ART_STYLE_PRESETS, type AnimationStyle, type ArtStyle, type AsciiCell, type AsciiFrame, type AsciiOptions, type AsciiResult, type AsciiTextFrame, type AsciifyLiveVideoOptions, type AsciifySimpleOptions, type AsciifyVideoOptions, type BigTextOptions, CHARSETS, CHARSET_SEQUENCES, type CanvasBleed, type CanvasObjectFit, type CanvasRenderSizeInput, type CharsetKey, type CharsetSequenceKey, type ColorMode, DEFAULT_OPTIONS, HOVER_PRESETS, type HoverEffect, type HoverPreset, type HoverShape, LIVING_STYLE_PRESETS, type LivingStylePresetKey, PALETTE_THEMES, type PaletteTheme, type RenderMode, type SnapshotOptions, type SourceType, type TextBackgroundOptions, type VideoScrollScrubOptions, type WebcamOptions, asciiText, asciiTextAnsi, asciify, asciifyGif, asciifyLiveVideo, asciifyText, asciifyVideo, asciifyWebcam, buildTextFrame, captureSnapshot, clearAsciifyCaches, computeCanvasRenderSize, createVideoScrollScrub, gifToAsciiFrames, gifToAsciiTextFrames, imageToAsciiFrame, imageToAsciiTextFrame, renderFrameToCanvas, renderTextBackground, renderTextFrameToCanvas, renderTextToCanvas, snapshotAndDownload, videoToAsciiFrames, videoToAsciiTextFrames };