const pptxgen = require('pptxgenjs'); const html2pptx = require('/Users/dohertj2/.claude/plugins/cache/anthropic-agent-skills/document-skills/unknown/skills/pptx/scripts/html2pptx.js'); const path = require('path'); const SLIDES_DIR = path.join(__dirname, 'slides'); const OUTPUT = '/Users/dohertj2/Desktop/plan/outputs/generated/plan-presentation.pptx'; // ZB template assets and design constants (see /Users/dohertj2/Desktop/plan/templates/README.md) const TEMPLATE_ASSETS = '/Users/dohertj2/Desktop/plan/templates/assets'; const COVER_BG = path.join(TEMPLATE_ASSETS, 'cover-background.jpeg'); const BOTTOM_BORDER = path.join(TEMPLATE_ASSETS, 'bottom-border.jpg'); const ZB_BLUE = '0066B3'; const ZB_NAVY = '2B2A80'; const ZB_TITLE_WHITE = 'F4F4F4'; const CONFIDENTIAL_GRAY = '404040'; const CONFIDENTIAL_TEXT = 'CONFIDENTIAL – FOR ZIMMER BIOMET INTERNAL USE ONLY'; // Roadmap-table colors (slide 13) const NAVY = '1C2833'; const SLATE = '2E4053'; const SILVER = 'AAB7B8'; const WHITE = 'FFFFFF'; const ROW_BG = 'FFFFFF'; const ROW_BG_ALT = 'EEF1F3'; async function main() { const pres = new pptxgen(); pres.layout = 'LAYOUT_16x9'; // 10.0 × 5.625 in — matches the HTML slide CSS pres.author = 'IT/OT Transformation Team'; pres.subject = 'SCADA IT/OT Transformation — 3-Year Plan'; pres.title = 'SCADA IT/OT Transformation'; // Slide 1 — ZB cover (replaces slide01.html; cover background + title overlay) console.log('Processing slide 1 (ZB cover)...'); addCoverSlide(pres); // Slides 2–16 — html2pptx-rendered content + ZB brand chrome (bottom border, confidentiality, page number) for (let i = 2; i <= 16; i++) { const num = String(i).padStart(2, '0'); const file = path.join(SLIDES_DIR, `slide${num}.html`); console.log(`Processing slide ${i}...`); const { slide } = await html2pptx(file, pres); addBrandChrome(slide, i); if (i === 13) { addRoadmapTable(pres, slide); } } await pres.writeFile({ fileName: OUTPUT }); console.log(`Saved: ${OUTPUT}`); } // Coordinates below are scaled 0.75x from the 13.33×7.5 ZB template (templates/README.md) // to fit pptxgen's LAYOUT_16x9 (10×5.625 in). The HTML CSS uses 720×405pt = 10×5.625 in. function addCoverSlide(pres) { const slide = pres.addSlide(); slide.background = { color: 'FFFFFF' }; slide.addImage({ path: COVER_BG, x: 0, y: 0, w: 10.0, h: 5.625 }); slide.addText('SCADA IT/OT Transformation', { x: 0.69, y: 1.04, w: 4.5, h: 1.13, fontFace: 'Arial', fontSize: 24, bold: true, color: ZB_TITLE_WHITE, valign: 'top', }); slide.addText('3-Year Plan', { x: 0.69, y: 2.21, w: 4.5, h: 0.38, fontFace: 'Arial', fontSize: 14, color: ZB_TITLE_WHITE, }); slide.addText('AS OF 2026-04-30', { x: 0.69, y: 2.59, w: 4.5, h: 0.30, fontFace: 'Arial', fontSize: 9, color: ZB_TITLE_WHITE, charSpacing: 1, }); return slide; } function addBrandChrome(slide, pageNumber) { slide.addImage({ path: BOTTOM_BORDER, x: 0.01, y: 5.07, w: 9.99, h: 0.555, }); slide.addText(CONFIDENTIAL_TEXT, { x: 0.22, y: 4.91, w: 6.0, h: 0.15, fontFace: 'Arial', fontSize: 6, color: CONFIDENTIAL_GRAY, }); slide.addText(String(pageNumber), { x: 9.55, y: 4.91, w: 0.35, h: 0.15, fontFace: 'Arial', fontSize: 8, color: ZB_NAVY, align: 'right', }); } function addRoadmapTable(pres, slide) { const headerOpts = { bold: true, color: WHITE, fill: { color: NAVY }, fontSize: 10, fontFace: 'Arial', align: 'center', valign: 'middle' }; const wsOpts = { bold: true, fontSize: 9, fontFace: 'Arial', color: NAVY, fill: { color: ROW_BG_ALT }, valign: 'middle' }; const cellOpts = { fontSize: 8, fontFace: 'Arial', color: SLATE, fill: { color: ROW_BG }, valign: 'middle' }; const rows = [ [ { text: 'Workstream', options: headerOpts }, { text: 'Year 1 — Foundation', options: headerOpts }, { text: 'Year 2 — Scale', options: headerOpts }, { text: 'Year 3 — Completion', options: headerOpts }, ], [ { text: 'OtOpcUa', options: wsOpts }, { text: 'Evolve LmxOpcUa; deploy to every site; tier-1 cutover begins; UNS hierarchy walk', options: cellOpts }, { text: 'Complete tier 1; begin tier 2 (Ignition); long-tail drivers on demand', options: cellOpts }, { text: 'Complete tier 2; execute tier 3 (System Platform IO) with compliance validation', options: cellOpts }, ], [ { text: 'Redpanda EventHub', options: wsOpts }, { text: 'Central cluster in SB; schema registry; canonical model v1 published (FANUC CNC pilot)', options: cellOpts }, { text: 'Expand topic coverage; WAN-outage replay drill; iterate canonical model', options: cellOpts }, { text: 'Steady state; canonical model mature; alerting + runbooks hardened', options: cellOpts }, ], [ { text: 'SnowBridge', options: wsOpts }, { text: 'Custom build in .NET; first adapter (Historian SQL); first curated tables aligned to canonical model', options: cellOpts }, { text: 'Add Redpanda adapter; operator UI + approval workflow; canonical-state OEE model; first "not possible before" use case in dev', options: cellOpts }, { text: 'All source adapters live; hardening. "Not possible before" use case in production — pillar 2 passes', options: cellOpts }, ], [ { text: 'ScadaBridge Extensions', options: wsOpts }, { text: 'Deadband / exception-based publishing; EventHub producer with store-and-forward', options: cellOpts }, { text: 'Roll to all integrated sites; tune deadband from observed Snowflake cost', options: cellOpts }, { text: 'Steady state; residual cleanup for onboarding / retirement tails', options: cellOpts }, ], [ { text: 'Site Onboarding', options: wsOpts }, { text: 'No new sites; define lightweight onboarding pattern for smaller sites', options: cellOpts }, { text: 'Pilot one smaller site; scale to additional smaller sites', options: cellOpts }, { text: 'Complete onboarding of all remaining smaller sites — pillar 1 passes', options: cellOpts }, ], [ { text: 'Legacy Retirement', options: wsOpts }, { text: 'Inventory populated (3 rows); retire first integration as pattern-proving exercise', options: cellOpts }, { text: 'Bulk migration against inventory; quarterly burn-down tracking', options: cellOpts }, { text: 'Inventory reaches zero — pillar 3 passes', options: cellOpts }, ], ]; slide.addTable(rows, { x: 0.42, y: 1.2, w: 9.2, colW: [1.5, 2.566, 2.567, 2.567], rowH: 0.55, border: { pt: 0.5, color: SILVER }, }); } main().catch((err) => { console.error(err); process.exit(1); });