dsh-ppt 0.4.3 → 0.4.4

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.
@@ -1,135 +1,135 @@
1
- #! /usr/bin/env node
2
- /**
3
- * build-deck.mjs —— dsh-ppt 的跨 harness 裸 CLI。
4
- *
5
- * DSH 插件内请优先使用 ppt_create 工具;把 skills/dsh-ppt 目录复制到
6
- * Claude Code / Cursor / Gemini CLI / Codex 等 agent 时,用本脚本生成三件套。
7
- *
8
- * 示例:
9
- * node build-deck.mjs --title "产品发布" --content "deck.md" --theme data --lang zh --out dist/deck
10
- * node build-deck.mjs --title "Pitch" --content "一句话介绍我们的产品。" --theme velvet --lang bilingual
11
- * node build-deck.mjs --list-themes
12
- */
13
-
14
- import { existsSync, readFileSync } from 'node:fs'
15
- import { resolve as resolvePath } from 'node:path'
16
- import { pathToFileURL } from 'node:url'
17
- import { buildDeck, listThemes, THEME_IDS, DEFAULT_THEME } from './deck-core.mjs'
18
-
19
- const HELP = `dsh-ppt —— 一句话 / 一篇文档 → HTML 放映 + PPTX 导出
20
-
21
- 用法:
22
- node build-deck.mjs --title <标题> --content <Markdown 或文件路径> [选项]
23
-
24
- 选项:
25
- --title <text> 演示文稿标题(必填)
26
- --content <text|@path> Markdown 正文;或文件路径(自动读取);或 @- 从 stdin 读
27
- --slides <json> 结构化 slides JSON(可选,与 --content 二选一)
28
- --theme <id> 视觉主题:${THEME_IDS.join(' / ')}(默认 ${DEFAULT_THEME})
29
- --lang <id> 界面语言:zh / en / bilingual(默认 zh)
30
- --motion <on|off> 页间转场与要点入场动画(默认 on;off 产出纯静态)
31
- --out <dir> 输出目录(默认当前目录)
32
- --file <name> 文件名前缀(默认取标题)
33
- --overwrite 显式覆盖同名三件套(默认自动追加 -1/-2…)
34
- --list-themes 列出内置主题
35
- --help 显示本帮助
36
-
37
- 输出:
38
- <file>.html 独立网页放映(无外链,可直接双击打开)
39
- <file>.pptx 可编辑 PPTX(16:9)
40
- <file>.json deck manifest
41
- `
42
-
43
- export function parseArgv(argv) {
44
- const args = { content: '', slides: null, lang: 'zh', theme: DEFAULT_THEME, motion: undefined, out: '.', file: '', title: '', overwrite: false }
45
- const positional = []
46
- for (let i = 0; i < argv.length; i += 1) {
47
- const raw = String(argv[i])
48
- const eq = raw.indexOf('=')
49
- const key = eq >= 0 ? raw.slice(0, eq) : raw
50
- const inline = eq >= 0 ? raw.slice(eq + 1) : null
51
- const nextValue = () => {
52
- if (inline !== null) return inline
53
- if (i + 1 >= argv.length) throw new Error('dsh-ppt CLI:' + key + ' 需要一个值')
54
- i += 1
55
- return String(argv[i])
56
- }
57
- if (key === '--help' || key === '-h') args.help = true
58
- else if (key === '--list-themes') args.listThemes = true
59
- else if (key === '--overwrite') args.overwrite = true
60
- else if (key === '--title') args.title = nextValue()
61
- else if (key === '--content') args.content = nextValue()
62
- else if (key === '--slides') args.slides = JSON.parse(nextValue())
63
- else if (key === '--theme') args.theme = nextValue()
64
- else if (key === '--lang') args.lang = nextValue()
65
- else if (key === '--motion') args.motion = nextValue()
66
- else if (key === '--out') args.out = nextValue()
67
- else if (key === '--file') args.file = nextValue()
68
- else positional.push(raw)
69
- }
70
- args.positional = positional
71
- return args
72
- }
73
-
74
- async function readStdin(stream) {
75
- let text = ''
76
- stream.setEncoding('utf8')
77
- for await (const chunk of stream) text += chunk
78
- return text
79
- }
80
-
81
- export async function main(argv = process.argv.slice(2), io = {}) {
82
- const log = io.log ?? ((message) => console.log(message))
83
- const args = parseArgv(argv)
84
-
85
- if (args.help) {
86
- log(HELP.trim())
87
- return 0
88
- }
89
- if (args.listThemes) {
90
- for (const theme of listThemes('zh')) {
91
- log('- ' + theme.id.padEnd(8) + theme.name + '|' + theme.mood + '|适合:' + theme.bestFor)
92
- }
93
- return 0
94
- }
95
-
96
- let content = String(args.content ?? '')
97
- let slides = args.slides
98
- if (content === '@-') {
99
- content = await readStdin(io.stdin ?? process.stdin)
100
- } else if (content.startsWith('@')) {
101
- content = readFileSync(resolvePath(content.slice(1)), 'utf8')
102
- } else if (content.trim() !== '' && existsSync(resolvePath(content.trim()))) {
103
- // README / SKILL.md 的示例直接传文件路径(不带 @)时也按文件读取
104
- content = readFileSync(resolvePath(content.trim()), 'utf8')
105
- } else if (content.trim() === '' && slides === null) {
106
- throw new Error('dsh-ppt CLI:--content 不能为空(或用 --slides 传结构化幻灯片)')
107
- }
108
-
109
- const options = {
110
- title: args.title,
111
- content,
112
- slides,
113
- theme: args.theme,
114
- lang: args.lang,
115
- motion: args.motion,
116
- outputDir: resolvePath(args.out || '.'),
117
- fileName: args.file,
118
- overwrite: args.overwrite,
119
- }
120
- const result = buildDeck(options)
121
- log('dsh-ppt 已生成 ' + result.slideCount + ' 页演示文稿:')
122
- log(' HTML 放映:' + result.htmlPath)
123
- log(' PPTX 导出:' + result.pptxPath)
124
- log(' Manifest :' + result.jsonPath)
125
- return 0
126
- }
127
-
128
- if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
129
- main().then((code) => {
130
- process.exitCode = code ?? 0
131
- }).catch((err) => {
132
- console.error('[dsh-ppt] ' + (err instanceof Error ? err.message : String(err)))
133
- process.exitCode = 1
134
- })
135
- }
1
+ #! /usr/bin/env node
2
+ /**
3
+ * build-deck.mjs —— dsh-ppt 的跨 harness 裸 CLI。
4
+ *
5
+ * DSH 插件内请优先使用 ppt_create 工具;把 skills/dsh-ppt 目录复制到
6
+ * Claude Code / Cursor / Gemini CLI / Codex 等 agent 时,用本脚本生成三件套。
7
+ *
8
+ * 示例:
9
+ * node build-deck.mjs --title "产品发布" --content "deck.md" --theme data --lang zh --out dist/deck
10
+ * node build-deck.mjs --title "Pitch" --content "一句话介绍我们的产品。" --theme velvet --lang bilingual
11
+ * node build-deck.mjs --list-themes
12
+ */
13
+
14
+ import { existsSync, readFileSync } from 'node:fs'
15
+ import { resolve as resolvePath } from 'node:path'
16
+ import { pathToFileURL } from 'node:url'
17
+ import { buildDeck, listThemes, THEME_IDS, DEFAULT_THEME } from './deck-core.mjs'
18
+
19
+ const HELP = `dsh-ppt —— 一句话 / 一篇文档 → HTML 放映 + PPTX 导出
20
+
21
+ 用法:
22
+ node build-deck.mjs --title <标题> --content <Markdown 或文件路径> [选项]
23
+
24
+ 选项:
25
+ --title <text> 演示文稿标题(必填)
26
+ --content <text|@path> Markdown 正文;或文件路径(自动读取);或 @- 从 stdin 读
27
+ --slides <json> 结构化 slides JSON(可选,与 --content 二选一)
28
+ --theme <id> 视觉主题:${THEME_IDS.join(' / ')}(默认 ${DEFAULT_THEME})
29
+ --lang <id> 界面语言:zh / en / bilingual(默认 zh)
30
+ --motion <on|off> 页间转场与要点入场动画(默认 on;off 产出纯静态)
31
+ --out <dir> 输出目录(默认当前目录)
32
+ --file <name> 文件名前缀(默认取标题)
33
+ --overwrite 显式覆盖同名三件套(默认自动追加 -1/-2…)
34
+ --list-themes 列出内置主题
35
+ --help 显示本帮助
36
+
37
+ 输出:
38
+ <file>.html 独立网页放映(无外链,可直接双击打开)
39
+ <file>.pptx 可编辑 PPTX(16:9)
40
+ <file>.json deck manifest
41
+ `
42
+
43
+ export function parseArgv(argv) {
44
+ const args = { content: '', slides: null, lang: 'zh', theme: DEFAULT_THEME, motion: undefined, out: '.', file: '', title: '', overwrite: false }
45
+ const positional = []
46
+ for (let i = 0; i < argv.length; i += 1) {
47
+ const raw = String(argv[i])
48
+ const eq = raw.indexOf('=')
49
+ const key = eq >= 0 ? raw.slice(0, eq) : raw
50
+ const inline = eq >= 0 ? raw.slice(eq + 1) : null
51
+ const nextValue = () => {
52
+ if (inline !== null) return inline
53
+ if (i + 1 >= argv.length) throw new Error('dsh-ppt CLI:' + key + ' 需要一个值')
54
+ i += 1
55
+ return String(argv[i])
56
+ }
57
+ if (key === '--help' || key === '-h') args.help = true
58
+ else if (key === '--list-themes') args.listThemes = true
59
+ else if (key === '--overwrite') args.overwrite = true
60
+ else if (key === '--title') args.title = nextValue()
61
+ else if (key === '--content') args.content = nextValue()
62
+ else if (key === '--slides') args.slides = JSON.parse(nextValue())
63
+ else if (key === '--theme') args.theme = nextValue()
64
+ else if (key === '--lang') args.lang = nextValue()
65
+ else if (key === '--motion') args.motion = nextValue()
66
+ else if (key === '--out') args.out = nextValue()
67
+ else if (key === '--file') args.file = nextValue()
68
+ else positional.push(raw)
69
+ }
70
+ args.positional = positional
71
+ return args
72
+ }
73
+
74
+ async function readStdin(stream) {
75
+ let text = ''
76
+ stream.setEncoding('utf8')
77
+ for await (const chunk of stream) text += chunk
78
+ return text
79
+ }
80
+
81
+ export async function main(argv = process.argv.slice(2), io = {}) {
82
+ const log = io.log ?? ((message) => console.log(message))
83
+ const args = parseArgv(argv)
84
+
85
+ if (args.help) {
86
+ log(HELP.trim())
87
+ return 0
88
+ }
89
+ if (args.listThemes) {
90
+ for (const theme of listThemes('zh')) {
91
+ log('- ' + theme.id.padEnd(8) + theme.name + '|' + theme.mood + '|适合:' + theme.bestFor)
92
+ }
93
+ return 0
94
+ }
95
+
96
+ let content = String(args.content ?? '')
97
+ let slides = args.slides
98
+ if (content === '@-') {
99
+ content = await readStdin(io.stdin ?? process.stdin)
100
+ } else if (content.startsWith('@')) {
101
+ content = readFileSync(resolvePath(content.slice(1)), 'utf8')
102
+ } else if (content.trim() !== '' && existsSync(resolvePath(content.trim()))) {
103
+ // README / SKILL.md 的示例直接传文件路径(不带 @)时也按文件读取
104
+ content = readFileSync(resolvePath(content.trim()), 'utf8')
105
+ } else if (content.trim() === '' && slides === null) {
106
+ throw new Error('dsh-ppt CLI:--content 不能为空(或用 --slides 传结构化幻灯片)')
107
+ }
108
+
109
+ const options = {
110
+ title: args.title,
111
+ content,
112
+ slides,
113
+ theme: args.theme,
114
+ lang: args.lang,
115
+ motion: args.motion,
116
+ outputDir: resolvePath(args.out || '.'),
117
+ fileName: args.file,
118
+ overwrite: args.overwrite,
119
+ }
120
+ const result = buildDeck(options)
121
+ log('dsh-ppt 已生成 ' + result.slideCount + ' 页演示文稿:')
122
+ log(' HTML 放映:' + result.htmlPath)
123
+ log(' PPTX 导出:' + result.pptxPath)
124
+ log(' Manifest :' + result.jsonPath)
125
+ return 0
126
+ }
127
+
128
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
129
+ main().then((code) => {
130
+ process.exitCode = code ?? 0
131
+ }).catch((err) => {
132
+ console.error('[dsh-ppt] ' + (err instanceof Error ? err.message : String(err)))
133
+ process.exitCode = 1
134
+ })
135
+ }