Problem
open-slide can export to static HTML and print-to-PDF, but there's no PowerPoint export. For audiences and orgs that live in PowerPoint / Keynote / Google Slides, a deck currently has to be hand-rebuilt before it can be handed off or co-edited.
The usual shortcut — screenshot each slide into a .pptx — is what most AI slide tools do, and it produces a deck where every slide is one flat image: you can't fix a typo, re-theme, or nudge a box. There's no way today to hand someone a .pptx where each block (text, image, shape) is an independent, editable object, the way a hand-built deck is.
Proposed solution
Add an "Export as PPTX" item to the existing Download menu (next to HTML/PDF), fully client-side, that turns each page into a real PowerPoint slide where every block is a native object:
- text → native text boxes (per-run bold/italic/color/size/letter-spacing;
<em>/<strong> become their own runs)
<img> → native pictures (embedded as base64)
- filled / bordered / rounded boxes → native autoshapes
<table> → native tables
- linear gradients → native vector gradient fills (recolorable in PowerPoint)
- only un-mappable decorative graphics (inline SVG, blurred/blended overlays) fall back to a rasterized image of just that minimal subtree — never the whole slide
Mechanics: mount each page offscreen at the fixed 1920×1080 canvas (reusing the existing PDF-export lifecycle — wait for fonts + animations to settle), walk the DOM, read geometry/style via getBoundingClientRect / getComputedStyle, and emit one PptxGenJS object per node. 1920px @ 96dpi maps exactly to a 20″×11.25″ 16:9 slide, so positioning is precise. Gradients are post-patched into the OOXML with the already-bundled fflate (PptxGenJS can't emit gradients itself).
API: exportSlideAsPptx(slide, slideId, onProgress?), exported from @open-slide/core, wired into the slide Download dropdown using the existing progress toast.
I've prototyped this end-to-end and verified it across the demo decks — happy to open a PR (will link here).
Alternatives considered
- Screenshot / PDF-rasterize each slide into a
.pptx — simplest, but produces non-editable flat images (the exact thing we want to avoid).
- python-pptx / server-side conversion — needs a backend; open-slide's export is fully client-side today.
- Re-implementing layout into pptx from scratch — unnecessary: pages already render at a fixed 1920×1080 with absolute pixels, so we can read the resolved geometry directly instead of re-deriving layout.
Which package would this affect?
@open-slide/core (runtime, inspector, present mode, CLI)
Additional context
Dependencies: PptxGenJS (the only mature, browser-capable generator of editable pptx; its only dependency is JSZip) + html-to-image (raster fallback). Both are lazy-imported via dynamic import() — exactly like fflate in export-html.ts — so they stay out of the main bundle. The gradient post-patch reuses the existing fflate dependency.
Verified (structural, by unzipping the OOXML; all open in PowerPoint without a repair prompt):
| Deck |
Native editable objects |
Images |
Notes |
| smoke test (controlled) |
100% |
0 |
gradient → vector gradFill |
| raycast-api (9 slides; gradients/SVG/images/blur) |
~88% |
brand logos / blur only |
165 native text runs |
| claude-code-intro (9 slides; full-page noise overlay) |
~93% |
1/slide (the noise overlay) |
rest native |
Known limitations (intentional / honest):
- Web fonts can't be embedded by PptxGenJS → substituted to system fonts (Aptos/Consolas/Georgia); text may re-wrap slightly.
- flex/grid is baked to absolute coordinates (editable, but not reflow-aware).
- radial/conic gradients fall back to a solid first stop (only linear is vectorized).
- inline SVG / blur / mix-blend decorative graphics are rasterized (minimal subtree, resolution-capped).
- transitions/animations are not exported — the final settled state is captured (same behaviour as the PDF export).
Problem
open-slide can export to static HTML and print-to-PDF, but there's no PowerPoint export. For audiences and orgs that live in PowerPoint / Keynote / Google Slides, a deck currently has to be hand-rebuilt before it can be handed off or co-edited.
The usual shortcut — screenshot each slide into a
.pptx— is what most AI slide tools do, and it produces a deck where every slide is one flat image: you can't fix a typo, re-theme, or nudge a box. There's no way today to hand someone a.pptxwhere each block (text, image, shape) is an independent, editable object, the way a hand-built deck is.Proposed solution
Add an "Export as PPTX" item to the existing Download menu (next to HTML/PDF), fully client-side, that turns each page into a real PowerPoint slide where every block is a native object:
<em>/<strong>become their own runs)<img>→ native pictures (embedded as base64)<table>→ native tablesMechanics: mount each page offscreen at the fixed 1920×1080 canvas (reusing the existing PDF-export lifecycle — wait for fonts + animations to settle), walk the DOM, read geometry/style via
getBoundingClientRect/getComputedStyle, and emit one PptxGenJS object per node. 1920px @ 96dpi maps exactly to a 20″×11.25″ 16:9 slide, so positioning is precise. Gradients are post-patched into the OOXML with the already-bundledfflate(PptxGenJS can't emit gradients itself).API:
exportSlideAsPptx(slide, slideId, onProgress?), exported from@open-slide/core, wired into the slide Download dropdown using the existing progress toast.I've prototyped this end-to-end and verified it across the demo decks — happy to open a PR (will link here).
Alternatives considered
.pptx— simplest, but produces non-editable flat images (the exact thing we want to avoid).Which package would this affect?
@open-slide/core (runtime, inspector, present mode, CLI)Additional context
Dependencies: PptxGenJS (the only mature, browser-capable generator of editable pptx; its only dependency is JSZip) + html-to-image (raster fallback). Both are lazy-imported via dynamic
import()— exactly likefflateinexport-html.ts— so they stay out of the main bundle. The gradient post-patch reuses the existingfflatedependency.Verified (structural, by unzipping the OOXML; all open in PowerPoint without a repair prompt):
Known limitations (intentional / honest):