asciify-engine 1.0.118 → 1.2.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/core.d.ts ADDED
@@ -0,0 +1,680 @@
1
+ import { e as AsciiOptions, d as AsciiFrame, b as ArtStyle, k as HoverEffect } from './types-w2CRgyk6.js';
2
+ export { A as ART_STYLE_PRESETS, a as AnimationStyle, c as AsciiCell, f as AsciiResult, C as CHARSETS, g as CHARSET_SEQUENCES, h as CharsetKey, i as CharsetSequenceKey, j as ColorMode, D as DEFAULT_OPTIONS, H as HOVER_PRESETS, l as HoverPreset, m as HoverShape, L as LIVING_STYLE_PRESETS, n as LivingStylePresetKey, P as PALETTE_THEMES, o as PaletteTheme, R as RenderMode, S as SourceType } from './types-w2CRgyk6.js';
3
+
4
+ /**
5
+ * Core frame-to-canvas renderer and source-to-frame converters.
6
+ */
7
+
8
+ interface AsciiTextFrame {
9
+ rows: string[];
10
+ cols: number;
11
+ rowCount: number;
12
+ colors?: Uint8ClampedArray;
13
+ }
14
+ /** Clear shared renderer caches. Useful for long-lived editors that cycle many fonts/charsets. */
15
+ declare function clearAsciifyCaches(): void;
16
+ /**
17
+ * Convert an image element or canvas to a single ASCII frame.
18
+ */
19
+ declare function imageToAsciiFrame(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options: AsciiOptions, targetWidth?: number, targetHeight?: number): {
20
+ frame: AsciiFrame;
21
+ cols: number;
22
+ rows: number;
23
+ };
24
+ /**
25
+ * Convert a source into pre-shaped text rows for fast monochrome/accent ASCII.
26
+ *
27
+ * This is the high-performance path for dense video scrubbers. It keeps the
28
+ * expensive pixel sampling work, but avoids allocating one object per cell and
29
+ * lets the renderer draw one string per row instead of one `fillText` per cell.
30
+ */
31
+ declare function imageToAsciiTextFrame(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options: AsciiOptions, targetWidth?: number, targetHeight?: number): AsciiTextFrame;
32
+ declare function renderTextFrameToCanvas(ctx: CanvasRenderingContext2D, textFrame: AsciiTextFrame, options: AsciiOptions, canvasWidth: number, canvasHeight: number): void;
33
+ /**
34
+ * Extract frames from a video element for ASCII animation.
35
+ */
36
+ declare function videoToAsciiFrames(video: HTMLVideoElement, options: AsciiOptions, targetWidth: number, targetHeight: number, targetFps?: number, maxDuration?: number, onProgress?: (progress: number) => void, startTime?: number): Promise<{
37
+ frames: AsciiFrame[];
38
+ cols: number;
39
+ rows: number;
40
+ fps: number;
41
+ }>;
42
+ /**
43
+ * Extract video frames into compact text rows for high-density monochrome/accent
44
+ * animations. This mirrors videoToAsciiFrames but avoids object-per-cell frames.
45
+ */
46
+ declare function videoToAsciiTextFrames(video: HTMLVideoElement, options: AsciiOptions, targetWidth: number, targetHeight: number, targetFps?: number, maxDuration?: number, onProgress?: (progress: number) => void, startTime?: number): Promise<{
47
+ frames: AsciiTextFrame[];
48
+ cols: number;
49
+ rows: number;
50
+ fps: number;
51
+ }>;
52
+ /**
53
+ * Extract frames from an animated GIF file buffer.
54
+ */
55
+ declare function gifToAsciiFrames(buffer: ArrayBuffer, options: AsciiOptions, targetWidth: number, targetHeight: number, onProgress?: (progress: number) => void): Promise<{
56
+ frames: AsciiFrame[];
57
+ cols: number;
58
+ rows: number;
59
+ fps: number;
60
+ }>;
61
+ /**
62
+ * Extract animated GIF frames into compact text rows for high-density
63
+ * monochrome/accent playback. Keeps the legacy object-frame extractor intact
64
+ * for full-color and interactive modes.
65
+ */
66
+ declare function gifToAsciiTextFrames(buffer: ArrayBuffer, options: AsciiOptions, targetWidth: number, targetHeight: number, onProgress?: (progress: number) => void): Promise<{
67
+ frames: AsciiTextFrame[];
68
+ cols: number;
69
+ rows: number;
70
+ fps: number;
71
+ }>;
72
+ /** Frames are immutable: replace a frame after editing cells to invalidate its cached drawing. */
73
+ declare function renderFrameToCanvas(ctx: CanvasRenderingContext2D, frame: AsciiFrame, options: AsciiOptions, width: number, height: number, time?: number, pointer?: {
74
+ x: number;
75
+ y: number;
76
+ intensity?: number;
77
+ } | null): void;
78
+
79
+ /**
80
+ * Simple one-call asciify API.
81
+ * Wraps imageToAsciiFrame + renderFrameToCanvas behind easy-to-use helpers.
82
+ */
83
+
84
+ interface AsciifySimpleOptions {
85
+ /** Character size in pixels. Default: 10 */
86
+ fontSize?: number;
87
+ /**
88
+ * Art style preset — controls charset, render mode, and color mode together.
89
+ * Shorthand for spreading `ART_STYLE_PRESETS[artStyle]` into options.
90
+ * Default: `'classic'`
91
+ */
92
+ artStyle?: ArtStyle;
93
+ /** Extra options to merge on top of the preset */
94
+ options?: Partial<AsciiOptions>;
95
+ }
96
+ type ScrollTriggerInstance = {
97
+ kill?: () => void;
98
+ };
99
+ type ScrollTriggerLike = {
100
+ create: (vars: Record<string, unknown>) => ScrollTriggerInstance;
101
+ };
102
+ type GsapLike = {
103
+ registerPlugin?: (...plugins: unknown[]) => void;
104
+ ScrollTrigger?: ScrollTriggerLike;
105
+ };
106
+ interface VideoScrollScrubOptions {
107
+ /**
108
+ * Element that controls scroll progress. Accepts an HTMLElement or selector.
109
+ * When used through `asciifyVideo`, defaults to `fitTo` or the canvas.
110
+ */
111
+ trigger?: HTMLElement | string | null;
112
+ /**
113
+ * GSAP instance. Pass this with ScrollTrigger for native GSAP scrub support.
114
+ *
115
+ * @example
116
+ * asciifyVideo('/hero.mp4', canvas, {
117
+ * fitTo: '#hero',
118
+ * scroll: { gsap, ScrollTrigger }
119
+ * });
120
+ */
121
+ gsap?: GsapLike;
122
+ /** GSAP ScrollTrigger plugin. Optional when available as `gsap.ScrollTrigger`. */
123
+ ScrollTrigger?: ScrollTriggerLike;
124
+ /** GSAP start value. Native fallback ignores this and uses viewport progress. */
125
+ start?: string;
126
+ /** GSAP end value. Native fallback ignores this and uses viewport progress. */
127
+ end?: string;
128
+ /** GSAP scrub value. Default: `true`. Native fallback always scrubs. */
129
+ scrub?: boolean | number;
130
+ /**
131
+ * Multiply scroll progress before mapping it to video time.
132
+ * `2` reaches the end of the clip in half the scroll distance, `0.5` makes it
133
+ * take twice as much scroll. Default: `1`.
134
+ */
135
+ speed?: number;
136
+ /** Video time to map from. Defaults to trim start or `0`. */
137
+ from?: number;
138
+ /** Video time to map to. Defaults to trim end or video duration. */
139
+ to?: number;
140
+ /** Optional progress transform before mapping to video time. */
141
+ ease?: (progress: number) => number;
142
+ /** Called whenever scroll progress updates. */
143
+ onUpdate?: (progress: number, video: HTMLVideoElement) => void;
144
+ }
145
+ type CanvasObjectFit = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
146
+ type CanvasBleed = number | string | {
147
+ x?: number | string;
148
+ y?: number | string;
149
+ };
150
+ interface AsciifyVideoOptions extends AsciifySimpleOptions {
151
+ /**
152
+ * Fit the canvas to a container element, maintaining the video's aspect ratio.
153
+ * Accepts an HTMLElement or a CSS selector string. The canvas is resized on
154
+ * load and again whenever the container resizes (via ResizeObserver).
155
+ * `stop()` automatically disconnects the observer.
156
+ *
157
+ * @example
158
+ * // Fill the hero div, re-size on window resize automatically:
159
+ * asciifyVideo('/clip.mp4', canvas, { fitTo: '#hero' });
160
+ */
161
+ fitTo?: HTMLElement | string | null;
162
+ /**
163
+ * CSS object-fit behavior for the visible canvas when `fitTo` or explicit
164
+ * `width`/`height` is used. This controls visual framing only; ASCII sampling
165
+ * resolution remains independent. When `options.sourceCrop` is present, the
166
+ * engine sizes from the resolved crop aspect to avoid stretch.
167
+ *
168
+ * Use `'cover'` for full-bleed heroes and `'contain'` for previews.
169
+ * Default: `'contain'`.
170
+ */
171
+ objectFit?: CanvasObjectFit;
172
+ /**
173
+ * CSS object-position for the visible canvas, e.g. `'center bottom'` or
174
+ * `'50% 62%'`. Useful with `objectFit: 'cover'`.
175
+ */
176
+ objectPosition?: string;
177
+ /**
178
+ * Visual scale applied with the CSS individual `scale` property. This is a
179
+ * layout-safe way to overfill a container without changing source proportions
180
+ * or overriding an app's `transform` styles.
181
+ */
182
+ scale?: number;
183
+ /**
184
+ * Visible CSS width for the canvas. Numbers are pixels; strings are used as
185
+ * provided (`'100%'`, `'100vw'`, `min(100vw, 1200px)`, etc.).
186
+ */
187
+ width?: number | string;
188
+ /**
189
+ * Visible CSS height for the canvas. Numbers are pixels; strings are used as
190
+ * provided.
191
+ */
192
+ height?: number | string;
193
+ /**
194
+ * Extra visible overfill added around the fitted canvas box before
195
+ * object-fit sizing. Useful for full-bleed ASCII heroes where glyph side
196
+ * bearings or character-cell quantization can leave a small edge gap even
197
+ * when the source media and canvas are full width.
198
+ *
199
+ * Numbers are pixels. Strings support `%`, `px`, `vw`, and `vh`. Pass an
200
+ * object to control axes independently, e.g. `{ x: '2vw', y: 0 }`.
201
+ */
202
+ bleed?: CanvasBleed;
203
+ /**
204
+ * Pre-extract all video frames into memory before starting playback.
205
+ * Useful for short clips where you need frame-perfect control.
206
+ *
207
+ * Default: `false` — streams live directly from the playing video (instant
208
+ * start, constant memory, unlimited duration).
209
+ *
210
+ * ⚠️ Memory-intensive. Capped at 10 s / 300 frames.
211
+ */
212
+ preExtract?: boolean;
213
+ /**
214
+ * Target render FPS.
215
+ * - In pre-extracted mode, this controls how many frames are decoded into memory.
216
+ * - In live mode, this throttles expensive ASCII frame rendering while the
217
+ * backing video continues decoding normally.
218
+ *
219
+ * Lower values use less CPU/memory and make scroll scrubbing cheaper.
220
+ * Defaults to `18` for pre-extracted scroll scrub and unthrottled rendering
221
+ * for normal live playback.
222
+ */
223
+ fps?: number;
224
+ /**
225
+ * Maximum long-edge render dimension for video frame extraction.
226
+ * Raise this for full-width or 4K heroes so the canvas is not upscaled by CSS.
227
+ * Default: `2048`.
228
+ */
229
+ maxRenderDimension?: number;
230
+ /**
231
+ * Trim the video to a specific time range (in seconds).
232
+ * - `start` — seek to this time before playback begins. Default: `0`
233
+ * - `end` — loop back to `start` when this time is reached.
234
+ * In `preExtract` mode, only frames up to `end` are extracted.
235
+ *
236
+ * @example
237
+ * // Play only seconds 2–8, looping:
238
+ * asciifyVideo('/clip.mp4', canvas, { trim: { start: 2, end: 8 } });
239
+ */
240
+ trim?: {
241
+ start?: number;
242
+ end?: number;
243
+ };
244
+ /**
245
+ * Sync video time to scroll progress.
246
+ *
247
+ * - `true` uses native scroll scrubbing with `fitTo`/canvas as the trigger.
248
+ * - Passing `gsap` + `ScrollTrigger` uses GSAP ScrollTrigger.
249
+ *
250
+ * @example
251
+ * await asciifyVideo('/hero.mp4', canvas, {
252
+ * fitTo: '#hero',
253
+ * scroll: true
254
+ * });
255
+ *
256
+ * @example
257
+ * await asciifyVideo('/hero.mp4', canvas, {
258
+ * fitTo: '#hero',
259
+ * scroll: { gsap, ScrollTrigger, start: 'top bottom', end: 'bottom top', scrub: 1 }
260
+ * });
261
+ */
262
+ scroll?: boolean | VideoScrollScrubOptions;
263
+ /**
264
+ * Called once when the video metadata is loaded and playback has started.
265
+ * Receives the backing video element.
266
+ */
267
+ onReady?: (video: HTMLVideoElement) => void;
268
+ /**
269
+ * Maximum number of decoded text frames kept in memory for live scroll scrub.
270
+ * The cache is nearest-frame and bounded; lower values reduce memory for long
271
+ * clips while preserving smooth scrubbing around the current scroll position.
272
+ * Default: `90`.
273
+ */
274
+ maxCachedFrames?: number;
275
+ /** Called after every rendered frame. */
276
+ onFrame?: () => void;
277
+ }
278
+ /** @deprecated Use {@link AsciifyVideoOptions} */
279
+ type AsciifyLiveVideoOptions = AsciifyVideoOptions;
280
+ /**
281
+ * Sync an HTMLVideoElement's currentTime to scroll progress.
282
+ *
283
+ * If `gsap` and `ScrollTrigger` are supplied, the helper uses GSAP. Otherwise
284
+ * it falls back to a tiny native scroll listener. Returns a cleanup function.
285
+ */
286
+ declare function createVideoScrollScrub(video: HTMLVideoElement, opts?: VideoScrollScrubOptions): () => void;
287
+ interface CanvasRenderSizeInput {
288
+ sourceWidth: number;
289
+ sourceHeight: number;
290
+ cssWidth?: number;
291
+ cssHeight?: number;
292
+ /** Accepted for callers that pass complete display context; DPR is applied separately by the canvas backing buffer. */
293
+ dpr?: number;
294
+ maxRenderDimension?: number;
295
+ }
296
+ declare function computeCanvasRenderSize({ sourceWidth, sourceHeight, cssWidth, cssHeight, maxRenderDimension, }: CanvasRenderSizeInput): {
297
+ renderW: number;
298
+ renderH: number;
299
+ };
300
+ /**
301
+ * Convert an image/video/canvas element to ASCII art and render it onto a canvas.
302
+ *
303
+ * When hover options are active (`hoverStrength > 0` in `options`), the engine
304
+ * automatically sets up mouse tracking and a `requestAnimationFrame` loop so
305
+ * the hover effect works out of the box. In that case, a `stop()` function is
306
+ * returned to tear down the loop and listeners.
307
+ *
308
+ * @example
309
+ * // Static (no hover):
310
+ * await asciify('/photo.jpg', canvas);
311
+ *
312
+ * // With hover — returns a cleanup handle:
313
+ * const stop = await asciify('/photo.jpg', canvas, {
314
+ * options: { hoverEffect: 'glitchText', hoverStrength: 0.8, hoverText: 'HI' }
315
+ * });
316
+ * // later: stop?.();
317
+ */
318
+ declare function asciify(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | string, canvas: HTMLCanvasElement, { fontSize, artStyle, options }?: AsciifySimpleOptions): Promise<() => void>;
319
+ /**
320
+ * Fetch a GIF, convert it to ASCII, and start an animation loop on a canvas.
321
+ * Returns a `stop()` function that cancels the loop.
322
+ *
323
+ * @example
324
+ * const stop = await asciifyGif('animation.gif', canvas);
325
+ * // later: stop();
326
+ */
327
+ declare function asciifyGif(source: string | ArrayBuffer, canvas: HTMLCanvasElement, { fontSize, artStyle, options }?: AsciifySimpleOptions): Promise<() => void>;
328
+ /**
329
+ * Render a video as ASCII art on a canvas. Defaults to live streaming —
330
+ * instant start, constant memory, unlimited duration.
331
+ *
332
+ * Pass `{ preExtract: true }` to pre-decode all frames before playback starts
333
+ * (useful for short clips that need frame-perfect looping).
334
+ *
335
+ * Pass `{ fitTo: '#container' }` to automatically size and re-size the canvas
336
+ * to fill a container element, maintaining the video's aspect ratio.
337
+ *
338
+ * Returns a `stop()` function that cancels the loop and cleans up.
339
+ *
340
+ * ⚠️ Never set the backing `<video>` to `display: none` — browsers skip GPU
341
+ * frame decoding for hidden elements. When given a URL string, this function
342
+ * handles that automatically.
343
+ *
344
+ * @example
345
+ * // Minimal
346
+ * const stop = await asciifyVideo('/clip.mp4', canvas);
347
+ *
348
+ * // Fit to container, re-size on window resize:
349
+ * const stop = await asciifyVideo('/clip.mp4', canvas, { fitTo: '#hero' });
350
+ *
351
+ * // Pre-extract frames (old behavior):
352
+ * const stop = await asciifyVideo('/clip.mp4', canvas, { preExtract: true });
353
+ */
354
+ 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>;
355
+ /**
356
+ * @deprecated Use {@link asciifyVideo} instead — it now defaults to live streaming
357
+ * and accepts the same options including `fitTo` and `preExtract`.
358
+ */
359
+ declare function asciifyLiveVideo(source: HTMLVideoElement | string, canvas: HTMLCanvasElement, opts?: AsciifyVideoOptions): Promise<() => void>;
360
+
361
+ /**
362
+ * asciiText — pure string output (no canvas required).
363
+ * Converts an image/video/canvas element to a plain-text ASCII string
364
+ * suitable for server-side rendering, clipboard export, or terminal output.
365
+ */
366
+
367
+ /**
368
+ * Convert any image/video/canvas source to a plain multi-line string of ASCII.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * const text = asciiText(imgEl, { fontSize: 8, colorMode: 'grayscale' });
373
+ * console.log(text);
374
+ * ```
375
+ *
376
+ * @example SSR (Node/JSDOM)
377
+ * ```ts
378
+ * const { createCanvas, loadImage } = require('@napi-rs/canvas');
379
+ * const img = await loadImage('./photo.jpg');
380
+ * const canvas = createCanvas(img.width, img.height);
381
+ * const ctx = canvas.getContext('2d');
382
+ * ctx.drawImage(img, 0, 0);
383
+ * const text = asciiText(canvas as unknown as HTMLCanvasElement, { fontSize: 10 });
384
+ * ```
385
+ */
386
+ declare function asciiText(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options?: Partial<AsciiOptions>, targetWidth?: number, targetHeight?: number): string;
387
+ /**
388
+ * Convert any source to an ASCII string with ANSI 256-colour escape codes.
389
+ * Works in any terminal that supports ANSI colours.
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * process.stdout.write(asciiTextAnsi(imgEl, { fontSize: 8 }));
394
+ * ```
395
+ */
396
+ declare function asciiTextAnsi(source: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement, options?: Partial<AsciiOptions>, targetWidth?: number, targetHeight?: number): string;
397
+
398
+ /**
399
+ * text-frame.ts — Build and render ASCII frames from tiling text patterns.
400
+ *
401
+ * Enables interactive text backgrounds with the full hover-effect system
402
+ * (spotlight, magnify, repel, glow, colorShift, …) without needing an image
403
+ * source.
404
+ *
405
+ * @example — basic usage
406
+ * ```ts
407
+ * import { renderTextBackground } from 'asciify-engine';
408
+ *
409
+ * function tick(ctx, w, h, mousePos) {
410
+ * renderTextBackground(ctx, w, h, 'asciify·engine·v1·', {
411
+ * hoverEffect: 'spotlight',
412
+ * hoverColor: '#d4ff00',
413
+ * }, mousePos);
414
+ * requestAnimationFrame(() => tick(ctx, w, h, mousePos));
415
+ * }
416
+ * ```
417
+ *
418
+ * @example — build the frame yourself and pass it to renderFrameToCanvas
419
+ * ```ts
420
+ * import { buildTextFrame, renderFrameToCanvas, DEFAULT_OPTIONS } from 'asciify-engine';
421
+ *
422
+ * const frame = buildTextFrame('hello world · ', 80, 24);
423
+ * renderFrameToCanvas(ctx, frame, {
424
+ * ...DEFAULT_OPTIONS,
425
+ * hoverEffect: 'repel',
426
+ * hoverStrength: 0.9,
427
+ * hoverColor: '#a0e8ff',
428
+ * }, width, height, 0, mousePos);
429
+ * ```
430
+ */
431
+
432
+ interface TextBackgroundOptions {
433
+ /**
434
+ * Character size in pixels.
435
+ * Controls the grid density: smaller → more cells, finer text.
436
+ * @default 10
437
+ */
438
+ fontSize?: number;
439
+ /**
440
+ * Line-height multiplier applied on top of `fontSize` to derive row height.
441
+ * Matches the default monospace aspect ratio used by the renderer.
442
+ * @default 1.6
443
+ */
444
+ lineHeight?: number;
445
+ /**
446
+ * Base colour for the text cells (inactive state).
447
+ * Accepts any CSS hex or rgb string.
448
+ * @default '#505050'
449
+ */
450
+ color?: string;
451
+ /**
452
+ * Base alpha for the text cells, 0–255.
453
+ * @default 100
454
+ */
455
+ opacity?: number;
456
+ /**
457
+ * Which hover effect to activate when `hoverPos` is supplied.
458
+ * @default 'spotlight'
459
+ */
460
+ hoverEffect?: HoverEffect;
461
+ /**
462
+ * Intensity of the hover distortion / glow, 0–1.
463
+ * @default 0.85
464
+ */
465
+ hoverStrength?: number;
466
+ /**
467
+ * Normalised radius of the hover influence zone, 0–1.
468
+ * @default 0.18
469
+ */
470
+ hoverRadius?: number;
471
+ /**
472
+ * Accent colour applied by the hover effect.
473
+ * @default '#d4ff00'
474
+ */
475
+ hoverColor?: string;
476
+ }
477
+ /**
478
+ * Build an `AsciiFrame` filled with a tiling text pattern.
479
+ *
480
+ * The `text` string is repeated (wrapping at column boundaries) across every
481
+ * cell of the `cols × rows` grid. Each cell carries the supplied base colour
482
+ * and opacity as `{r, g, b, a}` values.
483
+ *
484
+ * The returned frame can be passed directly to `renderFrameToCanvas` with any
485
+ * `AsciiOptions` including hover effects.
486
+ *
487
+ * @param text - Pattern string to tile across the grid (e.g. `'hello · '`)
488
+ * @param cols - Number of character columns
489
+ * @param rows - Number of character rows
490
+ * @param color - Base RGB colour string for the cells (default: `'#505050'`)
491
+ * @param opacity - Base alpha 0–255 (default: `100`)
492
+ */
493
+ declare function buildTextFrame(text: string, cols: number, rows: number, color?: string, opacity?: number): AsciiFrame;
494
+ /**
495
+ * All-in-one: build a tiling text grid and render it to a canvas with the
496
+ * full hover-effect system (spotlight, magnify, repel, glow, colorShift, …).
497
+ *
498
+ * Call this inside a `requestAnimationFrame` loop, passing updated `hoverPos`
499
+ * each frame to get smooth interactive effects.
500
+ *
501
+ * @param ctx - 2D rendering context of the target canvas
502
+ * @param width - Canvas logical width in pixels
503
+ * @param height - Canvas logical height in pixels
504
+ * @param text - Pattern string to tile (e.g. `'asciify·engine·v1·'`)
505
+ * @param options - {@link TextBackgroundOptions}
506
+ * @param hoverPos - Normalised mouse position `{x, y}` in 0–1 range,
507
+ * optionally with an `intensity` multiplier (0–1).
508
+ * Pass `null` or omit to render without hover.
509
+ */
510
+ declare function renderTextBackground(ctx: CanvasRenderingContext2D, width: number, height: number, text: string, options?: TextBackgroundOptions, hoverPos?: {
511
+ x: number;
512
+ y: number;
513
+ intensity?: number;
514
+ } | null): void;
515
+
516
+ interface SnapshotOptions {
517
+ /**
518
+ * Image format.
519
+ * - 'png' — lossless (default)
520
+ * - 'jpeg' — smaller, no transparency
521
+ * - 'webp' — best compression with transparency
522
+ */
523
+ format?: 'png' | 'jpeg' | 'webp';
524
+ /** 0–1 quality for jpeg/webp. Default: 0.92 */
525
+ quality?: number;
526
+ /**
527
+ * Scale factor applied before capture. Default: 1.
528
+ * Use 2 for a high-resolution export at 2× the canvas size.
529
+ */
530
+ scale?: number;
531
+ }
532
+ /**
533
+ * Capture a single frame from a canvas as a Blob.
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * const blob = await captureSnapshot(canvas);
538
+ * const url = URL.createObjectURL(blob);
539
+ * ```
540
+ */
541
+ declare function captureSnapshot(canvas: HTMLCanvasElement, { format, quality, scale }?: SnapshotOptions): Promise<Blob>;
542
+ /**
543
+ * Capture a single frame and immediately trigger a browser download.
544
+ *
545
+ * @example
546
+ * ```ts
547
+ * await snapshotAndDownload(canvas);
548
+ * await snapshotAndDownload(canvas, { format: 'jpeg', filename: 'my-art' });
549
+ * ```
550
+ */
551
+ declare function snapshotAndDownload(canvas: HTMLCanvasElement, options?: SnapshotOptions & {
552
+ filename?: string;
553
+ }): Promise<void>;
554
+
555
+ /**
556
+ * asciifyWebcam — live webcam → ASCII art on canvas.
557
+ *
558
+ * Requests camera access, attaches the stream to a hidden video element, and
559
+ * runs a rAF loop that converts each frame to ASCII and renders it onto a
560
+ * supplied canvas.
561
+ *
562
+ * @example
563
+ * const stop = await asciifyWebcam(canvas);
564
+ * // later: stop();
565
+ *
566
+ * @example
567
+ * const stop = await asciifyWebcam(canvas, {
568
+ * fontSize: 8,
569
+ * style: 'terminal',
570
+ * mirror: true, // horizontal flip (selfie mode)
571
+ * constraints: { facingMode: 'user' },
572
+ * });
573
+ */
574
+
575
+ interface WebcamOptions {
576
+ /** Character size in pixels. Default: 10 */
577
+ fontSize?: number;
578
+ /** Art style preset. Default: 'classic' */
579
+ style?: ArtStyle;
580
+ /** Extra AsciiOptions merged on top of the preset */
581
+ options?: Partial<AsciiOptions>;
582
+ /**
583
+ * Called every frame to get the latest options. Takes priority over `options`.
584
+ * Use this to keep the rendering in sync with live UI controls without
585
+ * restarting the camera.
586
+ *
587
+ * @example
588
+ * const optionsRef = useRef(currentOptions);
589
+ * optionsRef.current = currentOptions;
590
+ * asciifyWebcam(canvas, { liveOptions: () => optionsRef.current });
591
+ */
592
+ liveOptions?: () => Partial<AsciiOptions>;
593
+ /**
594
+ * Flip the output horizontally so it reads like a mirror / selfie camera.
595
+ * Default: true
596
+ */
597
+ mirror?: boolean;
598
+ /**
599
+ * Passed directly to `getUserMedia({ video: constraints })`.
600
+ * Defaults to `{ facingMode: 'user' }`.
601
+ */
602
+ constraints?: MediaTrackConstraints;
603
+ /**
604
+ * Device pixel ratio used to compute logical render dimensions from the
605
+ * canvas's physical pixel size. Defaults to `window.devicePixelRatio ?? 1`.
606
+ * Set this when you size the canvas at physical resolution (e.g. width × dpr)
607
+ * so that ASCII column/row counts are based on CSS pixels, not physical ones.
608
+ */
609
+ dpr?: number;
610
+ }
611
+ /**
612
+ * Start a live webcam ASCII-art loop and render it onto `canvas`.
613
+ * Returns a `stop()` function that cancels the loop and releases the camera.
614
+ *
615
+ * Throws if the browser doesn't support `getUserMedia` or the user denies
616
+ * camera permission.
617
+ */
618
+ declare function asciifyWebcam(canvas: HTMLCanvasElement, { fontSize, style, options, liveOptions, mirror, constraints, dpr: dprOverride, }?: WebcamOptions): Promise<() => void>;
619
+
620
+ /**
621
+ * big-text.ts — ASCII text rendering via an embedded 7×7 bitmap font.
622
+ *
623
+ * Each character is defined as 7 rows × 7 pixels stored as compact bit-patterns.
624
+ * No canvas, no font files, no pixel-sampling — output is always crisp and readable.
625
+ *
626
+ * @example
627
+ * ```ts
628
+ * import { asciifyText } from 'asciify-engine';
629
+ * console.log(asciifyText('Hello!'));
630
+ * console.log(asciifyText('v2', { scale: 2, char: '#' }));
631
+ * ```
632
+ */
633
+ interface BigTextOptions {
634
+ /**
635
+ * Character used for ink pixels. Any single grapheme — emoji work too.
636
+ * @default '█'
637
+ */
638
+ char?: string;
639
+ /**
640
+ * Pixel scale factor.
641
+ * `1` = each font pixel → 1 char (output is 7 rows tall).
642
+ * `2` = each pixel → 2×2 chars (14 rows tall), etc.
643
+ * @default 1
644
+ */
645
+ scale?: number;
646
+ /** Foreground colour (canvas output only). @default '#d4ff00' */
647
+ color?: string;
648
+ /** Background colour (canvas output only). @default transparent */
649
+ bgColor?: string;
650
+ /** Physical character cell size in px (canvas output only). @default 10 */
651
+ fontSize?: number;
652
+ }
653
+ /**
654
+ * Convert a string to multi-line ASCII text art.
655
+ *
656
+ * Uses an embedded 7×7 pixel bitmap font — no font files, no install step,
657
+ * works in every JS environment (browser, Node, Deno, Bun).
658
+ *
659
+ * @example
660
+ * ```ts
661
+ * import { asciifyText } from 'asciify-engine';
662
+ * console.log(asciifyText('Hello!'));
663
+ * console.log(asciifyText('v2.0', { char: '#' }));
664
+ * console.log(asciifyText('WOW', { scale: 2, char: '▓' }));
665
+ * ```
666
+ */
667
+ declare function asciifyText(text: string, options?: BigTextOptions): string;
668
+ /**
669
+ * Render ASCII text directly onto an HTMLCanvasElement.
670
+ * The canvas is automatically resized to fit the output.
671
+ *
672
+ * @example
673
+ * ```ts
674
+ * import { renderTextToCanvas } from 'asciify-engine';
675
+ * renderTextToCanvas(canvas, 'Hello!', { color: '#d4ff00', scale: 2 });
676
+ * ```
677
+ */
678
+ declare function renderTextToCanvas(canvas: HTMLCanvasElement, text: string, options?: BigTextOptions): void;
679
+
680
+ export { ArtStyle, AsciiFrame, AsciiOptions, type AsciiTextFrame, type AsciifyLiveVideoOptions, type AsciifySimpleOptions, type AsciifyVideoOptions, type BigTextOptions, type CanvasBleed, type CanvasObjectFit, type CanvasRenderSizeInput, HoverEffect, type SnapshotOptions, 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 };