@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
@@ -0,0 +1,1916 @@
1
+ #!/usr/bin/env node
2
+ import { join } from 'path';
3
+ import 'url';
4
+ import { z } from 'zod';
5
+ import matter from 'gray-matter';
6
+ import { mkdir, writeFile, rm, access, readFile, unlink } from 'fs/promises';
7
+ import fg from 'fast-glob';
8
+ import blessed from 'neo-blessed';
9
+ import gradient2 from 'gradient-string';
10
+ import 'yaml';
11
+ import 'deepmerge';
12
+ import { mermaidToAscii as mermaidToAscii$1 } from 'mermaid-ascii';
13
+ import figlet from 'figlet';
14
+ import { Command } from 'commander';
15
+ import { tmpdir } from 'os';
16
+ import { execa } from 'execa';
17
+
18
+ var __defProp = Object.defineProperty;
19
+ var __getOwnPropNames = Object.getOwnPropertyNames;
20
+ var __esm = (fn, res) => function __init() {
21
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
22
+ };
23
+ var __export = (target, all) => {
24
+ for (var name in all)
25
+ __defProp(target, name, { get: all[name], enumerable: true });
26
+ };
27
+ var init_esm_shims = __esm({
28
+ "node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
29
+ }
30
+ });
31
+ var HexColorSchema, GradientSchema, ThemeSchema, DEFAULT_THEME;
32
+ var init_theme = __esm({
33
+ "src/schemas/theme.ts"() {
34
+ init_esm_shims();
35
+ HexColorSchema = z.string().regex(/^#[0-9a-fA-F]{6}$/, {
36
+ message: "Color must be a valid hex color (e.g., #ff0066)"
37
+ });
38
+ GradientSchema = z.array(HexColorSchema).min(2, {
39
+ message: "Gradient must have at least 2 colors"
40
+ });
41
+ ThemeSchema = z.object({
42
+ // Theme metadata
43
+ name: z.string().min(1, { message: "Theme name is required" }),
44
+ description: z.string().optional(),
45
+ author: z.string().optional(),
46
+ version: z.string().optional(),
47
+ // Color palette
48
+ colors: z.object({
49
+ primary: HexColorSchema,
50
+ secondary: HexColorSchema.optional(),
51
+ accent: HexColorSchema,
52
+ background: HexColorSchema,
53
+ text: HexColorSchema,
54
+ muted: HexColorSchema,
55
+ success: HexColorSchema.optional(),
56
+ warning: HexColorSchema.optional(),
57
+ error: HexColorSchema.optional()
58
+ }),
59
+ // Named gradients for bigText
60
+ gradients: z.record(z.string(), GradientSchema).refine(
61
+ (g) => Object.keys(g).length >= 1,
62
+ { message: "At least one gradient must be defined" }
63
+ ),
64
+ // Glyph set for matrix rain background
65
+ glyphs: z.string().min(10, {
66
+ message: "Glyph set must have at least 10 characters"
67
+ }),
68
+ // Animation settings
69
+ animations: z.object({
70
+ // Speed multiplier (1.0 = normal, 0.5 = half speed, 2.0 = double speed)
71
+ revealSpeed: z.number().min(0.1).max(5).default(1),
72
+ // Matrix rain density (number of drops)
73
+ matrixDensity: z.number().min(10).max(200).default(50),
74
+ // Glitch effect iterations
75
+ glitchIterations: z.number().min(1).max(20).default(5),
76
+ // Delay between lines during reveal (ms)
77
+ lineDelay: z.number().min(0).max(500).default(30),
78
+ // Matrix rain update interval (ms)
79
+ matrixInterval: z.number().min(20).max(200).default(80)
80
+ }),
81
+ // Window appearance
82
+ window: z.object({
83
+ // Border style
84
+ borderStyle: z.enum(["line", "double", "rounded", "none"]).default("line"),
85
+ // Shadow effect
86
+ shadow: z.boolean().default(true),
87
+ // Padding inside windows
88
+ padding: z.object({
89
+ top: z.number().min(0).max(5).default(1),
90
+ bottom: z.number().min(0).max(5).default(1),
91
+ left: z.number().min(0).max(10).default(2),
92
+ right: z.number().min(0).max(10).default(2)
93
+ }).optional()
94
+ }).optional()
95
+ });
96
+ ThemeSchema.deepPartial();
97
+ DEFAULT_THEME = {
98
+ name: "matrix",
99
+ description: "Default cyberpunk/matrix theme",
100
+ colors: {
101
+ primary: "#00cc66",
102
+ accent: "#ff6600",
103
+ background: "#0a0a0a",
104
+ text: "#ffffff",
105
+ muted: "#666666"
106
+ },
107
+ gradients: {
108
+ fire: ["#ff6600", "#ff3300", "#ff0066"],
109
+ cool: ["#00ccff", "#0066ff", "#6600ff"],
110
+ pink: ["#ff0066", "#ff0099", "#cc00ff"],
111
+ hf: ["#99cc00", "#00cc66", "#00cccc"]
112
+ },
113
+ glyphs: "\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF83\uFF84\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E\uFF8F\uFF90\uFF91\uFF92\uFF93\uFF94\uFF95\uFF96\uFF97\uFF98\uFF99\uFF9A\uFF9B\uFF9C\uFF9D0123456789",
114
+ animations: {
115
+ revealSpeed: 1,
116
+ matrixDensity: 50,
117
+ glitchIterations: 5,
118
+ lineDelay: 30,
119
+ matrixInterval: 80
120
+ },
121
+ window: {
122
+ borderStyle: "line",
123
+ shadow: true,
124
+ padding: {
125
+ top: 1,
126
+ bottom: 1,
127
+ left: 2,
128
+ right: 2
129
+ }
130
+ }
131
+ };
132
+ }
133
+ });
134
+ var SettingsSchema, ExportSettingsSchema, DeckConfigSchema;
135
+ var init_config = __esm({
136
+ "src/schemas/config.ts"() {
137
+ init_esm_shims();
138
+ init_theme();
139
+ SettingsSchema = z.object({
140
+ // Start slide (0-indexed)
141
+ startSlide: z.number().min(0).default(0),
142
+ // Loop back to first slide after last
143
+ loop: z.boolean().default(false),
144
+ // Auto-advance slides (ms, 0 = disabled)
145
+ autoAdvance: z.number().min(0).default(0),
146
+ // Show slide numbers
147
+ showSlideNumbers: z.boolean().default(false),
148
+ // Show progress bar
149
+ showProgress: z.boolean().default(false)
150
+ });
151
+ ExportSettingsSchema = z.object({
152
+ // Output width in characters (min 80, max 400)
153
+ width: z.number().min(80).max(400).default(120),
154
+ // Output height in characters (min 24, max 100)
155
+ height: z.number().min(24).max(100).default(40),
156
+ // Frames per second for video (min 10, max 60)
157
+ fps: z.number().min(10).max(60).default(30)
158
+ });
159
+ DeckConfigSchema = z.object({
160
+ // Presentation metadata
161
+ title: z.string().optional(),
162
+ author: z.string().optional(),
163
+ date: z.string().optional(),
164
+ // Theme (already validated Theme object)
165
+ theme: ThemeSchema,
166
+ // Presentation settings
167
+ settings: SettingsSchema.optional(),
168
+ // Export settings
169
+ export: ExportSettingsSchema.optional()
170
+ });
171
+ }
172
+ });
173
+
174
+ // src/schemas/validation.ts
175
+ function formatZodError(error, context) {
176
+ const issues = error.issues.map((issue) => {
177
+ const path2 = issue.path.join(".");
178
+ return ` - ${path2 ? `${path2}: ` : ""}${issue.message}`;
179
+ });
180
+ return `Invalid ${context}:
181
+ ${issues.join("\n")}`;
182
+ }
183
+ function safeParse(schema, data, context) {
184
+ const result = schema.safeParse(data);
185
+ if (!result.success) {
186
+ throw new ValidationError(formatZodError(result.error, context));
187
+ }
188
+ return result.data;
189
+ }
190
+ var ValidationError;
191
+ var init_validation = __esm({
192
+ "src/schemas/validation.ts"() {
193
+ init_esm_shims();
194
+ ValidationError = class extends Error {
195
+ constructor(message) {
196
+ super(message);
197
+ this.name = "ValidationError";
198
+ }
199
+ };
200
+ }
201
+ });
202
+ var SlideFrontmatterSchema, SlideSchema;
203
+ var init_slide = __esm({
204
+ "src/schemas/slide.ts"() {
205
+ init_esm_shims();
206
+ SlideFrontmatterSchema = z.object({
207
+ // Required: window title
208
+ title: z.string().min(1, {
209
+ message: "Slide must have a title"
210
+ }),
211
+ // ASCII art text (figlet) - can be a single line or multiple lines
212
+ bigText: z.union([
213
+ z.string(),
214
+ z.array(z.string())
215
+ ]).optional(),
216
+ // Which gradient to use for bigText
217
+ gradient: z.string().optional(),
218
+ // Override theme for this slide
219
+ theme: z.string().optional(),
220
+ // Transition effect
221
+ transition: z.enum([
222
+ "glitch",
223
+ // Default: glitch reveal line by line
224
+ "fade",
225
+ // Fade in
226
+ "instant",
227
+ // No animation
228
+ "typewriter"
229
+ // Character by character
230
+ ]).default("glitch"),
231
+ // Custom metadata (ignored by renderer, useful for tooling)
232
+ meta: z.record(z.string(), z.unknown()).optional()
233
+ });
234
+ SlideSchema = z.object({
235
+ // Parsed frontmatter
236
+ frontmatter: SlideFrontmatterSchema,
237
+ // Markdown body content
238
+ body: z.string(),
239
+ // Presenter notes (extracted from <!-- notes --> block)
240
+ notes: z.string().optional(),
241
+ // Source file path
242
+ sourcePath: z.string(),
243
+ // Slide index in deck (0-indexed)
244
+ index: z.number()
245
+ });
246
+ }
247
+ });
248
+ function extractNotes(content) {
249
+ const notesStart = content.indexOf(NOTES_MARKER);
250
+ if (notesStart === -1) {
251
+ return { body: content };
252
+ }
253
+ const body = content.slice(0, notesStart).trim();
254
+ const notesEnd = content.indexOf(NOTES_END_MARKER, notesStart);
255
+ let notes;
256
+ if (notesEnd !== -1) {
257
+ notes = content.slice(notesStart + NOTES_MARKER.length, notesEnd).trim();
258
+ } else {
259
+ notes = content.slice(notesStart + NOTES_MARKER.length).trim();
260
+ }
261
+ return { body, notes: notes || void 0 };
262
+ }
263
+ async function parseSlide(filePath, index) {
264
+ const content = await readFile(filePath, "utf-8");
265
+ const { data, content: rawBody } = matter(content);
266
+ const { body, notes } = extractNotes(rawBody);
267
+ const frontmatter = safeParse(
268
+ SlideFrontmatterSchema,
269
+ data,
270
+ `frontmatter in ${filePath}`
271
+ );
272
+ const slide = {
273
+ frontmatter,
274
+ body: body.trim(),
275
+ notes: notes?.trim(),
276
+ sourcePath: filePath,
277
+ index
278
+ };
279
+ return safeParse(SlideSchema, slide, `slide ${filePath}`);
280
+ }
281
+ var NOTES_MARKER, NOTES_END_MARKER, SlideParseError;
282
+ var init_slide2 = __esm({
283
+ "src/core/slide.ts"() {
284
+ init_esm_shims();
285
+ init_slide();
286
+ init_validation();
287
+ NOTES_MARKER = "<!-- notes -->";
288
+ NOTES_END_MARKER = "<!-- /notes -->";
289
+ SlideParseError = class extends Error {
290
+ /**
291
+ * @param message - The error message describing what went wrong
292
+ * @param filePath - Path to the slide file that failed to parse
293
+ * @param cause - Optional underlying error that caused this failure
294
+ */
295
+ constructor(message, filePath, cause) {
296
+ super(message);
297
+ this.filePath = filePath;
298
+ this.cause = cause;
299
+ this.name = "SlideParseError";
300
+ }
301
+ };
302
+ }
303
+ });
304
+ async function findSlideFiles(dir) {
305
+ const pattern = join(dir, "*.md");
306
+ const foundFiles = await fg(pattern, { onlyFiles: true });
307
+ const files = [];
308
+ for (const filePath of foundFiles) {
309
+ const name = filePath.split("/").pop() || "";
310
+ if (name === "README.md" || name.startsWith("_")) {
311
+ continue;
312
+ }
313
+ files.push({
314
+ path: filePath,
315
+ name,
316
+ index: 0
317
+ // Will be set after sorting
318
+ });
319
+ }
320
+ files.sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
321
+ files.forEach((file, i) => {
322
+ file.index = i;
323
+ });
324
+ return files;
325
+ }
326
+ async function loadDeckConfig(slidesDir) {
327
+ const configPath = join(slidesDir, "deck.config.ts");
328
+ try {
329
+ try {
330
+ await access(configPath);
331
+ } catch {
332
+ return {
333
+ theme: DEFAULT_THEME
334
+ };
335
+ }
336
+ const cacheBuster = `${Date.now()}-${Math.random()}`;
337
+ const configModule = await import(configPath + "?t=" + cacheBuster);
338
+ if (!configModule.default) {
339
+ throw new Error("deck.config.ts must export default config");
340
+ }
341
+ return safeParse(DeckConfigSchema, configModule.default, "deck.config.ts");
342
+ } catch (error) {
343
+ if (error.code === "MODULE_NOT_FOUND") {
344
+ return { theme: DEFAULT_THEME };
345
+ }
346
+ throw error;
347
+ }
348
+ }
349
+ async function loadDeck(slidesDir) {
350
+ const config = await loadDeckConfig(slidesDir);
351
+ const slideFiles = await findSlideFiles(slidesDir);
352
+ const slides = await Promise.all(
353
+ slideFiles.map((file) => parseSlide(file.path, file.index))
354
+ );
355
+ return {
356
+ slides,
357
+ config,
358
+ basePath: slidesDir
359
+ };
360
+ }
361
+ var DeckLoadError;
362
+ var init_deck_loader = __esm({
363
+ "src/core/deck-loader.ts"() {
364
+ init_esm_shims();
365
+ init_config();
366
+ init_validation();
367
+ init_theme();
368
+ init_slide2();
369
+ DeckLoadError = class extends Error {
370
+ /**
371
+ * @param message - The error message describing what went wrong
372
+ * @param slidesDir - Path to the directory that was being loaded
373
+ * @param cause - Optional underlying error that caused this failure
374
+ */
375
+ constructor(message, slidesDir, cause) {
376
+ super(message);
377
+ this.slidesDir = slidesDir;
378
+ this.cause = cause;
379
+ this.name = "DeckLoadError";
380
+ }
381
+ };
382
+ }
383
+ });
384
+ function generateTrail(glyphs, length) {
385
+ return Array.from(
386
+ { length },
387
+ () => glyphs[Math.floor(Math.random() * glyphs.length)]
388
+ );
389
+ }
390
+ function renderMatrixRain(screen, state) {
391
+ const { matrixBox, matrixDrops, theme } = state;
392
+ const width = Math.max(20, screen.width || 80);
393
+ const height = Math.max(10, screen.height || 24);
394
+ const grid = Array.from(
395
+ { length: height },
396
+ () => Array(width).fill(" ")
397
+ );
398
+ for (const drop of matrixDrops) {
399
+ drop.y += drop.speed;
400
+ if (drop.y > height + drop.trail.length) {
401
+ drop.y = -drop.trail.length;
402
+ drop.x = Math.floor(Math.random() * width);
403
+ }
404
+ for (let i = 0; i < drop.trail.length; i++) {
405
+ const y = Math.floor(drop.y) - i;
406
+ if (y >= 0 && y < height && drop.x < width) {
407
+ grid[y][drop.x] = drop.trail[i];
408
+ }
409
+ }
410
+ }
411
+ let output = "";
412
+ for (let y = 0; y < height; y++) {
413
+ for (let x = 0; x < width; x++) {
414
+ const char = grid[y][x];
415
+ if (char !== " ") {
416
+ const brightness = Math.random() > 0.7 ? "{bold}" : "";
417
+ output += `${brightness}{${theme.colors.primary}-fg}${char}{/}`;
418
+ } else {
419
+ output += " ";
420
+ }
421
+ }
422
+ if (y < height - 1) output += "\n";
423
+ }
424
+ matrixBox.setContent(output);
425
+ }
426
+ function initMatrixRain(screen, state) {
427
+ const { theme } = state;
428
+ const width = screen.width || 80;
429
+ const height = screen.height || 24;
430
+ const density = theme.animations.matrixDensity;
431
+ state.matrixDrops = [];
432
+ for (let i = 0; i < density; i++) {
433
+ state.matrixDrops.push({
434
+ x: Math.floor(Math.random() * width),
435
+ y: Math.floor(Math.random() * height),
436
+ speed: 0.3 + Math.random() * 0.7,
437
+ trail: generateTrail(theme.glyphs, 5 + Math.floor(Math.random() * 10))
438
+ });
439
+ }
440
+ state.matrixInterval = setInterval(() => {
441
+ renderMatrixRain(screen, state);
442
+ screen.render();
443
+ }, theme.animations.matrixInterval);
444
+ }
445
+ function stopMatrixRain(state) {
446
+ if (state.matrixInterval) {
447
+ clearInterval(state.matrixInterval);
448
+ state.matrixInterval = null;
449
+ }
450
+ }
451
+ function createMatrixBox(screen) {
452
+ const matrixBox = blessed.box({
453
+ top: 0,
454
+ left: 0,
455
+ width: "100%",
456
+ height: "100%",
457
+ tags: true
458
+ });
459
+ screen.append(matrixBox);
460
+ return matrixBox;
461
+ }
462
+ var init_matrix_rain = __esm({
463
+ "src/renderer/effects/matrix-rain.ts"() {
464
+ init_esm_shims();
465
+ }
466
+ });
467
+ function getWindowColor(index, theme) {
468
+ const colors = [
469
+ theme.colors.primary,
470
+ theme.colors.accent,
471
+ theme.colors.secondary ?? theme.colors.primary,
472
+ "#ff0066",
473
+ // pink
474
+ "#9966ff",
475
+ // purple
476
+ "#ffcc00"
477
+ // yellow
478
+ ];
479
+ return colors[index % colors.length];
480
+ }
481
+ function createWindow(screen, windowStack, theme, options) {
482
+ const windowIndex = windowStack.length;
483
+ const color = options.color ?? getWindowColor(windowIndex, theme);
484
+ const screenWidth = screen.width || 120;
485
+ const screenHeight = screen.height || 40;
486
+ const width = options.width ?? Math.floor(screenWidth * 0.75);
487
+ const height = options.height ?? Math.floor(screenHeight * 0.7);
488
+ const maxTop = Math.max(1, screenHeight - height - 2);
489
+ const maxLeft = Math.max(1, screenWidth - width - 2);
490
+ const top = options.top ?? Math.floor(Math.random() * maxTop);
491
+ const left = options.left ?? Math.floor(Math.random() * maxLeft);
492
+ const window = theme.window ?? { borderStyle: "line", shadow: true };
493
+ const padding = window.padding ?? { top: 1, bottom: 1, left: 2, right: 2 };
494
+ const box = blessed.box({
495
+ top,
496
+ left,
497
+ width,
498
+ height,
499
+ border: {
500
+ type: window.borderStyle === "none" ? void 0 : "line"
501
+ },
502
+ label: ` ${options.title} `,
503
+ style: {
504
+ fg: theme.colors.text,
505
+ bg: theme.colors.background,
506
+ border: { fg: color },
507
+ label: { fg: color, bold: true }
508
+ },
509
+ padding,
510
+ tags: true,
511
+ shadow: window.shadow
512
+ });
513
+ screen.append(box);
514
+ windowStack.push(box);
515
+ return box;
516
+ }
517
+ function clearWindows(windowStack) {
518
+ for (const window of windowStack) {
519
+ window.destroy();
520
+ }
521
+ windowStack.length = 0;
522
+ }
523
+ var init_window_manager = __esm({
524
+ "src/renderer/window-manager.ts"() {
525
+ init_esm_shims();
526
+ }
527
+ });
528
+
529
+ // src/core/theme-errors.ts
530
+ var ThemeError;
531
+ var init_theme_errors = __esm({
532
+ "src/core/theme-errors.ts"() {
533
+ init_esm_shims();
534
+ init_validation();
535
+ ThemeError = class extends Error {
536
+ /**
537
+ * @param message - The error message
538
+ * @param themeName - Optional name of the theme that caused the error
539
+ * @param path - Optional path to the theme file or package
540
+ */
541
+ constructor(message, themeName, path2) {
542
+ super(message);
543
+ this.themeName = themeName;
544
+ this.path = path2;
545
+ this.name = "ThemeError";
546
+ }
547
+ };
548
+ }
549
+ });
550
+ function resolveColorToken(token, theme) {
551
+ switch (token) {
552
+ case "PRIMARY":
553
+ return theme.colors.primary;
554
+ case "SECONDARY":
555
+ return theme.colors.secondary ?? theme.colors.primary;
556
+ case "ACCENT":
557
+ return theme.colors.accent;
558
+ case "MUTED":
559
+ return theme.colors.muted;
560
+ case "TEXT":
561
+ return theme.colors.text;
562
+ case "BACKGROUND":
563
+ return theme.colors.background;
564
+ }
565
+ return BUILTIN_COLORS[token] ?? theme.colors.text;
566
+ }
567
+ function colorTokensToBlessedTags(content, theme) {
568
+ return content.replace(
569
+ /\{(GREEN|ORANGE|CYAN|PINK|WHITE|GRAY|PRIMARY|SECONDARY|ACCENT|MUTED|TEXT|BACKGROUND|\/)\}/g,
570
+ (_, token) => {
571
+ if (token === "/") {
572
+ return "{/}";
573
+ }
574
+ const color = resolveColorToken(token, theme);
575
+ return `{${color}-fg}`;
576
+ }
577
+ );
578
+ }
579
+ var BUILTIN_COLORS;
580
+ var init_theme_colors = __esm({
581
+ "src/core/theme-colors.ts"() {
582
+ init_esm_shims();
583
+ BUILTIN_COLORS = {
584
+ GREEN: "#00cc66",
585
+ ORANGE: "#ff6600",
586
+ CYAN: "#00ccff",
587
+ PINK: "#ff0066",
588
+ WHITE: "#ffffff",
589
+ GRAY: "#666666"
590
+ };
591
+ }
592
+ });
593
+ var init_theme2 = __esm({
594
+ "src/core/theme.ts"() {
595
+ init_esm_shims();
596
+ init_theme();
597
+ init_validation();
598
+ init_theme_errors();
599
+ init_theme_colors();
600
+ }
601
+ });
602
+ function hasMermaidDiagrams(content) {
603
+ MERMAID_BLOCK_PATTERN.lastIndex = 0;
604
+ return MERMAID_BLOCK_PATTERN.test(content);
605
+ }
606
+ function extractMermaidBlocks(content) {
607
+ const blocks = [];
608
+ let match;
609
+ MERMAID_BLOCK_PATTERN.lastIndex = 0;
610
+ while ((match = MERMAID_BLOCK_PATTERN.exec(content)) !== null) {
611
+ blocks.push(match[1].trim());
612
+ }
613
+ return blocks;
614
+ }
615
+ function formatMermaidError(code, _error) {
616
+ const lines = [
617
+ "\u250C\u2500 Diagram (parse error) \u2500\u2510",
618
+ "\u2502 \u2502"
619
+ ];
620
+ const codeLines = code.split("\n").slice(0, 5);
621
+ for (const line of codeLines) {
622
+ const truncated = line.slice(0, 23).padEnd(23);
623
+ lines.push(`\u2502 ${truncated} \u2502`);
624
+ }
625
+ if (code.split("\n").length > 5) {
626
+ lines.push("\u2502 ... \u2502");
627
+ }
628
+ lines.push("\u2502 \u2502");
629
+ lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
630
+ return lines.join("\n");
631
+ }
632
+ function mermaidToAscii(mermaidCode) {
633
+ try {
634
+ return mermaidToAscii$1(mermaidCode);
635
+ } catch (error) {
636
+ return formatMermaidError(mermaidCode);
637
+ }
638
+ }
639
+ function escapeRegex(str) {
640
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
641
+ }
642
+ function processMermaidDiagrams(content) {
643
+ if (!hasMermaidDiagrams(content)) {
644
+ return content;
645
+ }
646
+ let result = content;
647
+ const blocks = extractMermaidBlocks(content);
648
+ for (const block of blocks) {
649
+ const ascii = mermaidToAscii(block);
650
+ result = result.replace(
651
+ new RegExp("```mermaid\\n" + escapeRegex(block) + "\\n?```", "g"),
652
+ ascii
653
+ );
654
+ }
655
+ return result;
656
+ }
657
+ var MERMAID_BLOCK_PATTERN;
658
+ var init_mermaid = __esm({
659
+ "src/core/utils/mermaid.ts"() {
660
+ init_esm_shims();
661
+ MERMAID_BLOCK_PATTERN = /```mermaid\n([\s\S]*?)```/g;
662
+ }
663
+ });
664
+
665
+ // src/core/content-processor.ts
666
+ async function processSlideContent(body, theme) {
667
+ let processed = processMermaidDiagrams(body);
668
+ processed = colorTokensToBlessedTags(processed, theme);
669
+ return processed;
670
+ }
671
+ function normalizeBigText(bigText) {
672
+ if (!bigText) return [];
673
+ return Array.isArray(bigText) ? bigText : [bigText];
674
+ }
675
+ var init_content_processor = __esm({
676
+ "src/core/content-processor.ts"() {
677
+ init_esm_shims();
678
+ init_theme2();
679
+ init_mermaid();
680
+ }
681
+ });
682
+
683
+ // src/renderer/animations/constants.ts
684
+ var GLITCH_CHARS, PROTECTED_CHARS;
685
+ var init_constants = __esm({
686
+ "src/renderer/animations/constants.ts"() {
687
+ init_esm_shims();
688
+ GLITCH_CHARS = "\u2588\u2593\u2592\u2591\u2580\u2584\u258C\u2590\u25A0\u25A1\u25AA\u25AB\u25CF\u25CB\u25CA\u25D8\u25D9\u2666\u2663\u2660\u2665\u2605\u2606\u2302\u207F\xB2\xB3\xC6\xD8\u221E\u2248\u2260\xB1\xD7\xF7\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03BB\u03BC\u03C0\u03C3\u03C6\u03C9\u0394\u03A3\u03A9\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF83\uFF84\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E\uFF8F\uFF90\uFF91\uFF92\uFF93\uFF94\uFF95\uFF96\uFF97\uFF98\uFF99\uFF9A\uFF9B\uFF9C\uFF9D";
689
+ PROTECTED_CHARS = /* @__PURE__ */ new Set([
690
+ " ",
691
+ " ",
692
+ "\n",
693
+ "{",
694
+ "}",
695
+ "-",
696
+ "/",
697
+ "#",
698
+ "[",
699
+ "]",
700
+ "(",
701
+ ")",
702
+ ":",
703
+ ";",
704
+ ",",
705
+ ".",
706
+ "!",
707
+ "?",
708
+ "'",
709
+ '"',
710
+ "`",
711
+ "_",
712
+ "|",
713
+ "\\",
714
+ "<",
715
+ ">",
716
+ "=",
717
+ "+",
718
+ "*",
719
+ "&",
720
+ "^",
721
+ "%",
722
+ "$",
723
+ "@",
724
+ "~",
725
+ // Box drawing
726
+ "\u250C",
727
+ "\u2510",
728
+ "\u2514",
729
+ "\u2518",
730
+ "\u2502",
731
+ "\u2500",
732
+ "\u251C",
733
+ "\u2524",
734
+ "\u252C",
735
+ "\u2534",
736
+ "\u253C",
737
+ "\u2550",
738
+ "\u2551",
739
+ "\u2554",
740
+ "\u2557",
741
+ "\u255A",
742
+ "\u255D",
743
+ "\u2560",
744
+ "\u2563",
745
+ "\u2566",
746
+ "\u2569",
747
+ "\u256C",
748
+ "\u256D",
749
+ "\u256E",
750
+ "\u256F",
751
+ "\u2570",
752
+ // Arrows
753
+ "\u2192",
754
+ "\u2190",
755
+ "\u2191",
756
+ "\u2193",
757
+ "\u25B6",
758
+ "\u25C0",
759
+ "\u25B2",
760
+ "\u25BC",
761
+ "\u25BA",
762
+ "\u25C4"
763
+ ]);
764
+ }
765
+ });
766
+
767
+ // src/renderer/animations/helpers/animation-utils.ts
768
+ function sleep(ms) {
769
+ return new Promise((resolve) => setTimeout(resolve, ms));
770
+ }
771
+ function renderContent(box, screen, content) {
772
+ box.setContent(content);
773
+ screen.render();
774
+ }
775
+ var init_animation_utils = __esm({
776
+ "src/renderer/animations/helpers/animation-utils.ts"() {
777
+ init_esm_shims();
778
+ }
779
+ });
780
+
781
+ // src/renderer/animations/transitions/glitch-transition.ts
782
+ async function glitchLine(box, screen, currentLines, newLine, iterations = 5) {
783
+ for (let i = iterations; i >= 0; i--) {
784
+ const scrambleRatio = i / iterations;
785
+ let scrambledLine = "";
786
+ for (const char of newLine) {
787
+ if (PROTECTED_CHARS.has(char)) {
788
+ scrambledLine += char;
789
+ } else if (Math.random() < scrambleRatio) {
790
+ scrambledLine += GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)];
791
+ } else {
792
+ scrambledLine += char;
793
+ }
794
+ }
795
+ renderContent(box, screen, [...currentLines, scrambledLine].join("\n"));
796
+ await sleep(20);
797
+ }
798
+ }
799
+ async function lineByLineReveal(box, screen, content, theme) {
800
+ const lines = content.split("\n");
801
+ const revealedLines = [];
802
+ const lineDelay = theme.animations.lineDelay;
803
+ const glitchIterations = theme.animations.glitchIterations;
804
+ for (const line of lines) {
805
+ await glitchLine(box, screen, revealedLines, line, glitchIterations);
806
+ revealedLines.push(line);
807
+ renderContent(box, screen, revealedLines.join("\n"));
808
+ if (line.trim()) {
809
+ await sleep(lineDelay);
810
+ }
811
+ }
812
+ }
813
+ var init_glitch_transition = __esm({
814
+ "src/renderer/animations/transitions/glitch-transition.ts"() {
815
+ init_esm_shims();
816
+ init_constants();
817
+ init_animation_utils();
818
+ }
819
+ });
820
+
821
+ // src/renderer/animations/transitions/fade-transition.ts
822
+ async function fadeInReveal(box, screen, content, theme) {
823
+ const steps = 10;
824
+ const delay = theme.animations.lineDelay * 2 / steps;
825
+ for (let step = 0; step < steps; step++) {
826
+ const revealRatio = step / steps;
827
+ let revealed = "";
828
+ for (const char of content) {
829
+ if (char === "\n" || PROTECTED_CHARS.has(char) || Math.random() < revealRatio) {
830
+ revealed += char;
831
+ } else {
832
+ revealed += " ";
833
+ }
834
+ }
835
+ renderContent(box, screen, revealed);
836
+ await sleep(delay);
837
+ }
838
+ renderContent(box, screen, content);
839
+ }
840
+ var init_fade_transition = __esm({
841
+ "src/renderer/animations/transitions/fade-transition.ts"() {
842
+ init_esm_shims();
843
+ init_constants();
844
+ init_animation_utils();
845
+ }
846
+ });
847
+
848
+ // src/renderer/animations/transitions/typewriter-transition.ts
849
+ async function typewriterReveal(box, screen, content, theme) {
850
+ const charDelay = theme.animations.lineDelay / 5;
851
+ let revealed = "";
852
+ for (const char of content) {
853
+ revealed += char;
854
+ renderContent(box, screen, revealed);
855
+ if (char !== " " && char !== "\n") {
856
+ await sleep(charDelay);
857
+ }
858
+ }
859
+ }
860
+ var init_typewriter_transition = __esm({
861
+ "src/renderer/animations/transitions/typewriter-transition.ts"() {
862
+ init_esm_shims();
863
+ init_animation_utils();
864
+ }
865
+ });
866
+
867
+ // src/renderer/animations/transitions/instant-transition.ts
868
+ function instantReveal(box, screen, content) {
869
+ renderContent(box, screen, content);
870
+ }
871
+ var init_instant_transition = __esm({
872
+ "src/renderer/animations/transitions/instant-transition.ts"() {
873
+ init_esm_shims();
874
+ init_animation_utils();
875
+ }
876
+ });
877
+
878
+ // src/renderer/animations/transition-orchestrator.ts
879
+ async function applyTransition(box, screen, content, transition, theme) {
880
+ switch (transition) {
881
+ case "glitch":
882
+ await lineByLineReveal(box, screen, content, theme);
883
+ break;
884
+ case "fade":
885
+ await fadeInReveal(box, screen, content, theme);
886
+ break;
887
+ case "instant":
888
+ instantReveal(box, screen, content);
889
+ break;
890
+ case "typewriter":
891
+ await typewriterReveal(box, screen, content, theme);
892
+ break;
893
+ default:
894
+ instantReveal(box, screen, content);
895
+ }
896
+ }
897
+ var init_transition_orchestrator = __esm({
898
+ "src/renderer/animations/transition-orchestrator.ts"() {
899
+ init_esm_shims();
900
+ init_glitch_transition();
901
+ init_fade_transition();
902
+ init_typewriter_transition();
903
+ init_instant_transition();
904
+ }
905
+ });
906
+
907
+ // src/renderer/animations/transitions.ts
908
+ var init_transitions = __esm({
909
+ "src/renderer/animations/transitions.ts"() {
910
+ init_esm_shims();
911
+ init_transition_orchestrator();
912
+ init_glitch_transition();
913
+ init_fade_transition();
914
+ init_typewriter_transition();
915
+ init_instant_transition();
916
+ }
917
+ });
918
+ async function generateBigText(text, gradientColors, font = "Standard") {
919
+ return new Promise((resolve, reject) => {
920
+ figlet.text(text, { font }, (err, result) => {
921
+ if (err || !result) {
922
+ reject(err ?? new Error("Failed to generate figlet text"));
923
+ return;
924
+ }
925
+ const gradientFn = gradient2(gradientColors);
926
+ resolve(gradientFn(result));
927
+ });
928
+ });
929
+ }
930
+ async function generateMultiLineBigText(lines, gradientColors, font = "Standard") {
931
+ const results = await Promise.all(
932
+ lines.map((line) => generateBigText(line, gradientColors, font))
933
+ );
934
+ return results.join("\n");
935
+ }
936
+ var init_text_generator = __esm({
937
+ "src/renderer/text-generator.ts"() {
938
+ init_esm_shims();
939
+ }
940
+ });
941
+
942
+ // src/renderer/slide-renderer.ts
943
+ async function renderSlide(screen, windowStack, theme, slide) {
944
+ const { frontmatter, body } = slide;
945
+ const window = createWindow(screen, windowStack, theme, {
946
+ title: frontmatter.title
947
+ });
948
+ let content = "";
949
+ const bigTextLines = normalizeBigText(frontmatter.bigText);
950
+ if (bigTextLines.length > 0) {
951
+ const gradientName = frontmatter.gradient ?? "fire";
952
+ const gradientColors = theme.gradients[gradientName] ?? theme.gradients.fire;
953
+ const bigText = await generateMultiLineBigText(bigTextLines, gradientColors);
954
+ content += bigText + "\n\n";
955
+ }
956
+ const processedBody = await processSlideContent(body, theme);
957
+ content += processedBody;
958
+ const transition = frontmatter.transition ?? "glitch";
959
+ await applyTransition(window, screen, content, transition, theme);
960
+ return window;
961
+ }
962
+ var init_slide_renderer = __esm({
963
+ "src/renderer/slide-renderer.ts"() {
964
+ init_esm_shims();
965
+ init_content_processor();
966
+ init_transitions();
967
+ init_text_generator();
968
+ init_window_manager();
969
+ }
970
+ });
971
+ function createScreen(title = "term-deck") {
972
+ const screen = blessed.screen({
973
+ smartCSR: true,
974
+ title,
975
+ fullUnicode: true,
976
+ mouse: false,
977
+ altScreen: true
978
+ });
979
+ return screen;
980
+ }
981
+ function createRenderer(theme) {
982
+ const screen = createScreen();
983
+ const matrixBox = createMatrixBox(screen);
984
+ const matrixRain = {
985
+ matrixBox,
986
+ matrixDrops: [],
987
+ matrixInterval: null,
988
+ theme
989
+ };
990
+ const renderer = {
991
+ screen,
992
+ windowStack: [],
993
+ theme,
994
+ matrixRain
995
+ };
996
+ initMatrixRain(screen, matrixRain);
997
+ return renderer;
998
+ }
999
+ function destroyRenderer(renderer) {
1000
+ stopMatrixRain(renderer.matrixRain);
1001
+ clearWindows(renderer.windowStack);
1002
+ renderer.screen.destroy();
1003
+ }
1004
+ function clearWindows2(renderer) {
1005
+ clearWindows(renderer.windowStack);
1006
+ }
1007
+ async function renderSlide2(renderer, slide) {
1008
+ return renderSlide(renderer.screen, renderer.windowStack, renderer.theme, slide);
1009
+ }
1010
+ var init_screen = __esm({
1011
+ "src/renderer/screen.ts"() {
1012
+ init_esm_shims();
1013
+ init_matrix_rain();
1014
+ init_window_manager();
1015
+ init_slide_renderer();
1016
+ init_transitions();
1017
+ init_window_manager();
1018
+ init_text_generator();
1019
+ }
1020
+ });
1021
+ async function findAvailableTty() {
1022
+ const candidates = [
1023
+ "/dev/ttys001",
1024
+ "/dev/ttys002",
1025
+ "/dev/ttys003",
1026
+ "/dev/pts/1",
1027
+ "/dev/pts/2"
1028
+ ];
1029
+ for (const tty of candidates) {
1030
+ try {
1031
+ await access(tty);
1032
+ return tty;
1033
+ } catch {
1034
+ }
1035
+ }
1036
+ throw new Error(
1037
+ "Could not find available TTY for notes window. Open a second terminal, run `tty`, and pass the path with --notes-tty"
1038
+ );
1039
+ }
1040
+ async function createNotesWindow(ttyPath) {
1041
+ const blessed5 = (await import('neo-blessed')).default;
1042
+ const { openSync } = await import('fs');
1043
+ const tty = ttyPath ?? await findAvailableTty();
1044
+ const screen = blessed5.screen({
1045
+ smartCSR: true,
1046
+ title: "term-deck notes",
1047
+ fullUnicode: true,
1048
+ input: openSync(tty, "r"),
1049
+ output: openSync(tty, "w")
1050
+ });
1051
+ const contentBox = blessed5.box({
1052
+ top: 0,
1053
+ left: 0,
1054
+ width: "100%",
1055
+ height: "100%",
1056
+ tags: true,
1057
+ padding: 2,
1058
+ style: {
1059
+ fg: "#ffffff",
1060
+ bg: "#1a1a1a"
1061
+ }
1062
+ });
1063
+ screen.append(contentBox);
1064
+ screen.render();
1065
+ return {
1066
+ screen,
1067
+ contentBox,
1068
+ tty
1069
+ };
1070
+ }
1071
+ function updateNotesWindow(notesWindow, currentSlide, nextSlide2, currentIndex, totalSlides) {
1072
+ const { contentBox, screen } = notesWindow;
1073
+ let content = "";
1074
+ content += `{bold}Slide ${currentIndex + 1} of ${totalSlides}{/bold}
1075
+ `;
1076
+ content += `{gray-fg}${currentSlide.frontmatter.title}{/}
1077
+ `;
1078
+ content += "\n";
1079
+ content += "\u2500".repeat(50) + "\n";
1080
+ content += "\n";
1081
+ if (currentSlide.notes) {
1082
+ content += "{bold}PRESENTER NOTES:{/bold}\n\n";
1083
+ content += currentSlide.notes + "\n";
1084
+ } else {
1085
+ content += "{gray-fg}No notes for this slide{/}\n";
1086
+ }
1087
+ content += "\n";
1088
+ content += "\u2500".repeat(50) + "\n";
1089
+ content += "\n";
1090
+ if (nextSlide2) {
1091
+ content += `{bold}NEXT:{/bold} "${nextSlide2.frontmatter.title}"
1092
+ `;
1093
+ } else {
1094
+ content += "{gray-fg}Last slide{/}\n";
1095
+ }
1096
+ contentBox.setContent(content);
1097
+ screen.render();
1098
+ }
1099
+ function toggleNotesVisibility(notesWindow) {
1100
+ const { contentBox, screen } = notesWindow;
1101
+ contentBox.toggle();
1102
+ screen.render();
1103
+ }
1104
+ function destroyNotesWindow(notesWindow) {
1105
+ notesWindow.screen.destroy();
1106
+ }
1107
+ var init_notes_window = __esm({
1108
+ "src/presenter/notes-window.ts"() {
1109
+ init_esm_shims();
1110
+ }
1111
+ });
1112
+
1113
+ // src/presenter/navigation.ts
1114
+ async function showSlide(presenter, index) {
1115
+ if (presenter.isAnimating) return;
1116
+ if (index < 0 || index >= presenter.deck.slides.length) return;
1117
+ presenter.isAnimating = true;
1118
+ presenter.currentSlide = index;
1119
+ const slide = presenter.deck.slides[index];
1120
+ await renderSlide2(presenter.renderer, slide);
1121
+ presenter.renderer.screen.render();
1122
+ if (presenter.notesWindow) {
1123
+ const nextSlide2 = presenter.deck.slides[index + 1];
1124
+ updateNotesWindow(
1125
+ presenter.notesWindow,
1126
+ slide,
1127
+ nextSlide2,
1128
+ index,
1129
+ presenter.deck.slides.length
1130
+ );
1131
+ }
1132
+ if (presenter.progressBar) {
1133
+ updateProgress(presenter.progressBar, presenter.currentSlide, presenter.deck.slides.length);
1134
+ }
1135
+ presenter.isAnimating = false;
1136
+ }
1137
+ async function nextSlide(presenter) {
1138
+ const nextIndex = presenter.currentSlide + 1;
1139
+ const { slides } = presenter.deck;
1140
+ const loop = presenter.deck.config.settings?.loop ?? false;
1141
+ if (nextIndex >= slides.length) {
1142
+ if (loop) {
1143
+ await showSlide(presenter, 0);
1144
+ }
1145
+ return;
1146
+ }
1147
+ await showSlide(presenter, nextIndex);
1148
+ }
1149
+ async function prevSlide(presenter) {
1150
+ const prevIndex = presenter.currentSlide - 1;
1151
+ const { slides } = presenter.deck;
1152
+ const loop = presenter.deck.config.settings?.loop ?? false;
1153
+ if (prevIndex < 0) {
1154
+ if (loop) {
1155
+ clearWindows2(presenter.renderer);
1156
+ for (let i = 0; i < slides.length; i++) {
1157
+ await renderSlide2(presenter.renderer, slides[i]);
1158
+ }
1159
+ presenter.currentSlide = slides.length - 1;
1160
+ updateUIComponents(presenter, slides.length - 1);
1161
+ presenter.renderer.screen.render();
1162
+ }
1163
+ return;
1164
+ }
1165
+ clearWindows2(presenter.renderer);
1166
+ for (let i = 0; i <= prevIndex; i++) {
1167
+ await renderSlide2(presenter.renderer, slides[i]);
1168
+ }
1169
+ presenter.currentSlide = prevIndex;
1170
+ updateUIComponents(presenter, prevIndex);
1171
+ presenter.renderer.screen.render();
1172
+ }
1173
+ async function jumpToSlide(presenter, index) {
1174
+ if (index < 0 || index >= presenter.deck.slides.length) return;
1175
+ clearWindows2(presenter.renderer);
1176
+ for (let i = 0; i <= index; i++) {
1177
+ await renderSlide2(presenter.renderer, presenter.deck.slides[i]);
1178
+ }
1179
+ presenter.currentSlide = index;
1180
+ updateUIComponents(presenter, index);
1181
+ presenter.renderer.screen.render();
1182
+ }
1183
+ function updateProgress(progressBar, current, total) {
1184
+ const progress = (current + 1) / total * 100;
1185
+ progressBar.setProgress(progress);
1186
+ }
1187
+ function updateUIComponents(presenter, currentIndex) {
1188
+ const { slides } = presenter.deck;
1189
+ const currentSlide = slides[currentIndex];
1190
+ const nextSlide2 = slides[currentIndex + 1];
1191
+ if (presenter.notesWindow) {
1192
+ updateNotesWindow(
1193
+ presenter.notesWindow,
1194
+ currentSlide,
1195
+ nextSlide2,
1196
+ currentIndex,
1197
+ slides.length
1198
+ );
1199
+ }
1200
+ if (presenter.progressBar) {
1201
+ updateProgress(presenter.progressBar, currentIndex, slides.length);
1202
+ }
1203
+ }
1204
+ var init_navigation = __esm({
1205
+ "src/presenter/navigation.ts"() {
1206
+ init_esm_shims();
1207
+ init_screen();
1208
+ init_notes_window();
1209
+ }
1210
+ });
1211
+
1212
+ // src/presenter/keyboard-controls.ts
1213
+ function setupControls(presenter) {
1214
+ const { screen } = presenter.renderer;
1215
+ screen.key(["space", "enter", "right", "n"], () => {
1216
+ nextSlide(presenter);
1217
+ });
1218
+ screen.key(["left", "backspace", "p"], () => {
1219
+ prevSlide(presenter);
1220
+ });
1221
+ screen.key(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], (ch) => {
1222
+ const index = parseInt(ch, 10);
1223
+ jumpToSlide(presenter, index);
1224
+ });
1225
+ screen.key(["l"], () => {
1226
+ showSlideList(presenter);
1227
+ });
1228
+ screen.key(["N"], () => {
1229
+ if (presenter.notesWindow) {
1230
+ toggleNotesVisibility(presenter.notesWindow);
1231
+ }
1232
+ });
1233
+ }
1234
+ function showSlideList(presenter) {
1235
+ const { screen } = presenter.renderer;
1236
+ const { slides } = presenter.deck;
1237
+ const listContent = slides.map((slide, i) => {
1238
+ const marker = i === presenter.currentSlide ? "\u25B6 " : " ";
1239
+ return `${marker}${i}: ${slide.frontmatter.title}`;
1240
+ }).join("\n");
1241
+ const listBox = screen.box({
1242
+ top: "center",
1243
+ left: "center",
1244
+ width: 50,
1245
+ height: Math.min(slides.length + 4, 20),
1246
+ border: { type: "line" },
1247
+ label: " SLIDES (press number or Esc) ",
1248
+ style: {
1249
+ fg: "#ffffff",
1250
+ bg: "#0a0a0a",
1251
+ border: { fg: "#ffcc00" }
1252
+ },
1253
+ padding: 1,
1254
+ tags: true,
1255
+ content: listContent
1256
+ });
1257
+ screen.append(listBox);
1258
+ screen.render();
1259
+ const closeList = () => {
1260
+ listBox.destroy();
1261
+ screen.render();
1262
+ };
1263
+ screen.onceKey(["escape", "l", "q"], closeList);
1264
+ screen.onceKey(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], (ch) => {
1265
+ closeList();
1266
+ jumpToSlide(presenter, parseInt(ch ?? "0", 10));
1267
+ });
1268
+ }
1269
+ var init_keyboard_controls = __esm({
1270
+ "src/presenter/keyboard-controls.ts"() {
1271
+ init_esm_shims();
1272
+ init_navigation();
1273
+ init_notes_window();
1274
+ }
1275
+ });
1276
+
1277
+ // src/presenter/main.ts
1278
+ var main_exports = {};
1279
+ __export(main_exports, {
1280
+ jumpToSlide: () => jumpToSlide,
1281
+ present: () => present,
1282
+ prevSlide: () => prevSlide
1283
+ });
1284
+ async function present(slidesDir, options = {}) {
1285
+ const deck = await loadDeck(slidesDir);
1286
+ if (deck.slides.length === 0) {
1287
+ throw new Error(`No slides found in ${slidesDir}`);
1288
+ }
1289
+ const renderer = createRenderer(deck.config.theme);
1290
+ const presenter = {
1291
+ deck,
1292
+ renderer,
1293
+ currentSlide: options.startSlide ?? deck.config.settings?.startSlide ?? 0,
1294
+ isAnimating: false,
1295
+ notesWindow: null,
1296
+ autoAdvanceTimer: null,
1297
+ progressBar: null
1298
+ };
1299
+ if (options.showNotes) {
1300
+ presenter.notesWindow = await createNotesWindow(options.notesTty);
1301
+ }
1302
+ if (deck.config.settings?.showProgress) {
1303
+ presenter.progressBar = createProgressBar(presenter);
1304
+ }
1305
+ setupControls(presenter);
1306
+ await showSlide(presenter, presenter.currentSlide);
1307
+ if (presenter.progressBar) {
1308
+ updateProgress(presenter.progressBar, presenter.currentSlide, deck.slides.length);
1309
+ }
1310
+ presenter.autoAdvanceTimer = startAutoAdvance(presenter);
1311
+ await new Promise((resolve) => {
1312
+ renderer.screen.key(["q", "C-c", "escape"], () => {
1313
+ cleanup(presenter);
1314
+ resolve();
1315
+ });
1316
+ });
1317
+ }
1318
+ function cleanup(presenter) {
1319
+ stopAutoAdvance(presenter.autoAdvanceTimer);
1320
+ if (presenter.notesWindow) {
1321
+ destroyNotesWindow(presenter.notesWindow);
1322
+ }
1323
+ destroyRenderer(presenter.renderer);
1324
+ }
1325
+ function startAutoAdvance(presenter) {
1326
+ const interval = presenter.deck.config.settings?.autoAdvance;
1327
+ if (!interval || interval <= 0) {
1328
+ return null;
1329
+ }
1330
+ return setInterval(() => {
1331
+ if (!presenter.isAnimating) {
1332
+ nextSlide(presenter);
1333
+ }
1334
+ }, interval);
1335
+ }
1336
+ function stopAutoAdvance(timer) {
1337
+ if (timer) {
1338
+ clearInterval(timer);
1339
+ }
1340
+ }
1341
+ function createProgressBar(presenter) {
1342
+ const { screen } = presenter.renderer;
1343
+ const progressBar = blessed.progressbar({
1344
+ bottom: 0,
1345
+ left: 0,
1346
+ width: "100%",
1347
+ height: 1,
1348
+ style: {
1349
+ bg: "#333333",
1350
+ bar: { bg: "#00cc66" }
1351
+ },
1352
+ filled: 0
1353
+ });
1354
+ screen.append(progressBar);
1355
+ return progressBar;
1356
+ }
1357
+ var init_main = __esm({
1358
+ "src/presenter/main.ts"() {
1359
+ init_esm_shims();
1360
+ init_deck_loader();
1361
+ init_screen();
1362
+ init_notes_window();
1363
+ init_keyboard_controls();
1364
+ init_navigation();
1365
+ init_navigation();
1366
+ }
1367
+ });
1368
+
1369
+ // bin/term-deck.ts
1370
+ init_esm_shims();
1371
+
1372
+ // package.json
1373
+ var version = "1.0.16";
1374
+
1375
+ // src/cli/commands/present.ts
1376
+ init_esm_shims();
1377
+ init_main();
1378
+
1379
+ // src/cli/errors.ts
1380
+ init_esm_shims();
1381
+ init_validation();
1382
+ init_slide2();
1383
+ init_deck_loader();
1384
+ init_theme2();
1385
+ function handleError(error) {
1386
+ if (error instanceof ValidationError) {
1387
+ console.error(`
1388
+ ${error.message}`);
1389
+ process.exit(1);
1390
+ }
1391
+ if (error instanceof SlideParseError) {
1392
+ console.error(`
1393
+ Slide error in ${error.filePath}:`);
1394
+ console.error(` ${error.message}`);
1395
+ if (error.cause) {
1396
+ const causeMessage = error.cause instanceof Error ? error.cause.message : String(error.cause);
1397
+ console.error(` Caused by: ${causeMessage}`);
1398
+ }
1399
+ process.exit(1);
1400
+ }
1401
+ if (error instanceof DeckLoadError) {
1402
+ console.error(`
1403
+ Failed to load deck from ${error.slidesDir}:`);
1404
+ console.error(` ${error.message}`);
1405
+ process.exit(1);
1406
+ }
1407
+ if (error instanceof ThemeError) {
1408
+ console.error("\nTheme error:");
1409
+ console.error(` ${error.message}`);
1410
+ process.exit(1);
1411
+ }
1412
+ if (error instanceof Error) {
1413
+ if (error.message.includes("ENOENT")) {
1414
+ console.error("\nFile or directory not found.");
1415
+ console.error(` ${error.message}`);
1416
+ process.exit(1);
1417
+ }
1418
+ if (error.message.includes("ffmpeg")) {
1419
+ console.error("\nffmpeg error:");
1420
+ console.error(` ${error.message}`);
1421
+ console.error("\nMake sure ffmpeg is installed:");
1422
+ console.error(" macOS: brew install ffmpeg");
1423
+ console.error(" Ubuntu: sudo apt install ffmpeg");
1424
+ process.exit(1);
1425
+ }
1426
+ console.error(`
1427
+ Error: ${error.message}`);
1428
+ if (process.env.DEBUG) {
1429
+ console.error(error.stack);
1430
+ }
1431
+ process.exit(1);
1432
+ }
1433
+ console.error("\nUnknown error occurred");
1434
+ console.error(error);
1435
+ process.exit(1);
1436
+ }
1437
+
1438
+ // src/cli/commands/present.ts
1439
+ var presentCommand = new Command("present").description("Start a presentation").argument("<dir>", "Slides directory").option("-s, --start <n>", "Start at slide number", "0").option("-n, --notes", "Show presenter notes in separate terminal").option("--notes-tty <path>", "TTY device for notes window (e.g., /dev/ttys001)").option("-l, --loop", "Loop back to first slide after last").action(async (dir, options) => {
1440
+ try {
1441
+ await present(dir, {
1442
+ startSlide: Number.parseInt(options.start, 10),
1443
+ showNotes: options.notes,
1444
+ notesTty: options.notesTty,
1445
+ loop: options.loop
1446
+ });
1447
+ } catch (error) {
1448
+ handleError(error);
1449
+ }
1450
+ });
1451
+
1452
+ // src/cli/commands/export.ts
1453
+ init_esm_shims();
1454
+
1455
+ // src/export/recorder.ts
1456
+ init_esm_shims();
1457
+
1458
+ // src/export/recording-session.ts
1459
+ init_esm_shims();
1460
+ async function createRecordingSession(options) {
1461
+ const tempDir = join(tmpdir(), `term-deck-export-${Date.now()}`);
1462
+ await mkdir(tempDir, { recursive: true });
1463
+ return {
1464
+ tempDir,
1465
+ frameCount: 0,
1466
+ width: options.width ?? 120,
1467
+ height: options.height ?? 40,
1468
+ fps: options.fps ?? 30
1469
+ };
1470
+ }
1471
+ async function saveFrame(session, png) {
1472
+ const frameNum = session.frameCount.toString().padStart(6, "0");
1473
+ const framePath = join(session.tempDir, `frame_${frameNum}.png`);
1474
+ await writeFile(framePath, png);
1475
+ session.frameCount++;
1476
+ }
1477
+ async function cleanupSession(session) {
1478
+ await rm(session.tempDir, { recursive: true, force: true });
1479
+ }
1480
+
1481
+ // src/export/presentation-exporter.ts
1482
+ init_esm_shims();
1483
+ init_deck_loader();
1484
+ init_screen();
1485
+
1486
+ // src/renderer/types/screen.ts
1487
+ init_esm_shims();
1488
+ function setScreenDimensions(screen, width, height) {
1489
+ screen.width = width;
1490
+ screen.height = height;
1491
+ }
1492
+
1493
+ // src/export/utils/virtual-terminal.ts
1494
+ init_esm_shims();
1495
+ var VirtualTerminal = class {
1496
+ constructor(width, height) {
1497
+ this.width = width;
1498
+ this.height = height;
1499
+ this.buffer = Array.from(
1500
+ { length: height },
1501
+ () => Array(width).fill(" ")
1502
+ );
1503
+ this.colors = Array.from(
1504
+ { length: height },
1505
+ () => Array(width).fill("#ffffff")
1506
+ );
1507
+ }
1508
+ buffer;
1509
+ colors;
1510
+ /**
1511
+ * Set character at position with optional color
1512
+ */
1513
+ setChar(x, y, char, color = "#ffffff") {
1514
+ if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
1515
+ this.buffer[y][x] = char;
1516
+ this.colors[y][x] = color;
1517
+ }
1518
+ }
1519
+ /**
1520
+ * Clear the buffer to blank spaces
1521
+ */
1522
+ clear() {
1523
+ for (let y = 0; y < this.height; y++) {
1524
+ for (let x = 0; x < this.width; x++) {
1525
+ this.buffer[y][x] = " ";
1526
+ this.colors[y][x] = "#ffffff";
1527
+ }
1528
+ }
1529
+ }
1530
+ /**
1531
+ * Get buffer contents as plain text string
1532
+ */
1533
+ toString() {
1534
+ return this.buffer.map((row) => row.join("")).join("\n");
1535
+ }
1536
+ /**
1537
+ * Get the raw character buffer
1538
+ */
1539
+ getBuffer() {
1540
+ return this.buffer;
1541
+ }
1542
+ /**
1543
+ * Get the color buffer
1544
+ */
1545
+ getColors() {
1546
+ return this.colors;
1547
+ }
1548
+ /**
1549
+ * Convert buffer to PNG image data
1550
+ */
1551
+ async toPng() {
1552
+ return renderTerminalToPng(this.buffer, this.colors, this.width, this.height);
1553
+ }
1554
+ };
1555
+ var CHAR_WIDTH = 10;
1556
+ var CHAR_HEIGHT = 20;
1557
+ async function renderTerminalToPng(buffer, colors, width, height) {
1558
+ const { createCanvas } = await import('canvas');
1559
+ const canvas = createCanvas(width * CHAR_WIDTH, height * CHAR_HEIGHT);
1560
+ const ctx = canvas.getContext("2d");
1561
+ ctx.fillStyle = "#0a0a0a";
1562
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1563
+ ctx.font = `${CHAR_HEIGHT - 4}px monospace`;
1564
+ ctx.textBaseline = "top";
1565
+ for (let y = 0; y < height; y++) {
1566
+ for (let x = 0; x < width; x++) {
1567
+ const char = buffer[y][x];
1568
+ const color = colors[y][x];
1569
+ if (char !== " ") {
1570
+ ctx.fillStyle = color;
1571
+ ctx.fillText(char, x * CHAR_WIDTH, y * CHAR_HEIGHT + 2);
1572
+ }
1573
+ }
1574
+ }
1575
+ return canvas.toBuffer("image/png");
1576
+ }
1577
+
1578
+ // src/export/capture/screen-capture.ts
1579
+ init_esm_shims();
1580
+
1581
+ // src/export/utils/color-conversion.ts
1582
+ init_esm_shims();
1583
+ function ansi256ToHex(code) {
1584
+ const standard16 = [
1585
+ "#000000",
1586
+ "#800000",
1587
+ "#008000",
1588
+ "#808000",
1589
+ "#000080",
1590
+ "#800080",
1591
+ "#008080",
1592
+ "#c0c0c0",
1593
+ "#808080",
1594
+ "#ff0000",
1595
+ "#00ff00",
1596
+ "#ffff00",
1597
+ "#0000ff",
1598
+ "#ff00ff",
1599
+ "#00ffff",
1600
+ "#ffffff"
1601
+ ];
1602
+ if (code < 16) {
1603
+ return standard16[code];
1604
+ }
1605
+ if (code < 232) {
1606
+ const n = code - 16;
1607
+ const r = Math.floor(n / 36) * 51;
1608
+ const g = Math.floor(n % 36 / 6) * 51;
1609
+ const b = n % 6 * 51;
1610
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
1611
+ }
1612
+ const gray = (code - 232) * 10 + 8;
1613
+ const hex = gray.toString(16).padStart(2, "0");
1614
+ return `#${hex}${hex}${hex}`;
1615
+ }
1616
+ function extractColor(attr) {
1617
+ if (!attr) return null;
1618
+ if (typeof attr === "object" && attr.fg !== void 0) {
1619
+ if (typeof attr.fg === "string" && attr.fg.startsWith("#")) {
1620
+ return attr.fg;
1621
+ }
1622
+ if (typeof attr.fg === "number") {
1623
+ return ansi256ToHex(attr.fg);
1624
+ }
1625
+ }
1626
+ return null;
1627
+ }
1628
+
1629
+ // src/export/capture/screen-capture.ts
1630
+ function captureScreen(screen, vt) {
1631
+ const lines = screen.lines || [];
1632
+ for (let y = 0; y < Math.min(lines.length, vt.height); y++) {
1633
+ const line = lines[y];
1634
+ if (!line) continue;
1635
+ for (let x = 0; x < Math.min(line.length, vt.width); x++) {
1636
+ const cell = line[x];
1637
+ if (!cell) continue;
1638
+ const char = Array.isArray(cell) ? cell[0] : cell;
1639
+ const attr = Array.isArray(cell) ? cell[1] : null;
1640
+ const color = extractColor(attr) || "#ffffff";
1641
+ vt.setChar(x, y, char || " ", color);
1642
+ }
1643
+ }
1644
+ }
1645
+
1646
+ // src/export/encoding/ffmpeg-encoder.ts
1647
+ init_esm_shims();
1648
+ async function checkFfmpeg() {
1649
+ try {
1650
+ await execa("which", ["ffmpeg"]);
1651
+ } catch {
1652
+ throw new Error(
1653
+ "ffmpeg not found. Install it with:\n macOS: brew install ffmpeg\n Ubuntu: sudo apt install ffmpeg"
1654
+ );
1655
+ }
1656
+ }
1657
+ function detectFormat(output) {
1658
+ if (output.endsWith(".gif")) return "gif";
1659
+ if (output.endsWith(".mp4")) return "mp4";
1660
+ throw new Error(
1661
+ `Unknown output format for ${output}. Use .mp4 or .gif extension.`
1662
+ );
1663
+ }
1664
+ async function encodeVideo(options) {
1665
+ const { inputPattern, output, format, fps, quality } = options;
1666
+ if (format === "mp4") {
1667
+ await encodeMp4(inputPattern, output, fps, quality ?? 80);
1668
+ } else {
1669
+ await encodeGif(inputPattern, output, fps);
1670
+ }
1671
+ }
1672
+ async function encodeMp4(input, output, fps, quality) {
1673
+ const crf = Math.round(51 - quality / 100 * 33);
1674
+ await execa("ffmpeg", [
1675
+ "-y",
1676
+ // Overwrite output file
1677
+ "-framerate",
1678
+ fps.toString(),
1679
+ "-i",
1680
+ input,
1681
+ "-c:v",
1682
+ "libx264",
1683
+ // H.264 codec
1684
+ "-crf",
1685
+ crf.toString(),
1686
+ "-pix_fmt",
1687
+ "yuv420p",
1688
+ // Pixel format for compatibility
1689
+ output
1690
+ ]);
1691
+ }
1692
+ async function encodeGif(input, output, fps) {
1693
+ const { tmpdir: tmpdir2 } = await import('os');
1694
+ const { join: join5 } = await import('path');
1695
+ const paletteFile = join5(tmpdir2(), `palette-${Date.now()}.png`);
1696
+ try {
1697
+ await execa("ffmpeg", [
1698
+ "-y",
1699
+ "-framerate",
1700
+ fps.toString(),
1701
+ "-i",
1702
+ input,
1703
+ "-vf",
1704
+ `fps=${fps},scale=-1:-1:flags=lanczos,palettegen=stats_mode=diff`,
1705
+ paletteFile
1706
+ ]);
1707
+ await execa("ffmpeg", [
1708
+ "-y",
1709
+ "-framerate",
1710
+ fps.toString(),
1711
+ "-i",
1712
+ input,
1713
+ "-i",
1714
+ paletteFile,
1715
+ "-lavfi",
1716
+ `fps=${fps},scale=-1:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`,
1717
+ output
1718
+ ]);
1719
+ } finally {
1720
+ try {
1721
+ await unlink(paletteFile);
1722
+ } catch {
1723
+ }
1724
+ }
1725
+ }
1726
+
1727
+ // src/export/presentation-exporter.ts
1728
+ async function encodeFramesToVideo(tempDir, output, format, fps, quality) {
1729
+ const inputPattern = join(tempDir, "frame_%06d.png");
1730
+ await encodeVideo({
1731
+ inputPattern,
1732
+ output,
1733
+ format,
1734
+ fps,
1735
+ quality
1736
+ });
1737
+ }
1738
+ async function exportPresentation(slidesDir, options) {
1739
+ await checkFfmpeg();
1740
+ const format = detectFormat(options.output);
1741
+ const deck = await loadDeck(slidesDir);
1742
+ if (deck.slides.length === 0) {
1743
+ throw new Error(`No slides found in ${slidesDir}`);
1744
+ }
1745
+ const session = await createRecordingSession(options);
1746
+ const vt = new VirtualTerminal(session.width, session.height);
1747
+ const renderer = createRenderer(deck.config.theme);
1748
+ setScreenDimensions(renderer.screen, session.width, session.height);
1749
+ const slideTime = options.slideTime ?? 3;
1750
+ const framesPerSlide = session.fps * slideTime;
1751
+ console.log(`Exporting ${deck.slides.length} slides...`);
1752
+ try {
1753
+ for (let i = 0; i < deck.slides.length; i++) {
1754
+ const slide = deck.slides[i];
1755
+ console.log(` Slide ${i + 1}/${deck.slides.length}: ${slide.frontmatter.title}`);
1756
+ await renderSlide2(renderer, slide);
1757
+ for (let f = 0; f < framesPerSlide; f++) {
1758
+ renderer.screen.render();
1759
+ captureScreen(renderer.screen, vt);
1760
+ const png = await vt.toPng();
1761
+ await saveFrame(session, png);
1762
+ }
1763
+ }
1764
+ console.log("Encoding video...");
1765
+ await encodeFramesToVideo(
1766
+ session.tempDir,
1767
+ options.output,
1768
+ format,
1769
+ session.fps,
1770
+ options.quality
1771
+ );
1772
+ console.log(`Exported to ${options.output}`);
1773
+ } finally {
1774
+ destroyRenderer(renderer);
1775
+ await cleanupSession(session);
1776
+ }
1777
+ }
1778
+
1779
+ // src/export/ansi-recorder.ts
1780
+ init_esm_shims();
1781
+ init_deck_loader();
1782
+ init_screen();
1783
+
1784
+ // src/cli/commands/export.ts
1785
+ var exportCommand = new Command("export").description("Export presentation to GIF or MP4").argument("<dir>", "Slides directory").requiredOption("-o, --output <file>", "Output file (.mp4 or .gif)").option("-w, --width <n>", "Terminal width in characters", "120").option("-h, --height <n>", "Terminal height in characters", "40").option("--fps <n>", "Frames per second", "30").option("-t, --slide-time <n>", "Seconds per slide", "3").option("-q, --quality <n>", "Quality 1-100 (video only)", "80").action(async (dir, options) => {
1786
+ try {
1787
+ await exportPresentation(dir, {
1788
+ output: options.output,
1789
+ width: Number.parseInt(options.width, 10),
1790
+ height: Number.parseInt(options.height, 10),
1791
+ fps: Number.parseInt(options.fps, 10),
1792
+ slideTime: Number.parseFloat(options.slideTime),
1793
+ quality: Number.parseInt(options.quality, 10)
1794
+ });
1795
+ } catch (error) {
1796
+ handleError(error);
1797
+ }
1798
+ });
1799
+
1800
+ // src/cli/commands/init.ts
1801
+ init_esm_shims();
1802
+ var initCommand = new Command("init").description("Create a new presentation deck").argument("<name>", "Deck name (will create directory)").option("-t, --theme <name>", "Theme to use", "matrix").action(async (name, options) => {
1803
+ try {
1804
+ await initDeck(name, options.theme);
1805
+ console.log(`Created deck: ${name}/`);
1806
+ console.log("\nNext steps:");
1807
+ console.log(` cd ${name}/slides`);
1808
+ console.log(" term-deck present .");
1809
+ } catch (error) {
1810
+ handleError(error);
1811
+ }
1812
+ });
1813
+ async function initDeck(name, theme) {
1814
+ const deckDir = join(process.cwd(), name);
1815
+ const slidesDir = join(deckDir, "slides");
1816
+ await mkdir(slidesDir, { recursive: true });
1817
+ await writeFile(join(slidesDir, ".gitkeep"), "");
1818
+ const configContent = `import { defineConfig } from 'term-deck'
1819
+ import matrix from '@term-deck/theme-matrix'
1820
+
1821
+ export default defineConfig({
1822
+ title: '${name}',
1823
+ theme: matrix,
1824
+ })
1825
+ `;
1826
+ await writeFile(join(slidesDir, "deck.config.ts"), configContent);
1827
+ const slide1 = `---
1828
+ title: ${name.toUpperCase()}
1829
+ bigText: ${name.toUpperCase()}
1830
+ gradient: fire
1831
+ ---
1832
+
1833
+ {GREEN}Welcome to your presentation{/}
1834
+
1835
+ Press {CYAN}Space{/} or {CYAN}\u2192{/} to advance
1836
+ `;
1837
+ const slide2 = `---
1838
+ title: SLIDE TWO
1839
+ bigText: HELLO
1840
+ gradient: cool
1841
+ ---
1842
+
1843
+ {WHITE}This is the second slide{/}
1844
+
1845
+ - Point one
1846
+ - Point two
1847
+ - Point three
1848
+
1849
+ <!-- notes -->
1850
+ Remember to explain each point clearly.
1851
+ `;
1852
+ const slide3 = `---
1853
+ title: THE END
1854
+ bigText: FIN
1855
+ gradient: pink
1856
+ ---
1857
+
1858
+ {ORANGE}Thank you!{/}
1859
+
1860
+ Press {CYAN}q{/} to exit
1861
+ `;
1862
+ await writeFile(join(slidesDir, "01-intro.md"), slide1);
1863
+ await writeFile(join(slidesDir, "02-content.md"), slide2);
1864
+ await writeFile(join(slidesDir, "03-end.md"), slide3);
1865
+ const readme = `# ${name}
1866
+
1867
+ A term-deck presentation.
1868
+
1869
+ ## Usage
1870
+
1871
+ \`\`\`bash
1872
+ cd slides
1873
+ term-deck present .
1874
+ \`\`\`
1875
+
1876
+ ## Export
1877
+
1878
+ \`\`\`bash
1879
+ term-deck export slides/ -o ${name}.mp4
1880
+ term-deck export slides/ -o ${name}.gif
1881
+ \`\`\`
1882
+
1883
+ ## Hotkeys
1884
+
1885
+ | Key | Action |
1886
+ |-----|--------|
1887
+ | Space / \u2192 | Next slide |
1888
+ | \u2190 | Previous slide |
1889
+ | 0-9 | Jump to slide |
1890
+ | l | Show slide list |
1891
+ | q | Quit |
1892
+ `;
1893
+ await writeFile(join(deckDir, "README.md"), readme);
1894
+ }
1895
+
1896
+ // bin/term-deck.ts
1897
+ var program = new Command();
1898
+ program.name("term-deck").description("Terminal presentation tool with a cyberpunk aesthetic").version(version);
1899
+ program.addCommand(presentCommand);
1900
+ program.addCommand(exportCommand);
1901
+ program.addCommand(initCommand);
1902
+ program.argument("[dir]", "Slides directory to present").action(async (dir) => {
1903
+ if (dir) {
1904
+ try {
1905
+ const { present: present2 } = await Promise.resolve().then(() => (init_main(), main_exports));
1906
+ await present2(dir, {});
1907
+ } catch (error) {
1908
+ handleError(error);
1909
+ }
1910
+ } else {
1911
+ program.help();
1912
+ }
1913
+ });
1914
+ program.parse();
1915
+ //# sourceMappingURL=term-deck.js.map
1916
+ //# sourceMappingURL=term-deck.js.map