#!/usr/bin/env node // Assemble rendered slide PNGs into a 16:9 PPTX: one full-bleed image per slide, // native speaker notes, document metadata. // // Usage: // node build.mjs [--notes notes.json] [--title T] [--author A] [--subject S] import { createRequire } from 'node:module'; import { existsSync, readdirSync, readFileSync } from 'node:fs'; import path from 'node:path'; const require = createRequire(import.meta.url); const PptxGenJS = require('pptxgenjs'); const args = process.argv.slice(2); if (!args[0] || !args[1]) { console.error( 'usage: node build.mjs [--notes notes.json] [--title T] [--author A] [--subject S]', ); process.exit(1); } const invocationDir = process.env.INIT_CWD || process.cwd(); const dir = path.resolve(invocationDir, args[0]); const out = path.resolve(invocationDir, args[1]); const opt = (flag, fallback) => { const index = args.indexOf(flag); return index >= 0 ? args[index + 1] : fallback; }; const presentation = new PptxGenJS(); presentation.layout = 'LAYOUT_WIDE'; // 13.333 x 7.5 in presentation.title = opt('--title', path.basename(out, '.pptx')); presentation.author = opt('--author', 'ThothII'); presentation.subject = opt('--subject', ''); const notesOption = opt('--notes', null); const notesPath = notesOption ? path.resolve(invocationDir, notesOption) : path.join(dir, 'notes.json'); const notes = existsSync(notesPath) ? JSON.parse(readFileSync(notesPath, 'utf8')) : {}; const pngs = readdirSync(dir) .filter(file => /^\d+\.png$/.test(file)) .sort((left, right) => Number.parseInt(left, 10) - Number.parseInt(right, 10)); if (pngs.length === 0) { console.error('no NN.png files in ' + dir); process.exit(1); } for (const file of pngs) { const slideNumber = Number.parseInt(file, 10); const slide = presentation.addSlide(); slide.background = { color: 'FCFAFA' }; slide.addImage({ path: path.join(dir, file), x: 0, y: 0, w: 13.333, h: 7.5, }); if (notes[slideNumber]) { slide.addNotes(notes[slideNumber]); } } await presentation.writeFile({ fileName: out }); console.log('built', out, '-', pngs.length, 'slides');