"use strict";
const pptxgen = require("pptxgenjs");
// ─── PALETTE ─────────────────────────────────────────────────────────────────
// Deep crimson dominant, off-white light, steel blue accent, amber alert
const C = {
bg: "0D1B2A", // very dark navy (title/section bg)
bgLight: "F5F6FA", // near-white (content slides)
card: "FFFFFF",
crimson: "A61C2B", // dominant brand
crimsonDk: "7A1220", // dark variant
blue: "1A6EA8", // secondary
blueLt: "D6E9F6",
amber: "E07B00", // warning / Stage indicator
amberLt: "FFF3DC",
green: "1A7A4A",
greenLt: "D4EDDA",
teal: "117A65",
tealLt: "D1ECE6",
red: "C0392B",
redLt: "FDEDEC",
text: "1A1A2E",
textMid: "4A4A68",
textLight: "FFFFFF",
divider: "E0E0E8",
};
// ─── HELPERS ──────────────────────────────────────────────────────────────────
function addSlideLabel(slide, label, color = C.crimson) {
slide.addShape("rect", { x: 0, y: 0, w: 0.22, h: 5.625, fill: { color } });
slide.addText(label, {
x: 0.06, y: 1.5, w: 1.2, h: 2.5,
fontSize: 9, bold: true, color: C.textLight,
rotate: 270, align: "center", charSpacing: 3,
});
}
function sectionTitle(pres, title, subtitle, stageNum = "", stageColor = C.crimson) {
const sl = pres.addSlide();
sl.background = { color: C.bg };
// Left accent bar
sl.addShape("rect", { x: 0, y: 0, w: 0.18, h: 5.625, fill: { color: stageColor } });
// Top rule line
sl.addShape("rect", { x: 0.38, y: 1.2, w: 5.5, h: 0.05, fill: { color: stageColor } });
if (stageNum) {
sl.addText(`STAGE ${stageNum}`, {
x: 0.38, y: 0.7, w: 9, h: 0.5,
fontSize: 11, bold: true, color: stageColor, charSpacing: 6,
});
}
sl.addText(title, {
x: 0.38, y: 1.35, w: 9.3, h: 1.5,
fontSize: 36, bold: true, color: C.textLight,
});
sl.addText(subtitle, {
x: 0.38, y: 2.85, w: 8.5, h: 1.0,
fontSize: 16, color: "B0B8C8", italic: false,
});
return sl;
}
function contentSlide(pres, stageColor = C.crimson, vertLabel = "") {
const sl = pres.addSlide();
sl.background = { color: C.bgLight };
addSlideLabel(sl, vertLabel || "PPH PROTOCOL", stageColor);
return sl;
}
function slideHeader(sl, title, stageColor = C.crimson) {
sl.addShape("rect", { x: 0.35, y: 0, w: 9.65, h: 0.72, fill: { color: stageColor } });
sl.addText(title.toUpperCase(), {
x: 0.5, y: 0.08, w: 9.3, h: 0.55,
fontSize: 14, bold: true, color: C.textLight, charSpacing: 2,
});
}
function card(sl, x, y, w, h, fillColor = C.card, shadow = true) {
sl.addShape("rect", {
x, y, w, h,
fill: { color: fillColor },
shadow: shadow ? { type: "outer", color: "000000", blur: 6, offset: 2, angle: 135, opacity: 0.1 } : false,
});
}
function tag(sl, x, y, txt, bgColor, txtColor = "FFFFFF") {
sl.addShape("roundRect", { x, y, w: 1.2, h: 0.28, fill: { color: bgColor }, rectRadius: 0.06 });
sl.addText(txt, { x, y, w: 1.2, h: 0.28, fontSize: 8, bold: true, color: txtColor, align: "center" });
}
// ─── BUILD PRESENTATION ──────────────────────────────────────────────────────
const pres = new pptxgen();
pres.layout = "LAYOUT_16x9";
pres.title = "PPH Clinical Decision Support";
pres.author = "Clinical Decision Support Unit";
// ════════════════════════════════════════════════
// SLIDE 1 — TITLE
// ════════════════════════════════════════════════
{
const sl = pres.addSlide();
sl.background = { color: C.bg };
// Bold left accent
sl.addShape("rect", { x: 0, y: 0, w: 0.32, h: 5.625, fill: { color: C.crimson } });
sl.addShape("rect", { x: 0.32, y: 0, w: 9.68, h: 0.08, fill: { color: C.crimson } });
sl.addShape("rect", { x: 0.32, y: 5.5, w: 9.68, h: 0.125, fill: { color: C.crimson } });
// Title
sl.addText("POSTPARTUM\nHEMORRHAGE", {
x: 0.6, y: 0.9, w: 9, h: 2.2,
fontSize: 52, bold: true, color: C.textLight,
lineSpacingMultiple: 1.1,
});
sl.addText("Clinical Decision Support", {
x: 0.6, y: 3.05, w: 9, h: 0.55,
fontSize: 22, italic: true, color: C.crimson,
});
sl.addShape("rect", { x: 0.6, y: 3.62, w: 5, h: 0.04, fill: { color: "4A5568" } });
sl.addText("Recognition · Staging · Management · Escalation", {
x: 0.6, y: 3.75, w: 9, h: 0.45,
fontSize: 12, color: "8899BB", charSpacing: 1,
});
sl.addText("Based on ACOG Guidelines | Creasy & Resnik | ROSEN's | Tintinalli's | Miller's Anesthesia", {
x: 0.6, y: 5.1, w: 9, h: 0.35,
fontSize: 9, color: "556070", italic: true,
});
}
// ════════════════════════════════════════════════
// SLIDE 2 — OVERVIEW / AGENDA
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.crimson, "OVERVIEW");
slideHeader(sl, "Protocol Overview", C.crimson);
sl.addText("PPH is the leading cause of maternal mortality worldwide. This protocol covers:", {
x: 0.5, y: 0.88, w: 9.2, h: 0.45, fontSize: 11.5, color: C.textMid,
});
const blocks = [
{ n: "01", t: "DEFINITION & EPIDEMIOLOGY", sub: "Diagnosis criteria, incidence", c: C.crimson },
{ n: "02", t: "CAUSES — THE FOUR Ts", sub: "Tone • Trauma • Tissue • Thrombin", c: C.blue },
{ n: "03", t: "RISK STRATIFICATION", sub: "Pre-delivery risk scoring", c: C.amber },
{ n: "04", t: "STAGE 1 (Blood loss 500–999 mL)", sub: "First-line response", c: "#2E8B57" },
{ n: "05", t: "STAGE 2 (1000–1499 mL)", sub: "Escalation of uterotonics & blood", c: C.teal },
{ n: "06", t: "STAGE 3 (≥1500 mL)", sub: "Surgical & IR interventions", c: "#6A0572" },
{ n: "07", t: "UTEROTONIC DRUG REFERENCE", sub: "Doses, contraindications", c: C.crimsonDk },
{ n: "08", t: "TRANEXAMIC ACID (TXA)", sub: "WOMAN trial evidence & dosing", c: C.blue },
{ n: "09", t: "UTERINE INVERSION", sub: "Recognition & management", c: C.amber },
{ n: "10", t: "RESUSCITATION ENDPOINTS", sub: "Labs, targets, massive transfusion", c: C.teal },
];
const cols = 2, bw = 4.4, bh = 0.41, startX = 0.45, startY = 1.42, gap = 0.06;
blocks.forEach((b, i) => {
const col = i % cols;
const row = Math.floor(i / cols);
const x = startX + col * (bw + 0.2);
const y = startY + row * (bh + gap);
sl.addShape("rect", { x, y, w: 0.38, h: bh, fill: { color: b.c } });
sl.addShape("rect", { x: x + 0.38, y, w: bw - 0.38, h: bh, fill: { color: C.card } });
sl.addText(b.n, { x, y, w: 0.38, h: bh, fontSize: 9, bold: true, color: C.textLight, align: "center", valign: "middle" });
sl.addText(b.t, { x: x + 0.44, y: y + 0.03, w: bw - 0.5, h: 0.2, fontSize: 9, bold: true, color: C.text });
sl.addText(b.sub, { x: x + 0.44, y: y + 0.22, w: bw - 0.5, h: 0.18, fontSize: 8, color: C.textMid, italic: true });
});
}
// ════════════════════════════════════════════════
// SLIDE 3 — DEFINITION & EPIDEMIOLOGY
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.crimson, "DEFINITION");
slideHeader(sl, "Definition & Epidemiology", C.crimson);
// Two columns
const defs = [
{ label: "Traditional Definition", val: "> 500 mL blood loss after vaginal delivery\n> 1000 mL after cesarean delivery" },
{ label: "Clinical Definition", val: "Any bleeding causing hemodynamic instability — or would cause it if untreated" },
{ label: "PRIMARY (Early) PPH", val: "Occurs within the first 24 hours of delivery" },
{ label: "SECONDARY (Late) PPH", val: "Occurs 24 hours to 6 weeks postpartum" },
];
defs.forEach((d, i) => {
const y = 0.9 + i * 1.0;
card(sl, 0.45, y, 9.2, 0.82, C.card);
sl.addShape("rect", { x: 0.45, y, w: 0.12, h: 0.82, fill: { color: C.crimson } });
sl.addText(d.label, { x: 0.66, y: y + 0.07, w: 3.8, h: 0.25, fontSize: 10, bold: true, color: C.crimson });
sl.addText(d.val, { x: 0.66, y: y + 0.3, w: 8.8, h: 0.45, fontSize: 10, color: C.text });
});
// Stat box bottom
const stats = [
{ v: "#1 cause", l: "maternal mortality globally" },
{ v: "11%", l: "of obstetric deaths" },
{ v: "1500 mL", l: "blood loss before BP drops (maternal physiology)" },
{ v: "+40%", l: "plasma volume expansion at term" },
];
stats.forEach((s, i) => {
const x = 0.45 + i * 2.35;
card(sl, x, 4.95, 2.15, 0.55, C.crimson);
sl.addText(s.v, { x, y: 4.97, w: 2.15, h: 0.27, fontSize: 14, bold: true, color: C.textLight, align: "center" });
sl.addText(s.l, { x, y: 5.22, w: 2.15, h: 0.22, fontSize: 7.5, color: "FFCDD2", align: "center" });
});
}
// ════════════════════════════════════════════════
// SLIDE 4 — FOUR Ts
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.blue, "CAUSES");
slideHeader(sl, "Causes of PPH — The Four Ts", C.blue);
const ts = [
{
t: "TONE", pct: "75–90%", color: C.crimson, ltColor: C.redLt,
title: "Uterine Atony",
pts: ["Soft, boggy uterus", "Overdistension (polyhydramnios, multiples)", "Prolonged/rapid labor", "Chorioamnionitis", "Tocolytics, halogenated anaesthetics"],
},
{
t: "TRAUMA", pct: "~20%", color: C.blue, ltColor: C.blueLt,
title: "Lacerations / Rupture / Inversion",
pts: ["Cervical, vaginal, perineal tears (1°–4°)", "Uterine rupture (prior scar, grand multiparity)", "Uterine inversion (fundal placenta)", "Concealed hematomas"],
},
{
t: "TISSUE", pct: "~10%", color: C.amber, ltColor: C.amberLt,
title: "Retained Products / Accreta Spectrum",
pts: ["Retained cotyledons or membranes", "Placenta accreta (adherent)", "Placenta increta (invades myometrium)", "Placenta percreta (through myometrium)"],
},
{
t: "THROMBIN", pct: "<5%", color: C.teal, ltColor: C.tealLt,
title: "Coagulopathy",
pts: ["DIC (abruption, eclampsia, AFE)", "Dilutional coagulopathy from resuscitation", "Hereditary bleeding disorders", "Retained dead fetal tissue (thromboplastin)"],
},
];
ts.forEach((t, i) => {
const x = 0.4 + i * 2.38;
const cw = 2.22, ch = 4.35;
card(sl, x, 0.85, cw, ch, t.ltColor);
sl.addShape("rect", { x, y: 0.85, w: cw, h: 0.7, fill: { color: t.color } });
sl.addText(t.t, { x, y: 0.85, w: cw, h: 0.38, fontSize: 16, bold: true, color: C.textLight, align: "center", valign: "middle" });
sl.addText(t.pct + " of cases", { x, y: 1.22, w: cw, h: 0.28, fontSize: 9, color: "FFEEFF", align: "center" });
sl.addText(t.title, { x: x + 0.1, y: 1.6, w: cw - 0.2, h: 0.45, fontSize: 10, bold: true, color: t.color });
t.pts.forEach((p, j) => {
sl.addText([
{ text: "• ", options: { bold: true, color: t.color } },
{ text: p, options: { color: C.text } },
], { x: x + 0.1, y: 2.1 + j * 0.52, w: cw - 0.2, h: 0.48, fontSize: 9, wrap: true });
});
});
}
// ════════════════════════════════════════════════
// SLIDE 5 — RISK FACTORS
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.amber, "RISK FACTORS");
slideHeader(sl, "Risk Stratification — Pre-Delivery", C.amber);
const groups = [
{
title: "LOW RISK", color: C.green, ltColor: C.greenLt,
items: ["Uncomplicated singleton pregnancy", "Normal BMI", "No prior uterine surgery", "No prior PPH"],
},
{
title: "MEDIUM RISK", color: C.amber, ltColor: C.amberLt,
items: ["Multiparity (≥4)", "Prior cesarean section", "Large fibroids", "Fetal macrosomia", "Prolonged oxytocin use", "Prior uterine surgery"],
},
{
title: "HIGH RISK", color: C.red, ltColor: C.redLt,
items: ["Placenta previa / accreta spectrum", "Active hemorrhage on admission", "Known coagulopathy / anticoagulants", "Prior postpartum hemorrhage", "Multifetal gestation with complications", "Polyhydramnios"],
},
];
groups.forEach((g, i) => {
const x = 0.42 + i * 3.15;
card(sl, x, 0.88, 2.95, 4.45, g.ltColor);
sl.addShape("rect", { x, y: 0.88, w: 2.95, h: 0.55, fill: { color: g.color } });
sl.addText(g.title, { x, y: 0.88, w: 2.95, h: 0.55, fontSize: 12, bold: true, color: C.textLight, align: "center", valign: "middle" });
g.items.forEach((item, j) => {
sl.addText([
{ text: "▸ ", options: { bold: true, color: g.color } },
{ text: item, options: { color: C.text } },
], { x: x + 0.15, y: 1.52 + j * 0.52, w: 2.65, h: 0.48, fontSize: 9.5, wrap: true });
});
});
// Action box
sl.addShape("rect", { x: 0.42, y: 5.22, w: 9.3, h: 0.32, fill: { color: C.amber } });
sl.addText("⚠ High-risk patients: 2 large-bore IVs, type & screen, blood products on standby, active management of 3rd stage of labor", {
x: 0.55, y: 5.22, w: 9.1, h: 0.32, fontSize: 9.5, bold: true, color: C.textLight, valign: "middle",
});
}
// ════════════════════════════════════════════════
// SLIDE 6 — STAGE 1
// ════════════════════════════════════════════════
sectionTitle(pres, "Stage 1 Response", "Blood loss 500–999 mL · Signs of early hemorrhage", "1", "#2E8B57");
{
const stageColor = "#2E8B57";
const sl = contentSlide(pres, stageColor, "STAGE 1");
slideHeader(sl, "Stage 1 — Initial Response (500–999 mL)", stageColor);
// Left column: actions
card(sl, 0.42, 0.85, 4.5, 4.6, C.card);
sl.addShape("rect", { x: 0.42, y: 0.85, w: 4.5, h: 0.38, fill: { color: stageColor } });
sl.addText("IMMEDIATE ACTIONS", { x: 0.5, y: 0.85, w: 4.3, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const actions = [
"Call for help — activate PPH protocol",
"Establish 2 large-bore IV lines (14–16G)",
"Start IV crystalloid (Lactated Ringer's)",
"Supplemental oxygen",
"Continuous vital signs monitoring",
"Bimanual uterine massage",
"Foley catheter — monitor urine output",
"Oxytocin 20–30 units in 1L IV (≤100 mU/min)",
];
actions.forEach((a, i) => {
sl.addText([
{ text: `${i+1}. `, options: { bold: true, color: stageColor } },
{ text: a, options: { color: C.text } },
], { x: 0.55, y: 1.31 + i * 0.49, w: 4.2, h: 0.45, fontSize: 9.5, wrap: true });
});
// Right column: labs & assess
card(sl, 5.1, 0.85, 4.55, 2.18, C.card);
sl.addShape("rect", { x: 5.1, y: 0.85, w: 4.55, h: 0.38, fill: { color: C.blue } });
sl.addText("INVESTIGATIONS", { x: 5.18, y: 0.85, w: 4.38, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const labs = ["CBC with platelets", "Blood type & crossmatch", "PT / PTT / fibrinogen", "Fibrin split products / D-dimer", "BMP (electrolytes, renal function)"];
labs.forEach((l, i) => {
sl.addText([
{ text: "◆ ", options: { color: C.blue, bold: true } },
{ text: l, options: { color: C.text } },
], { x: 5.22, y: 1.31 + i * 0.33, w: 4.25, h: 0.3, fontSize: 9.5 });
});
card(sl, 5.1, 3.15, 4.55, 2.3, C.card);
sl.addShape("rect", { x: 5.1, y: 3.15, w: 4.55, h: 0.38, fill: { color: C.amber } });
sl.addText("ASSESS CAUSE — FOUR Ts", { x: 5.18, y: 3.15, w: 4.38, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const assess = [
"Tone: Is uterus firm? → bimanual massage",
"Trauma: Inspect cervix, vagina, perineum",
"Tissue: Placenta complete? → USS if unsure",
"Thrombin: Coag screen, signs of DIC",
];
assess.forEach((a, i) => {
sl.addText([
{ text: "▸ ", options: { bold: true, color: C.amber } },
{ text: a, options: { color: C.text } },
], { x: 5.22, y: 3.61 + i * 0.43, w: 4.3, h: 0.4, fontSize: 9, wrap: true });
});
// Bottom bar
sl.addShape("rect", { x: 0.42, y: 5.22, w: 9.3, h: 0.32, fill: { color: "#2E8B57" } });
sl.addText("If bleeding continues or blood loss reaches 1000 mL → ESCALATE TO STAGE 2", {
x: 0.55, y: 5.22, w: 9.1, h: 0.32, fontSize: 9.5, bold: true, color: C.textLight, valign: "middle",
});
}
// ════════════════════════════════════════════════
// SLIDE 8 — STAGE 2
// ════════════════════════════════════════════════
sectionTitle(pres, "Stage 2 Response", "Blood loss 1000–1499 mL · Hemodynamic compromise", "2", C.teal);
{
const stageColor = C.teal;
const sl = contentSlide(pres, stageColor, "STAGE 2");
slideHeader(sl, "Stage 2 — Escalation (1000–1499 mL)", stageColor);
// Uterotonics table
sl.addText("2ND & 3RD LINE UTEROTONICS", { x: 0.45, y: 0.86, w: 9.2, h: 0.3, fontSize: 10, bold: true, color: stageColor });
const rows = [
[
{ text: "Drug", options: { bold: true, color: "FFFFFF", fill: { color: stageColor } } },
{ text: "Dose & Route", options: { bold: true, color: "FFFFFF", fill: { color: stageColor } } },
{ text: "Notes / Contraindications", options: { bold: true, color: "FFFFFF", fill: { color: stageColor } } },
],
["Methylergonovine", "0.2 mg IM q2–4h", "CI: Hypertension · NOT IV · vasoconstriction risk"],
["Carboprost (15-methyl PGF2α)", "250 µg IM q15–90 min, max 8 doses", "CI: Asthma, CV disease · Monitor SpO₂"],
["Misoprostol (PGE1)", "800–1000 µg rectal or sublingual", "Use if other uterotonics unavailable"],
["Tranexamic acid", "1 g IV slowly (over 10 min)", "Give within 3 hours of diagnosis · WOMAN trial"],
];
sl.addTable(rows, {
x: 0.42, y: 1.18, w: 9.3, h: 2.0,
fontSize: 9,
border: { pt: 0.5, color: C.divider },
color: C.text,
rowH: 0.36,
colW: [2.2, 2.7, 4.4],
fill: { color: "F9FAFB" },
fontFace: "Calibri",
});
// Blood products
card(sl, 0.42, 3.28, 4.5, 2.1, C.card);
sl.addShape("rect", { x: 0.42, y: 3.28, w: 4.5, h: 0.38, fill: { color: C.red } });
sl.addText("BLOOD PRODUCTS", { x: 0.5, y: 3.28, w: 4.35, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const blood = [
"Activate blood bank — type & crossmatch",
"Packed RBCs: target Hb ≥8 g/dL",
"Consider 1:1:1 ratio (pRBC:FFP:platelets)",
"Cryoprecipitate if fibrinogen <2 g/L",
"O-negative uncrossmatched if emergency",
];
blood.forEach((b, i) => {
sl.addText([
{ text: "▸ ", options: { bold: true, color: C.red } },
{ text: b, options: { color: C.text } },
], { x: 0.55, y: 3.74 + i * 0.33, w: 4.2, h: 0.3, fontSize: 9, wrap: true });
});
// Monitoring
card(sl, 5.1, 3.28, 4.55, 2.1, C.card);
sl.addShape("rect", { x: 5.1, y: 3.28, w: 4.55, h: 0.38, fill: { color: C.blue } });
sl.addText("MONITORING TARGETS", { x: 5.18, y: 3.28, w: 4.38, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const targets = [
"MAP ≥ 65 mmHg",
"Urine output ≥ 30 mL/hr",
"SpO₂ ≥ 95%",
"Temperature > 35°C (avoid hypothermia)",
"Fibrinogen > 2 g/L",
];
targets.forEach((t, i) => {
sl.addText([
{ text: "◆ ", options: { bold: true, color: C.blue } },
{ text: t, options: { color: C.text } },
], { x: 5.22, y: 3.74 + i * 0.33, w: 4.3, h: 0.3, fontSize: 9.5 });
});
sl.addShape("rect", { x: 0.42, y: 5.22, w: 9.3, h: 0.32, fill: { color: stageColor } });
sl.addText("If bleeding uncontrolled or blood loss ≥1500 mL → ESCALATE TO STAGE 3 — Notify OR / IR now", {
x: 0.55, y: 5.22, w: 9.1, h: 0.32, fontSize: 9.5, bold: true, color: C.textLight, valign: "middle",
});
}
// ════════════════════════════════════════════════
// SLIDE 10 — STAGE 3
// ════════════════════════════════════════════════
sectionTitle(pres, "Stage 3 — Surgical Escalation", "Blood loss ≥ 1500 mL · Life-threatening hemorrhage", "3", "#6A0572");
{
const stageColor = "#6A0572";
const sl = contentSlide(pres, stageColor, "STAGE 3");
slideHeader(sl, "Stage 3 — Surgical & Interventional Options (≥1500 mL)", stageColor);
const steps = [
{
num: "1", title: "Uterine Balloon Tamponade",
detail: "Bakri balloon or Foley catheter · Fill with 300–500 mL saline · 85–95% success rate · Buys time for OR/IR",
color: "#6A0572",
},
{
num: "2", title: "Uterine Compression Sutures",
detail: "B-Lynch suture / Hayman suture · Compresses anterior & posterior uterine walls · Preserves fertility",
color: C.blue,
},
{
num: "3", title: "Pelvic Vessel Embolization (IR)",
detail: "Interventional radiology · Uterine / pudendal / hypogastric artery · 95–100% success · Fertility preserved (recanalization)",
color: C.teal,
},
{
num: "4", title: "Vessel Ligation (Surgical)",
detail: "Uterine artery ligation → Internal iliac (hypogastric) artery ligation · Collateral circulation preserved · Future fertility possible",
color: C.amber,
},
{
num: "5", title: "Peripartum Hysterectomy",
detail: "Last resort — definitive · Indications: refractory atony, placenta accreta, uterine rupture · Semi-lithotomy position · Have gynae-oncology available",
color: C.red,
},
];
steps.forEach((s, i) => {
const y = 0.88 + i * 0.87;
card(sl, 0.42, y, 9.3, 0.78, C.card);
sl.addShape("rect", { x: 0.42, y, w: 0.5, h: 0.78, fill: { color: s.color } });
sl.addText(s.num, { x: 0.42, y, w: 0.5, h: 0.78, fontSize: 18, bold: true, color: C.textLight, align: "center", valign: "middle" });
sl.addText(s.title, { x: 1.05, y: y + 0.06, w: 4.0, h: 0.28, fontSize: 11, bold: true, color: s.color });
sl.addText(s.detail, { x: 1.05, y: y + 0.36, w: 8.5, h: 0.36, fontSize: 8.5, color: C.textMid, wrap: true });
});
sl.addShape("rect", { x: 0.42, y: 5.28, w: 9.3, h: 0.27, fill: { color: C.red } });
sl.addText("All invasive options carry ~85–90% success. Escalate through the algorithm. Hysterectomy if all others fail.", {
x: 0.55, y: 5.28, w: 9.1, h: 0.27, fontSize: 9, bold: true, color: C.textLight, valign: "middle",
});
}
// ════════════════════════════════════════════════
// SLIDE 12 — UTEROTONIC DRUG REFERENCE
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.crimsonDk, "DRUG REF");
slideHeader(sl, "Uterotonic Drug Reference Card", C.crimsonDk);
const rows2 = [
[
{ text: "Drug", options: { bold: true, color: "FFFFFF", fill: { color: C.crimsonDk } } },
{ text: "Dose / Route", options: { bold: true, color: "FFFFFF", fill: { color: C.crimsonDk } } },
{ text: "Mechanism", options: { bold: true, color: "FFFFFF", fill: { color: C.crimsonDk } } },
{ text: "Contraindications", options: { bold: true, color: "FFFFFF", fill: { color: C.crimsonDk } } },
{ text: "Side Effects", options: { bold: true, color: "FFFFFF", fill: { color: C.crimsonDk } } },
],
[
{ text: "Oxytocin\n(1st line)", options: { bold: true } },
"20–30 units in 1L IV\n≤100 mU/min\nNEVER bolus IV",
"Oxytocin receptor agonist → myometrial contraction",
"None absolute;\ncaution in fluid overload",
"Hypotension (bolus), hyponatraemia, fluid overload",
],
[
{ text: "Methylergonovine\n(Ergometrine)", options: { bold: true } },
"0.2 mg IM q2–4h",
"α-adrenergic & 5-HT2 agonist → vasoconstriction + uterotonic",
"HYPERTENSION\nPreeclampsia\nDo NOT give IV",
"Severe HTN, CNS vasospasm, nausea, headache",
],
[
{ text: "Carboprost\n(15-methyl PGF2α)", options: { bold: true } },
"250 µg IM q15–90 min\nmax 8 doses",
"FP receptor agonist → sustained uterine contraction",
"Asthma\nCardiovascular disease",
"Bronchospasm, O₂ desaturation, diarrhoea, fever",
],
[
{ text: "Misoprostol\n(PGE1)", options: { bold: true } },
"800–1000 µg\nrectal / sublingual / SL",
"EP2/EP3 receptor agonist → uterotonic",
"Relative: allergy to prostaglandins",
"Diarrhoea, shivering, fever, PGE causes vasodilation",
],
[
{ text: "Tranexamic Acid\n(antifibrinolytic)", options: { bold: true } },
"1 g IV over 10 min\nRepeat once at 30 min if needed",
"Competitively inhibits plasminogen → reduces fibrinolysis",
"Thromboembolic history (relative)",
"Minimal; low thrombotic risk per WOMAN trial",
],
];
sl.addTable(rows2, {
x: 0.38, y: 0.85, w: 9.35, h: 4.5,
fontSize: 8.5,
border: { pt: 0.5, color: C.divider },
color: C.text,
rowH: 0.75,
colW: [1.55, 1.8, 2.0, 1.8, 2.2],
fontFace: "Calibri",
fill: { color: "FAFAFA" },
});
}
// ════════════════════════════════════════════════
// SLIDE 13 — TRANEXAMIC ACID SPOTLIGHT
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.blue, "TXA");
slideHeader(sl, "Tranexamic Acid (TXA) — WOMAN Trial Evidence", C.blue);
// Left: evidence
card(sl, 0.42, 0.88, 5.6, 4.55, C.card);
sl.addShape("rect", { x: 0.42, y: 0.88, w: 5.6, h: 0.38, fill: { color: C.blue } });
sl.addText("WOMAN TRIAL (Lancet, 2017) | N = 20,060", { x: 0.5, y: 0.88, w: 5.42, h: 0.38, fontSize: 10, bold: true, color: C.textLight, valign: "middle" });
const findings = [
{ label: "RR of death from bleeding (TXA vs placebo)", val: "0.69 (95% CI 0.52–0.91)", highlight: true },
{ label: "Benefit window", val: "Given within 3 hours of hemorrhage diagnosis", highlight: false },
{ label: "Thromboembolic events", val: "No difference vs placebo", highlight: false },
{ label: "Cord clamping", val: "Clamp cord BEFORE administering (crosses placenta)", highlight: false },
{ label: "Prophylactic use", val: "NOT recommended — no benefit over oxytocin alone", highlight: false },
];
findings.forEach((f, i) => {
const y = 1.36 + i * 0.72;
sl.addShape("rect", { x: 0.55, y, w: 5.3, h: 0.62, fill: { color: f.highlight ? C.blueLt : "F5F9FF" } });
sl.addText(f.label, { x: 0.68, y: y + 0.04, w: 5.0, h: 0.25, fontSize: 9, bold: false, color: C.textMid });
sl.addText(f.val, { x: 0.68, y: y + 0.3, w: 5.0, h: 0.28, fontSize: 10, bold: true, color: f.highlight ? C.blue : C.text });
});
// Right: dosing box
card(sl, 6.2, 0.88, 3.45, 2.2, C.card);
sl.addShape("rect", { x: 6.2, y: 0.88, w: 3.45, h: 0.38, fill: { color: C.crimson } });
sl.addText("DOSING PROTOCOL", { x: 6.28, y: 0.88, w: 3.3, h: 0.38, fontSize: 10, bold: true, color: C.textLight, valign: "middle" });
const dosing = [
"1 g IV over 10 minutes",
"Repeat 1 g IV if bleeding persists at 30 min",
"Give ≤ 3 hours from PPH diagnosis",
"Administer AFTER cord clamped",
];
dosing.forEach((d, i) => {
sl.addText([
{ text: `${i+1}. `, options: { bold: true, color: C.crimson } },
{ text: d, options: { color: C.text } },
], { x: 6.3, y: 1.34 + i * 0.38, w: 3.2, h: 0.35, fontSize: 9 });
});
card(sl, 6.2, 3.22, 3.45, 2.2, C.card);
sl.addShape("rect", { x: 6.2, y: 3.22, w: 3.45, h: 0.38, fill: { color: C.teal } });
sl.addText("MECHANISM", { x: 6.28, y: 3.22, w: 3.3, h: 0.38, fontSize: 10, bold: true, color: C.textLight, valign: "middle" });
sl.addText("Tranexamic acid competitively inhibits plasminogen activation, blocking fibrinolysis and stabilising formed clots. It does NOT cause new clot formation.", {
x: 6.3, y: 3.68, w: 3.25, h: 1.6, fontSize: 9, color: C.text, wrap: true,
});
}
// ════════════════════════════════════════════════
// SLIDE 14 — UTERINE INVERSION
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.amber, "INVERSION");
slideHeader(sl, "Uterine Inversion — Recognition & Management", C.amber);
// Recognition
card(sl, 0.42, 0.88, 4.5, 2.1, C.card);
sl.addShape("rect", { x: 0.42, y: 0.88, w: 4.5, h: 0.38, fill: { color: C.amber } });
sl.addText("RECOGNITION", { x: 0.5, y: 0.88, w: 4.35, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const signs = [
"Uterine fundus NOT palpable abdominally",
"Beefy red mass at introitus (complete)",
"Sudden severe hemorrhage + shock",
"More common with fundal placentation",
"Can follow excessive cord traction",
];
signs.forEach((s, i) => {
sl.addText([
{ text: "! ", options: { bold: true, color: C.amber } },
{ text: s, options: { color: C.text } },
], { x: 0.55, y: 1.34 + i * 0.33, w: 4.2, h: 0.3, fontSize: 9 });
});
// Degrees
card(sl, 5.1, 0.88, 4.55, 2.1, C.card);
sl.addShape("rect", { x: 5.1, y: 0.88, w: 4.55, h: 0.38, fill: { color: C.blue } });
sl.addText("DEGREES", { x: 5.18, y: 0.88, w: 4.38, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const degrees = [
{ d: "1st degree (Incomplete)", desc: "Inverted fundus does NOT pass through cervix" },
{ d: "2nd degree (Complete)", desc: "Inverted fundus passes through cervix but not introitus" },
{ d: "3rd degree (Prolapsed)", desc: "Inverted fundus visible at introitus" },
{ d: "4th degree (Total)", desc: "Vaginal walls also inverted" },
];
degrees.forEach((d, i) => {
sl.addText(d.d, { x: 5.22, y: 1.34 + i * 0.4, w: 4.3, h: 0.2, fontSize: 9, bold: true, color: C.blue });
sl.addText(d.desc, { x: 5.22, y: 1.52 + i * 0.4, w: 4.3, h: 0.2, fontSize: 8.5, color: C.textMid });
});
// Management steps
card(sl, 0.42, 3.1, 9.23, 2.0, C.card);
sl.addShape("rect", { x: 0.42, y: 3.1, w: 9.23, h: 0.38, fill: { color: C.red } });
sl.addText("MANAGEMENT — JOHNSON TECHNIQUE", { x: 0.5, y: 3.1, w: 9.0, h: 0.38, fontSize: 11, bold: true, color: C.textLight, valign: "middle" });
const mgmt = [
{ n: "1", t: "Resuscitate", d: "Aggressive IV fluids, blood products, summon anaesthesiologist NOW" },
{ n: "2", t: "Tocolysis", d: "MgSO₄, terbutaline, nitroglycerin, or β-mimetics to relax uterus" },
{ n: "3", t: "Johnson Maneuver", d: "Grasp uterus, push fundus firmly back up through cervix into abdomen; hold in place several minutes" },
{ n: "4", t: "If fails → Laparotomy", d: "Huntington procedure (round ligament traction) or Haultain procedure (posterior incision)" },
{ n: "5", t: "After reinversion", d: "Administer uterotonics immediately to prevent re-inversion" },
];
const mgW = 1.75;
mgmt.forEach((m, i) => {
const x = 0.5 + i * 1.86;
sl.addShape("rect", { x, y: 3.58, w: mgW, h: 0.25, fill: { color: C.red } });
sl.addText(`${m.n}. ${m.t}`, { x, y: 3.58, w: mgW, h: 0.25, fontSize: 8.5, bold: true, color: "FFFFFF", align: "center" });
sl.addText(m.d, { x, y: 3.85, w: mgW, h: 1.1, fontSize: 8, color: C.text, wrap: true, valign: "top" });
});
}
// ════════════════════════════════════════════════
// SLIDE 15 — RESUSCITATION ENDPOINTS
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.teal, "RESUSCITATION");
slideHeader(sl, "Resuscitation Endpoints & Massive Transfusion", C.teal);
const targetData = [
[
{ text: "Parameter", options: { bold: true, color: "FFFFFF", fill: { color: C.teal } } },
{ text: "Target", options: { bold: true, color: "FFFFFF", fill: { color: C.teal } } },
{ text: "Action if Not Met", options: { bold: true, color: "FFFFFF", fill: { color: C.teal } } },
],
["Hemoglobin", "≥ 8 g/dL (active hemorrhage: ≥ 10)", "Packed RBCs"],
["Platelet count", "≥ 50,000/µL (≥75,000 if ongoing)", "Platelet transfusion"],
["Fibrinogen", "≥ 2.0 g/L", "Cryoprecipitate (10 units)"],
["INR / PT", "< 1.5", "FFP (15 mL/kg)"],
["Temperature", "> 35°C", "Warming blanket, warm IV fluids"],
["pH", "> 7.35", "Address acidosis, ventilation support"],
["Urine output", "≥ 30 mL/hr", "Fluid bolus, check renal function"],
["MAP", "≥ 65 mmHg", "Vasopressors (noradrenaline) if vasodilatory"],
];
sl.addTable(targetData, {
x: 0.38, y: 0.85, w: 9.35, h: 3.3,
fontSize: 9.5,
border: { pt: 0.5, color: C.divider },
color: C.text,
rowH: 0.34,
colW: [2.0, 2.8, 4.55],
fontFace: "Calibri",
fill: { color: "F5FDFA" },
});
// Massive transfusion protocol
card(sl, 0.38, 4.25, 9.35, 1.0, C.tealLt);
sl.addShape("rect", { x: 0.38, y: 4.25, w: 0.18, h: 1.0, fill: { color: C.teal } });
sl.addText("MASSIVE TRANSFUSION PROTOCOL (MTP)", { x: 0.65, y: 4.28, w: 8.8, h: 0.28, fontSize: 10, bold: true, color: C.teal });
sl.addText([
{ text: "Trigger: ", options: { bold: true, color: C.teal } },
{ text: "≥ 10 units pRBCs in 24h, or ≥ 4 units pRBCs in 1h, or ongoing hemorrhage with hemodynamic compromise ", options: {} },
{ text: "Ratio: ", options: { bold: true, color: C.teal } },
{ text: "pRBC : FFP : Platelets = 1:1:1 ", options: {} },
{ text: "Consider: ", options: { bold: true, color: C.teal } },
{ text: "Recombinant Factor VIIa for refractory cases · Cell salvage with leukocyte-reduction filter", options: {} },
], { x: 0.65, y: 4.58, w: 9.0, h: 0.6, fontSize: 9, color: C.text, wrap: true });
}
// ════════════════════════════════════════════════
// SLIDE 16 — SUMMARY ALGORITHM
// ════════════════════════════════════════════════
{
const sl = contentSlide(pres, C.crimson, "ALGORITHM");
slideHeader(sl, "PPH Management Algorithm — Quick Reference", C.crimson);
const steps2 = [
{ stage: "RECOGNITION", bloss: "Any PPH", bg: C.crimsonDk, actions: "Call for help · 2 large-bore IVs · Bimanual massage · O₂ · Monitor vitals · FOUR Ts assessment · Labs & crossmatch" },
{ stage: "STAGE 1", bloss: "500–999 mL", bg: "#2E8B57", actions: "Oxytocin 20–30 units/L IV · Bimanual massage · Oxytocin IM if needed · Identify cause" },
{ stage: "STAGE 2", bloss: "1000–1499 mL", bg: C.teal, actions: "Methylergonovine 0.2 mg IM · Carboprost 250 µg IM · Tranexamic acid 1 g IV · Blood products 1:1:1 · Notify OR / IR" },
{ stage: "STAGE 3", bloss: "≥ 1500 mL", bg: "#6A0572", actions: "Bakri balloon · B-Lynch sutures · IR embolisation · Vessel ligation · Peripartum hysterectomy (last resort)" },
];
steps2.forEach((s, i) => {
const y = 0.88 + i * 1.13;
// Stage pill
sl.addShape("rect", { x: 0.42, y, w: 1.5, h: 0.9, fill: { color: s.bg } });
sl.addText(s.stage, { x: 0.42, y: y + 0.04, w: 1.5, h: 0.4, fontSize: 10, bold: true, color: C.textLight, align: "center" });
sl.addText(s.bloss, { x: 0.42, y: y + 0.46, w: 1.5, h: 0.38, fontSize: 8.5, color: "FFEEEE", align: "center" });
// Arrow
sl.addShape("rightArrow", { x: 2.0, y: y + 0.25, w: 0.4, h: 0.4, fill: { color: s.bg } });
// Action card
card(sl, 2.48, y, 7.18, 0.9, C.card);
sl.addShape("rect", { x: 2.48, y, w: 0.08, h: 0.9, fill: { color: s.bg } });
sl.addText(s.actions, { x: 2.65, y: y + 0.12, w: 6.9, h: 0.66, fontSize: 9, color: C.text, wrap: true });
});
sl.addShape("rect", { x: 0.42, y: 5.4, w: 9.3, h: 0.18, fill: { color: C.crimson } });
sl.addText("Sources: ACOG Practice Bulletin · Creasy & Resnik MFM · ROSEN's EM · Tintinalli's EM · Miller's Anesthesia 10e · WOMAN Trial (Lancet 2017)", {
x: 0.5, y: 5.4, w: 9.1, h: 0.18, fontSize: 7.5, color: C.textLight, italic: true, align: "center",
});
}
// ─── WRITE FILE ──────────────────────────────────────────────────────────────
const outPath = "/home/daytona/workspace/pph-ppt/PPH_Clinical_Decision_Support.pptx";
pres.writeFile({ fileName: outPath }).then(() => {
console.log("DONE:", outPath);
}).catch(err => {
console.error("ERROR:", err);
process.exit(1);
});