@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,157 +0,0 @@
1
- /**
2
- * Tests for CLI help text
3
- */
4
-
5
- import { describe, test, expect } from 'bun:test';
6
- import { Command } from 'commander';
7
- import { presentCommand } from '../commands/present.js';
8
- import { exportCommand } from '../commands/export.js';
9
- import { initCommand } from '../commands/init.js';
10
-
11
- describe('CLI help text', () => {
12
- test('present command has description', () => {
13
- expect(presentCommand.description()).toBeTruthy();
14
- expect(presentCommand.description()).toContain('present');
15
- });
16
-
17
- test('present command has all options', () => {
18
- const options = presentCommand.options;
19
-
20
- const optionNames = options.map((opt) => opt.long);
21
- expect(optionNames).toContain('--start');
22
- expect(optionNames).toContain('--notes');
23
- expect(optionNames).toContain('--notes-tty');
24
- expect(optionNames).toContain('--loop');
25
- });
26
-
27
- test('present command has short options', () => {
28
- const options = presentCommand.options;
29
-
30
- const shortNames = options.map((opt) => opt.short).filter(Boolean);
31
- expect(shortNames).toContain('-s');
32
- expect(shortNames).toContain('-n');
33
- expect(shortNames).toContain('-l');
34
- });
35
-
36
- test('present command requires dir argument', () => {
37
- const args = presentCommand.registeredArguments;
38
- expect(args.length).toBeGreaterThan(0);
39
- expect(args[0].name()).toBe('dir');
40
- expect(args[0].required).toBe(true);
41
- });
42
-
43
- test('export command has description', () => {
44
- expect(exportCommand.description()).toBeTruthy();
45
- expect(exportCommand.description().toLowerCase()).toContain('export');
46
- });
47
-
48
- test('export command has all options', () => {
49
- const options = exportCommand.options;
50
-
51
- const optionNames = options.map((opt) => opt.long);
52
- expect(optionNames).toContain('--output');
53
- expect(optionNames).toContain('--width');
54
- expect(optionNames).toContain('--height');
55
- expect(optionNames).toContain('--fps');
56
- expect(optionNames).toContain('--slide-time');
57
- expect(optionNames).toContain('--quality');
58
- });
59
-
60
- test('export command has short options', () => {
61
- const options = exportCommand.options;
62
-
63
- const shortNames = options.map((opt) => opt.short).filter(Boolean);
64
- expect(shortNames).toContain('-o');
65
- expect(shortNames).toContain('-w');
66
- expect(shortNames).toContain('-h');
67
- expect(shortNames).toContain('-t');
68
- expect(shortNames).toContain('-q');
69
- });
70
-
71
- test('export command requires dir argument', () => {
72
- const args = exportCommand.registeredArguments;
73
- expect(args.length).toBeGreaterThan(0);
74
- expect(args[0].name()).toBe('dir');
75
- expect(args[0].required).toBe(true);
76
- });
77
-
78
- test('export command requires output option', () => {
79
- const outputOption = exportCommand.options.find((opt) => opt.long === '--output');
80
- expect(outputOption).toBeTruthy();
81
- expect(outputOption?.required).toBe(true);
82
- });
83
-
84
- test('init command has description', () => {
85
- expect(initCommand.description()).toBeTruthy();
86
- expect(initCommand.description()).toContain('presentation deck');
87
- });
88
-
89
- test('init command has theme option', () => {
90
- const options = initCommand.options;
91
-
92
- const optionNames = options.map((opt) => opt.long);
93
- expect(optionNames).toContain('--theme');
94
- });
95
-
96
- test('init command has short theme option', () => {
97
- const options = initCommand.options;
98
-
99
- const shortNames = options.map((opt) => opt.short).filter(Boolean);
100
- expect(shortNames).toContain('-t');
101
- });
102
-
103
- test('init command requires name argument', () => {
104
- const args = initCommand.registeredArguments;
105
- expect(args.length).toBeGreaterThan(0);
106
- expect(args[0].name()).toBe('name');
107
- expect(args[0].required).toBe(true);
108
- });
109
-
110
- test('main program would have version', () => {
111
- // Test that version can be imported from package.json
112
- const pkg = require('../../../package.json');
113
- expect(pkg.version).toBeTruthy();
114
- expect(typeof pkg.version).toBe('string');
115
- });
116
-
117
- test('main program would have name', () => {
118
- const pkg = require('../../../package.json');
119
- expect(pkg.name).toBeTruthy();
120
- expect(pkg.name).toBe('term-deck');
121
- });
122
-
123
- test('all commands have help text', () => {
124
- const commands = [presentCommand, exportCommand, initCommand];
125
-
126
- for (const cmd of commands) {
127
- // Commander automatically generates help
128
- const helpInfo = cmd.helpInformation();
129
- expect(helpInfo).toBeTruthy();
130
- expect(helpInfo.length).toBeGreaterThan(0);
131
- }
132
- });
133
-
134
- test('present command help includes options descriptions', () => {
135
- const helpInfo = presentCommand.helpInformation();
136
-
137
- expect(helpInfo).toContain('start');
138
- expect(helpInfo).toContain('notes');
139
- expect(helpInfo).toContain('loop');
140
- });
141
-
142
- test('export command help includes options descriptions', () => {
143
- const helpInfo = exportCommand.helpInformation();
144
-
145
- expect(helpInfo).toContain('output');
146
- expect(helpInfo).toContain('width');
147
- expect(helpInfo).toContain('height');
148
- expect(helpInfo).toContain('fps');
149
- });
150
-
151
- test('init command help includes options descriptions', () => {
152
- const helpInfo = initCommand.helpInformation();
153
-
154
- expect(helpInfo).toContain('theme');
155
- expect(helpInfo).toContain('name');
156
- });
157
- });
@@ -1,110 +0,0 @@
1
- /**
2
- * Tests for init command
3
- */
4
-
5
- import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
6
- import { rmSync, existsSync } from 'node:fs';
7
- import { join } from 'node:path';
8
- import { initDeck } from '../commands/init.js';
9
-
10
- const TEST_DECK_NAME = 'test-deck-init';
11
- const TEST_DECK_PATH = join(process.cwd(), TEST_DECK_NAME);
12
-
13
- describe('initDeck', () => {
14
- beforeEach(() => {
15
- // Clean up if test directory exists
16
- if (existsSync(TEST_DECK_PATH)) {
17
- rmSync(TEST_DECK_PATH, { recursive: true, force: true });
18
- }
19
- });
20
-
21
- afterEach(() => {
22
- // Clean up after test
23
- if (existsSync(TEST_DECK_PATH)) {
24
- rmSync(TEST_DECK_PATH, { recursive: true, force: true });
25
- }
26
- });
27
-
28
- test('creates deck directory structure', async () => {
29
- await initDeck(TEST_DECK_NAME, 'matrix');
30
-
31
- // Check directories
32
- expect(existsSync(TEST_DECK_PATH)).toBe(true);
33
- expect(existsSync(join(TEST_DECK_PATH, 'slides'))).toBe(true);
34
- });
35
-
36
- test('creates deck.config.ts', async () => {
37
- await initDeck(TEST_DECK_NAME, 'matrix');
38
-
39
- const configPath = join(TEST_DECK_PATH, 'slides', 'deck.config.ts');
40
- expect(existsSync(configPath)).toBe(true);
41
-
42
- const content = await Bun.file(configPath).text();
43
- expect(content).toContain('import { defineConfig }');
44
- expect(content).toContain('import matrix from');
45
- expect(content).toContain(`title: '${TEST_DECK_NAME}'`);
46
- });
47
-
48
- test('creates sample slides', async () => {
49
- await initDeck(TEST_DECK_NAME, 'matrix');
50
-
51
- const slidesDir = join(TEST_DECK_PATH, 'slides');
52
- expect(existsSync(join(slidesDir, '01-intro.md'))).toBe(true);
53
- expect(existsSync(join(slidesDir, '02-content.md'))).toBe(true);
54
- expect(existsSync(join(slidesDir, '03-end.md'))).toBe(true);
55
- });
56
-
57
- test('slides have valid frontmatter', async () => {
58
- await initDeck(TEST_DECK_NAME, 'matrix');
59
-
60
- const slidesDir = join(TEST_DECK_PATH, 'slides');
61
- const slide1 = await Bun.file(join(slidesDir, '01-intro.md')).text();
62
- const slide2 = await Bun.file(join(slidesDir, '02-content.md')).text();
63
- const slide3 = await Bun.file(join(slidesDir, '03-end.md')).text();
64
-
65
- // Check frontmatter structure
66
- expect(slide1).toMatch(/^---\s*\ntitle:/);
67
- expect(slide1).toContain('bigText:');
68
- expect(slide1).toContain('gradient:');
69
-
70
- expect(slide2).toMatch(/^---\s*\ntitle:/);
71
- expect(slide2).toContain('bigText:');
72
- expect(slide2).toContain('gradient:');
73
-
74
- expect(slide3).toMatch(/^---\s*\ntitle:/);
75
- expect(slide3).toContain('bigText:');
76
- expect(slide3).toContain('gradient:');
77
- });
78
-
79
- test('slide 2 includes presenter notes', async () => {
80
- await initDeck(TEST_DECK_NAME, 'matrix');
81
-
82
- const slidesDir = join(TEST_DECK_PATH, 'slides');
83
- const slide2 = await Bun.file(join(slidesDir, '02-content.md')).text();
84
-
85
- expect(slide2).toContain('<!-- notes -->');
86
- });
87
-
88
- test('creates README.md', async () => {
89
- await initDeck(TEST_DECK_NAME, 'matrix');
90
-
91
- const readmePath = join(TEST_DECK_PATH, 'README.md');
92
- expect(existsSync(readmePath)).toBe(true);
93
-
94
- const content = await Bun.file(readmePath).text();
95
- expect(content).toContain(`# ${TEST_DECK_NAME}`);
96
- expect(content).toContain('term-deck present');
97
- expect(content).toContain('term-deck export');
98
- expect(content).toContain('Hotkeys');
99
- });
100
-
101
- test('uses deck name in slide titles', async () => {
102
- await initDeck(TEST_DECK_NAME, 'matrix');
103
-
104
- const slide1 = await Bun.file(
105
- join(TEST_DECK_PATH, 'slides', '01-intro.md'),
106
- ).text();
107
-
108
- expect(slide1).toContain(TEST_DECK_NAME.toUpperCase());
109
- });
110
- });
@@ -1,33 +0,0 @@
1
- /**
2
- * Export Command
3
- *
4
- * Exports a presentation to GIF or MP4 format.
5
- */
6
-
7
- import { Command } from 'commander';
8
- import { exportPresentation } from '../../export/recorder.js';
9
- import { handleError } from '../errors.js';
10
-
11
- export const exportCommand = new Command('export')
12
- .description('Export presentation to GIF or MP4')
13
- .argument('<dir>', 'Slides directory')
14
- .requiredOption('-o, --output <file>', 'Output file (.mp4 or .gif)')
15
- .option('-w, --width <n>', 'Terminal width in characters', '120')
16
- .option('-h, --height <n>', 'Terminal height in characters', '40')
17
- .option('--fps <n>', 'Frames per second', '30')
18
- .option('-t, --slide-time <n>', 'Seconds per slide', '3')
19
- .option('-q, --quality <n>', 'Quality 1-100 (video only)', '80')
20
- .action(async (dir, options) => {
21
- try {
22
- await exportPresentation(dir, {
23
- output: options.output,
24
- width: Number.parseInt(options.width, 10),
25
- height: Number.parseInt(options.height, 10),
26
- fps: Number.parseInt(options.fps, 10),
27
- slideTime: Number.parseFloat(options.slideTime),
28
- quality: Number.parseInt(options.quality, 10),
29
- });
30
- } catch (error) {
31
- handleError(error);
32
- }
33
- });
@@ -1,125 +0,0 @@
1
- /**
2
- * Init Command
3
- *
4
- * Creates a new presentation deck with sample slides and configuration.
5
- */
6
-
7
- import { Command } from 'commander';
8
- import { join } from 'node:path';
9
- import { handleError } from '../errors.js';
10
-
11
- export const initCommand = new Command('init')
12
- .description('Create a new presentation deck')
13
- .argument('<name>', 'Deck name (will create directory)')
14
- .option('-t, --theme <name>', 'Theme to use', 'matrix')
15
- .action(async (name, options) => {
16
- try {
17
- await initDeck(name, options.theme);
18
- console.log(`Created deck: ${name}/`);
19
- console.log('\nNext steps:');
20
- console.log(` cd ${name}/slides`);
21
- console.log(' term-deck present .');
22
- } catch (error) {
23
- handleError(error);
24
- }
25
- });
26
-
27
- /**
28
- * Initialize a new deck directory
29
- *
30
- * Creates the directory structure, configuration file, sample slides,
31
- * and README for a new presentation deck.
32
- */
33
- export async function initDeck(name: string, theme: string): Promise<void> {
34
- const deckDir = join(process.cwd(), name);
35
- const slidesDir = join(deckDir, 'slides');
36
-
37
- // Create directories
38
- await Bun.write(join(slidesDir, '.gitkeep'), '');
39
-
40
- // Create deck.config.ts
41
- const configContent = `import { defineConfig } from 'term-deck'
42
- import matrix from '@term-deck/theme-matrix'
43
-
44
- export default defineConfig({
45
- title: '${name}',
46
- theme: matrix,
47
- })
48
- `;
49
- await Bun.write(join(slidesDir, 'deck.config.ts'), configContent);
50
-
51
- // Create sample slides
52
- const slide1 = `---
53
- title: ${name.toUpperCase()}
54
- bigText: ${name.toUpperCase()}
55
- gradient: fire
56
- ---
57
-
58
- {GREEN}Welcome to your presentation{/}
59
-
60
- Press {CYAN}Space{/} or {CYAN}→{/} to advance
61
- `;
62
-
63
- const slide2 = `---
64
- title: SLIDE TWO
65
- bigText: HELLO
66
- gradient: cool
67
- ---
68
-
69
- {WHITE}This is the second slide{/}
70
-
71
- - Point one
72
- - Point two
73
- - Point three
74
-
75
- <!-- notes -->
76
- Remember to explain each point clearly.
77
- `;
78
-
79
- const slide3 = `---
80
- title: THE END
81
- bigText: FIN
82
- gradient: pink
83
- ---
84
-
85
- {ORANGE}Thank you!{/}
86
-
87
- Press {CYAN}q{/} to exit
88
- `;
89
-
90
- await Bun.write(join(slidesDir, '01-intro.md'), slide1);
91
- await Bun.write(join(slidesDir, '02-content.md'), slide2);
92
- await Bun.write(join(slidesDir, '03-end.md'), slide3);
93
-
94
- // Create README
95
- const readme = `# ${name}
96
-
97
- A term-deck presentation.
98
-
99
- ## Usage
100
-
101
- \`\`\`bash
102
- cd slides
103
- term-deck present .
104
- \`\`\`
105
-
106
- ## Export
107
-
108
- \`\`\`bash
109
- term-deck export slides/ -o ${name}.mp4
110
- term-deck export slides/ -o ${name}.gif
111
- \`\`\`
112
-
113
- ## Hotkeys
114
-
115
- | Key | Action |
116
- |-----|--------|
117
- | Space / → | Next slide |
118
- | ← | Previous slide |
119
- | 0-9 | Jump to slide |
120
- | l | Show slide list |
121
- | q | Quit |
122
- `;
123
-
124
- await Bun.write(join(deckDir, 'README.md'), readme);
125
- }
@@ -1,29 +0,0 @@
1
- /**
2
- * Present Command
3
- *
4
- * Starts a presentation with the given options.
5
- */
6
-
7
- import { Command } from 'commander';
8
- import { present } from '../../presenter/main.js';
9
- import { handleError } from '../errors.js';
10
-
11
- export const presentCommand = new Command('present')
12
- .description('Start a presentation')
13
- .argument('<dir>', 'Slides directory')
14
- .option('-s, --start <n>', 'Start at slide number', '0')
15
- .option('-n, --notes', 'Show presenter notes in separate terminal')
16
- .option('--notes-tty <path>', 'TTY device for notes window (e.g., /dev/ttys001)')
17
- .option('-l, --loop', 'Loop back to first slide after last')
18
- .action(async (dir, options) => {
19
- try {
20
- await present(dir, {
21
- startSlide: Number.parseInt(options.start, 10),
22
- showNotes: options.notes,
23
- notesTty: options.notesTty,
24
- loop: options.loop,
25
- });
26
- } catch (error) {
27
- handleError(error);
28
- }
29
- });
package/src/cli/errors.ts DELETED
@@ -1,77 +0,0 @@
1
- /**
2
- * CLI Error Handling
3
- *
4
- * Provides user-friendly error messages for various error types
5
- * that can occur during CLI operations.
6
- */
7
-
8
- import { ValidationError } from '../schemas/validation.js';
9
- import { SlideParseError, DeckLoadError } from '../core/slide.js';
10
- import { ThemeError } from '../core/theme.js';
11
-
12
- /**
13
- * Handle CLI errors with user-friendly messages
14
- *
15
- * Converts various error types into readable console output
16
- * and exits with appropriate status code.
17
- */
18
- export function handleError(error: unknown): never {
19
- if (error instanceof ValidationError) {
20
- console.error(`\n${error.message}`);
21
- process.exit(1);
22
- }
23
-
24
- if (error instanceof SlideParseError) {
25
- console.error(`\nSlide error in ${error.filePath}:`);
26
- console.error(` ${error.message}`);
27
- if (error.cause) {
28
- const causeMessage = error.cause instanceof Error ? error.cause.message : String(error.cause);
29
- console.error(` Caused by: ${causeMessage}`);
30
- }
31
- process.exit(1);
32
- }
33
-
34
- if (error instanceof DeckLoadError) {
35
- console.error(`\nFailed to load deck from ${error.slidesDir}:`);
36
- console.error(` ${error.message}`);
37
- process.exit(1);
38
- }
39
-
40
- if (error instanceof ThemeError) {
41
- console.error('\nTheme error:');
42
- console.error(` ${error.message}`);
43
- process.exit(1);
44
- }
45
-
46
- if (error instanceof Error) {
47
- // Check for common issues
48
- if (error.message.includes('ENOENT')) {
49
- console.error('\nFile or directory not found.');
50
- console.error(` ${error.message}`);
51
- process.exit(1);
52
- }
53
-
54
- if (error.message.includes('ffmpeg')) {
55
- console.error('\nffmpeg error:');
56
- console.error(` ${error.message}`);
57
- console.error('\nMake sure ffmpeg is installed:');
58
- console.error(' macOS: brew install ffmpeg');
59
- console.error(' Ubuntu: sudo apt install ffmpeg');
60
- process.exit(1);
61
- }
62
-
63
- // Generic error
64
- console.error(`\nError: ${error.message}`);
65
-
66
- if (process.env.DEBUG) {
67
- console.error(error.stack);
68
- }
69
-
70
- process.exit(1);
71
- }
72
-
73
- // Unknown error
74
- console.error('\nUnknown error occurred');
75
- console.error(error);
76
- process.exit(1);
77
- }