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); });