dsh-ppt 0.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/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - 首发:`dsh-ppt` 技能 + 工具插件。
6
+ - `ppt_create`:Markdown / 结构化 slides → HTML 网页放映 + PPTX 导出 + manifest。
7
+ - `ppt_themes`:5 套内置视觉主题(Swiss Pulse / Velvet Standard / Data Drift / Soft Signal / Maximalist Type)。
8
+ - 中英双语界面(zh / en / bilingual)。
9
+ - 裸 SKILL.md + `build-deck.mjs` CLI,跨 Claude Code / Cursor / Gemini CLI / Codex / DSH。
10
+ - 零运行时依赖:OOXML 与 zip 由 `node:zlib` 手写生成。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 stardustlc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,139 @@
1
+ # dsh-ppt
2
+
3
+ [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
4
+
5
+ > **One sentence or one document → a complete presentation**: HTML web slideshow + PPTX export, 5 visual themes, bilingual Chinese/English.
6
+
7
+ DeepSeek Harness (DSH) presentation skill + tool plugin: turns a sentence, a paragraph, or a Markdown document into a ready-to-present **HTML slideshow** and an editable **PPTX**. Pure Node, **zero runtime dependencies**, one codebase for Windows / macOS / Linux.
8
+
9
+ ## Capabilities
10
+
11
+ | Capability | Description |
12
+ | --- | --- |
13
+ | `ppt_create` tool | Markdown / structured slides → `*.html` + `*.pptx` + `*.json` |
14
+ | `ppt_themes` tool | Lists the 5 built-in themes and their best use cases |
15
+ | `dsh-ppt` skill | A complete six-step SOP registered into DSH |
16
+ | Standalone SKILL.md | Copy `skills/dsh-ppt/` into Claude Code / Cursor / Gemini CLI / Codex for cross-harness use |
17
+ | Visual engine | Reuses the hyperframes visual style library; HTML and PPTX share the same theme source |
18
+
19
+ Example:
20
+
21
+ > Turn this sentence into a deck: "AI support cuts first response time to 8 seconds", with a dark tech theme.
22
+ >
23
+ > Turn `docs/quarterly-review.md` into a bilingual presentation and export PPTX.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ dsh plugin --profile web add dsh-ppt
29
+ ```
30
+
31
+ After restart, the `ppt_create` / `ppt_themes` tools and the `dsh-ppt` skill are available. The plugin ships with an empty config and **won't crash startup**.
32
+
33
+ ## Quick start
34
+
35
+ ### Inside DSH (recommended)
36
+
37
+ ```
38
+ 1. ppt_themes # inspect the 5 themes
39
+ 2. ppt_create {
40
+ title: "Fewer Meetings",
41
+ content: "# Problem\n- Too many meetings\n\n# Solution\n- Async decisions",
42
+ theme: "data",
43
+ lang: "en"
44
+ }
45
+ 3. Open the returned HTML path in a browser; edit the PPTX in PowerPoint / WPS / Keynote
46
+ ```
47
+
48
+ ### Any harness (standalone SKILL.md)
49
+
50
+ Copy `skills/dsh-ppt/` to any Agent Skills directory, then:
51
+
52
+ ```bash
53
+ node <skill-dir>/scripts/build-deck.mjs \
54
+ --title "Product Launch" \
55
+ --content deck.md \
56
+ --theme data \
57
+ --lang en \
58
+ --out dist/deck
59
+ ```
60
+
61
+ Artifacts:
62
+
63
+ | File | Purpose |
64
+ | --- | --- |
65
+ | `*.html` | Standalone web slideshow: arrow keys/wheel/touch navigation, F fullscreen, G overview, P print/save as PDF |
66
+ | `*.pptx` | Editable 16:9 presentation (hand-written OOXML, zip via `node:zlib`, no third-party deps) |
67
+ | `*.json` | Structured manifest (version, theme, language, slides) |
68
+
69
+ ## Built-in themes
70
+
71
+ | ID | Name | Mood | Best for |
72
+ | --- | --- | --- | --- |
73
+ | `data` | Data Drift (default) | Futuristic / immersive | AI, tech launches, research |
74
+ | `swiss` | Swiss Pulse | Precise / rational | Data, SaaS, developer tools |
75
+ | `velvet` | Velvet Standard | Premium / restrained | Executive decks, brand, investor pitches |
76
+ | `soft` | Soft Signal | Warm / human | Brand stories, training, personal talks |
77
+ | `bold` | Maximalist Type | Loud / kinetic | Product launches, events, big moments |
78
+
79
+ Themes are derived from [dsh-hyperframes](https://github.com/STARDUSTLC666/dsh-hyperframes) `visual-styles.md`. Full palettes: `skills/dsh-ppt/references/themes.md`.
80
+
81
+ ## Markdown input rules
82
+
83
+ - The first `# heading` becomes the cover title; its first paragraph becomes the cover subtitle.
84
+ - Each `## section` becomes one slide: lists produce `bullets` slides; empty sections produce `section` dividers.
85
+ - Plain text without headings: the first paragraph is the cover, then every 5 sentences become one slide.
86
+ - A single sentence automatically produces a complete 3-slide structure: cover → core idea → closing.
87
+ - For precise control, use structured `slides` (`cover | section | bullets | statement | closing`).
88
+
89
+ ## Configuration
90
+
91
+ No required configuration. Optional:
92
+
93
+ ```yaml
94
+ - id: dsh-ppt
95
+ config:
96
+ outputDir: E:\decks # optional; defaults to the session working directory
97
+ maxSlides: 40 # optional; default 60 (3–120)
98
+ ```
99
+
100
+ The `DSH_PPT_OUTPUT_DIR` env var can also set the default output directory; the `ppt_create` `outputDir` argument has the highest priority.
101
+
102
+ ## Bilingual support
103
+
104
+ - `lang` argument: `zh` (default) / `en` / `bilingual` controls the player UI, page numbers, and closing defaults.
105
+ - Content language is up to the author: for a bilingual deck, prefer "Chinese title + English subtitle" or generate two decks from the same outline.
106
+ - Plugin docs, skill content, and error messages are bilingual.
107
+
108
+ ## Engineering quality
109
+
110
+ - Pure Node, zero runtime dependencies: HTML template, OOXML, and zip are hand-written using only `node:fs` / `node:path` / `node:zlib`.
111
+ - The skill and tools share one engine (`skills/dsh-ppt/scripts/deck-core.mjs`), so DSH and bare-skill output are identical.
112
+ - Unit tests cover registration contracts, JSON Schema, theme resolution, Markdown parsing, artifact generation, PPTX part integrity, and the CLI.
113
+ - No `eval` / `child_process` / secrets; artifacts are only written to the user-specified local directory.
114
+
115
+ ## Development
116
+
117
+ ```sh
118
+ pnpm install
119
+ pnpm run build # tsc → lib/
120
+ pnpm test # build + node --test (registration/config/engine/CLI)
121
+ pnpm run smoke:cli # bare CLI smoke test, generates .smoke-deck
122
+ ```
123
+
124
+ ## Known limitations
125
+
126
+ - The PPTX uses a blank layout plus text boxes: text is editable in PowerPoint / WPS, but no smart master placeholders yet.
127
+ - A one-sentence input produces a minimal 3-slide structure; for richer decks, expand the content into a Markdown outline first.
128
+ - `bilingual` only localizes the player UI; it does not translate content.
129
+ - Charts, images, speaker notes, and PPT animations are planned for v0.2+.
130
+
131
+ ## License
132
+
133
+ MIT. Community plugin, not affiliated with DeepSeek; `@deepseek-ai/*` is an officially reserved namespace.
134
+
135
+ ## Related projects
136
+
137
+ - [dsh-hyperframes](https://github.com/STARDUSTLC666/dsh-hyperframes) — HTML video skills (source of this plugin's visual styles)
138
+ - [dsh-remotion](https://github.com/STARDUSTLC666/dsh-remotion) — React programmatic video skills
139
+ - [dsh-email](https://github.com/STARDUSTLC666/dsh-email) — Email toolset
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ [English](README.en.md)
2
+
3
+ # dsh-ppt
4
+
5
+ > **一句话 / 一篇文档 → 完整演示文稿**:HTML 网页放映 + PPTX 导出,5 套视觉主题,中英双语。
6
+
7
+ ![npm version](https://img.shields.io/npm/v/dsh-ppt?label=npm&color=blue) ![npm downloads](https://img.shields.io/npm/dm/dsh-ppt) ![license](https://img.shields.io/npm/l/dsh-ppt) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-ppt?style=social)
8
+
9
+ [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
10
+
11
+ DSH(DeepSeek Harness)演示文稿技能 + 工具插件:把一句话、一段文字或一篇 Markdown 文档变成可直接放映的 **HTML 网页** 与可编辑的 **PPTX**。纯 Node 实现,**零运行时依赖**,Windows / macOS / Linux 同一份代码。
12
+
13
+ ## 能力一览
14
+
15
+ | 能力 | 说明 |
16
+ | --- | --- |
17
+ | `ppt_create` 工具 | Markdown / 结构化 slides → `*.html` + `*.pptx` + `*.json` 三件套 |
18
+ | `ppt_themes` 工具 | 列出 5 套内置主题与适用场景 |
19
+ | `dsh-ppt` 技能 | 完整 SOP 注册进 DSH:从一句话到成品 deck 的六步流水线 |
20
+ | 裸 SKILL.md | 把 `skills/dsh-ppt/` 复制到 Claude Code / Cursor / Gemini CLI / Codex 即可跨 harness 使用 |
21
+ | 视觉引擎 | 复用 hyperframes 视觉风格库,5 套主题 HTML 与 PPTX 同源同色 |
22
+
23
+ 示例对话:
24
+
25
+ > 把这句话做成 PPT:「AI 客服把首次响应时间压缩到 8 秒」,主题用科技感深色。
26
+ >
27
+ > 把 `docs/季度汇报.md` 做成双语演示文稿,导出 PPTX。
28
+
29
+ ## 安装
30
+
31
+ ```bash
32
+ dsh plugin --profile web add dsh-ppt
33
+ ```
34
+
35
+ 重启后即可使用 `ppt_create` / `ppt_themes` 工具,并自动加载 `dsh-ppt` 技能。插件自带空配置,**不会弄崩启动**。
36
+
37
+ ## 快速开始
38
+
39
+ ### DSH 内(推荐)
40
+
41
+ ```
42
+ 1. ppt_themes # 看 5 套主题
43
+ 2. ppt_create {
44
+ title: "把会议减半",
45
+ content: "# 问题\n- 周会太多\n\n# 方案\n- 异步决策",
46
+ theme: "data",
47
+ lang: "zh"
48
+ }
49
+ 3. 打开返回的 HTML 路径(浏览器放映),PPTX 用 PowerPoint / WPS / Keynote 编辑
50
+ ```
51
+
52
+ ### 任意 harness(裸 SKILL.md)
53
+
54
+ 把 `skills/dsh-ppt/` 复制到任意 Agent Skills 目录,然后:
55
+
56
+ ```bash
57
+ node <skill-dir>/scripts/build-deck.mjs \
58
+ --title "产品发布" \
59
+ --content deck.md \
60
+ --theme data \
61
+ --lang zh \
62
+ --out dist/deck
63
+ ```
64
+
65
+ 输出三件套:
66
+
67
+ | 文件 | 用途 |
68
+ | --- | --- |
69
+ | `*.html` | 独立网页放映:方向键/滚轮/触屏翻页,F 全屏,G 总览,P 打印或另存 PDF |
70
+ | `*.pptx` | 16:9 可编辑演示文稿(OOXML 由插件手写,zip 用 node:zlib,无第三方依赖) |
71
+ | `*.json` | 结构化 manifest(版本、主题、语言、每页内容) |
72
+
73
+ ## 内置主题
74
+
75
+ | ID | 名称 | 情绪 | 适用 |
76
+ | --- | --- | --- | --- |
77
+ | `data` | 数据漂移(默认) | 未来 / 沉浸 | AI、技术发布、研究 |
78
+ | `swiss` | 瑞士脉冲 | 精准 / 理性 | 数据、SaaS、开发者工具 |
79
+ | `velvet` | 天鹅绒标准 | 高级 / 克制 | 高管汇报、品牌、融资路演 |
80
+ | `soft` | 柔和信号 | 温暖 / 人本 | 品牌故事、培训、个人分享 |
81
+ | `bold` | 极繁大字 | 大声 / 动能 | 产品发布、活动、大事件 |
82
+
83
+ 主题灵感来自 [hyperframes](https://github.com/STARDUSTLC666/dsh-hyperframes) 的 `visual-styles.md`,完整色板见 `skills/dsh-ppt/references/themes.md`。
84
+
85
+ ## Markdown 输入规则
86
+
87
+ - 第一个 `# 标题` → 封面标题;其下第一段 → 封面副标题。
88
+ - 每个 `## 小节` → 一页:有列表生成 `bullets` 页,无内容生成 `section` 过渡页。
89
+ - 没有标题的纯文本 → 第一段作封面,后续每 5 句一页。
90
+ - 只有一句话 → 自动生成「封面 → 核心观点 → 结束页」三页完整结构。
91
+ - 需要精确控制时用结构化 `slides`(`cover | section | bullets | statement | closing`)。
92
+
93
+ ## 配置
94
+
95
+ 插件无必填配置。可选:
96
+
97
+ ```yaml
98
+ - id: dsh-ppt
99
+ config:
100
+ outputDir: E:\decks # 可选;默认会话工作目录
101
+ maxSlides: 40 # 可选;默认 60(3–120)
102
+ ```
103
+
104
+ 也可用环境变量 `DSH_PPT_OUTPUT_DIR` 指定默认输出目录;`ppt_create` 的 `outputDir` 参数优先级最高。
105
+
106
+ ## 中英双语
107
+
108
+ - `lang` 参数:`zh`(默认)/ `en` / `bilingual`,控制播放器界面、页码与结束页默认文案。
109
+ - 内容语言由你撰写:双语 deck 推荐「中文标题 + 英文副标题」,或同一大纲分别生成中英两份。
110
+ - 插件自身文档、技能正文、错误提示均中英双语。
111
+
112
+ ## 工程质量
113
+
114
+ - 纯 Node,零运行时依赖:HTML 模板、OOXML、zip 全部手写,只用 `node:fs` / `node:path` / `node:zlib`。
115
+ - 技能与工具共用一个引擎(`skills/dsh-ppt/scripts/deck-core.mjs`),不会出现「DSH 一个效果、裸技能另一个效果」。
116
+ - 单元测试覆盖注册契约、JSON Schema、主题解析、Markdown 解析、三件套落盘、PPTX 部件完整性、CLI。
117
+ - 无 `eval` / `child_process` / 密钥;产物只写用户指定的本地目录。
118
+
119
+ ## 开发
120
+
121
+ ```sh
122
+ pnpm install
123
+ pnpm run build # tsc → lib/
124
+ pnpm test # 构建 + node --test(注册/配置/引擎/CLI)
125
+ pnpm run smoke:cli # 裸 CLI 冒烟,生成 .smoke-deck
126
+ ```
127
+
128
+ ## 已知限制
129
+
130
+ - PPTX 采用空白版式 + 文本框实现:PowerPoint / WPS 中可正常编辑文字,但暂不生成智能母版占位符。
131
+ - 一句话输入自动生成三页最小结构;更丰富的内容需要先扩写成 Markdown 大纲再调用 `ppt_create`。
132
+ - `bilingual` 只双语化播放器界面,不自动翻译内容。
133
+ - 暂不支持图表、图片、演讲者备注与 PPT 动画;这些在 v0.2+ 规划。
134
+
135
+ ## 协议
136
+
137
+ MIT。社区插件,与 DeepSeek 官方无关;`@deepseek-ai/*` 为官方保留命名空间。
138
+
139
+ ## 相关项目
140
+
141
+ - [dsh-hyperframes](https://github.com/STARDUSTLC666/dsh-hyperframes) — HTML 视频创作技能(本插件视觉风格来源)
142
+ - [dsh-remotion](https://github.com/STARDUSTLC666/dsh-remotion) — React 编程式视频技能
143
+ - [dsh-email](https://github.com/STARDUSTLC666/dsh-email) — 邮件六件套
@@ -0,0 +1,17 @@
1
+ # dsh-ppt:演示文稿技能 + 工具插件(DeepSeek Harness 插件)。
2
+ #
3
+ # 安装即同时注册:
4
+ # 1. 技能 dsh-ppt(把一句话/一篇文档变成完整演示文稿的完整 SOP)
5
+ # 2. 工具 ppt_create / ppt_themes(确定性生成 HTML 网页放映 + PPTX 导出)
6
+ #
7
+ # 插件自带空配置,绝不弄崩启动;无需任何账号或密钥。可选配置:
8
+ #
9
+ # - id: dsh-ppt
10
+ # config:
11
+ # outputDir: E:\decks # 可选:默认写入会话工作目录
12
+ # maxSlides: 40 # 可选:单次生成幻灯片上限,默认 60
13
+ #
14
+ - insert:
15
+ - id: dsh-ppt
16
+ name: dsh-ppt
17
+ config: {}
@@ -0,0 +1,10 @@
1
+ import type { PptConfig } from './types.js';
2
+ export declare const PPT_OUTPUT_DIR_ENV = "DSH_PPT_OUTPUT_DIR";
3
+ export declare const DEFAULT_MAX_SLIDES = 60;
4
+ export interface ResolvedPptConfig {
5
+ outputDir: string;
6
+ maxSlides: number;
7
+ }
8
+ /** 解析并校验插件行配置。本插件无必填项,空配置永远可用。 */
9
+ export declare function resolvePptConfig(config: PptConfig | undefined | null): ResolvedPptConfig;
10
+ export declare function clampInt(value: unknown, fallback: number, min: number, max: number): number;
package/lib/config.js ADDED
@@ -0,0 +1,18 @@
1
+ export const PPT_OUTPUT_DIR_ENV = 'DSH_PPT_OUTPUT_DIR';
2
+ export const DEFAULT_MAX_SLIDES = 60;
3
+ /** 解析并校验插件行配置。本插件无必填项,空配置永远可用。 */
4
+ export function resolvePptConfig(config) {
5
+ const raw = config ?? {};
6
+ const outputDir = (typeof raw.outputDir === 'string' ? raw.outputDir : '').trim()
7
+ || (process.env[PPT_OUTPUT_DIR_ENV] ?? '').trim();
8
+ return {
9
+ outputDir,
10
+ maxSlides: clampInt(raw.maxSlides, DEFAULT_MAX_SLIDES, 3, 120),
11
+ };
12
+ }
13
+ export function clampInt(value, fallback, min, max) {
14
+ const n = typeof value === 'number' ? Math.trunc(value) : fallback;
15
+ if (!Number.isFinite(n))
16
+ return fallback;
17
+ return Math.min(max, Math.max(min, n));
18
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { type SkillsService } from './skill.js';
2
+ import type { PptConfig } from './types.js';
3
+ /** cordis 服务注入:apply 里要使用 ctx.tools 与 ctx.skills。 */
4
+ export declare const inject: string[];
5
+ export declare const name = "dsh-ppt";
6
+ export type Config = PptConfig;
7
+ export interface ToolDefinition {
8
+ name: string;
9
+ description: string;
10
+ parameters: Record<string, unknown>;
11
+ output: {
12
+ schema: Record<string, unknown>;
13
+ render: (args: unknown, value: unknown) => Array<{
14
+ type: 'text';
15
+ text: string;
16
+ }>;
17
+ };
18
+ execute: (rawArgs: unknown) => Promise<unknown>;
19
+ }
20
+ export interface PptPluginContext {
21
+ tools: {
22
+ register(definition: ToolDefinition): () => void;
23
+ };
24
+ skills: SkillsService;
25
+ logger?: {
26
+ warn?(message: string): void;
27
+ };
28
+ on?(event: string, listener: () => void): () => void;
29
+ }
30
+ export declare function apply(ctx: PptPluginContext, config?: Config): void;
31
+ export { resolvePptConfig, PPT_OUTPUT_DIR_ENV, DEFAULT_MAX_SLIDES, clampInt } from './config.js';
32
+ export { bundledSkillsDir, parseSkillFile, registerPptSkill, SKILL_NAMES } from './skill.js';
33
+ export type { PptConfig, PptCreateArgs, PptCreateResult, PptSlideSpec, PptThemeInfo, PptThemesResult } from './types.js';
package/lib/index.js ADDED
@@ -0,0 +1,200 @@
1
+ /**
2
+ * dsh-ppt —— 演示文稿技能 + 工具插件(v0.1.0)。
3
+ *
4
+ * 一句话或一篇 Markdown 文档 → 完整演示文稿三件套:
5
+ * deck.html 独立网页放映(无外链)
6
+ * deck.pptx 可编辑 PPTX(16:9,零依赖 OOXML 生成)
7
+ * deck.json 结构化 manifest
8
+ *
9
+ * 插件同时注册:
10
+ * 1. 技能 dsh-ppt(跨 harness 的完整 SOP,SKILL.md 随包分发)
11
+ * 2. 工具 ppt_create / ppt_themes(确定性生成与主题查询)
12
+ *
13
+ * 生成引擎是 skills/dsh-ppt/scripts/deck-core.mjs,由工具运行时动态加载;
14
+ * 裸 SKILL.md 复制到其他 agent 时可直接运行同目录 build-deck.mjs。
15
+ *
16
+ * @module dsh-ppt
17
+ */
18
+ import { resolvePptConfig } from './config.js';
19
+ import { registerPptSkill } from './skill.js';
20
+ /** cordis 服务注入:apply 里要使用 ctx.tools 与 ctx.skills。 */
21
+ export const inject = ['tools', 'skills'];
22
+ export const name = 'dsh-ppt';
23
+ /**
24
+ * 把作者 DSL 映射编译成原生 JSON Schema 对象,作为 defineTool 的
25
+ * definition.parameters 原样下发。原生 wire 请求会逐字携带该值。
26
+ */
27
+ function compileParameters(spec) {
28
+ const properties = {};
29
+ const required = [];
30
+ for (const [key, prop] of Object.entries(spec)) {
31
+ if (prop?.required === true)
32
+ required.push(key);
33
+ const node = {};
34
+ if (typeof prop?.type === 'string')
35
+ node.type = prop.type;
36
+ if (typeof prop?.description === 'string')
37
+ node.description = prop.description;
38
+ if (Array.isArray(prop?.enum))
39
+ node.enum = prop.enum;
40
+ if (prop?.type === 'array' && prop.items !== null && typeof prop.items === 'object') {
41
+ node.items = {
42
+ type: prop.items.type === 'object' ? 'object' : 'string',
43
+ ...(prop.items.type === 'object' ? { additionalProperties: true } : {}),
44
+ };
45
+ }
46
+ properties[key] = node;
47
+ }
48
+ return { type: 'object', properties, ...(required.length > 0 ? { required } : {}) };
49
+ }
50
+ const themeInfoSchema = {
51
+ type: 'object',
52
+ properties: {
53
+ id: { type: 'string' },
54
+ name: { type: 'string' },
55
+ mood: { type: 'string' },
56
+ bestFor: { type: 'string' },
57
+ dark: { type: 'boolean' },
58
+ palette: { type: 'object', additionalProperties: true },
59
+ fonts: { type: 'object', additionalProperties: true },
60
+ },
61
+ additionalProperties: true,
62
+ };
63
+ const themesResultSchema = {
64
+ type: 'object',
65
+ properties: {
66
+ ok: { type: 'boolean' },
67
+ themes: { type: 'array', items: themeInfoSchema },
68
+ },
69
+ additionalProperties: true,
70
+ };
71
+ const createResultSchema = {
72
+ type: 'object',
73
+ properties: {
74
+ ok: { type: 'boolean' },
75
+ title: { type: 'string' },
76
+ theme: { type: 'string' },
77
+ language: { type: 'string' },
78
+ slideCount: { type: 'integer' },
79
+ outputDir: { type: 'string' },
80
+ files: {
81
+ type: 'object',
82
+ properties: {
83
+ html: { type: 'string' },
84
+ pptx: { type: 'string' },
85
+ json: { type: 'string' },
86
+ },
87
+ additionalProperties: true,
88
+ },
89
+ htmlPath: { type: 'string' },
90
+ pptxPath: { type: 'string' },
91
+ jsonPath: { type: 'string' },
92
+ },
93
+ additionalProperties: true,
94
+ };
95
+ function oneText(text) {
96
+ return [{ type: 'text', text }];
97
+ }
98
+ function renderThemes(value) {
99
+ if (value.themes.length === 0)
100
+ return oneText('dsh-ppt 没有可用主题。');
101
+ const lines = value.themes.map((theme) => '- ' + theme.id + ':' + theme.name + '(' + theme.mood + ')|适合:' + theme.bestFor + '|' + (theme.dark ? '深色' : '浅色'));
102
+ return oneText('dsh-ppt 内置主题:\n\n' + lines.join('\n') + '\n\nppt_create 的 theme 参数填其中的 id(默认 data)。');
103
+ }
104
+ function renderCreate(value) {
105
+ return oneText('dsh-ppt 已生成 ' + value.slideCount + ' 页演示文稿(主题 ' + value.theme + ',语言 ' + value.language + '):\n' +
106
+ 'HTML 网页放映:' + value.htmlPath + '\n' +
107
+ 'PPTX 导出:' + value.pptxPath + '\n' +
108
+ 'Manifest:' + value.jsonPath + '\n' +
109
+ 'HTML 双击即可放映(方向键翻页 / F 全屏 / G 总览 / P 打印);PPTX 可用 PowerPoint / WPS / Keynote 打开。');
110
+ }
111
+ let enginePromise = null;
112
+ function getEngine() {
113
+ if (enginePromise === null) {
114
+ const engineUrl = new URL('../skills/dsh-ppt/scripts/deck-core.mjs', import.meta.url);
115
+ enginePromise = import(engineUrl.href).then((module) => module)
116
+ .catch((error) => {
117
+ enginePromise = null;
118
+ throw error;
119
+ });
120
+ }
121
+ return enginePromise;
122
+ }
123
+ function isRecord(value) {
124
+ return value !== null && typeof value === 'object';
125
+ }
126
+ export function apply(ctx, config = {}) {
127
+ const resolved = resolvePptConfig(config);
128
+ const warn = (message) => { ctx.logger?.warn?.(message); };
129
+ const disposers = [];
130
+ // 技能注册:单个技能文件缺失只告警,不弄崩宿主启动。
131
+ try {
132
+ disposers.push(registerPptSkill(ctx));
133
+ }
134
+ catch (error) {
135
+ warn('[dsh-ppt] 技能加载失败:' + (error instanceof Error ? error.message : String(error)));
136
+ }
137
+ disposers.push(ctx.tools.register({
138
+ name: 'ppt_themes',
139
+ description: 'List the built-in visual themes of dsh-ppt (id, name, mood, best-for, light/dark palette) before building a deck. Use a theme id as the theme argument of ppt_create. 中文:列出 dsh-ppt 内置视觉主题(id、名称、情绪、适用场景、明暗色板),用于选择 ppt_create 的 theme 参数。',
140
+ parameters: compileParameters({
141
+ lang: { type: 'string', description: 'Theme description language: zh (default), en, or bilingual.' },
142
+ }),
143
+ output: {
144
+ schema: themesResultSchema,
145
+ render: (_args, value) => renderThemes(value),
146
+ },
147
+ async execute(rawArgs) {
148
+ const args = isRecord(rawArgs) ? rawArgs : {};
149
+ const lang = typeof args.lang === 'string' && args.lang.trim() !== '' ? args.lang.trim() : 'zh';
150
+ const engine = await getEngine();
151
+ const themes = engine.listThemes(lang);
152
+ return { ok: true, themes };
153
+ },
154
+ }));
155
+ disposers.push(ctx.tools.register({
156
+ name: 'ppt_create',
157
+ description: 'Build a complete presentation deck from one sentence or a Markdown document and write three artifacts to outputDir: a standalone HTML web slideshow, an editable 16:9 PPTX, and a deck.json manifest. Five built-in visual themes are available (see ppt_themes). content is Markdown text (recommended); advanced callers may pass structured slides instead. 中文:把一句话或一篇 Markdown 文档生成完整演示文稿,写入 outputDir 三个文件:独立 HTML 网页放映、可编辑 16:9 PPTX、deck.json manifest;内置 5 套视觉主题。',
158
+ parameters: compileParameters({
159
+ title: { type: 'string', required: true, description: 'Deck title (used for the cover and file names).' },
160
+ content: { type: 'string', required: true, description: 'Markdown content: one sentence, a paragraph, or a full document. First # heading becomes the cover title; ## headings become slides; -/* lists become bullets. Required unless slides is provided.' },
161
+ theme: { type: 'string', description: 'Visual theme id: swiss / velvet / data / soft / bold. Default data. See ppt_themes.' },
162
+ lang: { type: 'string', description: 'UI language of the generated player: zh (default), en, or bilingual. Content language is whatever you write.' },
163
+ slides: { type: 'array', items: { type: 'object', additionalProperties: true }, description: 'Optional structured slides: [{ layout: cover|section|bullets|statement|closing, title, subtitle, kicker, bullets: [] }]. Use this for precise control instead of content.' },
164
+ outputDir: { type: 'string', description: 'Directory to write the files into. Default: session working directory (or the plugin outputDir config).' },
165
+ fileName: { type: 'string', description: 'Base file name for the three artifacts. Default: sanitized deck title.' },
166
+ }),
167
+ output: {
168
+ schema: createResultSchema,
169
+ render: (_args, value) => renderCreate(value),
170
+ },
171
+ async execute(rawArgs) {
172
+ const args = (isRecord(rawArgs) ? rawArgs : {});
173
+ if (typeof args.title !== 'string' || args.title.trim() === '')
174
+ throw new Error('dsh-ppt:title 不能为空');
175
+ const hasContent = typeof args.content === 'string' && args.content.trim() !== '';
176
+ const hasSlides = Array.isArray(args.slides) && args.slides.length > 0;
177
+ if (!hasContent && !hasSlides)
178
+ throw new Error('dsh-ppt:content 不能为空(或用 slides 传结构化幻灯片)');
179
+ const engine = await getEngine();
180
+ return engine.buildDeck({
181
+ title: args.title.trim(),
182
+ content: typeof args.content === 'string' ? args.content : '',
183
+ slides: hasSlides ? args.slides : undefined,
184
+ theme: typeof args.theme === 'string' && args.theme.trim() !== '' ? args.theme.trim() : undefined,
185
+ lang: typeof args.lang === 'string' && args.lang.trim() !== '' ? args.lang.trim() : undefined,
186
+ outputDir: typeof args.outputDir === 'string' && args.outputDir.trim() !== '' ? args.outputDir.trim() : (resolved.outputDir || undefined),
187
+ fileName: typeof args.fileName === 'string' && args.fileName.trim() !== '' ? args.fileName.trim() : undefined,
188
+ maxSlides: resolved.maxSlides,
189
+ });
190
+ },
191
+ }));
192
+ if (typeof ctx.on === 'function') {
193
+ ctx.on('dispose', () => {
194
+ for (const dispose of disposers)
195
+ dispose();
196
+ });
197
+ }
198
+ }
199
+ export { resolvePptConfig, PPT_OUTPUT_DIR_ENV, DEFAULT_MAX_SLIDES, clampInt } from './config.js';
200
+ export { bundledSkillsDir, parseSkillFile, registerPptSkill, SKILL_NAMES } from './skill.js';
package/lib/skill.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /** cordis 服务注入:apply 里要使用 ctx.skills。 */
2
+ export interface SkillRegistration {
3
+ name: string;
4
+ description: string;
5
+ content: string;
6
+ resourceBase: {
7
+ kind: 'directory';
8
+ path: string;
9
+ };
10
+ source?: string;
11
+ }
12
+ export interface SkillsService {
13
+ register(definition: SkillRegistration): () => void;
14
+ }
15
+ export declare const SKILL_NAMES: readonly ["dsh-ppt"];
16
+ /** 随包分发的技能目录绝对路径。 */
17
+ export declare function bundledSkillsDir(): string;
18
+ export declare function parseSkillFile(text: string): {
19
+ name: string;
20
+ description: string;
21
+ content: string;
22
+ };
23
+ /**
24
+ * 把技能注册进 DSH 技能注册表。技能文件缺失或 frontmatter 不完整只告警,
25
+ * 绝不弄崩宿主启动。
26
+ */
27
+ export declare function registerPptSkill(ctx: {
28
+ skills: SkillsService;
29
+ }): () => void;