Skip to content

feat(pptx): layout-instantiation hardening, diagram primitive, crop/radius + non-16:9 diagnostics#90

Merged
karthikmudunuri merged 3 commits into
mainfrom
karthikmudunuri/slidewise-layout-instantiation
Jun 10, 2026
Merged

feat(pptx): layout-instantiation hardening, diagram primitive, crop/radius + non-16:9 diagnostics#90
karthikmudunuri merged 3 commits into
mainfrom
karthikmudunuri/slidewise-layout-instantiation

Conversation

@karthikmudunuri

Copy link
Copy Markdown
Member

Forward-looking package work for host deck generation (Plan B, P1–P4). Each item ships with a changeset; the suite is green (141 passed / 9 skipped), tsc clean, build:lib succeeds.

P1 — finish layout instantiation (addSlideFromLayout) ⭐

  • Bug fix: a layout-instantiated slide with the default transparent background was serializing an explicit <p:bg><a:noFill/>, which overrides layout/master/theme inheritance in PowerPoint — so instantiated slides rendered with no background. They now stay <p:bg>-less and paint from their sourceLayoutId layout's chrome.
  • Layout selection metadata: DeckLayout.type carries the raw OOXML <p:sldLayout type>; new summarizeLayouts(deck) returns a compact, model-context-friendly menu (friendly role, fillable fills keys, per-placeholder kind/category/geometry); placeholderKey(ph) exposes a slot's fills key.

P2 — non-16:9 chrome diagnostics

Sizing for 4:3 / 16:10 / custom already drives the output sldSz (#86). This adds the machine-readable signal the plan asked for: serializeDeck(deck, { source, onWarning }) emits a structured SerializeWarning ("chrome-skipped" when a source size is unreadable and chrome falls back to generic; "element-write-failed" per element) instead of only a console line.

P3 — first-class diagram primitive

New DiagramElement (process / timeline / funnel / matrix / cycle / list) models labelled nodes. A single shared layoutDiagram feeds both the renderer (DiagramView) and the writer (synthesiseDiagram), so preview and export can't drift. Exports to a single labelled <p:grpSp> of real shapes + connectors — grouped and editable in PowerPoint instead of anonymous loose shapes. Round-trips to an editable GroupElement carrying the labels.

P4 — round-trip fidelity

  • Image crop + radius: crop was parsed on import but dropped on export, and radius was unhandled. Images carrying either now route through a dedicated <p:pic> synth writer (emitting <a:srcRect> + roundRect) — bypassing pptxgenjs, whose cover/contain sizing emits its own conflicting <a:srcRect> — and radius is parsed back. Plain images keep the old path.
  • Text-run cap: all-caps / small-caps (<a:rPr cap>) was parsed but dropped (pptxgenjs has no cap option); now re-applied per run in post-process.

Deliberately not built

renderDeckToImages (headless PNG export) — needs a DOM/canvas + ECharts runtime, so it can't be a pure package function without pulling a browser dep into the lib. buildChartOption and the new layoutDiagram already let a host render server-side without re-implementing the package's logic; recommend it stay host-side or ship as a separate optional render entrypoint.

Tests

New / extended: instantiable-layouts (chrome round-trip + summarizeLayouts), non-16:9 (16:10 + chrome-skipped warning), diagram (layout unit + serializer + renderer smoke), crop-radius-runs (image crop/radius + rich-run + cap round-trip).

…radius + non-16:9 diagnostics

P1 — layout instantiation hardening:
- Fix: layout-instantiated slides now inherit their layout background (no
  spurious <a:noFill/> overriding master/theme chrome).
- Add DeckLayout.type + summarizeLayouts()/placeholderKey() for model-facing
  layout selection.

P2 — non-16:9 chrome diagnostics:
- SerializeOptions.onWarning surfaces a machine-readable "chrome-skipped"
  warning when a source template's size is unreadable; 16:10 round-trip test.

P3 — first-class DiagramElement:
- process/timeline/funnel/matrix/cycle/list modelled as labelled nodes; shared
  layoutDiagram feeds renderer + a grouped, editable <p:grpSp> writer.

P4 — round-trip fidelity:
- Image crop (<a:srcRect>) + corner radius (roundRect) now round-trip via a
  dedicated <p:pic> synth writer + radius parse.
- Text-run letter-case (cap) re-applied on export (pptxgenjs has no cap option).
- summarizeLayouts(deck, { compact, dedupe }) for 85-layout templates: compact
  drops geometry; dedupe collapses same role+fillable into aliases.
- sourceLayoutId resolves from deck.layouts OR ppt/slideLayouts/<id>.xml in the
  source archive; layout-unresolved onWarning when neither resolves.
- chrome-skipped warning carries sourceAspect/outputAspect.
- README: author-a-slide contract, non-text slot recipe, layoutDiagram
  server-render recipe + DOM-free guarantee.
- Tests: compact/dedupe, filled-text round-trip, host-authored image slot,
  by-convention + unresolved layout resolution, multi-master instantiation,
  enriched chrome-skipped payload.
summarizeLayouts({dedupe}) keyed off role + text-fillable keys only, so a
chart-bearing layout could collapse into an otherwise-identical text-only twin
and the host would lose the data-visual variant. Key off the full placeholder
inventory (category:key over text AND chart/picture/table slots) instead.
Dedupe now runs on full summaries before compacting, so compact+dedupe is safe.
@karthikmudunuri karthikmudunuri merged commit 3e7c3f1 into main Jun 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant