import * as PptxModule from "pptxgenjs"; import { cleanHex, normalizeTheme } from "./themes.js"; export type SlideSpec = { layout: "title" | "section" | "bullets" | "chart_and_insight" | "image_reference"; title: string; subtitle?: string; bullets?: string[]; insights?: string[]; notes?: string; }; export type PresentationSpec = { title: string; theme: Record; slides: SlideSpec[]; provenance?: Record[]; }; export async function renderPresentation(spec: PresentationSpec): Promise { const PptxGenJS = ((PptxModule as unknown as { default?: unknown }).default ?? PptxModule) as new () => any; const pptx = new PptxGenJS(); pptx.layout = "LAYOUT_WIDE"; pptx.author = "GLM Visual Variable Runtime"; pptx.subject = "Generated from visual evidence"; pptx.title = spec.title; const theme = normalizeTheme(spec.theme); pptx.defineSlideMaster({ title: "VVR_MASTER", background: { color: cleanHex(theme.background) }, objects: [ { rect: { x: 0, y: 0, w: 13.333, h: 0.12, fill: { color: cleanHex(theme.accent) }, line: { color: cleanHex(theme.accent) } } } ], slideNumber: { x: 12.3, y: 7.15, color: cleanHex(theme.secondary) } }); const slides = spec.slides.length ? spec.slides : [{ layout: "title" as const, title: spec.title }]; for (const item of slides) { const slide = pptx.addSlide("VVR_MASTER"); slide.addText(item.title, { x: 0.7, y: 0.65, w: 11.9, h: 0.6, fontFace: "Aptos Display", fontSize: item.layout === "title" ? 34 : 26, bold: true, color: cleanHex(theme.primary), margin: 0 }); if (item.subtitle) { slide.addText(item.subtitle, { x: 0.75, y: 1.35, w: 11.7, h: 0.4, fontSize: 16, color: cleanHex(theme.secondary) }); } const bullets = [...(item.bullets ?? []), ...(item.insights ?? [])]; if (bullets.length) { slide.addText(bullets.map((text) => ({ text, options: { bullet: { indent: 14 }, hanging: 4 } })), { x: 0.9, y: 2.0, w: 11.4, h: 4.6, fontSize: 18, color: cleanHex(theme.text_dark), breakLine: false, fit: "shrink" }); } if (item.notes || spec.provenance?.length) { slide.addNotes(`Provenance: ${JSON.stringify(spec.provenance ?? [])}\n${item.notes ?? ""}`); } } const data = await pptx.write({ outputType: "nodebuffer" }); return Buffer.from(data as ArrayBuffer); }