HH/.agents/skills/huashu-design/scripts/export_deck_pptx.mjs
ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

108 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* export_deck_pptx.mjs — 把多文件 slide deck 导出为可编辑 PPTX
*
* 用法:
* node export_deck_pptx.mjs --slides <dir> --out <file.pptx>
*
* 行为:
* - 调用 scripts/html2pptx.js 把 HTML DOM 逐元素翻译成 PowerPoint 原生对象
* - 文字是真文本框PPT 里直接双击能编辑
* - body 尺寸 960pt × 540ptLAYOUT_WIDE13.333″ × 7.5″)
*
* ⚠️ HTML 必须符合 4 条硬约束(见 references/editable-pptx.md
* 1. 文字包在 <p>/<h1>-<h6> 里div 不能直接放文字)
* 2. 不用 CSS 渐变
* 3. <p>/<h*> 不能有 background/border/shadow放外层 div
* 4. div 不能 background-image用 <img>
*
* 视觉驱动的 HTML 几乎无法 pass —— 必须从写 HTML 的第一行就按约束写。
* 视觉自由度优先的场景动画、web component、CSS 渐变、复杂 SVG
* 应改用 export_deck_pdf.mjs / export_deck_stage_pdf.mjs 导出 PDF。
*
* 依赖npm install playwright pptxgenjs sharp
*
* 按文件名排序01-xxx.html → 02-xxx.html → ...)。
*/
import pptxgen from 'pptxgenjs';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
function parseArgs() {
const args = {};
const a = process.argv.slice(2);
for (let i = 0; i < a.length; i += 2) {
const k = a[i].replace(/^--/, '');
args[k] = a[i + 1];
}
if (!args.slides || !args.out) {
console.error('用法: node export_deck_pptx.mjs --slides <dir> --out <file.pptx>');
console.error('');
console.error('⚠️ HTML 必须符合 4 条硬约束(见 references/editable-pptx.md。');
console.error(' 视觉自由度优先的场景请改用 export_deck_pdf.mjs 导出 PDF。');
process.exit(1);
}
return args;
}
async function main() {
const { slides, out } = parseArgs();
const slidesDir = path.resolve(slides);
const outFile = path.resolve(out);
const files = (await fs.readdir(slidesDir))
.filter(f => f.endsWith('.html'))
.sort();
if (!files.length) {
console.error(`No .html files found in ${slidesDir}`);
process.exit(1);
}
console.log(`Converting ${files.length} slides via html2pptx...`);
const { createRequire } = await import('module');
const require = createRequire(import.meta.url);
let html2pptx;
try {
html2pptx = require(path.join(__dirname, 'html2pptx.js'));
} catch (e) {
console.error(`✗ 加载 html2pptx.js 失败:${e.message}`);
console.error(` 依赖缺失时请跑npm install playwright pptxgenjs sharp`);
process.exit(1);
}
const pres = new pptxgen();
pres.layout = 'LAYOUT_WIDE'; // 13.333 × 7.5 inch对应 HTML body 960 × 540 pt
const errors = [];
for (let i = 0; i < files.length; i++) {
const f = files[i];
const fullPath = path.join(slidesDir, f);
try {
await html2pptx(fullPath, pres);
console.log(` [${i + 1}/${files.length}] ${f}`);
} catch (e) {
console.error(` [${i + 1}/${files.length}] ${f}${e.message}`);
errors.push({ file: f, error: e.message });
}
}
if (errors.length) {
console.error(`\n⚠️ ${errors.length} 张 slide 转换失败。常见原因HTML 不符合 4 条硬约束。`);
console.error(` 详见 references/editable-pptx.md 的「常见错误速查」。`);
if (errors.length === files.length) {
console.error(`✗ 全部失败,不生成 PPTX。`);
process.exit(1);
}
}
await pres.writeFile({ fileName: outFile });
console.log(`\n✓ Wrote ${outFile} (${files.length - errors.length}/${files.length} slides, 可编辑 PPTX)`);
}
main().catch(e => { console.error(e); process.exit(1); });