Releases: textcortex/SlideWise
@textcortex/slidewise@1.21.1
Patch Changes
-
94e348b: fix(render): emit a valid SVG
<image>for image-fill backgroundsrenderDeckToSvgrendered a slide's image-fill background as a CSSbackground
shorthand inside an SVGfill="…"attribute —fill="center / cover no-repeat url("data:image…")". That is not valid SVG (a non-paint value plus nested
unescaped quotes); browsers tolerate it, but strict rasterisers
(@resvg/resvg-js, librsvg, batik) reject it, blocking a Chromium-free
parsePptx → renderDeckToSvg → resvg → PNGpath.The pptx importer stores image backgrounds as a CSS shorthand, but the
renderer's image-ref detection only inspected the start of the value, so the
shorthand fell through to thefillpath. The renderer now recognises a
url(...)anywhere in the value and emits a real<image>element
(preserveAspectRatio=sliceforcover,meetforcontain). A lock-in
test asserts every rendered slide is valid SVG a strict XML parser accepts,
with an image-background slide as the regression case.
@textcortex/slidewise@1.21.0
Minor Changes
-
652f804: feat(pptx): layout-instantiation in
applyEdits(lossless scale-with-variety)applyEditsnow supportssource: { layoutId, fills? }in aPlannedSlide—
instantiating a fresh slide from one of the template's own layouts inside
the lossless byte-patch path. Because the layout is already a part ofsource,
the new slide binds toppt/slideLayouts/<layoutId>.xml(inheriting theme /
master / background chrome) while every other part stays byte-identical. This
unlocks lossless and scale-with-variety in one deck: clone slides where you
want the exact thing, instantiate from layouts where you want variety.Each layout placeholder is materialised as an addressable, positioned element
with a deterministic id —layoutSlotElementId(layoutId, key)(exported) where
keyis theplaceholderKey/summarizeLayoutsslot key. Text/objslots
are populated fromfillsand editable viasetText; picture slots become a
<p:pic>with a transparent placeholder blip sosetImagecan repoint them;
chart/table/other slots expose their geometry so the host fills them with
addChart/addDiagram. Placeholder geometry is read EMU-native from the
layout (falling back to the matching master slot), so it stays correct without a
canvas-px round-trip. An unresolvablelayoutIdis surfaced viaonWarningand
the slide is skipped rather than shipped wrong. -
652f804: feat(render): headless
renderDeckToImages+deck.fontUsagefont transparencyHeadless render-to-image (visual-QA loop). New browser-free renderer that
draws what the editor draws — native charts (buildChartOption+ ECharts
SSR), diagrams (layoutDiagram), text/shapes/images/backgrounds in z-order —
not the OOXML raster fallbacks. No Playwright/Chromium/DOM.renderDeckToSvg(deck, opts?)→ one composed SVG per slide (ECharts is
loaded on demand, so it never bloats the editor bundle).renderDeckToImages(deck, opts?)/renderSlideToImage(deck, i, opts?)/
renderPptxToImages(bytes, opts?)→ raster bytes. Rasterisation is an
injected hook (opts.rasterizeSvg, e.g.@resvg/resvg-js); when omitted the
default tries a dynamic@resvg/resvg-jsimport and throws a clear error if
it isn't installed — so there's no hard native dependency.opts:slides(1-based subset),dpi(canvas scales bydpi/96),
format,maxWidth(thumbnail cap). Deterministic (no animation).
Enables the host's render → fresh-eyes inspect → targeted
applyEditsfix →
re-render cycle, rendering a finalapplyEditsoutput directly.Font transparency.
parsePptxnow stampsdeck.fontUsage: { family, embedded }[]— every font family the deck's text uses, flagged
whether the source PPTX actually embeds it (<p:embeddedFontLst>→ a real
ppt/fonts/*part) or merely references it (system-fallback risk on
viewers that don't ship the brand font). Hosts use it to warn at generation
time ("missing fonts for some ppts"). It's a read-only diagnostic, distinct from
deck.fonts(the embeddable payloads).
@textcortex/slidewise@1.20.0
Minor Changes
-
e56ddd2: feat(pptx):
applyEdits— lossless surgical-edit APIAdd
applyEdits(source, plan, options?): a patch on the original.pptxbytes
rather than a full re-serialize. The create flow can now emit anEditPlan
(subset/reorder/repeat of template slides, each with edits) and get back a valid
package where everything not named by an edit is byte-identical to the source —
masters, layouts, theme, embedded fonts,ppt/tags/*, notes, embeddings, and
any untouched element. This removes the lossy round-trip that produced the
custGeom/SVG-fallback/dangling-rel fidelity bugs and lets hosts drop their
defensive cleanup.serializeDeckstays for the live editor / from-scratch decks.Edits address elements by the same stable ids
parsePptxreturns; slides by
1-based template index. Supported ops:setText/clearText(preserve the
template box + run styling, or rebuild from supplied runs),setChartData
(repopulate a native chart in place — type/colours kept, caches and the
embeddedxlsxworkbook updated so Edit-Data still works),setTableData,
setImage,removeElement,addChart,addDiagram, plus per-slide
backgroundand decktitle. Removed slides and any parts that become
exclusive to them are reclaimed by a package-wide reachability sweep, then
dangling relationships and content-types are reconciled. Unresolved element ids
and unsupported layout-instantiation are surfaced viaonWarninginstead of
throwing.
@textcortex/slidewise@1.19.1
Patch Changes
-
305dc0f: fix(pptx): emit a structurally valid package on serialize
Three
serializeDeckbugs corrupted the generated.pptx(missing parts /
invalid image bytes) even from clean source templates, triggering a PowerPoint
repair prompt and outright rejection by stricter consumers (Google Slides,
LibreOffice, OOXML validators):- Dangling
tagsrelationships: the chrome-preserve path re-pointed a
slide's tag rel at aslidewise_preserved_*name, then clobbered that part by
re-copying the source tags under their original names. The rel now resolves
to the de-prefixed part it should always have pointed at. - Dangling
notesMasterrelationships: pptxgenjs writes a notesSlide per
slide linked to a notes master, which chrome preservation removed without a
source replacement. The orphaned (implicit, non-body-referenced) relationship
is now dropped. - SVG markup in
.pngraster fallbacks: dual SVG images (<a:blip>raster<asvg:svgBlip>vector) had the SVG source written into the.png
fallback. The fallback is now a real rasterized PNG (browser) or a valid
transparent PNG (SSR/Node); the vectorsvgBlippart is untouched.
Adds a final
reconcileDanglingRelsinvariant guard — every internal
relationship target must resolve to a shipped part — that backstops both
dangling-rel shapes (repairing recoverable targets, dropping only
safe-to-remove optional ones, and leaving critical rels untouched). Also runs
pruneDanglingContentTypeson the source-preservation path so stale
[Content_Types]overrides (pptxgenjs'sslideMaster1..N, leftover notes
overrides) can't invalidate the package either. - Dangling
@textcortex/slidewise@1.19.0
Minor Changes
-
3e7c3f1: First-class diagram element (P3 / F3). A new
DiagramElementmodels
process / timeline / funnel / matrix / cycle / list visuals as an ordered set
of labellednodesinstead of a hand-placed cluster of shapes and lines. The
renderer and the PPTX writer share one layout function (layoutDiagram, also
exported), so the on-canvas preview and the saved file can't drift. On export a
diagram serialises to a single labelled<p:grpSp>of real shapes + connectors
— so it stays grouped and editable in PowerPoint (move/resize as one unit)
rather than collapsing to anonymous floating shapes. Exposed via the
DiagramElement/DiagramNode/DiagramKindtypes and thelayoutDiagram
helper. -
3e7c3f1: Host deck-generation ergonomics (review follow-ups for layout instantiation):
summarizeLayouts(deck, options)—{ compact: true }returns the minimal
{ id, name?, role, fillable }shape (no geometry) for a tight model-context
budget;{ dedupe: true }collapses layouts that share a role + full
placeholder-slot inventory (text and non-text chart/picture/table slots)
into one representative carrying the rest asaliases, so an 85-layout
template surfaces as its handful of distinct kinds — and a chart-bearing
layout never collapses into a text-only twin. Composable.- Robust
sourceLayoutIdresolution — a host-authored slide's
sourceLayoutIdnow resolves fromdeck.layoutsor, when the host didn't
carry the layouts array, by theppt/slideLayouts/<id>.xmlid convention
against the{ source }archive. When it resolves to nothing,serializeDeck
emits a structured{ code: "layout-unresolved", slideIndex, layoutId }
warning instead of silently falling back. - Richer
chrome-skippedwarning — now carries the detectedsourceAspect
andoutputAspectso a host can explain why chrome was dropped.
Plus README: the host "author-a-slide" contract (
{ sourceLayoutId, background: "transparent", elements }without a JS call), the recipe for
placing non-text slots fromsummarizeLayoutsgeometry, and the server-side
layoutDiagramrender recipe with its DOM-free guarantee. -
3e7c3f1: Round-trip fidelity fixes (P4):
- Image
crop/radiusnow round-trip. Previously an image'scrop
(<a:srcRect>) was read on import but silently dropped on export, and corner
radiuswas neither parsed nor written.serializeDecknow routes any image
carrying acroporradiusthrough a dedicated<p:pic>writer (emitting
<a:srcRect>androundRectgeometry) instead of pptxgenjs — whose
cover/contain sizing emits its own<a:srcRect>and would fight a user crop —
andparsePptxreads a rounded picture's corner radius back. Plain
(uncropped, square-cornered) images keep the existing path unchanged. - Text-run letter-case (
cap) now round-trips. A run'scap
("all"/"small", OOXML<a:rPr cap>) was parsed on import but dropped on
export (pptxgenjs has nocapoption). It's now re-applied per run in
post-process, so all-caps / small-caps styling survives a save.
- Image
-
3e7c3f1: Harden layout instantiation for AI deck generation (P1 / F1):
- Layout-instantiated slides now inherit their layout's background. A slide minted by
addSlideFromLayoutwith the defaulttransparentbackground no longer serialises an explicit<a:noFill/><p:bg>— that empty background was overriding the layout/master/theme inheritance, so instantiated slides lost their on-brand background. They now stay<p:bg>-less and paint from theirsourceLayoutIdlayout's chrome (matching the source-slide guarantee for cloned/reordered slides). - Layout-selection metadata.
DeckLayout.typenow carries the raw OOXML<p:sldLayout type>role, and the newsummarizeLayouts(deck)returns a compact, model-context-friendly layout menu (friendlyrolelabel, fillablefillskeys, per-placeholder kind/category/geometry) so a host can have a model pick a layout per slide.placeholderKey(ph)exposes the exactfillskey for a placeholder.
- Layout-instantiated slides now inherit their layout's background. A slide minted by
-
3e7c3f1: Machine-readable serialization diagnostics (P2 / B3).
serializeDecknow
acceptsSerializeOptions.onWarning, a callback invoked with a structured
SerializeWarningwhen the output degrades. The key case is
"chrome-skipped"— emitted when asourcetemplate's masters / layouts /
theme / fonts can't be carried over because its slide size is unreadable, so
the deck falls back to generic chrome. Hosts can now detect and surface the
degradation instead of only seeing a console line. (Non-16:9 sizing for 4:3 /
16:10 / custom templates already drives the output slide size; this adds the
escape-hatch signal when it can't.)
@textcortex/slidewise@1.18.1
Patch Changes
-
f16cfa5: fix(pptx): respect z-order for synthesised content (charts, custGeom shapes, connectors)
Synthesised spTree content was always inserted at the back of the slide, so an
in-app chart / custGeom "SVG" / connector with a higher z than its background
card was buried behind that card (and its shadow) and rendered invisible. Each
synth item now anchors directly on top of the pptxgenjs node it sits above
(matched by shape name), preserving the deck's z-order, and only falls to the
back when it is genuinely below every model element.
@textcortex/slidewise@1.18.0
Minor Changes
-
17c069f: Template-faithful serialization fixes and new authoring primitives for AI deck generation:
- Per-slide source mapping —
Slide.sourceSlideIndexlets a host that clones / reorders / subsets imported slides declare which source slide each output slide replays its background and layout from, instead of mapping by output position (fixes blank/white-on-white slides on reordered decks). - Deep chart preservation — preserved charts now carry their full dependency tree (embedded workbook, colors/style parts, rels, content types), so they no longer trigger a PowerPoint repair and keep "Edit Data" + custom styling.
- Non-16:9 templates — a source's real slide size now drives the output (4:3, 16:10, custom), preserving its masters / layouts / theme / fonts and inverting the authoring-canvas fit for emitted elements, instead of silently falling back to a generic 16:9 deck.
- Instantiable layouts —
parsePptxexposes master layouts onDeck.layouts, and the newaddSlideFromLayout(deck, layoutId, opts)mints a fresh slide bound to a layout with its text placeholders ready to fill. - Chart-option helpers —
buildChartOption,defaultPaletteColor, andmakeValueFormatterare exported so hosts can build the exact ECharts options Slidewise renders (e.g. for server-side previews). - Connector primitive — a first-class
connectorelement (straight / bent / curved, arrowheads, flips) with renderer +<p:cxnSp>writer support.
- Per-slide source mapping —
@textcortex/slidewise@1.17.1
Patch Changes
-
83683e3: fix(pptx): render
<a:grpFill/>shapes by inheriting the group's fillDecorative line-art (e.g. the swoosh graphic on a title slide) is often
authored as many<a:custGeom>segments inside a single<p:grpSp>, with every
segment declaring<a:grpFill/>so they share the group's one translucent
colour.parsePptxhad nogrpFillbranch, so each segment fell through to
transparentand the entire graphic disappeared on import — present in the
source but invisible in the deck.The group's resolved fill is now threaded down to descendants, and a shape with
<a:grpFill/>paints with it (walking up the group chain when an inner group
defines no fill of its own). Geometry was already preserved; only the paint was
being dropped.
@textcortex/slidewise@1.17.0
Minor Changes
-
019e000: feat(pptx): support PowerPoint templates (.potx)
.potxand.pptxshare an identical OOXML package; only the main part's
content type in[Content_Types].xmldiffers. This adds first-class template
support across import and export:parsePptxalready parsed.potxtransparently (it reads parts by path, not
by content type) — now the rest of the pipeline preserves template-ness.- New exported
isPptxTemplate(blob)detects a template by inspecting the
package content type rather than trusting a filename extension (a mis-named
.pptxthat is really a template is detected correctly). serializeDeckgains anasTemplate?: booleanoption. When omitted,
template-ness is inherited from the source archive, so a parsed.potx
round-trips back to a.potx; passtrue/falseto force the output kind.
Templates are emitted with the…presentationml.template.main+xmlmain-part
content type and the.potxMIME type.
@textcortex/slidewise@1.16.4
Patch Changes
-
a085c8d: fix(pptx): import-fidelity fixes for think-cell / brand-template decks
- Skip shapes flagged
hidden="1"(e.g. think-cell "do not delete" data objects) - Render run-level text highlight (
<a:rPr><a:highlight>) end to end - Apply
cap="all"/"small"(including when inherited from a placeholder list style) as a render-time letter-case transform - Derive font weight from weight-named families ("Gilroy ExtraBold" → 800, "… Medium" → 500, …) so substitute fonts render at the right heaviness
- Tables: per-cell fills, text colours, per-side borders, proportional column widths / row heights, cell spans (
gridSpan/hMerge/rowSpan/vMerge), per-cell vertical anchor, and rich per-cell runs (highlight / bold / ✓ glyphs / bullet line breaks). Unfilled cells stay transparent instead of inheriting a sibling fill - Map Wingdings bullet glyphs to Unicode (
ü→✓,q→☐,§→▪, …) - Bullets: repeat a character bullet across in-paragraph line breaks, suppress the glyph on empty paragraphs, and trim trailing empty paragraphs
- Synthesise block-arrow paths (
down/up/left/rightArrow) and resolve outline colour from<p:style><a:lnRef>so dashed/outlined shapes draw - Keep a text-bearing preset or custom-geometry shape's fill, border, and corner radius behind its text (roundRect callouts, outlined chevrons)
- Honour
<a:bodyPr><a:spAutoFit>no-wrap for short single-line labels; skip the arrow-tip text inset on no-fill label shapes - Render per-paragraph hanging-indent bullets as one block per line so multi-line items align correctly
- Skip shapes flagged