@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,801 +0,0 @@
1
- import { describe, it, expect, mock } from 'bun:test'
2
- import {
3
- getWindowColor,
4
- createWindow,
5
- clearWindows,
6
- renderMatrixRain,
7
- initMatrixRain,
8
- applyTransition,
9
- renderSlide,
10
- createRenderer,
11
- destroyRenderer,
12
- } from '../screen'
13
- import { DEFAULT_THEME } from '../../schemas/theme'
14
- import type { Slide } from '../../schemas/slide'
15
-
16
- describe('getWindowColor', () => {
17
- const mockTheme = {
18
- ...DEFAULT_THEME,
19
- colors: {
20
- ...DEFAULT_THEME.colors,
21
- primary: '#00cc66',
22
- accent: '#ff6600',
23
- secondary: '#0066ff',
24
- },
25
- }
26
-
27
- it('returns primary color for index 0', () => {
28
- const color = getWindowColor(0, mockTheme)
29
- expect(color).toBe('#00cc66')
30
- })
31
-
32
- it('returns accent color for index 1', () => {
33
- const color = getWindowColor(1, mockTheme)
34
- expect(color).toBe('#ff6600')
35
- })
36
-
37
- it('returns secondary color for index 2', () => {
38
- const color = getWindowColor(2, mockTheme)
39
- expect(color).toBe('#0066ff')
40
- })
41
-
42
- it('cycles through extended color palette', () => {
43
- const color3 = getWindowColor(3, mockTheme)
44
- const color4 = getWindowColor(4, mockTheme)
45
- const color5 = getWindowColor(5, mockTheme)
46
-
47
- expect(color3).toBe('#ff0066') // pink
48
- expect(color4).toBe('#9966ff') // purple
49
- expect(color5).toBe('#ffcc00') // yellow
50
- })
51
-
52
- it('wraps around after all colors used', () => {
53
- // Index 6 should wrap back to index 0
54
- const color = getWindowColor(6, mockTheme)
55
- expect(color).toBe('#00cc66')
56
- })
57
-
58
- it('handles large indices correctly', () => {
59
- const color = getWindowColor(12, mockTheme) // 12 % 6 = 0
60
- expect(color).toBe('#00cc66')
61
- })
62
-
63
- it('uses primary color as fallback when secondary is undefined', () => {
64
- const themeNoSecondary = {
65
- ...mockTheme,
66
- colors: {
67
- ...mockTheme.colors,
68
- secondary: undefined,
69
- },
70
- }
71
-
72
- const color = getWindowColor(2, themeNoSecondary as any)
73
- expect(color).toBe('#00cc66') // falls back to primary
74
- })
75
- })
76
-
77
- describe('createWindow', () => {
78
- it('creates a window with title', () => {
79
- const renderer = createRenderer(DEFAULT_THEME)
80
- const window = createWindow(renderer, { title: 'Test Window' })
81
-
82
- expect(window).toBeDefined()
83
- expect(renderer.windowStack.length).toBe(1)
84
- expect(renderer.windowStack[0]).toBe(window)
85
-
86
- destroyRenderer(renderer)
87
- })
88
-
89
- it('adds window to stack', () => {
90
- const renderer = createRenderer(DEFAULT_THEME)
91
-
92
- expect(renderer.windowStack.length).toBe(0)
93
-
94
- const window1 = createWindow(renderer, { title: 'Window 1' })
95
- expect(renderer.windowStack.length).toBe(1)
96
-
97
- const window2 = createWindow(renderer, { title: 'Window 2' })
98
- expect(renderer.windowStack.length).toBe(2)
99
-
100
- expect(renderer.windowStack[0]).toBe(window1)
101
- expect(renderer.windowStack[1]).toBe(window2)
102
-
103
- destroyRenderer(renderer)
104
- })
105
-
106
- it('attaches window to screen', () => {
107
- const renderer = createRenderer(DEFAULT_THEME)
108
- const window = createWindow(renderer, { title: 'Attached Window' })
109
-
110
- expect(window.parent).toBe(renderer.screen)
111
-
112
- destroyRenderer(renderer)
113
- })
114
-
115
- it('uses custom color when provided', () => {
116
- const renderer = createRenderer(DEFAULT_THEME)
117
- const customColor = '#ff00ff'
118
-
119
- const window = createWindow(renderer, {
120
- title: 'Custom Color',
121
- color: customColor,
122
- })
123
-
124
- expect(window).toBeDefined()
125
-
126
- destroyRenderer(renderer)
127
- })
128
-
129
- it('uses theme-based color cycling when color not provided', () => {
130
- const renderer = createRenderer(DEFAULT_THEME)
131
-
132
- // Create multiple windows without specifying color
133
- createWindow(renderer, { title: 'Window 1' })
134
- createWindow(renderer, { title: 'Window 2' })
135
- createWindow(renderer, { title: 'Window 3' })
136
-
137
- expect(renderer.windowStack.length).toBe(3)
138
-
139
- destroyRenderer(renderer)
140
- })
141
-
142
- it('accepts custom dimensions', () => {
143
- const renderer = createRenderer(DEFAULT_THEME)
144
-
145
- const window = createWindow(renderer, {
146
- title: 'Custom Size',
147
- width: 50,
148
- height: 20,
149
- })
150
-
151
- expect(window).toBeDefined()
152
-
153
- destroyRenderer(renderer)
154
- })
155
-
156
- it('accepts custom position', () => {
157
- const renderer = createRenderer(DEFAULT_THEME)
158
-
159
- const window = createWindow(renderer, {
160
- title: 'Custom Position',
161
- top: 5,
162
- left: 10,
163
- })
164
-
165
- expect(window).toBeDefined()
166
-
167
- destroyRenderer(renderer)
168
- })
169
-
170
- it('uses default dimensions when not provided', () => {
171
- const renderer = createRenderer(DEFAULT_THEME)
172
-
173
- const window = createWindow(renderer, {
174
- title: 'Default Dimensions',
175
- })
176
-
177
- expect(window).toBeDefined()
178
-
179
- destroyRenderer(renderer)
180
- })
181
-
182
- it('creates multiple stacked windows', () => {
183
- const renderer = createRenderer(DEFAULT_THEME)
184
-
185
- for (let i = 0; i < 5; i++) {
186
- createWindow(renderer, { title: `Window ${i}` })
187
- }
188
-
189
- expect(renderer.windowStack.length).toBe(5)
190
-
191
- destroyRenderer(renderer)
192
- })
193
- })
194
-
195
- describe('clearWindows', () => {
196
- it('destroys all windows in stack', () => {
197
- const renderer = createRenderer(DEFAULT_THEME)
198
-
199
- const mockWindow1 = { destroy: mock(() => {}) }
200
- const mockWindow2 = { destroy: mock(() => {}) }
201
- const mockWindow3 = { destroy: mock(() => {}) }
202
-
203
- renderer.windowStack = [mockWindow1 as any, mockWindow2 as any, mockWindow3 as any]
204
-
205
- clearWindows(renderer)
206
-
207
- expect(mockWindow1.destroy).toHaveBeenCalledTimes(1)
208
- expect(mockWindow2.destroy).toHaveBeenCalledTimes(1)
209
- expect(mockWindow3.destroy).toHaveBeenCalledTimes(1)
210
-
211
- destroyRenderer(renderer)
212
- })
213
-
214
- it('empties the window stack', () => {
215
- const renderer = createRenderer(DEFAULT_THEME)
216
-
217
- createWindow(renderer, { title: 'Window 1' })
218
- createWindow(renderer, { title: 'Window 2' })
219
- createWindow(renderer, { title: 'Window 3' })
220
-
221
- expect(renderer.windowStack.length).toBe(3)
222
-
223
- clearWindows(renderer)
224
-
225
- expect(renderer.windowStack.length).toBe(0)
226
-
227
- destroyRenderer(renderer)
228
- })
229
-
230
- it('handles empty window stack', () => {
231
- const renderer = createRenderer(DEFAULT_THEME)
232
-
233
- expect(renderer.windowStack.length).toBe(0)
234
-
235
- // Should not throw
236
- expect(() => clearWindows(renderer)).not.toThrow()
237
-
238
- destroyRenderer(renderer)
239
- })
240
-
241
- it('can be called multiple times safely', () => {
242
- const renderer = createRenderer(DEFAULT_THEME)
243
-
244
- createWindow(renderer, { title: 'Window' })
245
- clearWindows(renderer)
246
-
247
- expect(renderer.windowStack.length).toBe(0)
248
-
249
- // Second call should not throw
250
- expect(() => clearWindows(renderer)).not.toThrow()
251
-
252
- destroyRenderer(renderer)
253
- })
254
- })
255
-
256
- describe('renderMatrixRain', () => {
257
- it('updates matrix box content', () => {
258
- const renderer = createRenderer(DEFAULT_THEME)
259
- const initialContent = renderer.matrixBox.getContent()
260
-
261
- renderMatrixRain(renderer)
262
-
263
- const updatedContent = renderer.matrixBox.getContent()
264
-
265
- // Content should be updated (may be empty or filled depending on drops)
266
- expect(typeof updatedContent).toBe('string')
267
-
268
- destroyRenderer(renderer)
269
- })
270
-
271
- it('updates drop positions', () => {
272
- const renderer = createRenderer(DEFAULT_THEME)
273
-
274
- // Get initial positions
275
- const initialPositions = renderer.matrixDrops.map((d) => ({ x: d.x, y: d.y }))
276
-
277
- // Render several frames
278
- for (let i = 0; i < 5; i++) {
279
- renderMatrixRain(renderer)
280
- }
281
-
282
- // At least some drops should have moved
283
- const movedDrops = renderer.matrixDrops.filter(
284
- (d, i) => d.y !== initialPositions[i].y
285
- )
286
-
287
- expect(movedDrops.length).toBeGreaterThan(0)
288
-
289
- destroyRenderer(renderer)
290
- })
291
-
292
- it('resets drops when they go off screen', () => {
293
- const renderer = createRenderer(DEFAULT_THEME)
294
- const height = (renderer.screen.height as number) || 24
295
-
296
- // Manually set a drop to be well off screen (beyond trail length)
297
- const trailLength = renderer.matrixDrops[0].trail.length
298
- renderer.matrixDrops[0].y = height + trailLength + 10
299
-
300
- renderMatrixRain(renderer)
301
-
302
- // Drop should be reset to top (negative y value)
303
- expect(renderer.matrixDrops[0].y).toBeLessThan(0)
304
-
305
- destroyRenderer(renderer)
306
- })
307
-
308
- it('uses theme colors', () => {
309
- const renderer = createRenderer(DEFAULT_THEME)
310
-
311
- renderMatrixRain(renderer)
312
-
313
- const content = renderer.matrixBox.getContent()
314
-
315
- // If there are drops, content should contain color tags
316
- if (renderer.matrixDrops.length > 0 && content.trim() !== '') {
317
- // Content may contain blessed color tags like {#00cc66-fg}
318
- expect(typeof content).toBe('string')
319
- }
320
-
321
- destroyRenderer(renderer)
322
- })
323
-
324
- it('handles screen resize gracefully', () => {
325
- const renderer = createRenderer(DEFAULT_THEME)
326
-
327
- // Should not throw even with different screen dimensions
328
- expect(() => renderMatrixRain(renderer)).not.toThrow()
329
-
330
- destroyRenderer(renderer)
331
- })
332
- })
333
-
334
- describe('initMatrixRain', () => {
335
- it('initializes matrix drops based on density', () => {
336
- const customTheme = {
337
- ...DEFAULT_THEME,
338
- animations: {
339
- ...DEFAULT_THEME.animations,
340
- matrixDensity: 10,
341
- },
342
- }
343
-
344
- const renderer = createRenderer(customTheme)
345
-
346
- expect(renderer.matrixDrops.length).toBe(10)
347
-
348
- destroyRenderer(renderer)
349
- })
350
-
351
- it('starts animation interval', () => {
352
- const renderer = createRenderer(DEFAULT_THEME)
353
-
354
- expect(renderer.matrixInterval).not.toBeNull()
355
-
356
- destroyRenderer(renderer)
357
- })
358
-
359
- it('creates drops with random positions', () => {
360
- const renderer = createRenderer(DEFAULT_THEME)
361
-
362
- const xPositions = renderer.matrixDrops.map((d) => d.x)
363
- const yPositions = renderer.matrixDrops.map((d) => d.y)
364
-
365
- // With default density, positions should vary (not all the same)
366
- // Note: With small screen dimensions, it's possible (but unlikely) for some
367
- // coordinates to overlap, so we just check that drops are created
368
- const uniqueX = new Set(xPositions)
369
- const uniqueY = new Set(yPositions)
370
-
371
- // Should have at least one position defined
372
- expect(xPositions.length).toBeGreaterThan(0)
373
- expect(yPositions.length).toBeGreaterThan(0)
374
-
375
- // With default density > 1, we expect some variation
376
- // (though with very small terminals, x positions might coincide)
377
- if (renderer.matrixDrops.length > 1) {
378
- // At least the y positions should vary since they're random over screen height
379
- expect(uniqueY.size).toBeGreaterThanOrEqual(1)
380
- }
381
-
382
- destroyRenderer(renderer)
383
- })
384
-
385
- it('creates drops with random speeds', () => {
386
- const renderer = createRenderer(DEFAULT_THEME)
387
-
388
- const speeds = renderer.matrixDrops.map((d) => d.speed)
389
-
390
- // All speeds should be within range [0.3, 1.0]
391
- for (const speed of speeds) {
392
- expect(speed).toBeGreaterThanOrEqual(0.3)
393
- expect(speed).toBeLessThanOrEqual(1.0)
394
- }
395
-
396
- destroyRenderer(renderer)
397
- })
398
-
399
- it('creates drops with trails', () => {
400
- const renderer = createRenderer(DEFAULT_THEME)
401
-
402
- for (const drop of renderer.matrixDrops) {
403
- expect(Array.isArray(drop.trail)).toBe(true)
404
- expect(drop.trail.length).toBeGreaterThanOrEqual(5)
405
- expect(drop.trail.length).toBeLessThanOrEqual(15)
406
- }
407
-
408
- destroyRenderer(renderer)
409
- })
410
-
411
- it('uses theme glyphs for trails', () => {
412
- const customTheme = {
413
- ...DEFAULT_THEME,
414
- glyphs: 'ABC123',
415
- }
416
-
417
- const renderer = createRenderer(customTheme)
418
-
419
- for (const drop of renderer.matrixDrops) {
420
- for (const char of drop.trail) {
421
- expect(customTheme.glyphs).toContain(char)
422
- }
423
- }
424
-
425
- destroyRenderer(renderer)
426
- })
427
-
428
- it('respects theme matrix interval', () => {
429
- const customTheme = {
430
- ...DEFAULT_THEME,
431
- animations: {
432
- ...DEFAULT_THEME.animations,
433
- matrixInterval: 50,
434
- },
435
- }
436
-
437
- const renderer = createRenderer(customTheme)
438
-
439
- expect(renderer.matrixInterval).not.toBeNull()
440
-
441
- destroyRenderer(renderer)
442
- })
443
- })
444
-
445
- describe('applyTransition', () => {
446
- it('applies instant transition', async () => {
447
- const renderer = createRenderer(DEFAULT_THEME)
448
- const box = {
449
- setContent: mock(() => {}),
450
- } as any
451
- const screen = renderer.screen
452
- const content = 'Test content'
453
-
454
- await applyTransition(box, screen, content, 'instant', DEFAULT_THEME)
455
-
456
- expect(box.setContent).toHaveBeenCalledWith(content)
457
-
458
- destroyRenderer(renderer)
459
- })
460
-
461
- it('applies glitch transition', async () => {
462
- const renderer = createRenderer(DEFAULT_THEME)
463
- const box = {
464
- setContent: mock(() => {}),
465
- } as any
466
- const screen = renderer.screen
467
- const content = 'Test glitch'
468
-
469
- await applyTransition(box, screen, content, 'glitch', DEFAULT_THEME)
470
-
471
- // Should have called setContent multiple times during animation
472
- expect(box.setContent).toHaveBeenCalled()
473
-
474
- destroyRenderer(renderer)
475
- })
476
-
477
- it('applies fade transition', async () => {
478
- const renderer = createRenderer(DEFAULT_THEME)
479
- const box = {
480
- setContent: mock(() => {}),
481
- } as any
482
- const screen = renderer.screen
483
- const content = 'Test fade'
484
-
485
- await applyTransition(box, screen, content, 'fade', DEFAULT_THEME)
486
-
487
- // Should have called setContent multiple times
488
- expect(box.setContent).toHaveBeenCalled()
489
-
490
- destroyRenderer(renderer)
491
- })
492
-
493
- it('applies typewriter transition', async () => {
494
- const renderer = createRenderer(DEFAULT_THEME)
495
- const box = {
496
- setContent: mock(() => {}),
497
- } as any
498
- const screen = renderer.screen
499
- const content = 'Test typewriter'
500
-
501
- await applyTransition(box, screen, content, 'typewriter', DEFAULT_THEME)
502
-
503
- // Should have called setContent multiple times
504
- expect(box.setContent).toHaveBeenCalled()
505
-
506
- destroyRenderer(renderer)
507
- })
508
-
509
- it('defaults to instant for unknown transition type', async () => {
510
- const renderer = createRenderer(DEFAULT_THEME)
511
- const box = {
512
- setContent: mock(() => {}),
513
- } as any
514
- const screen = renderer.screen
515
- const content = 'Test default'
516
-
517
- await applyTransition(box, screen, content, 'unknown' as any, DEFAULT_THEME)
518
-
519
- expect(box.setContent).toHaveBeenCalledWith(content)
520
-
521
- destroyRenderer(renderer)
522
- })
523
-
524
- it('handles empty content', async () => {
525
- const renderer = createRenderer(DEFAULT_THEME)
526
- const box = {
527
- setContent: mock(() => {}),
528
- } as any
529
- const screen = renderer.screen
530
-
531
- await applyTransition(box, screen, '', 'instant', DEFAULT_THEME)
532
-
533
- expect(box.setContent).toHaveBeenCalledWith('')
534
-
535
- destroyRenderer(renderer)
536
- })
537
-
538
- it('handles multi-line content', async () => {
539
- const renderer = createRenderer(DEFAULT_THEME)
540
- const box = {
541
- setContent: mock(() => {}),
542
- } as any
543
- const screen = renderer.screen
544
- const content = 'Line 1\nLine 2\nLine 3'
545
-
546
- await applyTransition(box, screen, content, 'instant', DEFAULT_THEME)
547
-
548
- expect(box.setContent).toHaveBeenCalledWith(content)
549
-
550
- destroyRenderer(renderer)
551
- })
552
- })
553
-
554
- describe('renderSlide', () => {
555
- it('creates a window for the slide', async () => {
556
- const renderer = createRenderer(DEFAULT_THEME)
557
-
558
- const slide: Slide = {
559
- frontmatter: {
560
- title: 'Test Slide',
561
- },
562
- body: 'Test content',
563
- notes: '',
564
- sourcePath: 'test.md',
565
- index: 0,
566
- }
567
-
568
- const window = await renderSlide(renderer, slide)
569
-
570
- expect(window).toBeDefined()
571
- expect(renderer.windowStack.length).toBe(1)
572
-
573
- destroyRenderer(renderer)
574
- })
575
-
576
- it('renders slide with title', async () => {
577
- const renderer = createRenderer(DEFAULT_THEME)
578
-
579
- const slide: Slide = {
580
- frontmatter: {
581
- title: 'My Slide Title',
582
- },
583
- body: 'Slide body content',
584
- notes: '',
585
- sourcePath: 'slide.md',
586
- index: 0,
587
- }
588
-
589
- const window = await renderSlide(renderer, slide)
590
-
591
- expect(window).toBeDefined()
592
-
593
- destroyRenderer(renderer)
594
- })
595
-
596
- it('renders slide with body content', async () => {
597
- const renderer = createRenderer(DEFAULT_THEME)
598
-
599
- const slide: Slide = {
600
- frontmatter: {
601
- title: 'Content Slide',
602
- },
603
- body: 'This is the slide content',
604
- notes: '',
605
- sourcePath: 'content.md',
606
- index: 0,
607
- }
608
-
609
- const window = await renderSlide(renderer, slide)
610
-
611
- expect(window).toBeDefined()
612
-
613
- destroyRenderer(renderer)
614
- })
615
-
616
- it('renders slide with bigText', async () => {
617
- const renderer = createRenderer(DEFAULT_THEME)
618
-
619
- const slide: Slide = {
620
- frontmatter: {
621
- title: 'Big Text Slide',
622
- bigText: 'HELLO',
623
- },
624
- body: 'Additional content',
625
- notes: '',
626
- sourcePath: 'big.md',
627
- index: 0,
628
- }
629
-
630
- const window = await renderSlide(renderer, slide)
631
-
632
- expect(window).toBeDefined()
633
-
634
- destroyRenderer(renderer)
635
- })
636
-
637
- it('renders slide with multi-line bigText', async () => {
638
- const renderer = createRenderer(DEFAULT_THEME)
639
-
640
- const slide: Slide = {
641
- frontmatter: {
642
- title: 'Multi-line Big Text',
643
- bigText: ['SPEC', 'MACHINE'],
644
- },
645
- body: 'Body text',
646
- notes: '',
647
- sourcePath: 'multi.md',
648
- index: 0,
649
- }
650
-
651
- const window = await renderSlide(renderer, slide)
652
-
653
- expect(window).toBeDefined()
654
-
655
- destroyRenderer(renderer)
656
- })
657
-
658
- it('uses gradient from frontmatter', async () => {
659
- const renderer = createRenderer(DEFAULT_THEME)
660
-
661
- const slide: Slide = {
662
- frontmatter: {
663
- title: 'Gradient Slide',
664
- bigText: 'TEST',
665
- gradient: 'matrix',
666
- },
667
- body: '',
668
- notes: '',
669
- sourcePath: 'gradient.md',
670
- index: 0,
671
- }
672
-
673
- const window = await renderSlide(renderer, slide)
674
-
675
- expect(window).toBeDefined()
676
-
677
- destroyRenderer(renderer)
678
- })
679
-
680
- it('uses default gradient when not specified', async () => {
681
- const renderer = createRenderer(DEFAULT_THEME)
682
-
683
- const slide: Slide = {
684
- frontmatter: {
685
- title: 'Default Gradient',
686
- bigText: 'TEST',
687
- },
688
- body: '',
689
- notes: '',
690
- sourcePath: 'default.md',
691
- index: 0,
692
- }
693
-
694
- const window = await renderSlide(renderer, slide)
695
-
696
- expect(window).toBeDefined()
697
-
698
- destroyRenderer(renderer)
699
- })
700
-
701
- it('applies transition from frontmatter', async () => {
702
- const renderer = createRenderer(DEFAULT_THEME)
703
-
704
- const slide: Slide = {
705
- frontmatter: {
706
- title: 'Transition Slide',
707
- transition: 'instant',
708
- },
709
- body: 'Content with instant transition',
710
- notes: '',
711
- sourcePath: 'transition.md',
712
- index: 0,
713
- }
714
-
715
- const window = await renderSlide(renderer, slide)
716
-
717
- expect(window).toBeDefined()
718
-
719
- destroyRenderer(renderer)
720
- })
721
-
722
- it('uses default glitch transition when not specified', async () => {
723
- const renderer = createRenderer(DEFAULT_THEME)
724
-
725
- const slide: Slide = {
726
- frontmatter: {
727
- title: 'Default Transition',
728
- },
729
- body: 'Content',
730
- notes: '',
731
- sourcePath: 'default-trans.md',
732
- index: 0,
733
- }
734
-
735
- const window = await renderSlide(renderer, slide)
736
-
737
- expect(window).toBeDefined()
738
-
739
- destroyRenderer(renderer)
740
- })
741
-
742
- it('handles slide with notes (notes not rendered)', async () => {
743
- const renderer = createRenderer(DEFAULT_THEME)
744
-
745
- const slide: Slide = {
746
- frontmatter: {
747
- title: 'Slide with Notes',
748
- },
749
- body: 'Visible content',
750
- notes: 'These are presenter notes',
751
- sourcePath: 'notes.md',
752
- index: 0,
753
- }
754
-
755
- const window = await renderSlide(renderer, slide)
756
-
757
- expect(window).toBeDefined()
758
-
759
- destroyRenderer(renderer)
760
- })
761
-
762
- it('handles slide with color tokens in body', async () => {
763
- const renderer = createRenderer(DEFAULT_THEME)
764
-
765
- const slide: Slide = {
766
- frontmatter: {
767
- title: 'Color Token Slide',
768
- },
769
- body: 'Text with {GREEN} color',
770
- notes: '',
771
- sourcePath: 'token.md',
772
- index: 0,
773
- }
774
-
775
- const window = await renderSlide(renderer, slide)
776
-
777
- expect(window).toBeDefined()
778
-
779
- destroyRenderer(renderer)
780
- })
781
-
782
- it('processes slide content through processSlideContent', async () => {
783
- const renderer = createRenderer(DEFAULT_THEME)
784
-
785
- const slide: Slide = {
786
- frontmatter: {
787
- title: 'Processed Slide',
788
- },
789
- body: 'Content with {PRIMARY} token',
790
- notes: '',
791
- sourcePath: 'processed.md',
792
- index: 0,
793
- }
794
-
795
- const window = await renderSlide(renderer, slide)
796
-
797
- expect(window).toBeDefined()
798
-
799
- destroyRenderer(renderer)
800
- })
801
- })