canvasdown 0.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +541 -0
  3. package/dist/advanced.d.ts +65 -0
  4. package/dist/advanced.d.ts.map +1 -0
  5. package/dist/advanced.js +442 -0
  6. package/dist/advanced.js.map +1 -0
  7. package/dist/canvas-provider.d.ts +11 -0
  8. package/dist/canvas-provider.d.ts.map +1 -0
  9. package/dist/canvas-provider.js +15 -0
  10. package/dist/canvas-provider.js.map +1 -0
  11. package/dist/index.d.ts +28 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +84 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/layout.d.ts +95 -0
  16. package/dist/layout.d.ts.map +1 -0
  17. package/dist/layout.js +179 -0
  18. package/dist/layout.js.map +1 -0
  19. package/dist/parser.d.ts +4 -0
  20. package/dist/parser.d.ts.map +1 -0
  21. package/dist/parser.js +252 -0
  22. package/dist/parser.js.map +1 -0
  23. package/dist/react.d.ts +33 -0
  24. package/dist/react.d.ts.map +1 -0
  25. package/dist/react.js +43 -0
  26. package/dist/react.js.map +1 -0
  27. package/dist/renderer.d.ts +4 -0
  28. package/dist/renderer.d.ts.map +1 -0
  29. package/dist/renderer.js +238 -0
  30. package/dist/renderer.js.map +1 -0
  31. package/dist/stream.d.ts +10 -0
  32. package/dist/stream.d.ts.map +1 -0
  33. package/dist/stream.js +72 -0
  34. package/dist/stream.js.map +1 -0
  35. package/dist/theme.d.ts +75 -0
  36. package/dist/theme.d.ts.map +1 -0
  37. package/dist/theme.js +123 -0
  38. package/dist/theme.js.map +1 -0
  39. package/dist/tokens.d.ts +66 -0
  40. package/dist/tokens.d.ts.map +1 -0
  41. package/dist/tokens.js +2 -0
  42. package/dist/tokens.js.map +1 -0
  43. package/dist/worker-bridge.d.ts +26 -0
  44. package/dist/worker-bridge.d.ts.map +1 -0
  45. package/dist/worker-bridge.js +31 -0
  46. package/dist/worker-bridge.js.map +1 -0
  47. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 canvasdown contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,541 @@
1
+ # canvasdown
2
+
3
+ > Markdown renderer for Canvas. No DOM. No reflow. Runs anywhere.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/canvasdown?color=f97316&style=flat-square)](https://www.npmjs.com/package/canvasdown)
6
+ [![license](https://img.shields.io/npm/l/canvasdown?color=f97316&style=flat-square)](./LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178c6?style=flat-square)](https://www.typescriptlang.org/)
8
+ [![built on Pretext](https://img.shields.io/badge/built%20on-Pretext-orange?style=flat-square)](https://github.com/chenglou/pretext)
9
+
10
+ canvasdown renders Markdown directly to HTML Canvas — no DOM, no layout reflow, no browser rendering pipeline. Powered by [Pretext](https://github.com/chenglou/pretext), which measures text ~300,000x faster than `getBoundingClientRect`.
11
+
12
+ Works in **browser**, **Node.js**, **Web Workers**, and **WebGL** contexts.
13
+
14
+ ---
15
+
16
+ ## Why canvasdown?
17
+
18
+ | | react-markdown | Satori (Vercel) | **canvasdown** |
19
+ |---|---|---|---|
20
+ | Input | Markdown | JSX | **Markdown** |
21
+ | Output | DOM (HTML) | SVG | **Canvas / PNG** |
22
+ | DOM reflow | Every render | N/A | **Never** |
23
+ | Streaming (AI chat) | Slow | No | **Yes** |
24
+ | Node.js | SSR only | Yes | **Yes** |
25
+ | Web Worker | No | No | **Yes** |
26
+ | Per-char animation | No | No | **Yes** |
27
+ | Text along path | No | No | **Yes** |
28
+ | Virtual list heights | Estimate | No | **Exact** |
29
+ | OG image generation | html2canvas | Yes | **Yes** |
30
+
31
+ ---
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ npm install canvasdown
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ```typescript
44
+ import { render, darkTheme } from 'canvasdown'
45
+
46
+ const markdown = `
47
+ # Hello canvasdown
48
+
49
+ Render **Markdown** to Canvas with *no DOM reflow*.
50
+
51
+ - Zero DOM reads
52
+ - Runs in Node.js
53
+ - Streams token-by-token
54
+ `
55
+
56
+ const canvas = document.getElementById('output') as HTMLCanvasElement
57
+ render(markdown, canvas, { width: 800, theme: darkTheme })
58
+ ```
59
+
60
+ ---
61
+
62
+ ## API Reference
63
+
64
+ ### Core
65
+
66
+ #### `render(markdown, canvas, options?)`
67
+
68
+ Render markdown to a canvas element. Automatically sets canvas width/height and handles HiDPI.
69
+
70
+ ```typescript
71
+ import { render } from 'canvasdown'
72
+
73
+ render(markdown, canvas, {
74
+ width: 800, // canvas width in px (default: 800)
75
+ theme: 'dark', // 'dark' | 'light' | Theme object
76
+ devicePixelRatio: 2, // HiDPI (default: window.devicePixelRatio)
77
+ })
78
+ ```
79
+
80
+ #### `measure(markdown, options?)`
81
+
82
+ Measure layout **without rendering** — get exact height before touching the canvas. Essential for virtual lists.
83
+
84
+ ```typescript
85
+ import { measure } from 'canvasdown'
86
+
87
+ const { totalHeight, blocks } = measure(markdown, { width: 680, theme: 'dark' })
88
+ console.log(`This markdown will be ${totalHeight}px tall`)
89
+ ```
90
+
91
+ #### `exportPNG(markdown, options?)`
92
+
93
+ Export as PNG dataURL. Works in browser and Node.js (with `canvasFactory`).
94
+
95
+ ```typescript
96
+ import { exportPNG } from 'canvasdown'
97
+
98
+ const dataURL = await exportPNG(markdown, {
99
+ width: 1200,
100
+ devicePixelRatio: 2, // @2x = 2400px wide PNG
101
+ theme: 'dark',
102
+ })
103
+ ```
104
+
105
+ #### `exportBlob(markdown, options?)`
106
+
107
+ Export as `Blob` for upload or download.
108
+
109
+ ```typescript
110
+ import { exportBlob } from 'canvasdown'
111
+
112
+ const blob = await exportBlob(markdown, { width: 1200 })
113
+ const formData = new FormData()
114
+ formData.append('image', blob, 'og-image.png')
115
+ ```
116
+
117
+ ---
118
+
119
+ ### Streaming (for AI Chat)
120
+
121
+ Stream markdown token-by-token with zero reflow. Built for LLM streaming responses.
122
+
123
+ ```typescript
124
+ import { createStream } from 'canvasdown/stream'
125
+
126
+ const canvas = document.getElementById('chat-canvas') as HTMLCanvasElement
127
+ const stream = createStream(canvas, { width: 680, theme: 'dark' })
128
+
129
+ // Connect to your LLM stream (OpenAI, Anthropic, etc.)
130
+ const response = await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ prompt }) })
131
+ const reader = response.body!.getReader()
132
+ const decoder = new TextDecoder()
133
+
134
+ while (true) {
135
+ const { done, value } = await reader.read()
136
+ if (done) break
137
+ stream.append(decoder.decode(value)) // only re-paints changed lines
138
+ }
139
+
140
+ stream.flush() // finalize
141
+ stream.destroy() // cleanup on unmount
142
+ ```
143
+
144
+ **Stream API:**
145
+
146
+ ```typescript
147
+ type StreamHandle = {
148
+ append(chunk: string): void // add text, schedules rAF render
149
+ flush(): void // immediate render, skip rAF
150
+ getLayout(): DocumentLayout | null // current layout state
151
+ destroy(): void // cancel pending rAF, cleanup
152
+ }
153
+ ```
154
+
155
+ ---
156
+
157
+ ### Advanced Features
158
+
159
+ Import from `canvasdown/advanced`:
160
+
161
+ ```typescript
162
+ import { shrinkwrap, fitText, createOverlay, renderTextOnPath, animateText, updateRender } from 'canvasdown/advanced'
163
+ ```
164
+
165
+ #### `shrinkwrap(markdown, options?)` — Auto-fit container
166
+
167
+ Find the **tightest width** that fits the text without overflow (binary search).
168
+
169
+ ```typescript
170
+ const { width, height } = shrinkwrap('Hello **world**', {
171
+ minWidth: 100,
172
+ maxWidth: 800,
173
+ theme: 'dark',
174
+ })
175
+ // width = smallest px where text doesn't overflow
176
+ ```
177
+
178
+ #### `fitText(markdown, options?)` — Shrink font to fit box
179
+
180
+ Find the **largest font size** where text fits within a container.
181
+
182
+ ```typescript
183
+ const { fontSize, lineHeight, height } = fitText(markdown, {
184
+ container: { width: 400, height: 200 },
185
+ fontSizeMin: 10,
186
+ fontSizeMax: 48,
187
+ theme: 'dark',
188
+ })
189
+ // fontSize = largest size that still fits
190
+ ```
191
+
192
+ #### `createOverlay(canvas, layout)` — Semantic highlight
193
+
194
+ Add highlight overlay **without re-rendering** the base canvas.
195
+
196
+ ```typescript
197
+ const layout = measure(markdown, { width: 680, theme: 'dark' })
198
+ render(markdown, canvas, { width: 680, theme: 'dark' })
199
+
200
+ const overlay = createOverlay(canvas, layout)
201
+ overlay.highlight('pretext', { background: '#ffff00', opacity: 0.4 })
202
+ overlay.highlight('canvas', { background: '#00ffff', opacity: 0.3 })
203
+ overlay.clear() // remove all highlights
204
+ overlay.destroy() // remove overlay element
205
+ ```
206
+
207
+ #### `renderTextOnPath(ctx, text, svgPath, options?)` — Text along curves
208
+
209
+ Render text following any SVG path — **not possible with DOM or SVG text**.
210
+
211
+ ```typescript
212
+ renderTextOnPath(ctx, 'canvasdown ✦ canvas text along any curve', 'M 0 100 Q 200 0 400 100', {
213
+ font: '700 20px Inter',
214
+ color: '#f97316',
215
+ offset: 10, // start offset in px
216
+ })
217
+ ```
218
+
219
+ Supports SVG path commands: `M`, `L`, `Q` (quadratic), `C` (cubic bezier).
220
+
221
+ #### `animateText(canvas, markdown, animOptions, renderOptions?)` — Per-character animation
222
+
223
+ Animate individual characters with independent transforms.
224
+
225
+ ```typescript
226
+ const anim = animateText(canvas, markdown, {
227
+ effect: 'wave', // 'wave' | 'fadeIn' | 'typewriter' | 'bounce' | 'explode'
228
+ stagger: 30, // ms delay between characters
229
+ duration: 600, // ms per character animation
230
+ loop: false,
231
+ onComplete: () => console.log('done'),
232
+ })
233
+
234
+ anim.start()
235
+ anim.stop()
236
+ anim.reset()
237
+ ```
238
+
239
+ #### `updateRender(prev, next, canvas, options?)` — Diff-aware re-render
240
+
241
+ Re-render only what changed. Efficient for live preview / collaborative editors.
242
+
243
+ ```typescript
244
+ updateRender(oldMarkdown, newMarkdown, canvas, { width: 680, theme: 'dark' })
245
+ ```
246
+
247
+ ---
248
+
249
+ ### Node.js Support
250
+
251
+ Inject a canvas factory to use canvasdown server-side:
252
+
253
+ ```typescript
254
+ import { exportPNG } from 'canvasdown'
255
+ import { createCanvas } from '@napi-rs/canvas'
256
+
257
+ const png = await exportPNG(markdown, {
258
+ width: 1200,
259
+ devicePixelRatio: 2,
260
+ theme: 'dark',
261
+ canvasFactory: (w, h) => createCanvas(w, h),
262
+ })
263
+
264
+ // Save to file
265
+ import { writeFileSync } from 'fs'
266
+ const base64 = png.split(',')[1]!
267
+ writeFileSync('og-image.png', Buffer.from(base64, 'base64'))
268
+ ```
269
+
270
+ ```bash
271
+ npm install @napi-rs/canvas # Rust-based, pre-built ARM64 binaries
272
+ ```
273
+
274
+ ---
275
+
276
+ ### React Component
277
+
278
+ ```typescript
279
+ import { createCanvasdownComponent } from 'canvasdown/react'
280
+ import React, { useState } from 'react'
281
+
282
+ const Canvasdown = createCanvasdownComponent(React)
283
+
284
+ function App() {
285
+ const [height, setHeight] = useState(0)
286
+
287
+ return (
288
+ <div style={{ position: 'relative' }}>
289
+ <Canvasdown
290
+ markdown={content}
291
+ width={680}
292
+ theme="dark"
293
+ className="chat-canvas"
294
+ onHeightChange={setHeight}
295
+ onRender={(layout) => console.log('blocks:', layout.blocks.length)}
296
+ />
297
+ </div>
298
+ )
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ ### Web Worker + OffscreenCanvas
305
+
306
+ Render off the main thread — keep UI at 60fps during heavy markdown rendering.
307
+
308
+ ```typescript
309
+ // main.ts
310
+ const worker = new Worker(new URL('./canvasdown.worker.js', import.meta.url))
311
+ const offscreen = canvas.transferControlToOffscreen()
312
+
313
+ worker.postMessage({ type: 'render', markdown, width: 800, theme: 'dark', dpr: 2 }, [offscreen])
314
+ worker.onmessage = (e) => console.log('rendered:', e.data.height, 'px')
315
+ ```
316
+
317
+ ```typescript
318
+ // canvasdown.worker.js
319
+ import { handleWorkerMessage } from 'canvasdown/worker'
320
+
321
+ let offscreen: OffscreenCanvas
322
+
323
+ self.onmessage = (e) => {
324
+ if (e.data.type === 'render') {
325
+ if (!offscreen) offscreen = e.ports[0] ?? e.data.canvas
326
+ const result = handleWorkerMessage(e.data, offscreen)
327
+ self.postMessage(result)
328
+ }
329
+ }
330
+ ```
331
+
332
+ ---
333
+
334
+ ## Markdown Support
335
+
336
+ | Element | Syntax | Status |
337
+ |---|---|---|
338
+ | Heading H1–H6 | `# H1` … `###### H6` | ✅ |
339
+ | Paragraph | plain text | ✅ |
340
+ | Bold | `**text**` | ✅ |
341
+ | Italic | `*text*` | ✅ |
342
+ | Bold italic | `***text***` | ✅ |
343
+ | Inline code | `` `code` `` | ✅ |
344
+ | Link | `[text](url)` | ✅ underline + color |
345
+ | Strikethrough | `~~text~~` | ✅ |
346
+ | Code block | ` ```lang ` | ✅ |
347
+ | Blockquote | `> text` | ✅ |
348
+ | Unordered list | `- item` | ✅ |
349
+ | Ordered list | `1. item` | ✅ |
350
+ | Nested list | indented items | ✅ |
351
+ | Task list | `- [x] done` | ✅ checkbox rendering |
352
+ | Table | `\| col \|` | ✅ |
353
+ | Image | `![alt](url)` | ✅ placeholder on load |
354
+ | Horizontal rule | `---` | ✅ |
355
+ | CJK (Chinese/Japanese/Korean) | 春天到了 | ✅ via Pretext |
356
+ | Emoji | 🎉🚀 | ✅ |
357
+ | Arabic / RTL | بدأت الرحلة | 🔜 planned |
358
+ | Syntax highlight | ` ```ts ` | 🔜 planned |
359
+
360
+ ---
361
+
362
+ ## Themes
363
+
364
+ ### Built-in themes
365
+
366
+ ```typescript
367
+ import { darkTheme, lightTheme } from 'canvasdown'
368
+
369
+ render(markdown, canvas, { theme: 'dark' }) // #0d1117 background
370
+ render(markdown, canvas, { theme: 'light' }) // #ffffff background
371
+ ```
372
+
373
+ ### Custom theme
374
+
375
+ ```typescript
376
+ import type { Theme } from 'canvasdown'
377
+
378
+ const myTheme: Theme = {
379
+ background: '#1a1b26',
380
+ text: '#c0caf5',
381
+ mutedText: '#565f89',
382
+ link: { color: '#7aa2f7' },
383
+ heading: {
384
+ color: '#c0caf5',
385
+ sizes: [36, 28, 22, 18, 16, 14],
386
+ weights: ['700', '600', '600', '600', '500', '500'],
387
+ lineHeights: [48, 38, 32, 28, 26, 24],
388
+ },
389
+ code: {
390
+ background: '#16161e',
391
+ color: '#c0caf5',
392
+ borderColor: '#292e42',
393
+ fontFamily: '"JetBrains Mono", monospace',
394
+ fontSize: 13,
395
+ lineHeight: 22,
396
+ borderRadius: 6,
397
+ padding: 16,
398
+ },
399
+ inlineCode: {
400
+ background: '#1f2335',
401
+ color: '#7aa2f7',
402
+ fontFamily: '"JetBrains Mono", monospace',
403
+ borderRadius: 4,
404
+ paddingH: 5,
405
+ },
406
+ blockquote: { borderColor: '#292e42', textColor: '#565f89', borderWidth: 4, paddingLeft: 16 },
407
+ table: {
408
+ headerBackground: '#16161e',
409
+ headerColor: '#c0caf5',
410
+ borderColor: '#292e42',
411
+ cellPadding: 12,
412
+ },
413
+ hr: { color: '#292e42' },
414
+ list: { bulletColor: '#565f89', indentX: 20, bulletGap: 8, itemGap: 4 },
415
+ image: { borderRadius: 6, maxHeight: 400 },
416
+ fontFamily: '"Inter", system-ui, sans-serif',
417
+ fontSize: 16,
418
+ lineHeight: 28,
419
+ padding: 48,
420
+ blockGap: 20,
421
+ }
422
+
423
+ render(markdown, canvas, { theme: myTheme })
424
+ ```
425
+
426
+ ---
427
+
428
+ ## Use Cases
429
+
430
+ ### 1. AI Chat with Streaming
431
+
432
+ ```typescript
433
+ // Stream LLM response to canvas — no DOM reflow on every token
434
+ import { createStream } from 'canvasdown/stream'
435
+
436
+ const stream = createStream(canvas, { width: 680, theme: 'dark' })
437
+ for await (const chunk of llmStream) {
438
+ stream.append(chunk)
439
+ }
440
+ stream.flush()
441
+ ```
442
+
443
+ ### 2. OG Image Generator (Next.js API Route)
444
+
445
+ ```typescript
446
+ // app/api/og/route.ts
447
+ import { exportPNG } from 'canvasdown'
448
+ import { createCanvas } from '@napi-rs/canvas'
449
+
450
+ export async function GET(req: Request) {
451
+ const { searchParams } = new URL(req.url)
452
+ const title = searchParams.get('title') ?? 'Hello'
453
+
454
+ const png = await exportPNG(`# ${title}\n\nGenerated with canvasdown`, {
455
+ width: 1200,
456
+ devicePixelRatio: 2,
457
+ theme: 'dark',
458
+ canvasFactory: (w, h) => createCanvas(w, h),
459
+ })
460
+
461
+ const buffer = Buffer.from(png.split(',')[1]!, 'base64')
462
+ return new Response(buffer, {
463
+ headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=86400' },
464
+ })
465
+ }
466
+ ```
467
+
468
+ ### 3. Virtual List with Exact Heights
469
+
470
+ ```typescript
471
+ import { measure } from 'canvasdown'
472
+
473
+ // Pre-compute exact heights for ALL messages (no DOM, ~0.001ms each)
474
+ const heights = messages.map(msg =>
475
+ measure(msg.content, { width: 680, theme: 'dark' }).totalHeight
476
+ )
477
+
478
+ // Pass to virtual list — no estimates, no scroll jumps
479
+ <VirtualList itemCount={messages.length} itemHeight={i => heights[i]} ... />
480
+ ```
481
+
482
+ ### 4. Text Animation for Hero Sections
483
+
484
+ ```typescript
485
+ import { animateText } from 'canvasdown/advanced'
486
+
487
+ const anim = animateText(canvas, '# Welcome to the future', {
488
+ effect: 'wave',
489
+ stagger: 40,
490
+ duration: 800,
491
+ loop: true,
492
+ }, { width: 800, theme: 'dark' })
493
+
494
+ anim.start()
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Development
500
+
501
+ ```bash
502
+ git clone https://github.com/nhoxtvt/canvasdown
503
+ cd canvasdown
504
+ npm install
505
+ npm run build # compile TypeScript → dist/
506
+ npm run typecheck # type checking only
507
+ node serve.cjs # serve demo at http://localhost:3847/demo/index.html
508
+ ```
509
+
510
+ ### Project Structure
511
+
512
+ ```
513
+ src/
514
+ ├── index.ts # public API: render, measure, exportPNG, exportBlob
515
+ ├── parser.ts # markdown → RenderToken[] (via marked.js)
516
+ ├── layout.ts # Pretext measurement → DocumentLayout
517
+ ├── renderer.ts # DocumentLayout → Canvas 2D
518
+ ├── theme.ts # Theme type + darkTheme + lightTheme
519
+ ├── tokens.ts # TypeScript types for tokens
520
+ ├── canvas-provider.ts # CanvasFactory abstraction (browser/Node/Worker)
521
+ ├── stream.ts # streaming render API
522
+ ├── advanced.ts # shrinkwrap, fitText, overlay, path text, animation
523
+ ├── react.ts # React component factory
524
+ └── worker-bridge.ts # OffscreenCanvas + Web Worker bridge
525
+ demo/
526
+ └── index.html # live playground
527
+ ```
528
+
529
+ ---
530
+
531
+ ## Credits
532
+
533
+ canvasdown is built on top of [Pretext](https://github.com/chenglou/pretext) by Cheng Lou — a pure JS/TS library for multiline text measurement that avoids DOM reflow.
534
+
535
+ The original architecture concept comes from [text-layout](https://github.com/Automattic/text-layout) by Sebastian Markbage.
536
+
537
+ ---
538
+
539
+ ## License
540
+
541
+ MIT © 2026 canvasdown contributors
@@ -0,0 +1,65 @@
1
+ import type { DocumentLayout } from './layout.js';
2
+ import type { RenderOptions } from './index.js';
3
+ export type ShrinkwrapOptions = {
4
+ minWidth?: number;
5
+ maxWidth?: number;
6
+ padding?: number;
7
+ theme?: RenderOptions['theme'];
8
+ };
9
+ export type ShrinkwrapResult = {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ export declare function shrinkwrap(markdown: string, options?: ShrinkwrapOptions): ShrinkwrapResult;
14
+ export type FitTextOptions = {
15
+ container: {
16
+ width: number;
17
+ height: number;
18
+ };
19
+ fontSizeMin?: number;
20
+ fontSizeMax?: number;
21
+ theme?: RenderOptions['theme'];
22
+ };
23
+ export type FitTextResult = {
24
+ fontSize: number;
25
+ lineHeight: number;
26
+ height: number;
27
+ };
28
+ export declare function fitText(markdown: string, options: FitTextOptions): FitTextResult;
29
+ export type HighlightStyle = {
30
+ background?: string | undefined;
31
+ color?: string | undefined;
32
+ opacity?: number | undefined;
33
+ };
34
+ export type HighlightHandle = {
35
+ highlight(query: string, style: HighlightStyle): void;
36
+ clear(): void;
37
+ destroy(): void;
38
+ };
39
+ export declare function createOverlay(canvas: HTMLCanvasElement, layout: DocumentLayout, theme?: RenderOptions['theme']): HighlightHandle;
40
+ export type PathTextOptions = {
41
+ font?: string;
42
+ color?: string;
43
+ theme?: RenderOptions['theme'];
44
+ offset?: number;
45
+ };
46
+ export declare function renderTextOnPath(ctx: CanvasRenderingContext2D, text: string, svgPath: string, options?: PathTextOptions): void;
47
+ export type AnimationEffect = 'wave' | 'fadeIn' | 'typewriter' | 'bounce' | 'explode';
48
+ export type AnimationOptions = {
49
+ effect: AnimationEffect;
50
+ stagger?: number;
51
+ duration?: number;
52
+ loop?: boolean;
53
+ onComplete?: () => void;
54
+ };
55
+ export type AnimationHandle = {
56
+ start(): void;
57
+ stop(): void;
58
+ reset(): void;
59
+ };
60
+ export declare function animateText(canvas: HTMLCanvasElement, markdown: string, animOptions: AnimationOptions, renderOptions?: RenderOptions): AnimationHandle;
61
+ export type DiffRenderOptions = RenderOptions & {
62
+ canvas: HTMLCanvasElement;
63
+ };
64
+ export declare function updateRender(prevMarkdown: string, nextMarkdown: string, canvas: HTMLCanvasElement, options?: RenderOptions): void;
65
+ //# sourceMappingURL=advanced.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"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"}