Architecture

August 31, 2026 ยท View on GitHub

pptwise turns semantic input into native editable PowerPoint through one causal chain:

intent -> narrative -> theme -> spec -> fill -> render

The first two steps are editorial decisions. The theme is selected before the spec because its menu determines which semantic page moves the deck can ask for. The spec orders those moves. Page files fill them with typed components. Render remains deterministic for the same bound inputs.

Owning domains

domainownerresponsibility
IRsrc/irStrict v5 deck, page, asset, metadata, brand, and 39-component schemas.
narrativesrc/narrativeStrategy, pacing, audience, presets, body baseline, and editorial budgets.
themessrc/themesPublic v2 schema, 24 presets, built-in declarations, workspace registration, tokens, occasions, and identity.
specssrc/specVersion 1 theme binding, page semantics, menu-kind validation, placeholders, and pure assembly.
facessrc/layoutsInternal page drawing code, slots, capacities, parameter declarations, and structural motif or brand facts.
componentssrc/componentsTyped content renderers that fill face slots.
motifssrc/motifsReusable decorative drawing code selected by theme menus.
rendersrc/renderTheme-menu resolution, context construction, SVG composition, branding, image routes, and static markup.
auditsrc/auditDeterministic SVG, browser, package, and optional pixel checks.
PPTXsrc/pptxSVG to native DrawingML conversion, PptxGenJS packaging, and ZIP-level patches.
CLIsrc/cli.ts, src/cliFilesystem boundary, theme lookup, commands, review server, image providers, and install operations.
platformsrc/platformRegistry seam for browser services. Node installs linkedom and sharp implementations.

The public model has four nouns: theme, spec, component, and kind. Internal faces are engine parts reached only through a theme menu.

Theme and spec boundary

A public theme file is complete and self-contained:

theme v2 = style + optional brand + occasions + identity + menu

The menu maps every boundary page and a non-empty subset of the eleven content kinds to one face each. It may also supply declared face parameters, motif posture, and page-level brand silence.

Theme creation is copy-based. theme new copies any resolved theme. theme fork copies a theme, preserves the menu, rederives the palette, and checks contrast. brand extract copies a donor menu and applies locally extracted Office anchors.

The CLI resolves a bound name from the deck directory, then upward workspace themes/ directories, then factory presets. Render has no theme override path.

The spec locks theme, narrative, page order, id, type, heading, and content kind. Page fills carry only components, background, image side, footnote, and notes. assembleDeck is a pure function that combines them into IR v5 without storing a rendering decision.

Shared face resolution

src/render/layout-selection.ts retains a historical filename, but its active job is direct menu lookup. The route is:

boundary page type -> theme.menu boundary entry -> face
content page kind  -> theme.menu.content[kind] -> face

Cover and chapter asset backgrounds take the dedicated image-cover route. Content image routes are still selected by content kind through the menu.

One resolved route record is consumed by:

  • spec and IR validation
  • slot compatibility checks
  • physical body-capacity calculation
  • rendering
  • asset briefs

This shared route is a parity invariant. Do not recreate menu lookup in a second consumer.

Pacing contributes a separate editorial capacity and body baseline. The effective component limit is the smaller of pacing budget and face capacity. Pacing never changes the chosen face.

Render chain

IR v5
  -> validate schema, theme, menu, components, assets, narrative, and capacity
  -> resolve theme tokens and one face per page
  -> build component context
  -> compose background, motif, local decor, face body, and branding in React SVG
  -> renderToStaticMarkup
  -> svg2pptx native shapes and text
  -> PptxGenJS package
  -> JSZip animation and gradient patches
  -> package audit
  -> .pptx

The SVG page is the single drawing source for preview and export. Shared text measurement, ink selection, and audit annotations keep browser review close to exported DrawingML.

Decoration and branding

src/render/full-slide-svg.tsx resolves decoration before composing the page:

  1. A face with suppressMotif: true is structurally silent.
  2. Otherwise a menu entry may request decor.kind: "silent" or another registered motif.
  3. Without a menu override, the built-in theme's ordinary motif is used.

The face fact wins over the menu. A copied public preset has ordinary motif choices written into its menu entries, so it needs no hidden inheritance.

Branding is independent. A face with branding: "none" or a menu entry with brand: "none" suppresses the shared fragment. Otherwise src/render/branding.tsx applies the deck's full, cover-only, or minimal posture. Omission equals cover-only.

Strict boundaries

src/index.ts and its dependency closure must remain free of Node-only dependencies. Filesystem, Commander, linkedom, sharp, and other Node services belong under src/cli or the Node platform installer.

The alias @/* maps to src/* in both TypeScript and Vitest configuration. Change both declarations together.

Source files stay grouped by business domain. Adding a component means its IR schema, renderer, tests, and related helpers live together. Adding a theme means one complete declaration or v2 file, menu, validation coverage, and visual evidence.

Extension paths

Add a component

Define its schema under src/ir/components, register it in the IR union, implement the renderer under src/components, declare its normal kind ownership, and add component, export, capacity, and audit tests. A component addition does not automatically add a new kind.

Add or change a face

Implement it under src/layouts, declare slots, capacity, parameters, and structural motif or brand facts, register it, then reference it from one or more theme menus. Validate every menu parameter against the face declaration.

Add a theme

Start with a complete copied theme, establish style and menu together, validate every offered kind, run the full matrix and gallery review, and compare it through theme try. A palette variation is another independent complete theme.

Change exported XML

Run the normal acceptance gate, end-to-end export, package audit, LibreOffice probe, and the PowerPoint repair-dialog probe described in Testing.