@pep/term-deck 1.0.31 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -1
- package/dist/bin/term-deck.js +168 -79
- package/dist/bin/term-deck.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# term-deck
|
|
2
2
|
|
|
3
|
-
A terminal-based presentation tool with a cyberpunk aesthetic. Create beautiful slideshows in your terminal with matrix rain backgrounds, glitch effects, and ASCII art.
|
|
3
|
+
A terminal-based presentation tool with a cyberpunk aesthetic. Create beautiful slideshows in your terminal with matrix rain backgrounds, glitch effects, and ASCII art. Share them on the web or play them anywhere.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
@@ -12,6 +12,8 @@ A terminal-based presentation tool with a cyberpunk aesthetic. Create beautiful
|
|
|
12
12
|
|
|
13
13
|
*Matrix rain backgrounds, glitch animations, and ASCII art in your terminal*
|
|
14
14
|
|
|
15
|
+
**Try it online:** [term-deck-web.vercel.app](https://term-deck-web.vercel.app)
|
|
16
|
+
|
|
15
17
|
## Features
|
|
16
18
|
|
|
17
19
|
- 🌊 **Matrix Rain Background** - Animated katakana/symbol rain effects
|
|
@@ -26,6 +28,7 @@ A terminal-based presentation tool with a cyberpunk aesthetic. Create beautiful
|
|
|
26
28
|
- 🔧 **Fully Themeable** - Create custom themes
|
|
27
29
|
- ⚡ **Beautiful CLI** - Colorful, styled terminal output
|
|
28
30
|
- 📦 **Type-Safe** - Full TypeScript with Zod validation
|
|
31
|
+
- 🌐 **Web Platform** - Share decks online and play from URLs
|
|
29
32
|
|
|
30
33
|
## Installation
|
|
31
34
|
|
|
@@ -52,6 +55,9 @@ term-deck present .
|
|
|
52
55
|
|
|
53
56
|
# Export to video
|
|
54
57
|
term-deck export . -o presentation.mp4
|
|
58
|
+
|
|
59
|
+
# Play a shared deck from the web
|
|
60
|
+
npx @pep/term-deck play https://term-deck-web.vercel.app/d/demo
|
|
55
61
|
```
|
|
56
62
|
|
|
57
63
|
Output:
|
|
@@ -116,6 +122,11 @@ Run `term-deck --help` to see the styled help:
|
|
|
116
122
|
init <name> Create a new presentation deck
|
|
117
123
|
-t, --theme <name> Theme preset (default: matrix)
|
|
118
124
|
|
|
125
|
+
play <url> Play a deck from term-deck web
|
|
126
|
+
-s, --start <n> Start at slide number
|
|
127
|
+
-n, --notes Show presenter notes
|
|
128
|
+
-l, --loop Loop back after last slide
|
|
129
|
+
|
|
119
130
|
▶ HOTKEYS:
|
|
120
131
|
|
|
121
132
|
Space / → Next slide
|
|
@@ -264,6 +275,35 @@ term-deck record . -o presentation.cast
|
|
|
264
275
|
asciinema play presentation.cast
|
|
265
276
|
```
|
|
266
277
|
|
|
278
|
+
## Web Platform
|
|
279
|
+
|
|
280
|
+
Share your presentations online at [term-deck-web.vercel.app](https://term-deck-web.vercel.app).
|
|
281
|
+
|
|
282
|
+
### Upload & Share
|
|
283
|
+
|
|
284
|
+
1. Go to [term-deck-web.vercel.app/upload](https://term-deck-web.vercel.app/upload)
|
|
285
|
+
2. Drag and drop your markdown slides
|
|
286
|
+
3. Get a shareable URL like `term-deck-web.vercel.app/d/abc123`
|
|
287
|
+
|
|
288
|
+
### Play from URL
|
|
289
|
+
|
|
290
|
+
Anyone can play a shared deck in their terminal:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Play a shared presentation
|
|
294
|
+
npx @pep/term-deck play https://term-deck-web.vercel.app/d/abc123
|
|
295
|
+
|
|
296
|
+
# With options
|
|
297
|
+
npx @pep/term-deck play https://term-deck-web.vercel.app/d/abc123 --notes --loop
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The web viewer includes:
|
|
301
|
+
- Matrix rain background
|
|
302
|
+
- Keyboard navigation (arrows, space, numbers)
|
|
303
|
+
- Fullscreen mode (F key)
|
|
304
|
+
- Slide list (L key)
|
|
305
|
+
- All transition effects (glitch, fade, typewriter)
|
|
306
|
+
|
|
267
307
|
## Themes
|
|
268
308
|
|
|
269
309
|
term-deck includes built-in themes. Set via `themePreset` in config:
|
package/dist/bin/term-deck.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { join } from 'path';
|
|
2
|
+
import path2, { join } from 'path';
|
|
3
3
|
import { pathToFileURL } from 'url';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import matter from 'gray-matter';
|
|
6
|
-
import { mkdir, writeFile, access, rm, readFile, unlink } from 'fs/promises';
|
|
6
|
+
import fs, { mkdir, writeFile, access, rm, readFile, unlink } from 'fs/promises';
|
|
7
7
|
import fg from 'fast-glob';
|
|
8
8
|
import blessed from 'neo-blessed';
|
|
9
9
|
import gradient2 from 'gradient-string';
|
|
@@ -11,10 +11,10 @@ import 'yaml';
|
|
|
11
11
|
import 'deepmerge';
|
|
12
12
|
import { mermaidToAscii as mermaidToAscii$1 } from 'mermaid-ascii';
|
|
13
13
|
import figlet from 'figlet';
|
|
14
|
-
import {
|
|
14
|
+
import { createWriteStream } from 'fs';
|
|
15
15
|
import { Command } from 'commander';
|
|
16
16
|
import { intro, log, outro, spinner } from '@clack/prompts';
|
|
17
|
-
import { tmpdir } from 'os';
|
|
17
|
+
import os, { tmpdir } from 'os';
|
|
18
18
|
import { execa } from 'execa';
|
|
19
19
|
import pc from 'picocolors';
|
|
20
20
|
|
|
@@ -181,8 +181,8 @@ var init_config = __esm({
|
|
|
181
181
|
// src/schemas/validation.ts
|
|
182
182
|
function formatZodError(error, context) {
|
|
183
183
|
const issues = error.issues.map((issue) => {
|
|
184
|
-
const
|
|
185
|
-
return ` - ${
|
|
184
|
+
const path3 = issue.path.join(".");
|
|
185
|
+
return ` - ${path3 ? `${path3}: ` : ""}${issue.message}`;
|
|
186
186
|
});
|
|
187
187
|
return `Invalid ${context}:
|
|
188
188
|
${issues.join("\n")}`;
|
|
@@ -568,10 +568,10 @@ var init_theme_errors = __esm({
|
|
|
568
568
|
* @param themeName - Optional name of the theme that caused the error
|
|
569
569
|
* @param path - Optional path to the theme file or package
|
|
570
570
|
*/
|
|
571
|
-
constructor(message, themeName,
|
|
571
|
+
constructor(message, themeName, path3) {
|
|
572
572
|
super(message);
|
|
573
573
|
this.themeName = themeName;
|
|
574
|
-
this.path =
|
|
574
|
+
this.path = path3;
|
|
575
575
|
this.name = "ThemeError";
|
|
576
576
|
}
|
|
577
577
|
};
|
|
@@ -1055,7 +1055,6 @@ async function createNotesWindow(ttyPath) {
|
|
|
1055
1055
|
if (!ttyPath) {
|
|
1056
1056
|
throw new Error(getMissingTtyError());
|
|
1057
1057
|
}
|
|
1058
|
-
const blessed5 = (await import('neo-blessed')).default;
|
|
1059
1058
|
try {
|
|
1060
1059
|
await access(ttyPath);
|
|
1061
1060
|
} catch {
|
|
@@ -1063,92 +1062,85 @@ async function createNotesWindow(ttyPath) {
|
|
|
1063
1062
|
|
|
1064
1063
|
Make sure the path is correct and the terminal is open.`);
|
|
1065
1064
|
}
|
|
1066
|
-
const input = createReadStream(ttyPath);
|
|
1067
1065
|
const output = createWriteStream(ttyPath);
|
|
1068
1066
|
await new Promise((resolve, reject) => {
|
|
1069
|
-
|
|
1070
|
-
const checkReady = () => {
|
|
1071
|
-
ready++;
|
|
1072
|
-
if (ready === 2) resolve();
|
|
1073
|
-
};
|
|
1074
|
-
input.once("open", checkReady);
|
|
1075
|
-
output.once("open", checkReady);
|
|
1076
|
-
input.once("error", reject);
|
|
1067
|
+
output.once("open", resolve);
|
|
1077
1068
|
output.once("error", reject);
|
|
1078
1069
|
});
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
terminal: "xterm-256color",
|
|
1086
|
-
forceUnicode: true
|
|
1087
|
-
});
|
|
1088
|
-
const contentBox = blessed5.box({
|
|
1089
|
-
top: 0,
|
|
1090
|
-
left: 0,
|
|
1091
|
-
width: "100%",
|
|
1092
|
-
height: "100%",
|
|
1093
|
-
tags: true,
|
|
1094
|
-
padding: 2,
|
|
1095
|
-
style: {
|
|
1096
|
-
fg: "#ffffff",
|
|
1097
|
-
bg: "#1a1a1a"
|
|
1098
|
-
}
|
|
1099
|
-
});
|
|
1100
|
-
screen.append(contentBox);
|
|
1101
|
-
screen.render();
|
|
1070
|
+
output.write(ANSI.CLEAR);
|
|
1071
|
+
output.write(`${ANSI.BG_DARK}${ANSI.GREEN}${ANSI.BOLD}term-deck notes${ANSI.RESET}
|
|
1072
|
+
|
|
1073
|
+
`);
|
|
1074
|
+
output.write(`${ANSI.GRAY}Waiting for presentation to start...${ANSI.RESET}
|
|
1075
|
+
`);
|
|
1102
1076
|
return {
|
|
1103
|
-
|
|
1104
|
-
contentBox,
|
|
1077
|
+
output,
|
|
1105
1078
|
tty: ttyPath
|
|
1106
1079
|
};
|
|
1107
1080
|
}
|
|
1108
1081
|
function updateNotesWindow(notesWindow, currentSlide, nextSlide2, currentIndex, totalSlides) {
|
|
1109
|
-
const {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1082
|
+
const { output } = notesWindow;
|
|
1083
|
+
const divider = "\u2500".repeat(50);
|
|
1084
|
+
output.write(ANSI.CLEAR);
|
|
1085
|
+
output.write(`${ANSI.GREEN}${ANSI.BOLD}term-deck notes${ANSI.RESET}
|
|
1086
|
+
|
|
1087
|
+
`);
|
|
1088
|
+
output.write(`${ANSI.CYAN}${ANSI.BOLD}Slide ${currentIndex + 1} of ${totalSlides}${ANSI.RESET}
|
|
1089
|
+
`);
|
|
1090
|
+
output.write(`${ANSI.GRAY}${currentSlide.frontmatter.title}${ANSI.RESET}
|
|
1091
|
+
`);
|
|
1092
|
+
output.write("\n");
|
|
1093
|
+
output.write(`${ANSI.GRAY}${divider}${ANSI.RESET}
|
|
1094
|
+
`);
|
|
1095
|
+
output.write("\n");
|
|
1118
1096
|
if (currentSlide.notes) {
|
|
1119
|
-
|
|
1120
|
-
|
|
1097
|
+
output.write(`${ANSI.YELLOW}${ANSI.BOLD}PRESENTER NOTES:${ANSI.RESET}
|
|
1098
|
+
|
|
1099
|
+
`);
|
|
1100
|
+
output.write(`${ANSI.WHITE}${currentSlide.notes}${ANSI.RESET}
|
|
1101
|
+
`);
|
|
1121
1102
|
} else {
|
|
1122
|
-
|
|
1103
|
+
output.write(`${ANSI.GRAY}No notes for this slide${ANSI.RESET}
|
|
1104
|
+
`);
|
|
1123
1105
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1106
|
+
output.write("\n");
|
|
1107
|
+
output.write(`${ANSI.GRAY}${divider}${ANSI.RESET}
|
|
1108
|
+
`);
|
|
1109
|
+
output.write("\n");
|
|
1127
1110
|
if (nextSlide2) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1111
|
+
output.write(`${ANSI.CYAN}${ANSI.BOLD}NEXT:${ANSI.RESET} ${ANSI.WHITE}"${nextSlide2.frontmatter.title}"${ANSI.RESET}
|
|
1112
|
+
`);
|
|
1130
1113
|
} else {
|
|
1131
|
-
|
|
1114
|
+
output.write(`${ANSI.GRAY}Last slide${ANSI.RESET}
|
|
1115
|
+
`);
|
|
1132
1116
|
}
|
|
1133
|
-
contentBox.setContent(content);
|
|
1134
|
-
screen.render();
|
|
1135
|
-
}
|
|
1136
|
-
function toggleNotesVisibility(notesWindow) {
|
|
1137
|
-
const { contentBox, screen } = notesWindow;
|
|
1138
|
-
contentBox.toggle();
|
|
1139
|
-
screen.render();
|
|
1140
1117
|
}
|
|
1141
1118
|
function destroyNotesWindow(notesWindow) {
|
|
1142
1119
|
try {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1120
|
+
notesWindow.output.write(ANSI.CLEAR);
|
|
1121
|
+
notesWindow.output.write(`${ANSI.GRAY}Presentation ended.${ANSI.RESET}
|
|
1122
|
+
`);
|
|
1123
|
+
notesWindow.output.end();
|
|
1146
1124
|
} catch {
|
|
1147
1125
|
}
|
|
1148
1126
|
}
|
|
1127
|
+
var ANSI;
|
|
1149
1128
|
var init_notes_window = __esm({
|
|
1150
1129
|
"src/presenter/notes-window.ts"() {
|
|
1151
1130
|
init_esm_shims();
|
|
1131
|
+
ANSI = {
|
|
1132
|
+
CLEAR: "\x1B[2J\x1B[H",
|
|
1133
|
+
// Clear screen and move cursor to top
|
|
1134
|
+
BOLD: "\x1B[1m",
|
|
1135
|
+
DIM: "\x1B[2m",
|
|
1136
|
+
RESET: "\x1B[0m",
|
|
1137
|
+
GREEN: "\x1B[32m",
|
|
1138
|
+
CYAN: "\x1B[36m",
|
|
1139
|
+
YELLOW: "\x1B[33m",
|
|
1140
|
+
WHITE: "\x1B[37m",
|
|
1141
|
+
GRAY: "\x1B[90m",
|
|
1142
|
+
BG_DARK: "\x1B[48;5;234m"
|
|
1143
|
+
};
|
|
1152
1144
|
}
|
|
1153
1145
|
});
|
|
1154
1146
|
|
|
@@ -1267,11 +1259,6 @@ function setupControls(presenter) {
|
|
|
1267
1259
|
screen.key(["l"], () => {
|
|
1268
1260
|
showSlideList(presenter);
|
|
1269
1261
|
});
|
|
1270
|
-
screen.key(["N"], () => {
|
|
1271
|
-
if (presenter.notesWindow) {
|
|
1272
|
-
toggleNotesVisibility(presenter.notesWindow);
|
|
1273
|
-
}
|
|
1274
|
-
});
|
|
1275
1262
|
}
|
|
1276
1263
|
function showSlideList(presenter) {
|
|
1277
1264
|
const { screen } = presenter.renderer;
|
|
@@ -1312,7 +1299,6 @@ var init_keyboard_controls = __esm({
|
|
|
1312
1299
|
"src/presenter/keyboard-controls.ts"() {
|
|
1313
1300
|
init_esm_shims();
|
|
1314
1301
|
init_navigation();
|
|
1315
|
-
init_notes_window();
|
|
1316
1302
|
}
|
|
1317
1303
|
});
|
|
1318
1304
|
|
|
@@ -1417,7 +1403,7 @@ var init_main = __esm({
|
|
|
1417
1403
|
init_esm_shims();
|
|
1418
1404
|
|
|
1419
1405
|
// package.json
|
|
1420
|
-
var version = "1.0
|
|
1406
|
+
var version = "1.1.0";
|
|
1421
1407
|
|
|
1422
1408
|
// src/cli/commands/present.ts
|
|
1423
1409
|
init_esm_shims();
|
|
@@ -1979,6 +1965,103 @@ term-deck export . -o ${name}.gif
|
|
|
1979
1965
|
await writeFile(join(deckDir, "README.md"), readme);
|
|
1980
1966
|
}
|
|
1981
1967
|
|
|
1968
|
+
// src/cli/commands/play.ts
|
|
1969
|
+
init_esm_shims();
|
|
1970
|
+
init_main();
|
|
1971
|
+
async function fetchDeck(url) {
|
|
1972
|
+
let apiUrl;
|
|
1973
|
+
try {
|
|
1974
|
+
const urlObj = new URL(url);
|
|
1975
|
+
if (urlObj.pathname.startsWith("/d/")) {
|
|
1976
|
+
const id = urlObj.pathname.split("/d/")[1];
|
|
1977
|
+
apiUrl = `${urlObj.origin}/api/deck/${id}/raw`;
|
|
1978
|
+
} else if (urlObj.pathname.startsWith("/api/deck/")) {
|
|
1979
|
+
apiUrl = url;
|
|
1980
|
+
} else {
|
|
1981
|
+
throw new Error("Invalid deck URL format");
|
|
1982
|
+
}
|
|
1983
|
+
} catch {
|
|
1984
|
+
apiUrl = `https://term-deck-web.vercel.app/api/deck/${url}/raw`;
|
|
1985
|
+
}
|
|
1986
|
+
const response = await fetch(apiUrl);
|
|
1987
|
+
if (!response.ok) {
|
|
1988
|
+
if (response.status === 404) {
|
|
1989
|
+
throw new Error("Deck not found");
|
|
1990
|
+
}
|
|
1991
|
+
throw new Error(`Failed to fetch deck: ${response.status} ${response.statusText}`);
|
|
1992
|
+
}
|
|
1993
|
+
return response.json();
|
|
1994
|
+
}
|
|
1995
|
+
async function writeTempSlides(deck) {
|
|
1996
|
+
const tempDir = await fs.mkdtemp(path2.join(os.tmpdir(), "term-deck-"));
|
|
1997
|
+
for (const slide of deck.slides) {
|
|
1998
|
+
const frontmatter = Object.entries(slide.frontmatter).filter(([, value]) => value !== void 0).map(([key, value]) => {
|
|
1999
|
+
if (typeof value === "string") {
|
|
2000
|
+
return `${key}: ${value}`;
|
|
2001
|
+
}
|
|
2002
|
+
if (Array.isArray(value)) {
|
|
2003
|
+
return `${key}:
|
|
2004
|
+
${value.map((v) => ` - ${v}`).join("\n")}`;
|
|
2005
|
+
}
|
|
2006
|
+
return `${key}: ${JSON.stringify(value)}`;
|
|
2007
|
+
}).join("\n");
|
|
2008
|
+
let content = `---
|
|
2009
|
+
${frontmatter}
|
|
2010
|
+
---
|
|
2011
|
+
|
|
2012
|
+
${slide.body}`;
|
|
2013
|
+
if (slide.notes) {
|
|
2014
|
+
content += `
|
|
2015
|
+
|
|
2016
|
+
<!-- notes -->
|
|
2017
|
+
${slide.notes}
|
|
2018
|
+
<!-- /notes -->`;
|
|
2019
|
+
}
|
|
2020
|
+
const filename = `${String(slide.index + 1).padStart(2, "0")}-${slide.frontmatter.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
2021
|
+
await fs.writeFile(path2.join(tempDir, filename), content, "utf-8");
|
|
2022
|
+
}
|
|
2023
|
+
if (deck.config.theme) {
|
|
2024
|
+
const themeContent = `import { defineTheme } from '@pep/term-deck'
|
|
2025
|
+
|
|
2026
|
+
export default defineTheme(${JSON.stringify(deck.config.theme, null, 2)})
|
|
2027
|
+
`;
|
|
2028
|
+
await fs.writeFile(path2.join(tempDir, "theme.ts"), themeContent, "utf-8");
|
|
2029
|
+
}
|
|
2030
|
+
return tempDir;
|
|
2031
|
+
}
|
|
2032
|
+
async function cleanupTempDir(tempDir) {
|
|
2033
|
+
try {
|
|
2034
|
+
await fs.rm(tempDir, { recursive: true });
|
|
2035
|
+
} catch {
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
var playCommand = new Command("play").description("Play a presentation from a term-deck web URL").argument("<url>", "Deck URL (e.g., https://termdeck.vercel.app/d/abc123 or just abc123)").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").option("-l, --loop", "Loop back to first slide after last").action(async (url, options) => {
|
|
2039
|
+
let tempDir = null;
|
|
2040
|
+
try {
|
|
2041
|
+
console.log("Fetching deck...");
|
|
2042
|
+
const deck = await fetchDeck(url);
|
|
2043
|
+
console.log(`Playing: ${deck.config.title || "Untitled Deck"}`);
|
|
2044
|
+
if (deck.config.author) {
|
|
2045
|
+
console.log(`By: ${deck.config.author}`);
|
|
2046
|
+
}
|
|
2047
|
+
console.log(`Slides: ${deck.slides.length}
|
|
2048
|
+
`);
|
|
2049
|
+
tempDir = await writeTempSlides(deck);
|
|
2050
|
+
await present(tempDir, {
|
|
2051
|
+
startSlide: Number.parseInt(options.start, 10),
|
|
2052
|
+
showNotes: options.notes,
|
|
2053
|
+
notesTty: options.notesTty,
|
|
2054
|
+
loop: options.loop
|
|
2055
|
+
});
|
|
2056
|
+
} catch (error) {
|
|
2057
|
+
handleError(error);
|
|
2058
|
+
} finally {
|
|
2059
|
+
if (tempDir) {
|
|
2060
|
+
await cleanupTempDir(tempDir);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
2064
|
+
|
|
1982
2065
|
// src/cli/help.ts
|
|
1983
2066
|
init_esm_shims();
|
|
1984
2067
|
function showHelp() {
|
|
@@ -2022,6 +2105,11 @@ function showHelp() {
|
|
|
2022
2105
|
console.log(pc.green(" init") + pc.dim(" <name> ") + pc.white("Create a new presentation deck"));
|
|
2023
2106
|
console.log(pc.dim(" -t, --theme <name> ") + pc.white("Theme preset (default: matrix)"));
|
|
2024
2107
|
console.log("");
|
|
2108
|
+
console.log(pc.green(" play") + pc.dim(" <url> ") + pc.white("Play a deck from term-deck web"));
|
|
2109
|
+
console.log(pc.dim(" -s, --start <n> ") + pc.white("Start at slide number"));
|
|
2110
|
+
console.log(pc.dim(" -n, --notes ") + pc.white("Show presenter notes"));
|
|
2111
|
+
console.log(pc.dim(" -l, --loop ") + pc.white("Loop back after last slide"));
|
|
2112
|
+
console.log("");
|
|
2025
2113
|
console.log(pc.bold(pc.cyan("\u25B6 HOTKEYS:")));
|
|
2026
2114
|
console.log("");
|
|
2027
2115
|
console.log(pc.dim(" Space / \u2192 ") + pc.white("Next slide"));
|
|
@@ -2058,7 +2146,7 @@ function showVersion(version2) {
|
|
|
2058
2146
|
// bin/term-deck.ts
|
|
2059
2147
|
var args = process.argv.slice(2);
|
|
2060
2148
|
if (args.includes("-h") || args.includes("--help") || args.length === 0) {
|
|
2061
|
-
if (!args.some((arg) => ["present", "export", "init"].includes(arg))) {
|
|
2149
|
+
if (!args.some((arg) => ["present", "export", "init", "play"].includes(arg))) {
|
|
2062
2150
|
showHelp();
|
|
2063
2151
|
process.exit(0);
|
|
2064
2152
|
}
|
|
@@ -2072,6 +2160,7 @@ program.name("term-deck").description("Terminal presentation tool with a cyberpu
|
|
|
2072
2160
|
program.addCommand(presentCommand);
|
|
2073
2161
|
program.addCommand(exportCommand);
|
|
2074
2162
|
program.addCommand(initCommand);
|
|
2163
|
+
program.addCommand(playCommand);
|
|
2075
2164
|
program.argument("[dir]", "Slides directory to present").action(async (dir) => {
|
|
2076
2165
|
if (dir) {
|
|
2077
2166
|
try {
|