@pep/term-deck 1.0.14 → 1.0.16

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 (37) hide show
  1. package/dist/bin/term-deck.d.ts +1 -0
  2. package/dist/bin/term-deck.js +1916 -0
  3. package/dist/bin/term-deck.js.map +1 -0
  4. package/dist/index.d.ts +670 -0
  5. package/dist/index.js +159 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +16 -13
  8. package/bin/term-deck.js +0 -14
  9. package/bin/term-deck.ts +0 -45
  10. package/src/cli/__tests__/errors.test.ts +0 -201
  11. package/src/cli/__tests__/help.test.ts +0 -157
  12. package/src/cli/__tests__/init.test.ts +0 -110
  13. package/src/cli/commands/export.ts +0 -33
  14. package/src/cli/commands/init.ts +0 -125
  15. package/src/cli/commands/present.ts +0 -29
  16. package/src/cli/errors.ts +0 -77
  17. package/src/core/__tests__/slide.test.ts +0 -1759
  18. package/src/core/__tests__/theme.test.ts +0 -1103
  19. package/src/core/slide.ts +0 -509
  20. package/src/core/theme.ts +0 -388
  21. package/src/export/__tests__/recorder.test.ts +0 -566
  22. package/src/export/recorder.ts +0 -639
  23. package/src/index.ts +0 -36
  24. package/src/presenter/__tests__/main.test.ts +0 -244
  25. package/src/presenter/main.ts +0 -658
  26. package/src/renderer/__tests__/screen-extended.test.ts +0 -801
  27. package/src/renderer/__tests__/screen.test.ts +0 -525
  28. package/src/renderer/screen.ts +0 -671
  29. package/src/schemas/__tests__/config.test.ts +0 -429
  30. package/src/schemas/__tests__/slide.test.ts +0 -349
  31. package/src/schemas/__tests__/theme.test.ts +0 -970
  32. package/src/schemas/__tests__/validation.test.ts +0 -256
  33. package/src/schemas/config.ts +0 -58
  34. package/src/schemas/slide.ts +0 -56
  35. package/src/schemas/theme.ts +0 -203
  36. package/src/schemas/validation.ts +0 -64
  37. package/src/themes/matrix/index.ts +0 -53
@@ -1,671 +0,0 @@
1
- import blessed from 'neo-blessed'
2
- import figlet from 'figlet'
3
- import gradient from 'gradient-string'
4
- import type { Theme } from '../schemas/theme.js'
5
- import type { Slide } from '../schemas/slide.js'
6
- import { normalizeBigText, processSlideContent } from '../core/slide.js'
7
-
8
- /**
9
- * Main renderer state.
10
- * Manages the blessed screen, matrix rain background, window stack, and theme.
11
- */
12
- export interface Renderer {
13
- /** The blessed screen instance */
14
- screen: blessed.Widgets.Screen
15
- /** Box element for matrix rain background */
16
- matrixBox: blessed.Widgets.BoxElement
17
- /** Stack of window elements (slides render on top of each other) */
18
- windowStack: blessed.Widgets.BoxElement[]
19
- /** Active theme for rendering */
20
- theme: Theme
21
- /** Array of matrix rain drops for animation */
22
- matrixDrops: MatrixDrop[]
23
- /** Interval timer for matrix rain animation (null if stopped) */
24
- matrixInterval: NodeJS.Timer | null
25
- }
26
-
27
- /**
28
- * Matrix rain drop.
29
- * Represents a single falling column of glyphs in the matrix background.
30
- */
31
- export interface MatrixDrop {
32
- /** Horizontal position (column) */
33
- x: number
34
- /** Vertical position (row, can be fractional for smooth animation) */
35
- y: number
36
- /** Fall speed (rows per animation frame) */
37
- speed: number
38
- /** Array of glyph characters forming the drop's trail */
39
- trail: string[]
40
- }
41
-
42
- /**
43
- * Window creation options.
44
- * Configuration for creating slide windows with stacking effect.
45
- */
46
- export interface WindowOptions {
47
- /** Window title displayed in the border */
48
- title: string
49
- /** Border color (defaults to theme-based cycling) */
50
- color?: string
51
- /** Window width (number for absolute, string for percentage) */
52
- width?: number | string
53
- /** Window height (number for absolute, string for percentage) */
54
- height?: number | string
55
- /** Top position (number for absolute, string for percentage) */
56
- top?: number | string
57
- /** Left position (number for absolute, string for percentage) */
58
- left?: number | string
59
- }
60
-
61
- /**
62
- * Rendered slide content.
63
- * Structured content ready for display in a window.
64
- */
65
- export interface RenderedContent {
66
- /** ASCII art big text (from figlet) */
67
- bigText?: string
68
- /** Main body content */
69
- body: string
70
- /** Mermaid diagram converted to ASCII */
71
- diagram?: string
72
- }
73
-
74
- /**
75
- * Create the main blessed screen.
76
- * Configures the screen with optimal settings for presentation:
77
- * - smartCSR: Enables smart cursor movement for efficient rendering
78
- * - fullUnicode: Enables full unicode support for glyphs
79
- * - altScreen: Uses alternate screen buffer (preserves terminal on exit)
80
- * - mouse: Disabled (keyboard-only navigation)
81
- *
82
- * @param title - Window title (defaults to 'term-deck')
83
- * @returns Configured blessed screen instance
84
- */
85
- export function createScreen(title: string = 'term-deck'): blessed.Widgets.Screen {
86
- const screen = blessed.screen({
87
- smartCSR: true,
88
- title,
89
- fullUnicode: true,
90
- mouse: false,
91
- altScreen: true,
92
- })
93
-
94
- return screen
95
- }
96
-
97
- /**
98
- * Generate a trail of random glyphs.
99
- * Randomly selects glyphs from the theme's glyph set to form a drop trail.
100
- *
101
- * @param glyphs - String of available glyphs to choose from
102
- * @param length - Number of characters in the trail
103
- * @returns Array of random glyph characters
104
- */
105
- function generateTrail(glyphs: string, length: number): string[] {
106
- return Array.from({ length }, () =>
107
- glyphs[Math.floor(Math.random() * glyphs.length)]
108
- )
109
- }
110
-
111
- /**
112
- * Render one frame of matrix rain.
113
- * Updates the matrix background with falling glyph trails.
114
- * This function is called repeatedly by the animation interval.
115
- *
116
- * @param renderer - The renderer instance to update
117
- */
118
- export function renderMatrixRain(renderer: Renderer): void {
119
- const { screen, matrixBox, matrixDrops, theme } = renderer
120
- const width = Math.max(20, (screen.width as number) || 80)
121
- const height = Math.max(10, (screen.height as number) || 24)
122
-
123
- // Create grid for positioning characters
124
- const grid: string[][] = Array.from({ length: height }, () =>
125
- Array(width).fill(' ')
126
- )
127
-
128
- // Update and render drops
129
- for (const drop of matrixDrops) {
130
- drop.y += drop.speed
131
-
132
- // Reset if off screen
133
- if (drop.y > height + drop.trail.length) {
134
- drop.y = -drop.trail.length
135
- drop.x = Math.floor(Math.random() * width)
136
- }
137
-
138
- // Draw trail
139
- for (let i = 0; i < drop.trail.length; i++) {
140
- const y = Math.floor(drop.y) - i
141
- if (y >= 0 && y < height && drop.x < width) {
142
- grid[y][drop.x] = drop.trail[i]
143
- }
144
- }
145
- }
146
-
147
- // Convert grid to string with colors
148
- let output = ''
149
- for (let y = 0; y < height; y++) {
150
- for (let x = 0; x < width; x++) {
151
- const char = grid[y][x]
152
- if (char !== ' ') {
153
- const brightness = Math.random() > 0.7 ? '{bold}' : ''
154
- output += `${brightness}{${theme.colors.primary}-fg}${char}{/}`
155
- } else {
156
- output += ' '
157
- }
158
- }
159
- if (y < height - 1) output += '\n'
160
- }
161
-
162
- matrixBox.setContent(output)
163
- }
164
-
165
- /**
166
- * Initialize matrix rain drops.
167
- * Creates the initial set of drops and starts the animation loop.
168
- * This is called automatically by createRenderer.
169
- *
170
- * @param renderer - The renderer instance to initialize
171
- */
172
- export function initMatrixRain(renderer: Renderer): void {
173
- const { screen, theme } = renderer
174
- const width = (screen.width as number) || 80
175
- const height = (screen.height as number) || 24
176
- const density = theme.animations.matrixDensity
177
-
178
- renderer.matrixDrops = []
179
-
180
- for (let i = 0; i < density; i++) {
181
- renderer.matrixDrops.push({
182
- x: Math.floor(Math.random() * width),
183
- y: Math.floor(Math.random() * height),
184
- speed: 0.3 + Math.random() * 0.7,
185
- trail: generateTrail(theme.glyphs, 5 + Math.floor(Math.random() * 10)),
186
- })
187
- }
188
-
189
- // Start animation loop
190
- renderer.matrixInterval = setInterval(() => {
191
- renderMatrixRain(renderer)
192
- renderer.screen.render()
193
- }, theme.animations.matrixInterval)
194
- }
195
-
196
- /**
197
- * Create the renderer with all components.
198
- * Initializes the blessed screen, matrix rain background box,
199
- * empty window stack, and starts the matrix rain animation.
200
- *
201
- * @param theme - Theme configuration for rendering
202
- * @returns Fully initialized Renderer instance
203
- */
204
- export function createRenderer(theme: Theme): Renderer {
205
- const screen = createScreen()
206
-
207
- // Create matrix background box covering full screen
208
- const matrixBox = blessed.box({
209
- top: 0,
210
- left: 0,
211
- width: '100%',
212
- height: '100%',
213
- tags: true,
214
- })
215
- screen.append(matrixBox)
216
-
217
- const renderer: Renderer = {
218
- screen,
219
- matrixBox,
220
- windowStack: [],
221
- theme,
222
- matrixDrops: [],
223
- matrixInterval: null,
224
- }
225
-
226
- // Initialize matrix rain
227
- initMatrixRain(renderer)
228
-
229
- return renderer
230
- }
231
-
232
- /**
233
- * Destroy renderer and cleanup resources.
234
- * Stops the matrix rain animation, destroys all windows in the stack,
235
- * and destroys the blessed screen to restore the terminal.
236
- *
237
- * @param renderer - The renderer instance to destroy
238
- */
239
- export function destroyRenderer(renderer: Renderer): void {
240
- // Clear matrix rain animation interval
241
- if (renderer.matrixInterval) {
242
- clearInterval(renderer.matrixInterval)
243
- renderer.matrixInterval = null
244
- }
245
-
246
- // Destroy all windows in the stack
247
- for (const win of renderer.windowStack) {
248
- win.destroy()
249
- }
250
- renderer.windowStack = []
251
-
252
- // Destroy the screen (restores terminal)
253
- renderer.screen.destroy()
254
- }
255
-
256
- /**
257
- * Get window border color based on index.
258
- * Cycles through theme colors and additional cyberpunk colors
259
- * to create a stacking effect with varied border colors.
260
- *
261
- * @param index - Window index in the stack
262
- * @param theme - Theme configuration
263
- * @returns Hex color string for the window border
264
- */
265
- export function getWindowColor(index: number, theme: Theme): string {
266
- const colors = [
267
- theme.colors.primary,
268
- theme.colors.accent,
269
- theme.colors.secondary ?? theme.colors.primary,
270
- '#ff0066', // pink
271
- '#9966ff', // purple
272
- '#ffcc00', // yellow
273
- ]
274
- return colors[index % colors.length]
275
- }
276
-
277
- /**
278
- * Create a slide window with stacking effect.
279
- * Creates a bordered box element with theme-based styling,
280
- * random position for stacking effect, and adds it to the window stack.
281
- *
282
- * @param renderer - The renderer instance
283
- * @param options - Window configuration options
284
- * @returns The created window box element
285
- */
286
- export function createWindow(
287
- renderer: Renderer,
288
- options: WindowOptions
289
- ): blessed.Widgets.BoxElement {
290
- const { screen, windowStack, theme } = renderer
291
- const windowIndex = windowStack.length
292
- const color = options.color ?? getWindowColor(windowIndex, theme)
293
-
294
- const screenWidth = (screen.width as number) || 120
295
- const screenHeight = (screen.height as number) || 40
296
-
297
- // Default dimensions: 75% width, 70% height
298
- const width = options.width ?? Math.floor(screenWidth * 0.75)
299
- const height = options.height ?? Math.floor(screenHeight * 0.7)
300
-
301
- // Random position within bounds (for stacking effect)
302
- const maxTop = Math.max(1, screenHeight - (height as number) - 2)
303
- const maxLeft = Math.max(1, screenWidth - (width as number) - 2)
304
- const top = options.top ?? Math.floor(Math.random() * maxTop)
305
- const left = options.left ?? Math.floor(Math.random() * maxLeft)
306
-
307
- const window = theme.window ?? { borderStyle: 'line', shadow: true }
308
- const padding = window.padding ?? { top: 1, bottom: 1, left: 2, right: 2 }
309
-
310
- const box = blessed.box({
311
- top,
312
- left,
313
- width,
314
- height,
315
- border: {
316
- type: window.borderStyle === 'none' ? undefined : 'line',
317
- },
318
- label: ` ${options.title} `,
319
- style: {
320
- fg: theme.colors.text,
321
- bg: theme.colors.background,
322
- border: { fg: color },
323
- label: { fg: color, bold: true },
324
- },
325
- padding,
326
- tags: true,
327
- shadow: window.shadow,
328
- })
329
-
330
- screen.append(box)
331
- windowStack.push(box)
332
-
333
- return box
334
- }
335
-
336
- /**
337
- * Clear all windows from stack.
338
- * Destroys all window elements in the stack and resets the stack to empty.
339
- * This is typically called when transitioning between slides.
340
- *
341
- * @param renderer - The renderer instance containing the window stack
342
- */
343
- export function clearWindows(renderer: Renderer): void {
344
- for (const window of renderer.windowStack) {
345
- window.destroy()
346
- }
347
- renderer.windowStack = []
348
- }
349
-
350
- /**
351
- * Generate ASCII art text with gradient.
352
- * Uses figlet to convert text to ASCII art and applies a gradient color effect.
353
- * This function is asynchronous because figlet uses a callback-based API.
354
- *
355
- * @param text - The text to convert to ASCII art
356
- * @param gradientColors - Array of hex colors for the gradient effect
357
- * @param font - Figlet font to use (defaults to 'Standard')
358
- * @returns Promise resolving to the gradient-colored ASCII art text
359
- */
360
- export async function generateBigText(
361
- text: string,
362
- gradientColors: string[],
363
- font: string = 'Standard'
364
- ): Promise<string> {
365
- return new Promise((resolve, reject) => {
366
- figlet.text(text, { font }, (err, result) => {
367
- if (err || !result) {
368
- reject(err ?? new Error('Failed to generate figlet text'))
369
- return
370
- }
371
-
372
- // Apply gradient
373
- const gradientFn = gradient(gradientColors)
374
- resolve(gradientFn(result))
375
- })
376
- })
377
- }
378
-
379
- /**
380
- * Generate multi-line big text (for arrays like ['SPEC', 'MACHINE']).
381
- * Creates ASCII art for each line separately and joins them with newlines.
382
- * Each line gets the same gradient applied independently.
383
- *
384
- * @param lines - Array of text strings to convert to ASCII art
385
- * @param gradientColors - Array of hex colors for the gradient effect
386
- * @param font - Figlet font to use (defaults to 'Standard')
387
- * @returns Promise resolving to the combined gradient-colored ASCII art
388
- */
389
- export async function generateMultiLineBigText(
390
- lines: string[],
391
- gradientColors: string[],
392
- font: string = 'Standard'
393
- ): Promise<string> {
394
- const results = await Promise.all(
395
- lines.map((line) => generateBigText(line, gradientColors, font))
396
- )
397
- return results.join('\n')
398
- }
399
-
400
- /**
401
- * Characters used for glitch effect (avoiding box drawing).
402
- * These characters create a cyberpunk glitch aesthetic when scrambling text.
403
- * Includes: block characters, shapes, math symbols, Greek letters, and katakana.
404
- */
405
- const GLITCH_CHARS =
406
- '█▓▒░▀▄▌▐■□▪▫●○◊◘◙♦♣♠♥★☆⌂ⁿ²³ÆØ∞≈≠±×÷αβγδεζηθλμπσφωΔΣΩアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン'
407
-
408
- /**
409
- * Characters to never glitch.
410
- * Protects structural characters like spaces, punctuation, box drawing,
411
- * and arrows to maintain readability and layout integrity during glitch effects.
412
- */
413
- const PROTECTED_CHARS = new Set([
414
- ' ', '\t', '\n', '{', '}', '-', '/', '#', '[', ']', '(', ')', ':', ';',
415
- ',', '.', '!', '?', "'", '"', '`', '_', '|', '\\', '<', '>', '=', '+',
416
- '*', '&', '^', '%', '$', '@', '~',
417
- // Box drawing
418
- '┌', '┐', '└', '┘', '│', '─', '├', '┤', '┬', '┴', '┼', '═', '║',
419
- '╔', '╗', '╚', '╝', '╠', '╣', '╦', '╩', '╬', '╭', '╮', '╯', '╰',
420
- // Arrows
421
- '→', '←', '↑', '↓', '▶', '◀', '▲', '▼', '►', '◄',
422
- ])
423
-
424
- /**
425
- * Sleep helper for async animations.
426
- * Returns a promise that resolves after the specified delay.
427
- *
428
- * @param ms - Delay in milliseconds
429
- * @returns Promise that resolves after the delay
430
- */
431
- function sleep(ms: number): Promise<void> {
432
- return new Promise((resolve) => setTimeout(resolve, ms))
433
- }
434
-
435
- /**
436
- * Glitch-reveal a single line of text.
437
- * Animates the transition from scrambled characters to the final text.
438
- * The scramble ratio decreases with each iteration, gradually revealing the text.
439
- * Protected characters (spaces, punctuation, box drawing) are never scrambled.
440
- *
441
- * @param box - The blessed box element to render into
442
- * @param screen - The blessed screen for rendering
443
- * @param currentLines - Array of already-revealed lines
444
- * @param newLine - The new line to glitch-reveal
445
- * @param iterations - Number of glitch iterations (default: 5)
446
- */
447
- export async function glitchLine(
448
- box: blessed.Widgets.BoxElement,
449
- screen: blessed.Widgets.Screen,
450
- currentLines: string[],
451
- newLine: string,
452
- iterations: number = 5
453
- ): Promise<void> {
454
- for (let i = iterations; i >= 0; i--) {
455
- const scrambleRatio = i / iterations
456
- let scrambledLine = ''
457
-
458
- for (const char of newLine) {
459
- if (PROTECTED_CHARS.has(char)) {
460
- scrambledLine += char
461
- } else if (Math.random() < scrambleRatio) {
462
- scrambledLine += GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)]
463
- } else {
464
- scrambledLine += char
465
- }
466
- }
467
-
468
- box.setContent([...currentLines, scrambledLine].join('\n'))
469
- screen.render()
470
- await sleep(20)
471
- }
472
- }
473
-
474
- /**
475
- * Reveal content line by line with glitch effect.
476
- * Animates the transition of multi-line content by revealing each line
477
- * sequentially with a glitch effect. Uses theme-configured line delay
478
- * and glitch iteration count for consistent animation timing.
479
- *
480
- * @param box - The blessed box element to render into
481
- * @param screen - The blessed screen for rendering
482
- * @param content - The complete content string (with newlines)
483
- * @param theme - Theme configuration for animation timing
484
- */
485
- export async function lineByLineReveal(
486
- box: blessed.Widgets.BoxElement,
487
- screen: blessed.Widgets.Screen,
488
- content: string,
489
- theme: Theme
490
- ): Promise<void> {
491
- const lines = content.split('\n')
492
- const revealedLines: string[] = []
493
- const lineDelay = theme.animations.lineDelay
494
- const glitchIterations = theme.animations.glitchIterations
495
-
496
- for (const line of lines) {
497
- await glitchLine(box, screen, revealedLines, line, glitchIterations)
498
- revealedLines.push(line)
499
- box.setContent(revealedLines.join('\n'))
500
- screen.render()
501
-
502
- // Delay between lines (skip for empty lines)
503
- if (line.trim()) {
504
- await sleep(lineDelay)
505
- }
506
- }
507
- }
508
-
509
- /**
510
- * Transition type for slide animations.
511
- * Defines the available transition effects for revealing slide content.
512
- */
513
- export type TransitionType = 'glitch' | 'fade' | 'instant' | 'typewriter'
514
-
515
- /**
516
- * Fade-in reveal (character by character, all at once).
517
- * Gradually reveals characters randomly across the entire content
518
- * to create a fade-in effect. Uses multiple steps with increasing
519
- * reveal probability for a smooth animation.
520
- *
521
- * @param box - The blessed box element to render into
522
- * @param screen - The blessed screen for rendering
523
- * @param content - The complete content string to reveal
524
- * @param theme - Theme configuration for animation timing
525
- */
526
- async function fadeInReveal(
527
- box: blessed.Widgets.BoxElement,
528
- screen: blessed.Widgets.Screen,
529
- content: string,
530
- theme: Theme
531
- ): Promise<void> {
532
- const steps = 10
533
- const delay = (theme.animations.lineDelay * 2) / steps
534
-
535
- for (let step = 0; step < steps; step++) {
536
- const revealRatio = step / steps
537
- let revealed = ''
538
-
539
- for (const char of content) {
540
- if (char === '\n' || PROTECTED_CHARS.has(char) || Math.random() < revealRatio) {
541
- revealed += char
542
- } else {
543
- revealed += ' '
544
- }
545
- }
546
-
547
- box.setContent(revealed)
548
- screen.render()
549
- await sleep(delay)
550
- }
551
-
552
- box.setContent(content)
553
- screen.render()
554
- }
555
-
556
- /**
557
- * Typewriter reveal (character by character, sequentially).
558
- * Reveals content character by character in order, like a typewriter.
559
- * Skips delay for spaces and newlines to maintain smooth flow.
560
- *
561
- * @param box - The blessed box element to render into
562
- * @param screen - The blessed screen for rendering
563
- * @param content - The complete content string to reveal
564
- * @param theme - Theme configuration for animation timing
565
- */
566
- async function typewriterReveal(
567
- box: blessed.Widgets.BoxElement,
568
- screen: blessed.Widgets.Screen,
569
- content: string,
570
- theme: Theme
571
- ): Promise<void> {
572
- const charDelay = theme.animations.lineDelay / 5
573
- let revealed = ''
574
-
575
- for (const char of content) {
576
- revealed += char
577
- box.setContent(revealed)
578
- screen.render()
579
-
580
- if (char !== ' ' && char !== '\n') {
581
- await sleep(charDelay)
582
- }
583
- }
584
- }
585
-
586
- /**
587
- * Apply transition effect to reveal content.
588
- * Dispatcher function that selects and applies the appropriate
589
- * transition animation based on the specified transition type.
590
- *
591
- * @param box - The blessed box element to render into
592
- * @param screen - The blessed screen for rendering
593
- * @param content - The complete content string to reveal
594
- * @param transition - The type of transition effect to apply
595
- * @param theme - Theme configuration for animation timing
596
- */
597
- export async function applyTransition(
598
- box: blessed.Widgets.BoxElement,
599
- screen: blessed.Widgets.Screen,
600
- content: string,
601
- transition: TransitionType,
602
- theme: Theme
603
- ): Promise<void> {
604
- switch (transition) {
605
- case 'glitch':
606
- await lineByLineReveal(box, screen, content, theme)
607
- break
608
-
609
- case 'fade':
610
- await fadeInReveal(box, screen, content, theme)
611
- break
612
-
613
- case 'instant':
614
- box.setContent(content)
615
- screen.render()
616
- break
617
-
618
- case 'typewriter':
619
- await typewriterReveal(box, screen, content, theme)
620
- break
621
-
622
- default:
623
- box.setContent(content)
624
- screen.render()
625
- }
626
- }
627
-
628
- /**
629
- * Render a slide to a window.
630
- * Creates a window, generates bigText if present, processes the body content,
631
- * and applies the specified transition effect to reveal the slide.
632
- *
633
- * @param renderer - The renderer instance
634
- * @param slide - The slide to render
635
- * @returns The created window box element containing the rendered slide
636
- */
637
- export async function renderSlide(
638
- renderer: Renderer,
639
- slide: Slide
640
- ): Promise<blessed.Widgets.BoxElement> {
641
- const { theme } = renderer
642
- const { frontmatter, body } = slide
643
-
644
- // Create window
645
- const window = createWindow(renderer, {
646
- title: frontmatter.title,
647
- })
648
-
649
- // Build content
650
- let content = ''
651
-
652
- // Big text (figlet)
653
- const bigTextLines = normalizeBigText(frontmatter.bigText)
654
- if (bigTextLines.length > 0) {
655
- const gradientName = frontmatter.gradient ?? 'fire'
656
- const gradientColors = theme.gradients[gradientName] ?? theme.gradients.fire
657
-
658
- const bigText = await generateMultiLineBigText(bigTextLines, gradientColors)
659
- content += bigText + '\n\n'
660
- }
661
-
662
- // Process body content (color tokens, mermaid)
663
- const processedBody = await processSlideContent(body, theme)
664
- content += processedBody
665
-
666
- // Apply transition
667
- const transition = frontmatter.transition ?? 'glitch'
668
- await applyTransition(window, renderer.screen, content, transition, theme)
669
-
670
- return window
671
- }