Steps in small incision cataract surgery
manual small incision cataract surgery MSICS steps scleral tunnel nucleus expression




| Feature | ECCE | MSICS | Phaco |
|---|---|---|---|
| Incision size | 8–10 mm | 6–7 mm (self-sealing) | 2–3 mm |
| Sutures required | Yes | Usually no | No |
| Nucleus handling | Expression | Manual expression via tunnel | Ultrasonic fragmentation |
| Cost/equipment | Low | Low | High |
| Astigmatism induced | High | Low–moderate | Minimal |
| Dense cataracts | Suitable | Ideal | Challenging |
Photos of haemostasis in MSICS
haemostasis cautery MSICS manual small incision cataract surgery scleral bleeding
wet field cautery bipolar cautery ophthalmic surgery conjunctival bleeding



| Method | Use |
|---|---|
| Wet-field (bipolar) cautery | Primary tool; applied to episcleral bleeders after conjunctival flap reflection |
| Monopolar/thermal cautery | Used in some settings; applied to the scleral bed before the groove |
| Direct pressure | Gentle tamponade with a sponge while preparing instruments |
| Adrenaline-soaked sponge | Occasionally used on episcleral vessels to induce vasoconstriction |
Make this into aa ppt
node /home/daytona/skills/shared/scripts/fetch_images.js \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_39fd2c06d07d8a35e399e887d6b117e45796d7c548ae79cb50e227f8d1f107c6.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e786ca87c47a91e47c81ca4905dfe83e85ef1ea39135e082775fec07d450f248.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_217dfc19f8ad0dff37f579527bb2599ad43535590fa51c05efe7abbc4256e480.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_ef37296fbd9b17f557d1fcb0cb3dfd3422da9c96bbf03b5af62361ebef8017ee.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_d869b7a97961e9261fecaf818ef925efb95a6228836b24d3ba5615693fc5dcac.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_86899785e985375b9e09010dabc3717f7e737921b06d9bb77e54fb7f3f00a6db.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_b814788b80435b88497e1f8d3cfa94f633c8239e386da3dc4d4910b1a87e87cf.jpg" \ 2>/dev/null | node -e "const d=require('fs').readFileSync('/dev/stdin','utf8'); const arr=JSON.parse(d); arr.forEach((x,i)=>{ if(x.error) console.log(i,'ERROR',x.error); else console.log(i,'OK len=',x.base64.length); });"
mkdir -p /home/daytona/workspace/msics-ppt
~/msics-ppt/build.js
const pptxgen = require("pptxgenjs");
const { execSync } = require("child_process");
// ── Fetch all images ──────────────────────────────────────────────────────────
const urls = [
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_39fd2c06d07d8a35e399e887d6b117e45796d7c548ae79cb50e227f8d1f107c6.jpg", // 0: scleral tunnel VR diagram
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e786ca87c47a91e47c81ca4905dfe83e85ef1ea39135e082775fec07d450f248.jpg", // 1: frown incision crescent blade
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_217dfc19f8ad0dff37f579527bb2599ad43535590fa51c05efe7abbc4256e480.jpg", // 2: nucleus delivery
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_ef37296fbd9b17f557d1fcb0cb3dfd3422da9c96bbf03b5af62361ebef8017ee.jpg", // 3: fish-hook nucleus
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_d869b7a97961e9261fecaf818ef925efb95a6228836b24d3ba5615693fc5dcac.jpg", // 4: bipolar diathermy conjunctiva
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_86899785e985375b9e09010dabc3717f7e737921b06d9bb77e54fb7f3f00a6db.jpg", // 5: haemostasis general vs modified
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_b814788b80435b88497e1f8d3cfa94f633c8239e386da3dc4d4910b1a87e87cf.jpg", // 6: exposed sclera pre-haemostasis
];
const raw = execSync(
`node /home/daytona/skills/shared/scripts/fetch_images.js ${urls.map(u => `"${u}"`).join(" ")}`
).toString();
const imgs = JSON.parse(raw);
// ── Colour palette ────────────────────────────────────────────────────────────
// Deep navy dominant, teal accent, white text, amber highlight
const C = {
navy: "0D1B2A",
navyMid: "1A2E42",
teal: "1E8A8A",
tealLt: "2ABFBF",
amber: "E8A030",
white: "FFFFFF",
offWhite:"E8EDF2",
muted: "8FA8BF",
};
// ── Helper: slide header bar ──────────────────────────────────────────────────
function addHeader(slide, title, stepLabel) {
// Full-width navy background
slide.addShape("rect", { x: 0, y: 0, w: 10, h: 1.05, fill: { color: C.navy }, line: { color: C.navy } });
// Teal left accent stripe
slide.addShape("rect", { x: 0, y: 0, w: 0.08, h: 1.05, fill: { color: C.teal }, line: { color: C.teal } });
if (stepLabel) {
slide.addText(stepLabel, {
x: 0.18, y: 0.08, w: 1.4, h: 0.38,
fontSize: 9, bold: true, color: C.amber, fontFace: "Calibri", margin: 0,
});
}
slide.addText(title, {
x: 0.18, y: stepLabel ? 0.42 : 0.22, w: 9.6, h: 0.55,
fontSize: stepLabel ? 20 : 24, bold: true, color: C.white, fontFace: "Calibri", margin: 0,
});
}
// ── Helper: footer ────────────────────────────────────────────────────────────
function addFooter(slide, text) {
slide.addShape("rect", { x: 0, y: 5.32, w: 10, h: 0.3, fill: { color: C.navyMid }, line: { color: C.navyMid } });
slide.addText(text, {
x: 0.2, y: 5.33, w: 9.6, h: 0.25,
fontSize: 7.5, color: C.muted, fontFace: "Calibri", margin: 0,
});
}
// ── Helper: info card (dark rounded rect) ─────────────────────────────────────
function addCard(slide, x, y, w, h, color) {
slide.addShape("roundRect", {
x, y, w, h,
fill: { color: color || C.navyMid },
line: { color: C.teal, pt: 1 },
rectRadius: 0.08,
});
}
// ── Helper: step number badge ─────────────────────────────────────────────────
function addBadge(slide, num, x, y) {
slide.addShape("ellipse", { x, y, w: 0.42, h: 0.42, fill: { color: C.teal }, line: { color: C.teal } });
slide.addText(String(num), {
x, y, w: 0.42, h: 0.42,
fontSize: 14, bold: true, color: C.white, fontFace: "Calibri",
align: "center", valign: "middle", margin: 0,
});
}
// ─────────────────────────────────────────────────────────────────────────────
let pres = new pptxgen();
pres.layout = "LAYOUT_16x9";
pres.title = "Manual Small-Incision Cataract Surgery (MSICS)";
pres.author = "Orris Medical";
// ══════════════════════════════════════════════════════════════════
// SLIDE 1 — Title slide
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
s.addShape("rect", { x: 0, y: 0, w: 0.12, h: 5.625, fill: { color: C.teal }, line: { color: C.teal } });
s.addShape("rect", { x: 0, y: 4.5, w: 10, h: 1.125, fill: { color: C.navyMid }, line: { color: C.navyMid } });
s.addText("Manual Small-Incision", {
x: 0.5, y: 1.0, w: 9, h: 0.9,
fontSize: 38, bold: true, color: C.white, fontFace: "Calibri",
align: "center", charSpacing: 1,
});
s.addText("Cataract Surgery", {
x: 0.5, y: 1.85, w: 9, h: 0.9,
fontSize: 38, bold: true, color: C.tealLt, fontFace: "Calibri",
align: "center", charSpacing: 1,
});
s.addText("(MSICS)", {
x: 0.5, y: 2.65, w: 9, h: 0.5,
fontSize: 22, color: C.amber, fontFace: "Calibri", align: "center", bold: false,
});
s.addText("Step-by-Step Surgical Technique", {
x: 0.5, y: 3.2, w: 9, h: 0.4,
fontSize: 14, color: C.offWhite, fontFace: "Calibri", align: "center", italic: true,
});
s.addText("Source: Kanski's Clinical Ophthalmology, 10th ed.", {
x: 0.5, y: 4.6, w: 9, h: 0.25,
fontSize: 9, color: C.muted, fontFace: "Calibri", align: "center",
});
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 2 — What is MSICS?
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "What is MSICS?");
const points = [
["Variant of ECCE", "Manual small-incision cataract surgery is a refined form of extracapsular cataract extraction (ECCE)."],
["Self-sealing tunnel", "Creates a 6–7 mm sclero-corneal tunnel — no sutures typically needed."],
["High-volume settings", "Designed for resource-limited environments; faster and cheaper than phacoemulsification."],
["Ideal for dense cataracts", "Particularly suited for mature, brunescent, and white cataracts."],
["Comparable outcomes", "Visual rehabilitation equivalent to phacoemulsification."],
];
points.forEach(([title, body], i) => {
const y = 1.2 + i * 0.82;
addBadge(s, i + 1, 0.2, y + 0.06);
addCard(s, 0.72, y, 9.1, 0.68);
s.addText(title, { x: 0.85, y: y + 0.04, w: 2.2, h: 0.28, fontSize: 11, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText(body, { x: 0.85, y: y + 0.3, w: 8.7, h: 0.32, fontSize: 10, color: C.offWhite, fontFace: "Calibri", margin: 0 });
});
addFooter(s, "Kanski's Clinical Ophthalmology, 10th ed.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 3 — Steps overview (flow)
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Surgical Steps — Overview");
const steps = [
"Patient Prep & Anaesthesia",
"Conjunctival Flap",
"Haemostasis",
"Scleral Groove",
"Tunnel Dissection",
"Capsulotomy",
"Hydrodissection",
"Nucleus Expression",
"Cortical Aspiration",
"IOL Implantation",
"Wound Closure",
];
// Two columns
const col1 = steps.slice(0, 6);
const col2 = steps.slice(6);
col1.forEach((step, i) => {
const y = 1.18 + i * 0.68;
addBadge(s, i + 1, 0.18, y);
s.addText(step, { x: 0.68, y: y + 0.04, w: 4.3, h: 0.35, fontSize: 11.5, color: C.offWhite, fontFace: "Calibri", bold: i === 2, margin: 0 });
if (i === 2) {
s.addShape("rect", { x: 0.68, y: y + 0.39, w: 4.3, h: 0.02, fill: { color: C.teal }, line: { color: C.teal } });
}
});
col2.forEach((step, i) => {
const y = 1.18 + i * 0.68;
addBadge(s, i + 7, 5.2, y);
s.addText(step, { x: 5.7, y: y + 0.04, w: 4.1, h: 0.35, fontSize: 11.5, color: C.offWhite, fontFace: "Calibri", margin: 0 });
});
// Vertical divider
s.addShape("rect", { x: 4.98, y: 1.15, w: 0.04, h: 4.1, fill: { color: C.teal }, line: { color: C.teal } });
addFooter(s, "Step 3 (Haemostasis) is expanded in detail in subsequent slides.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 4 — Steps 1 & 2: Prep + Conjunctival Flap
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Patient Preparation & Conjunctival Flap", "STEPS 1–2");
// Step 1 card
addCard(s, 0.2, 1.2, 4.55, 3.9);
addBadge(s, 1, 0.28, 1.28);
s.addText("Patient Preparation & Anaesthesia", { x: 0.78, y: 1.28, w: 3.8, h: 0.38, fontSize: 12, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• Topical, peribulbar, or sub-Tenon's anaesthesia", options: { breakLine: true } },
{ text: "• Lid speculum inserted; eye cleaned and draped", options: { breakLine: true } },
{ text: "• Pupil fully dilated with mydriatic drops", options: { breakLine: true } },
{ text: "• Informed consent; biometry completed pre-op", options: { breakLine: true } },
{ text: "• Penicillin allergy status; antibiotic drops started", options: {} },
], { x: 0.35, y: 1.75, w: 4.2, h: 2.6, fontSize: 10.5, color: C.offWhite, fontFace: "Calibri" });
// Step 2 card
addCard(s, 5.1, 1.2, 4.65, 3.9);
addBadge(s, 2, 5.18, 1.28);
s.addText("Conjunctival Flap", { x: 5.68, y: 1.28, w: 3.9, h: 0.38, fontSize: 12, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• Fornix-based conjunctival flap raised superiorly", options: { breakLine: true } },
{ text: "• Tenon's capsule dissected to expose bare sclera", options: { breakLine: true } },
{ text: "• Bridle suture (4-0 silk) placed through superior rectus for globe control", options: { breakLine: true } },
{ text: "• Exposes the limbal zone for scleral incision", options: { breakLine: true } },
{ text: "• Haemostasis achieved with wet-field cautery (Step 3)", options: {} },
], { x: 5.25, y: 1.75, w: 4.3, h: 2.6, fontSize: 10.5, color: C.offWhite, fontFace: "Calibri" });
addFooter(s, "Kanski's Clinical Ophthalmology, 10th ed.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 5 — Step 3: Haemostasis (with images)
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Haemostasis", "STEP 3");
// Left: text
addCard(s, 0.2, 1.15, 3.95, 4.15);
s.addText([
{ text: "Wet-field bipolar cautery applied to episcleral vessels after conjunctival flap is raised.", options: { breakLine: true } },
{ text: " ", options: { breakLine: true } },
{ text: "Why it matters:", options: { bold: true, color: C.amber, breakLine: true } },
{ text: "• Bleeding into AC during nucleus expression → expulsive haemorrhage risk", options: { breakLine: true } },
{ text: "• Blood-stained field reduces visibility for subsequent steps", options: { breakLine: true } },
{ text: " ", options: { breakLine: true } },
{ text: "Methods:", options: { bold: true, color: C.amber, breakLine: true } },
{ text: "• Wet-field (bipolar) cautery — primary tool", options: { breakLine: true } },
{ text: "• Prophylactic thermal cautery to scleral bed", options: { breakLine: true } },
{ text: "• Adrenaline-soaked sponge (vasoconstriction)", options: { breakLine: true } },
{ text: "• Direct pressure with dry sponge", options: {} },
], { x: 0.32, y: 1.25, w: 3.65, h: 3.9, fontSize: 9.5, color: C.offWhite, fontFace: "Calibri" });
// Centre: bipolar cautery image
if (!imgs[4].error) {
s.addImage({ data: imgs[4].base64, x: 4.28, y: 1.18, w: 2.55, h: 2.0 });
s.addText("Bipolar diathermy forceps\ncoagulating conjunctival vessels", {
x: 4.28, y: 3.22, w: 2.55, h: 0.45, fontSize: 7.5, color: C.muted, fontFace: "Calibri", align: "center",
});
}
// Right: general vs modified haemostasis
if (!imgs[5].error) {
s.addImage({ data: imgs[5].base64, x: 6.98, y: 1.18, w: 2.82, h: 3.7 });
s.addText("General vs. modified procedure:\nprophylactic cautery reduces intraoperative bleeding", {
x: 6.98, y: 4.92, w: 2.82, h: 0.45, fontSize: 7.5, color: C.muted, fontFace: "Calibri", align: "center",
});
}
addFooter(s, "Adequate haemostasis before the scleral groove is mandatory for a clear surgical field.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 6 — Step 3b: Pre-haemostasis field (exposed sclera)
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Exposed Sclera — Before Haemostasis", "STEP 3 — CLINICAL VIEW");
if (!imgs[6].error) {
s.addImage({ data: imgs[6].base64, x: 0.3, y: 1.2, w: 5.5, h: 3.85 });
}
addCard(s, 6.0, 1.2, 3.75, 3.85);
s.addText("What this image shows:", { x: 6.15, y: 1.3, w: 3.45, h: 0.35, fontSize: 11, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• Conjunctival flap reflected, bare sclera exposed", options: { breakLine: true } },
{ text: "• Perilimbal vascularity clearly visible", options: { breakLine: true } },
{ text: "• Focal bleeding points present — cautery not yet applied", options: { breakLine: true } },
{ text: "• Toothed forceps stabilising the globe", options: { breakLine: true } },
{ text: "• Mature white cataract visible through dilated pupil", options: { breakLine: true } },
{ text: " ", options: { breakLine: true } },
{ text: "Key point:", options: { bold: true, color: C.tealLt, breakLine: true } },
{ text: "Cautery must be applied NOW — before the scleral groove is made — to achieve a dry, clear field for tunnel dissection.", options: {} },
], { x: 6.15, y: 1.72, w: 3.45, h: 3.1, fontSize: 9.5, color: C.offWhite, fontFace: "Calibri" });
addFooter(s, "Intraoperative photo — exposed bare sclera with perilimbal bleeding points before cautery.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 7 — Steps 4–5: Scleral Groove + Tunnel
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Scleral Groove & Tunnel Dissection", "STEPS 4–5");
// Image: frown incision
if (!imgs[1].error) {
s.addImage({ data: imgs[1].base64, x: 0.2, y: 1.18, w: 4.6, h: 3.5 });
s.addText("Frown incision: crescent blade creating sclerocorneal tunnel;\nTrypan Blue highlights tunnel path", {
x: 0.2, y: 4.72, w: 4.6, h: 0.45, fontSize: 7.5, color: C.muted, fontFace: "Calibri", align: "center",
});
}
// Right: text
addCard(s, 5.0, 1.18, 4.8, 2.0);
addBadge(s, 4, 5.08, 1.26);
s.addText("Scleral Groove", { x: 5.6, y: 1.26, w: 4.0, h: 0.35, fontSize: 11.5, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• Partial-thickness (~300 µm) frown or straight groove", options: { breakLine: true } },
{ text: "• 6–7 mm wide, 1–2 mm behind limbus", options: { breakLine: true } },
{ text: "• Made with a #15 blade or crescent knife", options: {} },
], { x: 5.15, y: 1.65, w: 4.5, h: 1.35, fontSize: 10, color: C.offWhite, fontFace: "Calibri" });
addCard(s, 5.0, 3.3, 4.8, 2.0);
addBadge(s, 5, 5.08, 3.38);
s.addText("Tunnel Dissection", { x: 5.6, y: 3.38, w: 4.0, h: 0.35, fontSize: 11.5, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• Crescent blade dissects lamellar tunnel forward into clear cornea", options: { breakLine: true } },
{ text: "• Uniform depth essential — too shallow risks button-hole; too deep risks perforation", options: { breakLine: true } },
{ text: "• Keratome enters anterior chamber through internal lip", options: {} },
], { x: 5.15, y: 3.77, w: 4.5, h: 1.35, fontSize: 10, color: C.offWhite, fontFace: "Calibri" });
addFooter(s, "The self-sealing architecture of the tunnel is the key innovation of MSICS over conventional ECCE.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 8 — Steps 6 & 7: Capsulorhexis + Hydrodissection (VR image)
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Capsulotomy & Hydrodissection", "STEPS 6–7");
// VR simulator image (tunnel creation)
if (!imgs[0].error) {
s.addImage({ data: imgs[0].base64, x: 0.2, y: 1.18, w: 4.6, h: 2.9 });
s.addText("Keratome entry into anterior chamber (VR simulator view):\nA — tunnel dissection; B — AC entry with keratome", {
x: 0.2, y: 4.12, w: 4.6, h: 0.45, fontSize: 7.5, color: C.muted, fontFace: "Calibri", align: "center",
});
}
addCard(s, 5.0, 1.18, 4.8, 2.05);
addBadge(s, 6, 5.08, 1.26);
s.addText("Anterior Capsulotomy (CCC)", { x: 5.6, y: 1.26, w: 4.0, h: 0.35, fontSize: 11.5, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• Trypan blue 0.06% stains anterior capsule", options: { breakLine: true } },
{ text: "• OVD fills AC to maintain depth", options: { breakLine: true } },
{ text: "• Continuous curvilinear capsulorhexis (CCC) with needle or forceps", options: { breakLine: true } },
{ text: "• Critical step — tear must be controlled and circular", options: {} },
], { x: 5.15, y: 1.65, w: 4.5, h: 1.42, fontSize: 10, color: C.offWhite, fontFace: "Calibri" });
addCard(s, 5.0, 3.35, 4.8, 1.95);
addBadge(s, 7, 5.08, 3.43);
s.addText("Hydrodissection", { x: 5.6, y: 3.43, w: 4.0, h: 0.35, fontSize: 11.5, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText([
{ text: "• BSS injected under anterior capsule", options: { breakLine: true } },
{ text: "• Frees nucleus from cortical attachments", options: { breakLine: true } },
{ text: "• Hydrodelineation separates nucleus from epinucleus", options: { breakLine: true } },
{ text: "• Allows free rotation and nucleus delivery", options: {} },
], { x: 5.15, y: 3.82, w: 4.5, h: 1.35, fontSize: 10, color: C.offWhite, fontFace: "Calibri" });
addFooter(s, "Kanski's Clinical Ophthalmology, 10th ed.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 9 — Step 8: Nucleus Expression (with photos)
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Nucleus Prolapse & Expression", "STEP 8");
// Two images side by side
if (!imgs[3].error) {
s.addImage({ data: imgs[3].base64, x: 0.2, y: 1.18, w: 3.1, h: 2.8 });
s.addText("Fish-hook technique:\nnucleus engaged and\ndelivered through tunnel", {
x: 0.2, y: 4.02, w: 3.1, h: 0.55, fontSize: 7.5, color: C.muted, fontFace: "Calibri", align: "center",
});
}
if (!imgs[2].error) {
s.addImage({ data: imgs[2].base64, x: 3.5, y: 1.18, w: 3.1, h: 2.8 });
s.addText("Dense brunescent nucleus\nbeing delivered through\nsclerocorneal tunnel", {
x: 3.5, y: 4.02, w: 3.1, h: 0.55, fontSize: 7.5, color: C.muted, fontFace: "Calibri", align: "center",
});
}
addCard(s, 6.75, 1.18, 3.05, 3.95);
s.addText([
{ text: "Nucleus prolapsed into anterior chamber using Sinskey hook or lens dialer.", options: { breakLine: true } },
{ text: " ", options: { breakLine: true } },
{ text: "Techniques:", options: { bold: true, color: C.amber, breakLine: true } },
{ text: "• Wire vectis beneath nucleus", options: { breakLine: true } },
{ text: "• Irrigating vectis", options: { breakLine: true } },
{ text: "• Fish-hook needle", options: { breakLine: true } },
{ text: "• Sandwich/tumbling technique", options: { breakLine: true } },
{ text: " ", options: { breakLine: true } },
{ text: "OVD protects the corneal endothelium throughout.", options: { italic: true, color: C.tealLt } },
], { x: 6.88, y: 1.3, w: 2.72, h: 3.65, fontSize: 9.5, color: C.offWhite, fontFace: "Calibri" });
addFooter(s, "OVD = ophthalmic viscosurgical device. Endothelial protection is critical during nucleus delivery.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 10 — Steps 9–11: Cortex Aspiration, IOL, Closure
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Cortical Aspiration · IOL Implantation · Wound Closure", "STEPS 9–11");
const cards = [
{
n: 9, title: "Cortical Aspiration",
body: [
"Simcoe (two-way irrigation-aspiration) cannula",
"Cortex removed from capsular bag",
"Thorough removal prevents posterior capsule opacification (PCO)",
"AC depth maintained with irrigation throughout",
],
},
{
n: 10, title: "IOL Implantation",
body: [
"Capsular bag refilled with OVD",
"PMMA rigid IOL implanted (no folding needed for 6–7 mm tunnel)",
"Foldable IOLs can also be used",
"Haptics positioned in capsular bag",
],
},
{
n: 11, title: "OVD Removal & Wound Closure",
body: [
"OVD thoroughly aspirated from AC",
"AC reformed with balanced salt solution (BSS)",
"Self-sealing tunnel — sutures usually not required",
"10-0 nylon suture placed if wound not watertight",
"Conjunctival flap repositioned with cautery or suture",
],
},
];
cards.forEach((c, i) => {
const x = 0.18 + i * 3.28;
addCard(s, x, 1.15, 3.1, 4.2);
addBadge(s, c.n, x + 0.08, 1.23);
s.addText(c.title, { x: x + 0.6, y: 1.23, w: 2.35, h: 0.38, fontSize: 11, bold: true, color: C.amber, fontFace: "Calibri", margin: 0 });
s.addText(c.body.map(b => `• ${b}`).join("\n"), {
x: x + 0.12, y: 1.7, w: 2.85, h: 3.42,
fontSize: 9.5, color: C.offWhite, fontFace: "Calibri",
});
});
addFooter(s, "Postoperatively: subconjunctival antibiotic/steroid injection, eye pad and shield applied.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 11 — Comparison Table: ECCE vs MSICS vs Phaco
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "MSICS vs. ECCE vs. Phacoemulsification");
const rows = [
["Feature", "ECCE", "MSICS", "Phaco"],
["Incision size", "8–10 mm", "6–7 mm", "2–3 mm"],
["Self-sealing", "No", "Yes", "Yes"],
["Sutures", "Required", "Usually none", "None"],
["Nucleus handling", "Expression", "Manual via tunnel", "Ultrasonic"],
["Equipment cost", "Low", "Low", "High"],
["Astigmatism induced", "High", "Low–moderate", "Minimal"],
["Dense cataracts", "Suitable", "Ideal", "Challenging"],
["Visual outcomes", "Good", "Comparable to phaco", "Best"],
];
const colWidths = [2.8, 2.2, 2.5, 2.2];
const colX = [0.18, 3.08, 5.38, 7.98];
const rowH = 0.44;
const startY = 1.12;
rows.forEach((row, ri) => {
row.forEach((cell, ci) => {
const y = startY + ri * rowH;
const isHeader = ri === 0;
const isMSICS = ci === 2;
const fillColor = isHeader ? C.teal : isMSICS ? C.navyMid : C.navy;
const borderColor = isHeader ? C.teal : C.teal;
s.addShape("rect", {
x: colX[ci], y, w: colWidths[ci] - 0.04, h: rowH - 0.02,
fill: { color: fillColor },
line: { color: borderColor, pt: 0.5 },
});
s.addText(cell, {
x: colX[ci] + 0.08, y: y + 0.04, w: colWidths[ci] - 0.2, h: rowH - 0.1,
fontSize: isHeader ? 10.5 : 9.5,
bold: isHeader || (ci === 0),
color: isHeader ? C.white : isMSICS ? C.amber : (ci === 0 ? C.muted : C.offWhite),
fontFace: "Calibri",
valign: "middle", margin: 0,
});
});
});
addFooter(s, "MSICS is the preferred technique in high-volume settings for dense cataracts. — Kanski's Clinical Ophthalmology, 10th ed.");
}
// ══════════════════════════════════════════════════════════════════
// SLIDE 12 — Key Points / Summary
// ══════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x: 0, y: 0, w: 10, h: 5.625, fill: { color: C.navy }, line: { color: C.navy } });
addHeader(s, "Key Takeaways");
const pts = [
["Self-sealing incision", "The 6–7 mm sclero-corneal tunnel architecture makes sutures unnecessary in most cases."],
["Haemostasis is critical", "Wet-field bipolar cautery before the scleral groove prevents a blood-stained field and expulsive haemorrhage risk."],
["OVD throughout", "Ophthalmic viscosurgical devices protect the corneal endothelium at every step from capsulotomy to IOL loading."],
["Dense cataract workhorse", "MSICS handles brunescent and white cataracts where phacoemulsification poses higher risk."],
["High-volume, low-cost", "No ultrasound machine needed — affordable, fast, and scalable for eye camps in low-resource settings."],
["Outcomes comparable to phaco", "Visual rehabilitation with MSICS equals phacoemulsification with significantly lower equipment cost."],
];
pts.forEach(([title, body], i) => {
const y = 1.18 + i * 0.7;
s.addShape("rect", { x: 0.18, y: y + 0.04, w: 0.04, h: 0.45, fill: { color: C.teal }, line: { color: C.teal } });
s.addText(title + ": ", { x: 0.32, y, w: 2.4, h: 0.55, fontSize: 10.5, bold: true, color: C.amber, fontFace: "Calibri", valign: "middle", margin: 0 });
s.addText(body, { x: 2.72, y, w: 7.1, h: 0.55, fontSize: 10, color: C.offWhite, fontFace: "Calibri", valign: "middle", margin: 0 });
});
addFooter(s, "Source: Kanski's Clinical Ophthalmology, 10th ed. | Images: PMC Clinical VQA Dataset");
}
// ── Write file ────────────────────────────────────────────────────────────────
pres.writeFile({ fileName: "/home/daytona/workspace/msics-ppt/MSICS_Surgery.pptx" })
.then(() => console.log("Done: MSICS_Surgery.pptx"))
.catch(e => { console.error(e); process.exit(1); });
cd /home/daytona/workspace/msics-ppt && node build.js 2>&1
ls -lh /home/daytona/workspace/msics-ppt/MSICS_Surgery.pptx
MSICS Surgery
Presentation · PPTX
| Slide | Content |
|---|---|
| 1 | Title |
| 2 | What is MSICS? |
| 3 | All 11 steps — overview flowchart |
| 4 | Steps 1–2: Patient prep & conjunctival flap |
| 5 | Step 3: Haemostasis — with bipolar cautery photo + general vs. modified comparison chart |
| 6 | Step 3 clinical view — exposed sclera before cautery |
| 7 | Steps 4–5: Scleral groove & tunnel dissection (with intraop photo) |
| 8 | Steps 6–7: Capsulorhexis & hydrodissection |
| 9 | Step 8: Nucleus expression — fish-hook & delivery photos |
| 10 | Steps 9–11: Cortical aspiration, IOL, wound closure |
| 11 | Comparison table: ECCE vs MSICS vs Phaco |
| 12 | Key takeaways |
What is side port entry
side port incision cataract surgery paracentesis anterior chamber keratome
| Purpose | Detail |
|---|---|
| Second instrument access | Allows a chopper, manipulator, or hook to enter the AC and assist nucleus management during phaco or MSICS |
| OVD/viscoelastic injection | Cannula inserted through side port to fill AC before and during surgery |
| Cortex aspiration | Bimanual I&A uses the side port for the irrigation cannula |
| AC maintainer | In MSICS, an AC maintainer may be inserted here to keep IOP stable during nucleus delivery |
| Iris manipulation | Stretching or repositioning the iris if needed |
| Pressure relief | Quick decompression of the AC if IOP spikes intraoperatively |



Side port entry steps to add in a ppt
node /home/daytona/skills/shared/scripts/fetch_images.js \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2a3c9bfc869a71e4c8274b3aa9926cb23e22d723d597d5feb1c7b0f749422781.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2a8915c4d1aae26de535e4dcceac00ae82e7269e26e6c60cf49c3f0bb25af2d3.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_340cfa24942acd21d70352337071719094c91f6f651a48bd57b5d14d2434d704.jpg" \ 2>/dev/null | node -e " const d=require('fs').readFileSync('/dev/stdin','utf8'); const arr=JSON.parse(d); arr.forEach((x,i)=>{ if(x.error) console.log(i,'ERROR',x.error); else console.log(i,'OK len=',x.base64.length); }); "
~/msics-ppt/add_sideport.js
const pptxgen = require("pptxgenjs");
const { execSync } = require("child_process");
// ── Fetch images ─────────────────────────────────────────────────────────────
const urls = [
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2a3c9bfc869a71e4c8274b3aa9926cb23e22d723d597d5feb1c7b0f749422781.jpg", // 0: keratome + side port instrument
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2a8915c4d1aae26de535e4dcceac00ae82e7269e26e6c60cf49c3f0bb25af2d3.jpg", // 1: bent cannula through side port
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_340cfa24942acd21d70352337071719094c91f6f651a48bd57b5d14d2434d704.jpg", // 2: FLACS planning showing side ports
];
const raw = execSync(
`node /home/daytona/skills/shared/scripts/fetch_images.js ${urls.map(u => `"${u}"`).join(" ")}`
).toString();
const imgs = JSON.parse(raw);
// ── Colour palette (same as main deck) ───────────────────────────────────────
const C = {
navy: "0D1B2A",
navyMid: "1A2E42",
teal: "1E8A8A",
tealLt: "2ABFBF",
amber: "E8A030",
white: "FFFFFF",
offWhite:"E8EDF2",
muted: "8FA8BF",
};
function addHeader(slide, title, stepLabel) {
slide.addShape("rect", { x:0, y:0, w:10, h:1.05, fill:{color:C.navy}, line:{color:C.navy} });
slide.addShape("rect", { x:0, y:0, w:0.08, h:1.05, fill:{color:C.teal}, line:{color:C.teal} });
if (stepLabel) {
slide.addText(stepLabel, { x:0.18, y:0.08, w:2, h:0.38, fontSize:9, bold:true, color:C.amber, fontFace:"Calibri", margin:0 });
}
slide.addText(title, {
x:0.18, y: stepLabel ? 0.42 : 0.22, w:9.6, h:0.55,
fontSize: stepLabel ? 20 : 24, bold:true, color:C.white, fontFace:"Calibri", margin:0,
});
}
function addFooter(slide, text) {
slide.addShape("rect", { x:0, y:5.32, w:10, h:0.3, fill:{color:C.navyMid}, line:{color:C.navyMid} });
slide.addText(text, { x:0.2, y:5.33, w:9.6, h:0.25, fontSize:7.5, color:C.muted, fontFace:"Calibri", margin:0 });
}
function addCard(slide, x, y, w, h, color) {
slide.addShape("roundRect", {
x, y, w, h,
fill:{ color: color || C.navyMid },
line:{ color:C.teal, pt:1 },
rectRadius:0.08,
});
}
function addBadge(slide, label, x, y, color) {
slide.addShape("ellipse", { x, y, w:0.42, h:0.42, fill:{color: color||C.teal}, line:{color: color||C.teal} });
slide.addText(String(label), {
x, y, w:0.42, h:0.42, fontSize:12, bold:true, color:C.white,
fontFace:"Calibri", align:"center", valign:"middle", margin:0,
});
}
// ── Load existing PPTX ────────────────────────────────────────────────────────
// pptxgenjs cannot edit existing files — we rebuild from scratch using the same
// script logic, then append the new slide at the correct position (after slide 4,
// before the current slide 5 on haemostasis).
// Strategy: read build.js, re-run it to produce a fresh base deck, then inject
// the new slide via a second pass. Since pptxgenjs only supports append, we
// instead re-run build.js with the new slide injected inline.
// ── Standalone: just produce a single slide PPTX to merge ────────────────────
let pres = new pptxgen();
pres.layout = "LAYOUT_16x9";
pres.title = "Side Port Entry — MSICS";
// ════════════════════════════════════════════════════════════════════
// SLIDE A — Side Port Entry: What & Where
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x:0, y:0, w:10, h:5.625, fill:{color:C.navy}, line:{color:C.navy} });
addHeader(s, "Side Port Entry — Overview", "STEP 2b | MSICS & PHACOEMULSIFICATION");
// Left column: definition + position
addCard(s, 0.18, 1.12, 4.7, 4.18);
s.addText("What is a side port?", {
x:0.32, y:1.2, w:4.3, h:0.38,
fontSize:12, bold:true, color:C.amber, fontFace:"Calibri", margin:0,
});
s.addText([
{ text: "A small accessory corneal stab incision (1.0–1.2 mm) made separately from the main wound. Also called a paracentesis.", options: { breakLine:true } },
{ text: " ", options: { breakLine:true } },
{ text: "Location:", options: { bold:true, color:C.tealLt, breakLine:true } },
{ text: "• ~60° to the LEFT of the main incision (right-handed surgeons)", options: { breakLine:true } },
{ text: "• Placed in clear cornea at the limbus", options: { breakLine:true } },
{ text: "• Some surgeons use TWO side ports ~180° apart", options: { breakLine:true } },
{ text: " ", options: { breakLine:true } },
{ text: "Instrument:", options: { bold:true, color:C.tealLt, breakLine:true } },
{ text: "• 15° MVR (micro-vitreoretinal) blade, or dedicated side-port keratome", options: { breakLine:true } },
{ text: " ", options: { breakLine:true } },
{ text: "Self-sealing — valve tunnel architecture closes without suture once instrument is removed.", options: { italic:true, color:C.muted } },
], { x:0.32, y:1.65, w:4.42, h:3.5, fontSize:10, color:C.offWhite, fontFace:"Calibri" });
// Right column: uses
addCard(s, 5.08, 1.12, 4.72, 4.18);
s.addText("Uses of the side port", {
x:5.22, y:1.2, w:4.3, h:0.38,
fontSize:12, bold:true, color:C.amber, fontFace:"Calibri", margin:0,
});
const uses = [
["Second instrument", "Chopper / manipulator / Sinskey hook for nucleus management"],
["OVD injection", "Viscoelastic cannula fills AC before capsulorhexis and IOL loading"],
["Bimanual I&A", "Irrigation cannula enters here; aspiration through main port"],
["AC maintainer", "In MSICS: keeps IOP stable during nucleus delivery"],
["Iris manipulation", "Stretch or reposition pupil if needed"],
["Pressure relief", "Rapid AC decompression if IOP spikes intraoperatively"],
];
uses.forEach(([title, body], i) => {
const y = 1.65 + i * 0.57;
addBadge(s, i+1, 5.15, y + 0.05, C.teal);
s.addText(title, { x:5.65, y: y+0.04, w:1.8, h:0.24, fontSize:9.5, bold:true, color:C.tealLt, fontFace:"Calibri", margin:0 });
s.addText(body, { x:5.65, y: y+0.27, w:3.95, h:0.26, fontSize:9, color:C.offWhite, fontFace:"Calibri", margin:0 });
});
addFooter(s, "Kanski's Clinical Ophthalmology, 10th ed.");
}
// ════════════════════════════════════════════════════════════════════
// SLIDE B — Side Port Entry: Step-by-Step Technique
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x:0, y:0, w:10, h:5.625, fill:{color:C.navy}, line:{color:C.navy} });
addHeader(s, "Side Port Entry — Technique", "STEP-BY-STEP");
// Steps (left + centre column)
const steps = [
["1", "Globe stabilisation", "Fix eye with fixation forceps or toothed forceps on conjunctiva. Bridle suture already in place."],
["2", "Blade selection", "15° MVR blade (1.0–1.2 mm width). Some surgeons use a 20-gauge or dedicated side-port keratome."],
["3", "Entry angle", "Hold blade tangentially (~20° to corneal surface) to create a bevelled, stepped entry — key to self-sealing."],
["4", "Tunnel construction", "Advance blade through anterior corneal stroma parallel to iris plane for 1.0–1.5 mm, then tilt and enter AC."],
["5", "Enter anterior chamber", "Blade tip passes through Descemet's membrane into AC. Gentle aqueous egress confirms correct placement."],
["6", "Withdraw blade", "Remove smoothly — the stepped tunnel collapses and self-seals under IOP. No suture needed."],
["7", "Confirm patency", "Insert OVD cannula or second instrument — ensure free passage without iris prolapse or wound leak."],
];
steps.forEach(([num, title, body], i) => {
const y = 1.15 + i * 0.615;
addBadge(s, num, 0.18, y + 0.05);
s.addText(title, { x:0.7, y: y+0.04, w:2.35, h:0.28, fontSize:10.5, bold:true, color:C.amber, fontFace:"Calibri", margin:0 });
s.addText(body, { x:3.12, y: y+0.04, w:6.7, h:0.5, fontSize:9.5, color:C.offWhite, fontFace:"Calibri", margin:0 });
// divider line
if (i < steps.length - 1) {
s.addShape("rect", { x:0.18, y: y+0.59, w:9.65, h:0.01, fill:{color:C.teal}, line:{color:C.teal} });
}
});
addFooter(s, "The self-sealing stepped tunnel is the critical design principle — correct angulation prevents iris prolapse and leaks.");
}
// ════════════════════════════════════════════════════════════════════
// SLIDE C — Side Port Entry: Clinical Photos
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape("rect", { x:0, y:0, w:10, h:5.625, fill:{color:C.navy}, line:{color:C.navy} });
addHeader(s, "Side Port Entry — Intraoperative Views");
// Image 0: keratome + side port instrument (main incision with side port already in)
if (!imgs[0].error) {
s.addImage({ data:imgs[0].base64, x:0.18, y:1.15, w:3.0, h:2.45 });
s.addText("Main keratome incision being made;\nirrigation instrument already\nplaced through side port (right)", {
x:0.18, y:3.63, w:3.0, h:0.6, fontSize:8, color:C.muted, fontFace:"Calibri", align:"center",
});
}
// Image 1: bent cannula through side port
if (!imgs[1].error) {
s.addImage({ data:imgs[1].base64, x:3.48, y:1.15, w:3.0, h:2.45 });
s.addText("27-gauge bent cannula inserted\nthrough side port for cortical\nhydro-dispersion", {
x:3.48, y:3.63, w:3.0, h:0.6, fontSize:8, color:C.muted, fontFace:"Calibri", align:"center",
});
}
// Image 2: FLACS planning (side port positions marked)
if (!imgs[2].error) {
s.addImage({ data:imgs[2].base64, x:6.68, y:1.15, w:3.12, h:2.45 });
s.addText("FLACS planning view: main port\n(~210°) and two side ports\n(~95° and ~275°) marked", {
x:6.68, y:3.63, w:3.12, h:0.6, fontSize:8, color:C.muted, fontFace:"Calibri", align:"center",
});
}
// Key points strip
addCard(s, 0.18, 4.3, 9.65, 0.9, C.navyMid);
s.addText([
{ text: "Key points: ", options:{ bold:true, color:C.amber } },
{ text: "Self-sealing (no suture) • ", options:{ color:C.offWhite } },
{ text: "1.0–1.2 mm width • ", options:{ color:C.offWhite } },
{ text: "~60° from main incision • ", options:{ color:C.offWhite } },
{ text: "15° MVR blade at low angle • ", options:{ color:C.offWhite } },
{ text: "Bevelled stepped tunnel prevents iris prolapse", options:{ color:C.offWhite } },
], { x:0.32, y:4.42, w:9.35, h:0.65, fontSize:10, fontFace:"Calibri", valign:"middle", margin:0 });
addFooter(s, "Kanski's Clinical Ophthalmology, 10th ed. | Images: PMC Clinical VQA Dataset");
}
pres.writeFile({ fileName:"/home/daytona/workspace/msics-ppt/SidePort_Slides.pptx" })
.then(() => console.log("Done: SidePort_Slides.pptx"))
.catch(e => { console.error(e); process.exit(1); });
cd /home/daytona/workspace/msics-ppt && node add_sideport.js 2>&1
~/msics-ppt/merge.py
"""
Merge SidePort_Slides.pptx into MSICS_Surgery.pptx after slide index 3 (slide 4).
Output: MSICS_Surgery_Full.pptx
"""
import copy
from pptx import Presentation
from pptx.util import Pt
from lxml import etree
def merge_pptx(base_path, insert_path, after_index, output_path):
base = Presentation(base_path)
insert = Presentation(insert_path)
# We'll rebuild slide order by copying XML elements
# pptx slide list is base.slides._sldIdLst
base_sldIdLst = base.slides._sldIdLst
inserted_ids = []
for slide in insert.slides:
# Clone slide XML
slide_xml = copy.deepcopy(slide._element)
# Add a new blank slide to base, then replace its XML
new_slide = base.slides.add_slide(base.slide_layouts[6]) # blank layout
# Copy XML content
new_slide._element.getparent().replace(new_slide._element, slide_xml)
# We need to re-register the slide relationship
# Actually add_slide appends — track the new slide id
inserted_ids.append(base.slides._sldIdLst[-1])
# Now reorder: move inserted slides from end to position after_index+1
# Current order: [0..11 original] [12,13,14 inserted]
# Target: [0..3] [inserted 3] [4..11 original]
all_ids = list(base_sldIdLst)
n_orig = len(all_ids) - len(inserted_ids)
orig_ids = all_ids[:n_orig]
new_ids = all_ids[n_orig:]
reordered = orig_ids[:after_index+1] + new_ids + orig_ids[after_index+1:]
# Clear and repopulate sldIdLst
for el in list(base_sldIdLst):
base_sldIdLst.remove(el)
for el in reordered:
base_sldIdLst.append(el)
base.save(output_path)
print(f"Saved: {output_path} ({len(reordered)} slides)")
merge_pptx(
"/home/daytona/workspace/msics-ppt/MSICS_Surgery.pptx",
"/home/daytona/workspace/msics-ppt/SidePort_Slides.pptx",
after_index=3, # insert after slide 4 (0-indexed: 3)
output_path="/home/daytona/workspace/msics-ppt/MSICS_Surgery_Full.pptx"
)
cd /home/daytona/workspace/msics-ppt && python3 merge.py 2>&1
pip install --user python-pptx -q 2>&1 | tail -3
cd /home/daytona/workspace/msics-ppt && python3 merge.py 2>&1
~/msics-ppt/merge2.py
"""
Low-level PPTX merge: insert slides from SidePort_Slides.pptx into MSICS_Surgery.pptx
after slide 4 (index 3), by directly manipulating the zip contents.
"""
import zipfile, shutil, os, re, copy
from lxml import etree
BASE = "/home/daytona/workspace/msics-ppt/MSICS_Surgery.pptx"
INSERT = "/home/daytona/workspace/msics-ppt/SidePort_Slides.pptx"
OUTPUT = "/home/daytona/workspace/msics-ppt/MSICS_Surgery_Full.pptx"
AFTER = 4 # insert after slide number 4 (1-indexed)
NSMAP = {
'p': 'http://schemas.openxmlformats.org/presentationml/2006/main',
'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
}
REL_SLIDE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide'
def read_zip(path):
zf = zipfile.ZipFile(path, 'r')
contents = {name: zf.read(name) for name in zf.namelist()}
zf.close()
return contents
def write_zip(path, contents):
with zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED) as zf:
for name, data in contents.items():
zf.writestr(name, data)
def get_slide_rels(contents, slide_num):
"""Return list of (rId, target, type) for a slide."""
rel_path = f"ppt/slides/_rels/slide{slide_num}.xml.rels"
if rel_path not in contents:
return []
root = etree.fromstring(contents[rel_path])
rels = []
for r in root:
rels.append((r.get('Id'), r.get('Target'), r.get('Type')))
return rels
def get_presentation_slide_order(contents):
"""Return list of slide filenames in presentation order."""
prs_rels = etree.fromstring(contents['ppt/_rels/presentation.xml.rels'])
prs_xml = etree.fromstring(contents['ppt/presentation.xml'])
# Build rId -> target map
rid_to_target = {}
for r in prs_rels:
if r.get('Type', '').endswith('/slide'):
rid_to_target[r.get('Id')] = r.get('Target')
# Get ordered list from sldIdLst
sldIdLst = prs_xml.find('.//{%s}sldIdLst' % NSMAP['p'])
order = []
for sldId in sldIdLst:
rid = sldId.get('{%s}id' % NSMAP['r'])
if rid in rid_to_target:
target = rid_to_target[rid]
# target is like '../slides/slide1.xml' or 'slides/slide1.xml'
fname = target.replace('../', 'ppt/')
order.append(fname)
return order
# ── Read both zips ────────────────────────────────────────────────────────────
base_contents = read_zip(BASE)
insert_contents = read_zip(INSERT)
# ── Find next free slide number in base ──────────────────────────────────────
existing_slides = [k for k in base_contents if re.match(r'ppt/slides/slide\d+\.xml$', k)]
max_slide_num = max(int(re.search(r'slide(\d+)', s).group(1)) for s in existing_slides)
# ── Find slides in insert file ────────────────────────────────────────────────
insert_slides = sorted(
[k for k in insert_contents if re.match(r'ppt/slides/slide\d+\.xml$', k)],
key=lambda x: int(re.search(r'slide(\d+)', x).group(1))
)
print(f"Base slides: {len(existing_slides)}, Insert slides: {len(insert_slides)}")
# ── Copy insert slide files + their rels + media into base contents ───────────
num_map = {} # old insert slide num -> new slide num in base
for i, slide_path in enumerate(insert_slides):
old_num = int(re.search(r'slide(\d+)', slide_path).group(1))
new_num = max_slide_num + 1 + i
num_map[old_num] = new_num
# Copy slide XML
new_slide_path = f'ppt/slides/slide{new_num}.xml'
base_contents[new_slide_path] = insert_contents[slide_path]
# Copy slide rels XML (renaming media references)
old_rel_path = f'ppt/slides/_rels/slide{old_num}.xml.rels'
new_rel_path = f'ppt/slides/_rels/slide{new_num}.xml.rels'
if old_rel_path in insert_contents:
base_contents[new_rel_path] = insert_contents[old_rel_path]
print(f" slide{old_num} -> slide{new_num}")
# ── Copy media from insert into base ─────────────────────────────────────────
for name, data in insert_contents.items():
if name.startswith('ppt/media/'):
if name not in base_contents:
base_contents[name] = data
# ── Update presentation.xml and its rels to include new slides ───────────────
prs_rels_root = etree.fromstring(base_contents['ppt/_rels/presentation.xml.rels'])
prs_xml_root = etree.fromstring(base_contents['ppt/presentation.xml'])
# Find max rId in presentation rels
existing_rids = [int(r.get('Id','r0').replace('rId','')) for r in prs_rels_root if r.get('Id','').startswith('rId')]
max_rid = max(existing_rids) if existing_rids else 0
# Find max sldId in sldIdLst
sldIdLst = prs_xml_root.find('.//{%s}sldIdLst' % NSMAP['p'])
existing_sldids = [int(s.get('id', 256)) for s in sldIdLst]
max_sldid = max(existing_sldids) if existing_sldids else 255
# Get current ordered slide rIds from sldIdLst (to know which rId = which position)
prs_rels_map = {} # target -> rId
for r in prs_rels_root:
if r.get('Type','').endswith('/slide'):
t = r.get('Target','').replace('../slides/', 'ppt/slides/')
prs_rels_map[t] = r.get('Id')
# Build new rIds and sldId elements for the inserted slides
new_slide_elements = [] # (new_num, new_rId, new_sldId_val)
for i, (old_num, new_num) in enumerate(num_map.items()):
new_rid = f'rId{max_rid + 1 + i}'
new_sldid = max_sldid + 1 + i
new_slide_elements.append((new_num, new_rid, new_sldid))
# Add to rels
rel_el = etree.SubElement(prs_rels_root, 'Relationship')
rel_el.set('Id', new_rid)
rel_el.set('Type', REL_SLIDE)
rel_el.set('Target', f'../slides/slide{new_num}.xml')
# Current sldId list as ordered list of elements
current_sldid_els = list(sldIdLst)
# Build reordered list: insert new slides after position AFTER (1-indexed -> index AFTER)
insert_idx = AFTER # insert after slide AFTER (0-indexed: AFTER)
# Find the rId for each existing slide by position
def find_rid_for_position(pos_0indexed):
"""pos_0indexed: 0-based index in current_sldid_els"""
el = current_sldid_els[pos_0indexed]
return el.get('{%s}id' % NSMAP['r'])
reordered_els = current_sldid_els[:insert_idx]
# New sldId elements to insert
ns_p = NSMAP['p']
ns_r = NSMAP['r']
for new_num, new_rid, new_sldid_val in new_slide_elements:
el = etree.Element('{%s}sldId' % ns_p)
el.set('id', str(new_sldid_val))
el.set('{%s}id' % ns_r, new_rid)
reordered_els.append(el)
reordered_els += current_sldid_els[insert_idx:]
# Replace sldIdLst contents
for child in list(sldIdLst):
sldIdLst.remove(child)
for el in reordered_els:
sldIdLst.append(el)
# Save updated XMLs
base_contents['ppt/_rels/presentation.xml.rels'] = etree.tostring(prs_rels_root, xml_declaration=True, encoding='UTF-8', standalone=True)
base_contents['ppt/presentation.xml'] = etree.tostring(prs_xml_root, xml_declaration=True, encoding='UTF-8', standalone=True)
# ── Write output ──────────────────────────────────────────────────────────────
write_zip(OUTPUT, base_contents)
print(f"\nDone! Total slides: {len(reordered_els)}")
print(f"Output: {OUTPUT}")
cd /home/daytona/workspace/msics-ppt && python3 merge2.py 2>&1
cd /home/daytona/workspace/msics-ppt && node -e " const pptxgen = require('pptxgenjs'); const {execSync} = require('child_process'); // fetch all 10 images at once const urls = [ 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_39fd2c06d07d8a35e399e887d6b117e45796d7c548ae79cb50e227f8d1f107c6.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e786ca87c47a91e47c81ca4905dfe83e85ef1ea39135e082775fec07d450f248.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_217dfc19f8ad0dff37f579527bb2599ad43535590fa51c05efe7abbc4256e480.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_ef37296fbd9b17f557d1fcb0cb3dfd3422da9c96bbf03b5af62361ebef8017ee.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_d869b7a97961e9261fecaf818ef925efb95a6228836b24d3ba5615693fc5dcac.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_86899785e985375b9e09010dabc3717f7e737921b06d9bb77e54fb7f3f00a6db.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_b814788b80435b88497e1f8d3cfa94f633c8239e386da3dc4d4910b1a87e87cf.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2a3c9bfc869a71e4c8274b3aa9926cb23e22d723d597d5feb1c7b0f749422781.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2a8915c4d1aae26de535e4dcceac00ae82e7269e26e6c60cf49c3f0bb25af2d3.jpg', 'https://cdn.orris.care/cdss_images/pmc_clinical_VQA_340cfa24942acd21d70352337071719094c91f6f651a48bd57b5d14d2434d704.jpg', ]; const raw = execSync('node /home/daytona/skills/shared/scripts/fetch_images.js ' + urls.map(u=>'\"'+u+'\"').join(' ')).toString(); const imgs = JSON.parse(raw); imgs.forEach((x,i)=>console.log(i, x.error?'ERR:'+x.error:'OK len='+x.base64.length)); " 2>/dev/null
Image processing blocked due to content policy violation.
We encountered an error while processing your request. Please try again or reach out to us for help.