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 CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  [历史英文记录](docs/CHANGELOG.en.md)
6
6
 
7
+ ## 0.9.0 (2026-10-05)
8
+
9
+ - 增加成品 ZIP 下载:一次交付 HTML、可编辑 PPTX、工程 JSON 和使用说明;只打包本项目登记的当前修订,未保存或产物缺失时阻止交付。
10
+
7
11
  ## 0.8.1 (2026-10-03)
8
12
 
9
13
  - 演示文稿工作台、质量提醒和后台失败引导跟随宿主中文 / English 切换;正文、成品语言与工程内容保持原样。
package/README.en.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  [中文](README.md)
4
4
 
5
+ ![dsh-ppt whale girl plugin cover](https://raw.githubusercontent.com/STARDUSTLC666/dsh-ppt/master/assets/cover-whale-girl.png)
6
+
5
7
  Turn source material into an HTML slideshow, editable PPTX and a reusable project.
6
8
 
7
- [![npm](https://img.shields.io/npm/v/dsh-ppt)](https://www.npmjs.com/package/dsh-ppt) [![downloads](https://img.shields.io/npm/dm/dsh-ppt)](https://www.npmjs.com/package/dsh-ppt)
9
+ [![npm](https://img.shields.io/npm/v/dsh-ppt)](https://www.npmjs.com/package/dsh-ppt) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-ppt-downloads.svg)](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
+ ![dsh-ppt 鲸鱼娘插件封面](https://raw.githubusercontent.com/STARDUSTLC666/dsh-ppt/master/assets/cover-whale-girl.png)
6
+
5
7
  把材料制作成 HTML 放映、可编辑 PPTX 和可继续修改的工程文件。
6
8
 
7
- [![npm](https://img.shields.io/npm/v/dsh-ppt)](https://www.npmjs.com/package/dsh-ppt) [![downloads](https://img.shields.io/npm/dm/dsh-ppt)](https://www.npmjs.com/package/dsh-ppt)
9
+ [![npm](https://img.shields.io/npm/v/dsh-ppt)](https://www.npmjs.com/package/dsh-ppt) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-ppt-downloads.svg)](https://www.npmjs.com/package/dsh-ppt)
8
10
 
9
11
  ## 功能
10
12
 
@@ -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
@@ -2,6 +2,10 @@
2
2
 
3
3
  [返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [验证记录](VALIDATION.md)
4
4
 
5
+ ## 本次改进
6
+
7
+ 在演示文稿设置页保存修改后点击“下载成品包 ZIP”。单文件下载仍可使用。ZIP 不包含配置、密钥或工作区其他文件;单文件 ≤64 MiB、合计 ≤128 MiB。
8
+
5
9
  ## 能力一览
6
10
 
7
11
  | 能力 | 说明 |
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('div', { className: 'tools' }, 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("备份工程")))
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
- const path = files[kind], bytes = ordinaryFile(path);
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(basename(path))}`,
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.8.1",
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"