dsh-ppt 0.5.1 → 0.7.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 +96 -78
- package/LICENSE +21 -21
- package/README.en.md +219 -168
- package/README.md +234 -170
- package/cordis.patch.yml +19 -19
- package/docs/FEATURE-PLAN-2026-10-01.md +50 -0
- package/docs/LIBREOFFICE-INTEGRATION-2026-10-01.md +69 -0
- package/lib/execution.d.ts +8 -0
- package/lib/execution.js +41 -6
- package/lib/index.d.ts +1 -1
- package/lib/index.js +5 -5
- package/lib/tools.js +137 -15
- package/lib/types.d.ts +110 -2
- package/package.json +84 -70
- package/skills/dsh-ppt/SKILL.md +74 -69
- package/skills/dsh-ppt/package.json +11 -0
- package/skills/dsh-ppt/references/advanced.md +39 -0
- package/skills/dsh-ppt/references/copywriting.md +59 -59
- package/skills/dsh-ppt/references/syntax.md +1 -1
- package/skills/dsh-ppt/references/themes.md +61 -61
- package/skills/dsh-ppt/references/troubleshooting.md +19 -13
- package/skills/dsh-ppt/scripts/build-deck.mjs +184 -135
- package/skills/dsh-ppt/scripts/deck-advanced.mjs +462 -0
- package/skills/dsh-ppt/scripts/deck-core.mjs +691 -634
- package/skills/dsh-ppt/scripts/deck-delivery.mjs +104 -0
- package/skills/dsh-ppt/scripts/deck-media.mjs +160 -0
- package/skills/dsh-ppt/scripts/deck-render.mjs +236 -0
package/lib/execution.js
CHANGED
|
@@ -2,7 +2,7 @@ import { resolve } from 'node:path';
|
|
|
2
2
|
let enginePromise = null;
|
|
3
3
|
function getEngine() {
|
|
4
4
|
if (enginePromise === null) {
|
|
5
|
-
const engineUrl = new URL('../skills/dsh-ppt/scripts/deck-
|
|
5
|
+
const engineUrl = new URL('../skills/dsh-ppt/scripts/deck-advanced.mjs', import.meta.url);
|
|
6
6
|
enginePromise = import(engineUrl.href).then((module) => module)
|
|
7
7
|
.catch((error) => {
|
|
8
8
|
enginePromise = null;
|
|
@@ -26,7 +26,7 @@ export function createPptExecutors(config, loadEngine = getEngine) {
|
|
|
26
26
|
const themes = engine.listThemes(lang);
|
|
27
27
|
if (args.preview === true) {
|
|
28
28
|
const cwd = exec?.agent?.session.header.cwd ?? process.cwd();
|
|
29
|
-
const requested = typeof args.outputDir === 'string' && args.outputDir.trim() !== '' ? args.outputDir.trim() : '.';
|
|
29
|
+
const requested = typeof args.outputDir === 'string' && args.outputDir.trim() !== '' ? args.outputDir.trim() : config.outputDir || '.';
|
|
30
30
|
const preview = engine.buildThemePreview({
|
|
31
31
|
outputDir: resolve(cwd, requested),
|
|
32
32
|
lang,
|
|
@@ -43,14 +43,14 @@ export function createPptExecutors(config, loadEngine = getEngine) {
|
|
|
43
43
|
throw new Error('dsh-ppt:title 不能为空');
|
|
44
44
|
const hasContent = typeof args.content === 'string' && args.content.trim() !== '';
|
|
45
45
|
const hasSlides = Array.isArray(args.slides) && args.slides.length > 0;
|
|
46
|
-
if (!hasContent && !hasSlides)
|
|
47
|
-
throw new Error('dsh-ppt:content 不能为空(或用 slides 传结构化幻灯片)');
|
|
46
|
+
if (!hasContent && !hasSlides && !args.template)
|
|
47
|
+
throw new Error('dsh-ppt:content 不能为空(或用 slides / template 传结构化幻灯片)');
|
|
48
48
|
const engine = await loadEngine();
|
|
49
|
-
//
|
|
49
|
+
// Check cancellation after loading and before artifact commit inside the async engine.
|
|
50
50
|
exec?.signal?.throwIfAborted();
|
|
51
51
|
const cwd = exec?.agent?.session.header.cwd ?? process.cwd();
|
|
52
52
|
const outputDir = typeof args.outputDir === 'string' && args.outputDir.trim() !== '' ? args.outputDir.trim() : config.outputDir;
|
|
53
|
-
return engine.buildDeck({
|
|
53
|
+
return (engine.buildDeckAsync ?? engine.buildDeck)({
|
|
54
54
|
title: args.title.trim(),
|
|
55
55
|
content: typeof args.content === 'string' ? args.content : '',
|
|
56
56
|
slides: hasSlides ? args.slides : undefined,
|
|
@@ -61,7 +61,42 @@ export function createPptExecutors(config, loadEngine = getEngine) {
|
|
|
61
61
|
fileName: typeof args.fileName === 'string' && args.fileName.trim() !== '' ? args.fileName.trim() : undefined,
|
|
62
62
|
overwrite: args.overwrite === true,
|
|
63
63
|
maxSlides: config.maxSlides,
|
|
64
|
+
template: args.template,
|
|
65
|
+
brand: args.brand,
|
|
66
|
+
cwd,
|
|
67
|
+
signal: exec?.signal,
|
|
64
68
|
});
|
|
65
69
|
},
|
|
70
|
+
async templates(rawArgs, exec) {
|
|
71
|
+
exec?.signal?.throwIfAborted();
|
|
72
|
+
const args = asRecord(rawArgs);
|
|
73
|
+
const engine = await loadEngine();
|
|
74
|
+
exec?.signal?.throwIfAborted();
|
|
75
|
+
return { ok: true, templates: engine.listTemplates(typeof args.lang === 'string' ? args.lang : config.defaultLang || 'zh') };
|
|
76
|
+
},
|
|
77
|
+
async edit(rawArgs, exec) {
|
|
78
|
+
exec?.signal?.throwIfAborted();
|
|
79
|
+
const engine = await loadEngine();
|
|
80
|
+
exec?.signal?.throwIfAborted();
|
|
81
|
+
return engine.editDeck({ ...asRecord(rawArgs), cwd: exec?.agent?.session.header.cwd ?? process.cwd(), signal: exec?.signal });
|
|
82
|
+
},
|
|
83
|
+
async undo(rawArgs, exec) {
|
|
84
|
+
exec?.signal?.throwIfAborted();
|
|
85
|
+
const engine = await loadEngine();
|
|
86
|
+
exec?.signal?.throwIfAborted();
|
|
87
|
+
return engine.undoDeck({ ...asRecord(rawArgs), cwd: exec?.agent?.session.header.cwd ?? process.cwd(), signal: exec?.signal });
|
|
88
|
+
},
|
|
89
|
+
async check(rawArgs, exec) {
|
|
90
|
+
exec?.signal?.throwIfAborted();
|
|
91
|
+
const engine = await loadEngine();
|
|
92
|
+
exec?.signal?.throwIfAborted();
|
|
93
|
+
return (engine.checkDeckAsync ?? engine.checkDeck)({ ...asRecord(rawArgs), cwd: exec?.agent?.session.header.cwd ?? process.cwd(), signal: exec?.signal });
|
|
94
|
+
},
|
|
95
|
+
async render(rawArgs, exec) {
|
|
96
|
+
exec?.signal?.throwIfAborted();
|
|
97
|
+
const engine = await loadEngine();
|
|
98
|
+
exec?.signal?.throwIfAborted();
|
|
99
|
+
return engine.renderDeck({ ...asRecord(rawArgs), cwd: exec?.agent?.session.header.cwd ?? process.cwd(), signal: exec?.signal });
|
|
100
|
+
},
|
|
66
101
|
};
|
|
67
102
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -20,4 +20,4 @@ export interface PptPluginContext {
|
|
|
20
20
|
export declare function apply(ctx: PptPluginContext, config?: Config): void;
|
|
21
21
|
export { resolvePptConfig, PPT_OUTPUT_DIR_ENV, DEFAULT_MAX_SLIDES, clampInt } from './config.js';
|
|
22
22
|
export { bundledSkillsDir, parseSkillFile, registerPptSkill, SKILL_NAMES } from './skill.js';
|
|
23
|
-
export type { PptConfig, PptCreateArgs, PptCreateResult, PptLanguage, PptSlideLayout, PptSlideSpec, PptThemeId, PptThemeInfo, PptThemesResult, } from './types.js';
|
|
23
|
+
export type { PptConfig, PptCreateArgs, PptCreateResult, PptLanguage, PptSlideLayout, PptSlideSpec, PptThemeId, PptThemeInfo, PptThemesResult, PptTemplateId, PptImageSpec, PptChartSpec, PptBrand, PptQualityIssue, PptQualityResult, PptVerification, PptRenderArgs, PptRenderResult, PptCheckResult, PptEditArgs, PptProjectArgs, } from './types.js';
|
package/lib/index.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 一句话或一篇 Markdown 文档 → 完整演示文稿三件套:
|
|
5
5
|
* deck.html 独立网页放映(无外链)
|
|
6
|
-
* deck.pptx 可编辑 PPTX(16:9
|
|
7
|
-
* deck.json
|
|
6
|
+
* deck.pptx 可编辑 PPTX(16:9,原生文字、图片、表格与图表)
|
|
7
|
+
* deck.json 支持按页编辑与撤销的工程
|
|
8
8
|
*
|
|
9
9
|
* 插件同时注册:
|
|
10
10
|
* 1. 技能 dsh-ppt(跨 harness 的完整 SOP,SKILL.md 随包分发)
|
|
11
|
-
* 2.
|
|
11
|
+
* 2. 创建、主题、结构参考、编辑、撤销、检查与可选渲染工具
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* 生成引擎按需加载;图片与图表采用 @office-kit/pptx。
|
|
14
|
+
* 官方 LibreOffice Kit 可选安装,用于读取最终 PPTX 生成 PNG / PDF。
|
|
15
15
|
*
|
|
16
16
|
* @module dsh-ppt
|
|
17
17
|
*/
|
package/lib/tools.js
CHANGED
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
import { createPptExecutors } from './execution.js';
|
|
2
|
+
/** Harness uses disjoint oneOf branches for unions, never type arrays. */
|
|
3
|
+
function compileNode(node) {
|
|
4
|
+
if (Array.isArray(node.type)) {
|
|
5
|
+
const { type, description, properties, additionalProperties, items, required, ...shared } = node;
|
|
6
|
+
return {
|
|
7
|
+
...(description !== undefined ? { description } : {}),
|
|
8
|
+
oneOf: type.map(kind => compileNode({
|
|
9
|
+
...shared, type: kind,
|
|
10
|
+
...(kind === 'object' ? {
|
|
11
|
+
...(properties !== undefined ? { properties } : {}),
|
|
12
|
+
...(additionalProperties !== undefined ? { additionalProperties } : {}),
|
|
13
|
+
...(required !== undefined ? { required } : {}),
|
|
14
|
+
} : {}),
|
|
15
|
+
...(kind === 'array' && items !== undefined ? { items } : {}),
|
|
16
|
+
})),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const compiled = { ...node };
|
|
20
|
+
if (node.properties)
|
|
21
|
+
compiled.properties = Object.fromEntries(Object.entries(node.properties).map(([key, value]) => [key, compileNode(value)]));
|
|
22
|
+
if (node.items)
|
|
23
|
+
compiled.items = compileNode(node.items);
|
|
24
|
+
if (Array.isArray(node.oneOf))
|
|
25
|
+
compiled.oneOf = node.oneOf.map(value => compileNode(value));
|
|
26
|
+
return compiled;
|
|
27
|
+
}
|
|
2
28
|
function compileParameters(spec) {
|
|
3
29
|
const properties = {};
|
|
4
30
|
const required = [];
|
|
@@ -6,19 +32,19 @@ function compileParameters(spec) {
|
|
|
6
32
|
if (prop?.required === true)
|
|
7
33
|
required.push(key);
|
|
8
34
|
const node = {};
|
|
9
|
-
if (typeof prop?.type === 'string')
|
|
35
|
+
if (typeof prop?.type === 'string' || Array.isArray(prop?.type))
|
|
10
36
|
node.type = prop.type;
|
|
11
37
|
if (typeof prop?.description === 'string')
|
|
12
38
|
node.description = prop.description;
|
|
13
39
|
if (Array.isArray(prop?.enum))
|
|
14
40
|
node.enum = prop.enum;
|
|
15
|
-
if (prop?.type === 'array' && prop.items !== null && typeof prop.items === 'object')
|
|
16
|
-
node.items =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
properties[key] = node;
|
|
41
|
+
if (prop?.type === 'array' && prop.items !== null && typeof prop.items === 'object')
|
|
42
|
+
node.items = prop.items;
|
|
43
|
+
if (prop.properties)
|
|
44
|
+
node.properties = prop.properties;
|
|
45
|
+
if (prop.additionalProperties !== undefined)
|
|
46
|
+
node.additionalProperties = prop.additionalProperties;
|
|
47
|
+
properties[key] = compileNode(node);
|
|
22
48
|
}
|
|
23
49
|
return { type: 'object', properties, ...(required.length > 0 ? { required } : {}) };
|
|
24
50
|
}
|
|
@@ -82,12 +108,60 @@ function renderThemes(value) {
|
|
|
82
108
|
return oneText(summary + '\n\n想要并排对比:ppt_themes { preview: true, outputDir: "..." } 会生成 themes-preview.html 和每套主题的 SVG 色板卡。');
|
|
83
109
|
}
|
|
84
110
|
function renderCreate(value) {
|
|
85
|
-
return oneText('dsh-ppt 已生成 ' + value.slideCount + '
|
|
86
|
-
'
|
|
87
|
-
'PPTX
|
|
88
|
-
'
|
|
89
|
-
'HTML
|
|
111
|
+
return oneText('dsh-ppt 已生成 ' + value.slideCount + ' 页' + (value.deliveryStatus === 'draft' ? '草稿(还有待填写内容,不能作为成品交付)' : '演示文稿') + '(主题 ' + value.theme + ',语言 ' + value.language + '):\n' +
|
|
112
|
+
'打开演示文稿:' + value.htmlPath + '\n' +
|
|
113
|
+
'可直接修改的 PPTX:' + value.pptxPath + '\n' +
|
|
114
|
+
'可继续编辑的项目:' + value.jsonPath + '\n' +
|
|
115
|
+
'HTML 双击即可放映;可点击上一页/下一页、输入页码,以及“检查与导出”定位问题或下载 PPTX。PPTX 可用 PowerPoint / WPS / Keynote 直接编辑。' +
|
|
116
|
+
(value.revision !== undefined ? '\n修订号:' + value.revision + ';可撤销修改:' + value.undoAvailable + '。用 ppt_edit 修改指定页,ppt_undo 撤销。' : '') +
|
|
117
|
+
(value.quality ? '\n\n' + qualityText(value.quality) : ''));
|
|
118
|
+
}
|
|
119
|
+
function qualityText(value) {
|
|
120
|
+
return '质量检查:' + value.errorCount + ' 个错误,' + value.warningCount + ' 个提醒。\n' +
|
|
121
|
+
value.issues.slice(0, 12).map(issue => '第 ' + issue.slide + ' 页:' + issue.message + ';' + issue.suggestion).join('\n') +
|
|
122
|
+
(value.issues.length > 12 ? '\n其余提醒请查看结构化结果。' : '') + '\n' + value.note;
|
|
90
123
|
}
|
|
124
|
+
function renderCheck(value) {
|
|
125
|
+
return oneText(qualityText(value) + '\nPPTX 渲染:' + (value.verification?.pptxRender === 'rendered' ? '已核验当前文件的渲染输出' : '尚未验证') +
|
|
126
|
+
';视觉检查:需查看实际画面。' + (value.render?.message ? '\n' + value.render.message : '') +
|
|
127
|
+
(value.render?.missingFonts?.length ? '\n缺失字体:' + value.render.missingFonts.join('、') + '。请替换为可用字体,重新生成并检查每页;解决前需标注字体问题。' : ''));
|
|
128
|
+
}
|
|
129
|
+
function renderPreview(value) {
|
|
130
|
+
if (!value.ok)
|
|
131
|
+
return oneText((value.message || 'PPTX 尚未渲染。') + (value.installationHint ? '\n' + value.installationHint : ''));
|
|
132
|
+
return oneText('PPTX 已渲染 ' + value.slideCount + ' 页(' + value.backend + '):\n' +
|
|
133
|
+
(value.pngPaths?.length ? '逐页 PNG:\n' + value.pngPaths.slice(0, 6).join('\n') + (value.pngPaths.length > 6 ? '\n其余页面见结构化结果。' : '') + '\n' : '') +
|
|
134
|
+
(value.pdfPath ? 'PDF:' + value.pdfPath + '\n' : '') + '渲染收据:' + value.receiptPath +
|
|
135
|
+
'\n缺失字体:' + (value.missingFonts?.join('、') || '引擎未报告缺失字体') +
|
|
136
|
+
(value.missingFonts?.length ? '\n请先替换为环境和目标机器可用字体,重新生成并渲染检查;解决前不能声称已验收。' : '') +
|
|
137
|
+
'\n请查看实际画面,确认文字、图片和图表后再交付。原可编辑 PPTX 保留。');
|
|
138
|
+
}
|
|
139
|
+
const imageSchema = { type: ['object', 'string', 'null'], properties: {
|
|
140
|
+
src: { type: 'string', description: 'Local PNG/JPEG path relative to the session cwd, or image data URI. Embedded into all output files.' },
|
|
141
|
+
alt: { type: 'string' }, fit: { type: 'string', enum: ['contain', 'cover'] }, caption: { type: 'string' },
|
|
142
|
+
}, additionalProperties: false };
|
|
143
|
+
const chartSchema = { type: ['object', 'null'], properties: {
|
|
144
|
+
kind: { type: 'string', enum: ['column', 'bar', 'line', 'pie'] },
|
|
145
|
+
categories: { type: 'array', items: { type: ['string', 'number'] } },
|
|
146
|
+
series: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, values: { type: 'array', items: { type: ['number', 'string'] } } }, required: ['name', 'values'], additionalProperties: false } },
|
|
147
|
+
rows: { type: 'array', items: { type: 'array', items: { type: ['string', 'number'] } }, description: 'Alternative: header row [category, series names], then category/value rows.' },
|
|
148
|
+
unit: { type: 'string' }, caption: { type: 'string' },
|
|
149
|
+
}, additionalProperties: false };
|
|
150
|
+
const brandProperties = {
|
|
151
|
+
name: { type: 'string' }, primaryColor: { type: 'string' }, backgroundColor: { type: 'string' }, textColor: { type: 'string' },
|
|
152
|
+
fontFamily: { type: 'string', description: 'One installed font family; choose a font available on the recipient computer.' },
|
|
153
|
+
logo: { type: ['string', 'null'], description: 'Local PNG/JPEG logo path; null clears logo during edit.' }, footer: { type: 'string' },
|
|
154
|
+
};
|
|
155
|
+
const slideProperties = {
|
|
156
|
+
layout: { type: 'string', enum: ['cover', 'section', 'bullets', 'statement', 'quote', 'table', 'closing', 'image', 'image-left', 'image-right', 'chart'] },
|
|
157
|
+
title: { type: 'string' }, subtitle: { type: 'string' }, kicker: { type: 'string' }, text: { type: 'string' }, notes: { type: 'string' },
|
|
158
|
+
bullets: { type: 'array', items: { type: 'string' } }, rows: { type: 'array', items: { type: 'array', items: { type: ['string', 'number'] } } },
|
|
159
|
+
image: imageSchema, chart: chartSchema,
|
|
160
|
+
};
|
|
161
|
+
const projectParameters = {
|
|
162
|
+
deckPath: { type: 'string', required: true, description: 'Path to a dsh-ppt .json project, relative to this session cwd or absolute. Arbitrary external PPTX editing is not supported.' },
|
|
163
|
+
expectedRevision: { type: 'integer', description: 'Use revision returned by ppt_create/ppt_check to prevent overwriting another edit.' },
|
|
164
|
+
};
|
|
91
165
|
/** Assemble presentation schemas, rendering and the session-aware executors. */
|
|
92
166
|
export function buildPptTools(config) {
|
|
93
167
|
const executors = createPptExecutors(config);
|
|
@@ -109,14 +183,16 @@ export function buildPptTools(config) {
|
|
|
109
183
|
},
|
|
110
184
|
{
|
|
111
185
|
name: 'ppt_create',
|
|
112
|
-
description: '
|
|
186
|
+
description: 'Generate a standalone HTML slideshow, editable 16:9 PPTX and JSON project from prepared Markdown or structured slides. Organize actual facts and content before calling; this deterministic generator does not invent missing facts or research them. Supports five themes, local images, native editable charts and shared branding. Check and visually review artifacts before delivery; template-only output is an unfilled draft. 中文:先组织事实与内容,再生成可直接放映和继续编辑的三件套;支持图片、原生图表与品牌,交付前检查成品。',
|
|
113
187
|
parameters: compileParameters({
|
|
114
188
|
title: { type: 'string', required: true, description: 'Deck title (used for the cover and file names).' },
|
|
115
189
|
content: { type: 'string', description: 'Markdown content: one sentence, a paragraph, or a full document. First # heading becomes the cover title; ## headings become slides; -/* lists become bullets; | ... | tables paginate with repeated headers (8 data rows/page, up to 8 columns and 60 characters/cell); > blockquotes become quote slides; <!-- 备注: ... --> comments become speaker notes. Required unless slides is provided.' },
|
|
116
190
|
theme: { type: 'string', description: 'Visual theme id: swiss / velvet / data / soft / bold. Default data. See ppt_themes.' },
|
|
117
191
|
lang: { type: 'string', description: 'UI language of the generated player: zh (default), en, or bilingual. Content language is whatever you write.' },
|
|
118
192
|
motion: { type: 'string', enum: ['on', 'off'], description: 'Slide transitions plus bullet entrance animations: on (default) or off for a fully static deck.' },
|
|
119
|
-
slides: { type: 'array', items: { type: 'object', additionalProperties: true }, description: '
|
|
193
|
+
slides: { type: 'array', items: { type: 'object', properties: slideProperties, additionalProperties: true }, description: 'Structured slides including image/image-left/image-right and chart. Images: {src, alt, fit: contain|cover, caption}. Charts: {kind: column|bar|line|pie, categories, series:[{name,values}]} or rows with header. Tables paginate at 8 data rows. Keep full content editable; use images only for actual photos/screenshots/logos.' },
|
|
194
|
+
template: { type: 'string', enum: ['weekly', 'defense', 'project', 'pitch'], description: 'Scenario reference from ppt_templates. Provide finished content/slides following its outline. Template alone creates a clearly marked draft with unfilled slots, never a finished presentation.' },
|
|
195
|
+
brand: { type: 'object', properties: brandProperties, additionalProperties: false, description: 'Shared brand: #RRGGBB primary/background/text colors, fontFamily, logo and footer. Applied to HTML and PPTX.' },
|
|
120
196
|
outputDir: { type: 'string', description: 'Directory to write the files into. Default: session working directory (or the plugin outputDir config).' },
|
|
121
197
|
fileName: { type: 'string', description: 'Base file name for the three artifacts. Default: sanitized deck title.' },
|
|
122
198
|
overwrite: { type: 'boolean', description: 'Replace an existing same-name HTML/PPTX/JSON trio. Default false: choose a unique numeric suffix instead.' },
|
|
@@ -127,5 +203,51 @@ export function buildPptTools(config) {
|
|
|
127
203
|
},
|
|
128
204
|
execute: executors.create,
|
|
129
205
|
},
|
|
206
|
+
{
|
|
207
|
+
name: 'ppt_templates',
|
|
208
|
+
description: 'List weekly report, thesis defense, project update and pitch scenario outlines and draft structures. Use as internal content planning references; fill actual facts before delivering a finished deck. 中文:查看周报、答辩、项目汇报和路演的结构参考,组织并填写真实内容后再调用 ppt_create 交付成品。',
|
|
209
|
+
parameters: compileParameters({ lang: { type: 'string', description: 'zh / en / bilingual, default zh.' } }),
|
|
210
|
+
output: { schema: { type: 'object', additionalProperties: true }, render: (_args, value) => oneText(JSON.stringify(value, null, 2)) },
|
|
211
|
+
execute: executors.templates,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'ppt_edit',
|
|
215
|
+
description: 'Modify selected slides in an existing dsh-ppt JSON project, preserving other pages and stable IDs. Each edit uses a 1-based slide number or ID and a patch of changed fields. Regenerates the HTML/PPTX/JSON trio atomically, embeds new images, stores undo history. Use ppt_check first and expectedRevision. 中文:按页码或 ID 修改指定页,其他页不变,支持撤销;可同步调整品牌。',
|
|
216
|
+
parameters: compileParameters({ ...projectParameters,
|
|
217
|
+
edits: { type: 'array', items: { type: 'object', properties: { slide: { type: ['integer', 'string'], description: '1-based page number or stable slide ID' }, patch: { type: 'object', properties: slideProperties, additionalProperties: false } }, required: ['slide', 'patch'], additionalProperties: false } },
|
|
218
|
+
brand: { type: ['object', 'null'], properties: brandProperties, additionalProperties: false, description: 'Merge shared branding. null clears all branding. Empty footer clears footer; logo:null clears logo.' },
|
|
219
|
+
}),
|
|
220
|
+
output: { schema: createResultSchema, render: (_args, value) => renderCreate(value) },
|
|
221
|
+
execute: executors.edit,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'ppt_undo',
|
|
225
|
+
description: 'Undo the most recent edit in a dsh-ppt JSON project, restoring slides, charts, embedded images and branding together. Revision increases so stale edits cannot overwrite the undo. Keeps up to 20 edits. 中文:撤销最近一次修改,恢复正文、图表、图片和品牌设置。',
|
|
226
|
+
parameters: compileParameters(projectParameters),
|
|
227
|
+
output: { schema: createResultSchema, render: (_args, value) => renderCreate(value) },
|
|
228
|
+
execute: executors.undo,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'ppt_check',
|
|
232
|
+
description: 'Inspect a dsh-ppt JSON project without rewriting artifacts. Report revision, slide IDs, page-specific errors and warnings for dense text, estimated overflow, small table fonts, missing/low-resolution images, chart labels, contrast and unfilled template slots. Static estimates require final visual review in browser and PowerPoint/WPS. 中文:交付前检查工程,定位具体页的问题并给出修改建议,不改文件。',
|
|
233
|
+
parameters: compileParameters({ deckPath: projectParameters.deckPath,
|
|
234
|
+
renderReceipt: { type: 'string', description: 'Optional receiptPath returned by ppt_render. Rechecks current PPTX hash and every rendered output; never implies a visual pass.' },
|
|
235
|
+
}),
|
|
236
|
+
output: { schema: { type: 'object', additionalProperties: true }, render: (_args, value) => renderCheck(value) },
|
|
237
|
+
execute: executors.check,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'ppt_render',
|
|
241
|
+
description: 'Read the final PPTX and render all pages to PNG, PDF, or both using optional official @deepseek-ai/libreoffice-kit@0.1.3. Returns unavailable plus installation guidance when absent; ordinary deck creation remains usable. Retains original editable PPTX and creates a separate unique output folder and integrity receipt. Reports missing fonts. Inspect actual pages before delivery. 中文:把最终 PPTX 渲染为逐页图片和 PDF,报告缺失字体;需安装可选官方引擎,渲染成功仍需检查画面。',
|
|
242
|
+
parameters: compileParameters({
|
|
243
|
+
pptxPath: { type: 'string', required: true, description: 'Final .pptx path, absolute or relative to session cwd.' },
|
|
244
|
+
outputDir: { type: 'string', description: 'Base output folder. Existing folders are preserved; creates a unique sibling when needed. Default <deck>-render.' },
|
|
245
|
+
format: { type: 'string', enum: ['png', 'pdf', 'both'], description: 'png (default), pdf, or both.' },
|
|
246
|
+
width: { type: 'integer', description: 'PNG target width 320–3840, default 1440; scales from actual page dimensions.' },
|
|
247
|
+
timeoutMs: { type: 'integer', description: 'Whole rendering deadline in milliseconds, 1000–300000, default 120000.' },
|
|
248
|
+
}),
|
|
249
|
+
output: { schema: { type: 'object', additionalProperties: true }, render: (_args, value) => renderPreview(value) },
|
|
250
|
+
execute: executors.render,
|
|
251
|
+
},
|
|
130
252
|
];
|
|
131
253
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,99 @@
|
|
|
1
1
|
/** dsh-ppt 的公共类型。 */
|
|
2
2
|
export type PptThemeId = 'swiss' | 'velvet' | 'data' | 'soft' | 'bold';
|
|
3
3
|
export type PptLanguage = 'zh' | 'en' | 'bilingual';
|
|
4
|
-
export type PptSlideLayout = 'cover' | 'section' | 'bullets' | 'statement' | 'quote' | 'table' | 'closing';
|
|
4
|
+
export type PptSlideLayout = 'cover' | 'section' | 'bullets' | 'statement' | 'quote' | 'table' | 'closing' | 'image' | 'image-left' | 'image-right' | 'chart';
|
|
5
|
+
export type PptTemplateId = 'weekly' | 'defense' | 'project' | 'pitch';
|
|
6
|
+
export interface PptImageSpec {
|
|
7
|
+
src?: string;
|
|
8
|
+
assetId?: string;
|
|
9
|
+
alt?: string;
|
|
10
|
+
fit?: 'contain' | 'cover';
|
|
11
|
+
caption?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PptChartSpec {
|
|
14
|
+
kind?: 'column' | 'bar' | 'line' | 'pie';
|
|
15
|
+
categories?: Array<string | number>;
|
|
16
|
+
series?: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
values: Array<string | number>;
|
|
19
|
+
}>;
|
|
20
|
+
rows?: Array<Array<string | number>>;
|
|
21
|
+
unit?: string;
|
|
22
|
+
caption?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface PptBrand {
|
|
25
|
+
name?: string;
|
|
26
|
+
primaryColor?: string;
|
|
27
|
+
backgroundColor?: string;
|
|
28
|
+
textColor?: string;
|
|
29
|
+
fontFamily?: string;
|
|
30
|
+
logo?: string | null;
|
|
31
|
+
footer?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface PptQualityIssue {
|
|
34
|
+
slide: number;
|
|
35
|
+
slideId?: string;
|
|
36
|
+
severity: 'warning' | 'error';
|
|
37
|
+
code: string;
|
|
38
|
+
message: string;
|
|
39
|
+
suggestion: string;
|
|
40
|
+
}
|
|
41
|
+
export interface PptQualityResult {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
slideCount: number;
|
|
44
|
+
errorCount: number;
|
|
45
|
+
warningCount: number;
|
|
46
|
+
issues: PptQualityIssue[];
|
|
47
|
+
note: string;
|
|
48
|
+
}
|
|
49
|
+
export interface PptVerification {
|
|
50
|
+
static: 'checked';
|
|
51
|
+
pptxRender: 'not-verified' | 'rendered';
|
|
52
|
+
visual: 'not-verified';
|
|
53
|
+
}
|
|
54
|
+
export interface PptRenderArgs {
|
|
55
|
+
pptxPath: string;
|
|
56
|
+
outputDir?: string;
|
|
57
|
+
format?: 'png' | 'pdf' | 'both';
|
|
58
|
+
width?: number;
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
}
|
|
61
|
+
export interface PptRenderResult {
|
|
62
|
+
ok: boolean;
|
|
63
|
+
status: 'unavailable' | 'not-verified' | 'rendered';
|
|
64
|
+
backend?: string;
|
|
65
|
+
rendererVersion?: string;
|
|
66
|
+
sourceSha256?: string;
|
|
67
|
+
outputDir?: string;
|
|
68
|
+
reason?: string;
|
|
69
|
+
slideCount?: number;
|
|
70
|
+
pngPaths?: string[];
|
|
71
|
+
pdfPath?: string;
|
|
72
|
+
receiptPath?: string;
|
|
73
|
+
missingFonts?: string[];
|
|
74
|
+
message?: string;
|
|
75
|
+
installationHint?: string;
|
|
76
|
+
visual?: 'not-verified';
|
|
77
|
+
}
|
|
78
|
+
export interface PptCheckResult extends PptQualityResult {
|
|
79
|
+
verification: PptVerification;
|
|
80
|
+
render?: PptRenderResult;
|
|
81
|
+
}
|
|
82
|
+
export interface PptEditArgs {
|
|
83
|
+
deckPath: string;
|
|
84
|
+
edits?: Array<{
|
|
85
|
+
slide: number | string;
|
|
86
|
+
patch: Partial<Omit<PptSlideSpec, 'id'>>;
|
|
87
|
+
}>;
|
|
88
|
+
brand?: PptBrand | null;
|
|
89
|
+
expectedRevision?: number;
|
|
90
|
+
}
|
|
91
|
+
export interface PptProjectArgs {
|
|
92
|
+
deckPath: string;
|
|
93
|
+
expectedRevision?: number;
|
|
94
|
+
}
|
|
5
95
|
export interface PptSlideSpec {
|
|
96
|
+
id?: string;
|
|
6
97
|
layout?: PptSlideLayout;
|
|
7
98
|
title?: string;
|
|
8
99
|
subtitle?: string;
|
|
@@ -13,6 +104,8 @@ export interface PptSlideSpec {
|
|
|
13
104
|
rows?: Array<Array<string | number>>;
|
|
14
105
|
/** 演讲者备注;写入 HTML 备注面板与 PPTX 原生备注页。 */
|
|
15
106
|
notes?: string;
|
|
107
|
+
image?: PptImageSpec | string | null;
|
|
108
|
+
chart?: PptChartSpec | null;
|
|
16
109
|
}
|
|
17
110
|
export interface PptConfig {
|
|
18
111
|
/** 默认输出目录;调用 ppt_create 时可用 outputDir 覆盖。相对路径按会话工作目录解析。 */
|
|
@@ -38,6 +131,8 @@ export interface PptCreateArgs {
|
|
|
38
131
|
fileName?: string;
|
|
39
132
|
/** 是否覆盖同名三件套;默认 false,同名时自动选择唯一后缀。 */
|
|
40
133
|
overwrite?: boolean;
|
|
134
|
+
template?: PptTemplateId;
|
|
135
|
+
brand?: PptBrand;
|
|
41
136
|
}
|
|
42
137
|
export interface PptThemeInfo {
|
|
43
138
|
id: string;
|
|
@@ -80,9 +175,22 @@ export interface PptCreateResult {
|
|
|
80
175
|
htmlPath: string;
|
|
81
176
|
pptxPath: string;
|
|
82
177
|
jsonPath: string;
|
|
178
|
+
deckId?: string;
|
|
179
|
+
revision?: number;
|
|
180
|
+
undoAvailable?: number;
|
|
181
|
+
deliveryStatus?: 'draft' | 'ready-for-review';
|
|
182
|
+
quality?: PptQualityResult;
|
|
183
|
+
verification?: PptVerification;
|
|
83
184
|
}
|
|
84
|
-
/**
|
|
185
|
+
/** Shared generator and optional renderer exposed to the plugin. */
|
|
85
186
|
export interface DeckEngine {
|
|
187
|
+
buildDeckAsync?(options: Record<string, unknown>): Promise<PptCreateResult>;
|
|
188
|
+
listTemplates(lang?: string): unknown[];
|
|
189
|
+
editDeck(options: Record<string, unknown>): Promise<PptCreateResult>;
|
|
190
|
+
undoDeck(options: Record<string, unknown>): Promise<PptCreateResult>;
|
|
191
|
+
checkDeck(options: Record<string, unknown>): PptQualityResult;
|
|
192
|
+
checkDeckAsync?(options: Record<string, unknown>): Promise<PptCheckResult>;
|
|
193
|
+
renderDeck(options: Record<string, unknown>): Promise<PptRenderResult>;
|
|
86
194
|
buildDeck(options: Record<string, unknown>): PptCreateResult;
|
|
87
195
|
buildThemePreview(options: Record<string, unknown>): PptThemePreviewResult;
|
|
88
196
|
listThemes(lang?: string): PptThemeInfo[];
|
package/package.json
CHANGED
|
@@ -1,70 +1,84 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-ppt",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DSH 演示文稿插件:
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"types": "lib/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./lib/index.d.ts",
|
|
11
|
-
"default": "./lib/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./skills/*": "./skills/*",
|
|
14
|
-
"./deck-core": "./skills/dsh-ppt/scripts/deck-core.mjs",
|
|
15
|
-
"./
|
|
16
|
-
"./
|
|
17
|
-
"./
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"url": "https://github.com/STARDUSTLC666/dsh-ppt
|
|
66
|
-
},
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-ppt",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "DSH 演示文稿插件:HTML 与可编辑 PPTX、图文排版、原生图表、按页编辑撤销、质量检查与可选官方 PNG/PDF 渲染。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./skills/*": "./skills/*",
|
|
14
|
+
"./deck-core": "./skills/dsh-ppt/scripts/deck-core.mjs",
|
|
15
|
+
"./deck-advanced": "./skills/dsh-ppt/scripts/deck-advanced.mjs",
|
|
16
|
+
"./deck-render": "./skills/dsh-ppt/scripts/deck-render.mjs",
|
|
17
|
+
"./build-deck": "./skills/dsh-ppt/scripts/build-deck.mjs",
|
|
18
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"lib",
|
|
23
|
+
"skills",
|
|
24
|
+
"docs",
|
|
25
|
+
"cordis.patch.yml",
|
|
26
|
+
"README.md",
|
|
27
|
+
"README.en.md",
|
|
28
|
+
"CHANGELOG.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
34
|
+
"test": "pnpm run build && node --test \"test/*.test.mjs\"",
|
|
35
|
+
"test:core": "node --test \"test/*.test.mjs\"",
|
|
36
|
+
"smoke:cli": "node skills/dsh-ppt/scripts/build-deck.mjs --title \"DSH PPT Smoke\" --content @smoke/sample-deck.md --theme data --lang zh --out .smoke-deck"
|
|
37
|
+
},
|
|
38
|
+
"dsh": {
|
|
39
|
+
"bundle": {
|
|
40
|
+
"patch": "./cordis.patch.yml"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"dsh",
|
|
45
|
+
"deepseek-harness",
|
|
46
|
+
"dsh-plugin",
|
|
47
|
+
"plugin",
|
|
48
|
+
"skills",
|
|
49
|
+
"ppt",
|
|
50
|
+
"pptx",
|
|
51
|
+
"presentation",
|
|
52
|
+
"slides",
|
|
53
|
+
"html"
|
|
54
|
+
],
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "^24.0.0",
|
|
61
|
+
"typescript": "^5.6.0"
|
|
62
|
+
},
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "git+https://github.com/STARDUSTLC666/dsh-ppt.git"
|
|
66
|
+
},
|
|
67
|
+
"bugs": {
|
|
68
|
+
"url": "https://github.com/STARDUSTLC666/dsh-ppt/issues"
|
|
69
|
+
},
|
|
70
|
+
"homepage": "https://github.com/STARDUSTLC666/dsh-ppt#readme",
|
|
71
|
+
"author": "stardustlc",
|
|
72
|
+
"packageManager": "pnpm@11.7.0",
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@office-kit/pptx": "0.21.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@deepseek-ai/libreoffice-kit": "0.1.3"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"@deepseek-ai/libreoffice-kit": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|