# OPF 0.18: the consistent API

Approved by the owner on 2026-10-09. 0.18 is one breaking lockstep 0.x minor (core, renderer, PPTX, editor and CLI):
one name for each thing, one import for every runtime, and logos that live on the organization. It ships without
aliases or shims, as the pre-v1 rule of the format audit allows. It opens after 0.17 has shipped and the three sites
have adopted it. Items RR-70 to RR-75 in the [burndown](burndown.md) track it.

## 1. Naming (RR-73: core, renderer, editor, CLI)

| 0.17 | 0.18 |
| --- | --- |
| `importData`, `opf import-data` | `ingest`, `opf ingest` |
| `renderSvg`, `renderSlideSvg` | `toSvg(deck, slides?, options?)` |
| `renderDeckHtml` | `toHtml(deck)` |
| `svgToPdf`, `svgToPng` | removed: `toPdf(deck)` and `toPng(deck)` draw the SVG themselves and also accept SVG strings |
| the editor's `exportDeck` | `convert(deck, { format })`: the same name and result as core's in-memory `convert` |

The rule: `convert` works on a whole file. An engine is `toX(deck, slides?, options?)` or `fromX(bytes)`. The second
argument is either a slide selection (`3`, `"1-3"`) or the options object, told apart by type. Slide numbers count
from 1.

## 2. One import for every runtime (RR-70: core)

- The `@openpresentation/opf/node` subpath is removed. `import * as opf from "@openpresentation/opf"` works everywhere
  through conditional exports:
  - `node` (and Bun and Deno) gets the full build, with `open`, `save` and `convert`;
  - `browser`, `worker` and `default` get a browser-safe build with the same names, where the file functions throw a
    clear "Node only" error.
- There is one type surface. `check:browser-safe` checks the browser build.
- The CLI-internal `/node/engine` moves behind the `node` condition or an internal entry.
- The docs mention the optional alias `npm i opf@npm:@openpresentation/opf`.

## 3. Logos and header/footer zones (RR-71 and RR-72: core schema, `resolveLogo`, furniture, renderer, PPTX, editor)

- **Logos live on the organization.** `organizations[].logo` has `full`, `stacked`, `icon` and `wordmark`.
  - Each one is a path or an asset, or `{ onLight, onDark }`.
  - A single string is one logo for every shape.
  - A missing shape falls back (`icon` to `full`, and so on).
- **Placement goes through the variable system.** `image: "var:organization.logo.icon"`, and likewise `.wordmark` and
  `.stacked`. A bare `var:organization.logo` is the full logo. A partner's logo is
  `var:organization.<id>.logo.icon`. `onLight` or `onDark` is chosen per slide from the background.
- **Removed:**
  - the 12-field `LogoSet` on `design.logo`;
  - `logo: true` in zones.

  `design.logo` becomes a variable override (`"var:organization.beta.logo"`) or `false`. Covers and sections draw the
  primary organization's full logo.
- **Zones** lay their parts out in a row by default (image, text, socials, date). `\n` still breaks text lines. There
  is no `arrange` field.
- **Docs** show plain file paths first; `asset:<id>` is optional.
- **PowerPoint (RR-72).**
  - Keep RR-11's native `ftr`, `dt` and `sldNum` placeholders, the master and layout placeholders, and `p:hf`.
  - Furniture that has no PowerPoint placeholder (logo, images, socials, header text) and is the same on every slide
    that shows furniture is written **once, on the slide master or layout**, not on each slide.
  - Slides that hide the footer use a layout without it.
  - A part that differs per slide uses its own layout, or a per-slide shape with a diagnostic.
  - Acceptance:
    - a native Windows PowerPoint pass shows the logo on the master;
    - Insert > Header & Footer still works;
    - `fromPptx` reads the master logo back as `var:organization.logo.*`.

## 4. `convert` options (RR-74)

| 0.17 | 0.18 |
| --- | --- |
| `chartex` | `charts: "auto" \| "native" \| "picture"` |
| `imageFormat` | `images` |
| `pdfMode` | `raster: true` |
| `svgFonts`, the renderer's `textAsPaths` | `text: "fonts" \| "system" \| "paths"` |
| `fonts`, `fontDirs` | one `fonts`: a handle, or folders |
| `filename` | `name` |
| `zip` | only for in-memory output |

## 5. CLI (RR-75, from the developer-experience audit)

- `opf convert` absorbs `render`, `export`, `import`, `from-md`/`to-md` and `from-yaml`/`to-yaml`.
- One output convention for every command:
  - an optional positional output (stdout by default);
  - `-i`/`--in-place`;
  - `--check`.
- Flag meanings are fixed:
  - `--format` is only the report format;
  - `--from` and `--to` name document formats;
  - `--data-format` names the tabular input.
- Core's Node build gets the CLI's verbs (`stats`, `paginate`, `embed`, `fill`, `edit`, `diff`, `merge`), with the
  CLI's defaults, including the default catalog.
- Additive changes:
  - an exact install line, and `opf doctor`;
  - a multi-file `opf validate` with `--format github`;
  - errors that state the fix, with "did you mean" suggestions;
  - help per command;
  - one report envelope.
- No-op flags such as `--json` are removed.
- From the walkthrough's CLI audit, RR-75 also covers:
  - `opf paginate` without fonts reports `layout: "estimated"` and points to the measured path;
  - `from-md` on an outline with no `---` splits by default (`split: "auto"`);
  - stdin with no `--from` sniffs `{` or `[` as JSON, and otherwise asks for `--from yaml|md`;
  - `opf create --example <slug>` from core's bundled examples, and `opf catalog examples`;
  - install hints that match the package manager in use (`npm_config_user_agent`);
  - no `agentSkills` in `create` reports, and no `dryRun: false` on commands that have no dry run;
  - small fixes:
    - the dead `config` entry in `valueOptions`;
    - the blank line in an empty `validate --format text` report;
    - `paginate` printing its fence warning before the "Output already exists" error.

## Order

Each item lands core first, with `Depends-On` on its sibling PRs (the coordinated-release rules in
[the release process](../../release-process.md)):

1. Core: RR-70, then the RR-71 schema and furniture.
2. The renderer and PPTX: RR-73 and RR-71, then RR-72.
3. The editor.
4. The CLI: RR-75 (RR-74 lands with each engine's rename).

The docs, skills, the developer walkthrough and the three sites move to 0.18 when they adopt it.

## Not in 0.18

- **0.19, adaptive layouts.** This is catalog work in pptx.gallery:
  - repeatable placeholders;
  - variants as design settings;
  - composition that adapts to the content;
  - a single list that flows into columns;
  - intent-named layouts in place of the roughly 230 `list-2x`-style records.
- **Parked:**
  - ODP (opf#505);
  - Marp and Slidev import (opf#506);
  - `opf dev` with HTML export;
  - math with OMML;
  - Mermaid;
  - `opf mcp`;
  - a VS Code extension;
  - presenter upgrades;
  - Markdown conveniences;
  - motion v2;
  - a catalog convention;
  - icons;
  - `opf.config.json`;
  - `fromPdf` (pptx.dev).
- **Not pursued:**
  - auto-loading a `fonts/` folder;
  - live code, recording, tunnels and PlantUML;
  - CSS or Vue themes, and dialect plugins;
  - `toSlidev`, `fromKeynote`, `fromGoogleSlides` and `fromReveal`;
  - new packages or aliases.
