dsh-ppt 0.8.1 → 0.9.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 +4 -0
- package/README.en.md +3 -1
- package/README.md +3 -1
- package/docs/CHANGELOG.en.md +4 -0
- package/docs/USAGE.en.md +4 -0
- package/docs/USAGE.md +4 -0
- package/lib/client.js +4 -1
- package/lib/workbench-language.js +2 -0
- package/lib/workbench.js +32 -4
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
package/README.en.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[中文](README.md)
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
Turn source material into an HTML slideshow, editable PPTX and a reusable project.
|
|
6
8
|
|
|
7
|
-
[](https://www.npmjs.com/package/dsh-ppt) [](https://www.npmjs.com/package/dsh-ppt) [](https://www.npmjs.com/package/dsh-ppt)
|
|
8
10
|
|
|
9
11
|
## What it does
|
|
10
12
|
|
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.en.md)
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
把材料制作成 HTML 放映、可编辑 PPTX 和可继续修改的工程文件。
|
|
6
8
|
|
|
7
|
-
[](https://www.npmjs.com/package/dsh-ppt) [](https://www.npmjs.com/package/dsh-ppt) [](https://www.npmjs.com/package/dsh-ppt)
|
|
8
10
|
|
|
9
11
|
## 功能
|
|
10
12
|
|
package/docs/CHANGELOG.en.md
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
These English notes preserve the earlier translations. The main changelog contains the consolidated version history.
|
|
6
6
|
|
|
7
|
+
## 0.9.0 (2026-10-05)
|
|
8
|
+
|
|
9
|
+
- Add a delivery ZIP containing HTML, editable PPTX, project JSON and usage notes. Only indexed files at the saved revision are included; missing or stale artifacts block delivery.
|
|
10
|
+
|
|
7
11
|
## 0.7.0 (2026-10-02)
|
|
8
12
|
|
|
9
13
|
- Generated HTML decks now have previous/next buttons and page-number navigation, with keyboard, touch and a usable 390px layout.
|
package/docs/USAGE.en.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[Overview](../README.en.md) · [Changelog](../CHANGELOG.md) · [Validation](VALIDATION.md)
|
|
4
4
|
|
|
5
|
+
## Current improvements
|
|
6
|
+
|
|
7
|
+
Save workbench edits, then choose Download delivery ZIP. Individual downloads remain available. The ZIP excludes configuration, secrets and other workspace files; each file is limited to 64 MiB and the total to 128 MiB.
|
|
8
|
+
|
|
5
9
|
## Capabilities
|
|
6
10
|
|
|
7
11
|
| Capability | Description |
|
package/docs/USAGE.md
CHANGED
package/lib/client.js
CHANGED
|
@@ -77,6 +77,8 @@ const EN = Object.freeze({
|
|
|
77
77
|
"第 ": "Slide ",
|
|
78
78
|
" 页:": ": ",
|
|
79
79
|
"下载最新 PPTX": "Download latest PPTX",
|
|
80
|
+
"下载成品包 ZIP": "Download delivery ZIP",
|
|
81
|
+
"成品包包含 PPTX、HTML 和工程文件。请先保存修改,再下载最新版本。": "The bundle includes the PPTX, HTML and project file. Save your edits before downloading the latest version.",
|
|
80
82
|
"下载 HTML": "Download HTML",
|
|
81
83
|
"备份工程": "Back up project",
|
|
82
84
|
"演示文稿": "Presentations"
|
|
@@ -290,7 +292,8 @@ function useLanguage(locale) {
|
|
|
290
292
|
image && h(React.Fragment, null, h('p', { className: 'muted' }, t("已选择:") + image.name), h('label', { htmlFor: 'pptw-alt' }, t("图片说明")), h('input', { id: 'pptw-alt', value: image.alt, disabled: busy, onChange: e => setImage({ ...image, alt: e.target.value }) }), h('label', { htmlFor: 'pptw-fit' }, t("图片适应方式")), h('select', { id: 'pptw-fit', value: image.fit, disabled: busy, onChange: e => setImage({ ...image, fit: e.target.value }) }, h('option', { value: 'contain' }, t("完整显示")), h('option', { value: 'cover' }, t("填满并裁切")))),
|
|
291
293
|
h('div', { className: 'tools' }, h('button', { className: 'primary', disabled: busy || !dirty, onClick: save }, t("保存此页")), h('button', { disabled: busy || !dirty, onClick: () => { setDraft(draftFor(slide)); setImage(null); setDirty(false); setError(''); setConflict(false); setOverlaps([]); } }, t("取消修改")), h('button', { disabled: busy || dirty || !project.undoAvailable, onClick: undo }, t("撤销上次修改")))),
|
|
292
294
|
h('details', { className: 'card' }, h('summary', null, t("质量提醒(") + project.quality.warningCount + (languageOf() === 'en' ? ')' : ')')), (project.quality.issues || []).map((issue, i) => h('p', { key: i }, t("第 ") + issue.slide + t(" 页:") + (languageOf() === 'en' ? issue.messageEn : issue.message) + (languageOf() === 'en' ? ' ' : ';') + (languageOf() === 'en' ? issue.suggestionEn : issue.suggestion)))),
|
|
293
|
-
h('
|
|
295
|
+
h('p', { className: 'muted' }, t("成品包包含 PPTX、HTML 和工程文件。请先保存修改,再下载最新版本。")),
|
|
296
|
+
h('div', { className: 'tools' }, h('button', { className: 'primary', disabled: busy || dirty, onClick: () => download('bundle') }, t("下载成品包 ZIP")), h('button', { disabled: busy || dirty, onClick: () => download('pptx') }, t("下载最新 PPTX")), h('button', { disabled: busy || dirty, onClick: () => download('html') }, t("下载 HTML")), h('button', { disabled: busy || dirty, onClick: () => download('json') }, t("备份工程")))
|
|
294
297
|
)
|
|
295
298
|
)
|
|
296
299
|
)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** UI messages are independent of the deck's language; slide content is never translated. */
|
|
2
2
|
const messages = {
|
|
3
|
+
'成品文件不完整,请先保存一次再下载成品包': 'The delivery files are incomplete. Save this presentation before downloading the bundle.',
|
|
4
|
+
'成品包超过 128 MiB,请分别下载文件': 'The delivery bundle exceeds 128 MiB. Download the files separately.',
|
|
3
5
|
'文件不是普通文件或超过 64 MiB': 'The file is not a regular file or exceeds 64 MiB.',
|
|
4
6
|
'项目索引超过 8 MiB,请备份后整理 projects.jsonl': 'The project index exceeds 8 MiB. Back it up before reviewing projects.jsonl.',
|
|
5
7
|
'项目索引已满,请备份后整理 projects.jsonl': 'The project index is full. Back it up before reviewing projects.jsonl.',
|
package/lib/workbench.js
CHANGED
|
@@ -5,10 +5,12 @@ import { homedir } from 'node:os';
|
|
|
5
5
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { getEngine } from './execution.js';
|
|
7
7
|
import { bilingualQuality, englishWorkbenchMessage } from './workbench-language.js';
|
|
8
|
+
import { zipSync, strToU8 } from 'fflate';
|
|
8
9
|
export const PPT_WORKBENCH_ROUTE = '/api/dsh-ppt/workbench';
|
|
9
10
|
export const PPT_DOWNLOAD_ROUTE = '/api/dsh-ppt/download';
|
|
10
11
|
const REQUEST_LIMIT = 12 * 1024 * 1024;
|
|
11
12
|
const FILE_LIMIT = 64 * 1024 * 1024;
|
|
13
|
+
const BUNDLE_LIMIT = 128 * 1024 * 1024;
|
|
12
14
|
const digest = (value) => createHash('sha256').update(value).digest('hex');
|
|
13
15
|
function ordinaryFile(path) {
|
|
14
16
|
const stat = lstatSync(path);
|
|
@@ -202,19 +204,45 @@ export class PptWorkbench {
|
|
|
202
204
|
return denied;
|
|
203
205
|
try {
|
|
204
206
|
const params = new URL(request.url).searchParams, kind = params.get('kind');
|
|
205
|
-
if (!['html', 'pptx', 'json'].includes(String(kind)))
|
|
207
|
+
if (!['html', 'pptx', 'json', 'bundle'].includes(String(kind)))
|
|
206
208
|
return fail(400, '不支持此文件类型');
|
|
207
209
|
const { entry, manifest, files } = await this.project(params.get('id'));
|
|
208
210
|
if (String(manifest.revision) !== params.get('revision'))
|
|
209
211
|
return fail(409, '项目已被修改,请重新打开后下载');
|
|
210
212
|
request.signal.throwIfAborted();
|
|
211
|
-
|
|
213
|
+
let name, bytes;
|
|
214
|
+
if (kind === 'bundle') {
|
|
215
|
+
const entries = {};
|
|
216
|
+
let total = 0;
|
|
217
|
+
for (const path of Object.values(files)) {
|
|
218
|
+
if (!existsSync(path))
|
|
219
|
+
return fail(400, '成品文件不完整,请先保存一次再下载成品包');
|
|
220
|
+
const content = ordinaryFile(path);
|
|
221
|
+
total += content.byteLength;
|
|
222
|
+
if (total > BUNDLE_LIMIT)
|
|
223
|
+
return fail(413, '成品包超过 128 MiB,请分别下载文件');
|
|
224
|
+
entries[basename(path)] = content;
|
|
225
|
+
}
|
|
226
|
+
// Only the three known sibling artifacts are included. No directory walks,
|
|
227
|
+
// local configuration, attachment paths or journal files enter the archive.
|
|
228
|
+
entries['README.txt'] = strToU8('演示文稿成品包 / Presentation delivery bundle\n\nPPTX: 在 PowerPoint / WPS 中编辑。 / Edit in PowerPoint or WPS.\nHTML: 解压后用浏览器打开,支持离线放映。 / Extract and open in a browser for offline playback.\nJSON: 保留以便继续在 DSH 中编辑。 / Keep this project file for future DSH edits.\n\n修订 / Revision: ' + manifest.revision + '\n');
|
|
229
|
+
// PPTX and embedded images are already compressed; store to avoid a large
|
|
230
|
+
// synchronous recompression on the host's event loop.
|
|
231
|
+
bytes = zipSync(entries, { level: 0 });
|
|
232
|
+
name = basename(entry.path, '.json') + '-delivery.zip';
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const path = files[kind];
|
|
236
|
+
bytes = ordinaryFile(path);
|
|
237
|
+
name = basename(path);
|
|
238
|
+
}
|
|
239
|
+
request.signal.throwIfAborted();
|
|
212
240
|
if ((await this.project(entry.id)).manifest.revision !== manifest.revision)
|
|
213
241
|
return fail(409, '项目已被修改,请重新打开后下载');
|
|
214
|
-
const type = kind === 'pptx' ? 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : kind === 'html' ? 'text/html; charset=utf-8' : 'application/json; charset=utf-8';
|
|
242
|
+
const type = kind === 'bundle' ? 'application/zip' : kind === 'pptx' ? 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : kind === 'html' ? 'text/html; charset=utf-8' : 'application/json; charset=utf-8';
|
|
215
243
|
return new Response(new Uint8Array(bytes), { headers: {
|
|
216
244
|
'content-type': type, 'cache-control': 'no-store', 'x-content-type-options': 'nosniff',
|
|
217
|
-
'content-disposition': `attachment; filename="presentation.${kind}"; filename*=UTF-8''${encodeURIComponent(
|
|
245
|
+
'content-disposition': `attachment; filename="presentation.${kind === 'bundle' ? 'zip' : kind}"; filename*=UTF-8''${encodeURIComponent(name)}`,
|
|
218
246
|
} });
|
|
219
247
|
}
|
|
220
248
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ppt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "DSH 演示文稿插件:生成 HTML、可编辑 PPTX 和工程文件,支持图表、轻量编辑、撤销与质量检查。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -78,7 +78,8 @@
|
|
|
78
78
|
"author": "stardustlc",
|
|
79
79
|
"packageManager": "pnpm@11.7.0",
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@office-kit/pptx": "0.21.0"
|
|
81
|
+
"@office-kit/pptx": "0.21.0",
|
|
82
|
+
"fflate": "0.8.3"
|
|
82
83
|
},
|
|
83
84
|
"peerDependencies": {
|
|
84
85
|
"@deepseek-ai/libreoffice-kit": "0.1.5"
|