@openpresentation/opf 0.22.1 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-LKMR3OLD.js → chunk-AHKRJSCF.js} +20 -3
- package/dist/{conversion-CSD65VLi.d.ts → conversion-C_MOkYGH.d.ts} +12 -0
- package/dist/{conversion-JDFXRXTK.js → conversion-LBQMY7LX.js} +1 -1
- package/dist/docs.js +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/node-engine.d.ts +2 -2
- package/dist/node-engine.js +2 -2
- package/dist/repo-readme.js +1 -1
- package/package.json +3 -3
|
@@ -118,6 +118,18 @@ function localAssetProblems(deck, root) {
|
|
|
118
118
|
var RENDER_PACKAGE = "@openpresentation/opf-render";
|
|
119
119
|
var PPTX_PACKAGE = "@openpresentation/opf-pptx";
|
|
120
120
|
var PEER_RANGES = { [RENDER_PACKAGE]: "^0.22.0", [PPTX_PACKAGE]: "^0.22.0" };
|
|
121
|
+
var PPTX_TEXT_OPTIONS_FROM = "0.22.0";
|
|
122
|
+
var numbers = (version) => version.replace(/[-+].*$/, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
123
|
+
function atLeast(version, minimum) {
|
|
124
|
+
const [a, b] = [numbers(version), numbers(minimum)];
|
|
125
|
+
for (let i = 0; i < 3; i++) if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
function requirePptxOptions(pptx, options, flags = false) {
|
|
129
|
+
const asked = [options.bodyText === "native" ? flags ? "--body-text native" : 'bodyText: "native"' : void 0, options.slideNumbers ? flags ? "--slide-numbers" : "slideNumbers" : void 0].filter(Boolean);
|
|
130
|
+
if (asked.length && pptx.version !== "unknown" && !atLeast(pptx.version, PPTX_TEXT_OPTIONS_FROM))
|
|
131
|
+
throw new OPFApiError(`${asked.join(" and ")} needs ${pptx.name}@${PPTX_TEXT_OPTIONS_FROM} or later; ${pptx.version} is installed and would ignore it.`, "peer-too-old", { details: { package: pptx.name } });
|
|
132
|
+
}
|
|
121
133
|
var RENDER_EXTRAS = {
|
|
122
134
|
/** The faces of the office pack, which the CLI and `convert` always load (every format). */
|
|
123
135
|
fonts: ["@expo-google-fonts/roboto@0.4.3", "@expo-google-fonts/roboto-mono@0.4.2", "@expo-google-fonts/caladea@0.4.2", "@expo-google-fonts/arimo@0.4.3", "@expo-google-fonts/tinos@0.4.2", "@expo-google-fonts/cousine@0.4.3", "@expo-google-fonts/gelasio@0.4.1", "@expo-google-fonts/noto-sans@0.4.2"],
|
|
@@ -618,9 +630,11 @@ function resolveExportOptions(options) {
|
|
|
618
630
|
const charts = oneOf("charts", options.charts, ["auto", "native", "picture"]);
|
|
619
631
|
const provenance = oneOf("provenance", options.provenance, ["full", "references-only", "none"]);
|
|
620
632
|
const images = oneOf("images", options.images, ["compatible", "preserve"]);
|
|
633
|
+
const bodyText = oneOf("bodyText", options.bodyText, ["measured", "native"]);
|
|
634
|
+
const slideNumbers = oneOf("slideNumbers", options.slideNumbers, ["static", "live"]);
|
|
621
635
|
const text = oneOf("text", options.text, EXPORT_TEXT);
|
|
622
636
|
if (options.raster && format !== "pdf") throw invalid("raster applies to format pdf.");
|
|
623
|
-
if ((charts !== void 0 || provenance !== void 0 || images !== void 0) && format !== "pptx") throw invalid("charts, provenance and
|
|
637
|
+
if ((charts !== void 0 || provenance !== void 0 || images !== void 0 || bodyText !== void 0 || slideNumbers !== void 0) && format !== "pptx") throw invalid("charts, provenance, images, bodyText and slideNumbers apply to format pptx.");
|
|
624
638
|
if (options.scale !== void 0 && format !== "png" && format !== "pdf") throw invalid("scale applies to format png (and raster pdf).");
|
|
625
639
|
if (options.includeHidden && format === "pptx") throw invalid("includeHidden applies to per-slide image and PDF output; the PPTX keeps a hidden slide as a hidden slide.");
|
|
626
640
|
if (options.slides !== void 0 && format === "pptx") throw invalid("slides is not available for pptx: the whole presentation is exported.");
|
|
@@ -705,6 +719,7 @@ async function drawWith(presentation, handle, options, ctx, environment) {
|
|
|
705
719
|
const run = (extra) => ({ fonts: fontSummary(), ...pagination ? { pagination } : {}, skippedHidden, ...extra });
|
|
706
720
|
try {
|
|
707
721
|
if (format === "pptx") {
|
|
722
|
+
requirePptxOptions(pptx, options, ctx.flags);
|
|
708
723
|
const module = pptx.module;
|
|
709
724
|
const bytes = await module.toPptx(deck, {
|
|
710
725
|
fonts: handle,
|
|
@@ -718,6 +733,8 @@ async function drawWith(presentation, handle, options, ctx, environment) {
|
|
|
718
733
|
...options.charts ? { chartex: options.charts === "picture" ? "fallback" : options.charts } : {},
|
|
719
734
|
...options.provenance ? { provenance: options.provenance === "none" ? false : options.provenance } : {},
|
|
720
735
|
...options.images ? { imageFormat: options.images } : {},
|
|
736
|
+
...options.bodyText ? { bodyText: options.bodyText } : {},
|
|
737
|
+
...options.slideNumbers ? { slideNumberFields: options.slideNumbers } : {},
|
|
721
738
|
...date ? { date } : {}
|
|
722
739
|
});
|
|
723
740
|
return run({ files: [{ name: `${stem}.pptx`, type: MEDIA2.pptx, bytes }], skippedHidden: [] });
|
|
@@ -898,7 +915,7 @@ function targetOf(output, options, flags) {
|
|
|
898
915
|
checkOutputPattern(output, exported === "png" || exported === "svg");
|
|
899
916
|
return exported ? { kind: "export", format: exported, zip: false } : { kind: "deck", format: deck };
|
|
900
917
|
}
|
|
901
|
-
var EXPORT_ONLY = ["slides", "includeHidden", "paginate", "scale", "raster", "text", "charts", "provenance", "images", "date", "fonts", "assetDir"];
|
|
918
|
+
var EXPORT_ONLY = ["slides", "includeHidden", "paginate", "scale", "raster", "text", "charts", "provenance", "images", "bodyText", "slideNumbers", "date", "fonts", "assetDir"];
|
|
902
919
|
var given = (value) => value !== void 0 && value !== false && !(Array.isArray(value) && value.length === 0);
|
|
903
920
|
function checkApplicable(source, target, options, flags) {
|
|
904
921
|
const name = (key) => flags ? `--${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}` : key;
|
|
@@ -911,7 +928,7 @@ function checkApplicable(source, target, options, flags) {
|
|
|
911
928
|
}
|
|
912
929
|
const format = target.format;
|
|
913
930
|
if (given(options.raster) && format !== "pdf") throw invalid2(`${name("raster")} applies to pdf output.`);
|
|
914
|
-
if ((options.charts !== void 0 || options.provenance !== void 0 || options.images !== void 0) && format !== "pptx") throw invalid2(`${names("charts", "provenance", "images")} apply to pptx output.`);
|
|
931
|
+
if ((options.charts !== void 0 || options.provenance !== void 0 || options.images !== void 0 || options.bodyText !== void 0 || options.slideNumbers !== void 0) && format !== "pptx") throw invalid2(`${names("charts", "provenance", "images", "bodyText", "slideNumbers")} apply to pptx output.`);
|
|
915
932
|
if (options.scale !== void 0 && format !== "png" && format !== "pdf") throw invalid2(`${name("scale")} applies to png output (and raster pdf).`);
|
|
916
933
|
if (given(options.includeHidden) && format === "pptx") throw invalid2(`${name("includeHidden")} applies to per-slide image and PDF output; the PPTX keeps a hidden slide as a hidden slide.`);
|
|
917
934
|
if (options.slides !== void 0 && format === "pptx") throw invalid2(`${name("slides")} is not available for pptx output: the whole presentation is exported.`);
|
|
@@ -221,6 +221,18 @@ interface ExportOptions {
|
|
|
221
221
|
provenance?: "full" | "references-only" | "none";
|
|
222
222
|
/** `pptx`: keep images as they are (`preserve`) or convert them for older PowerPoint (`compatible`). */
|
|
223
223
|
images?: "compatible" | "preserve";
|
|
224
|
+
/**
|
|
225
|
+
* `pptx`: `measured` (the default) writes each wrapped line as its own text box, so the text lays out exactly as the preview draws it.
|
|
226
|
+
* `native` opts in to whole paragraphs: a wrapped title is one Title placeholder and a text, list or bullets region one body, which
|
|
227
|
+
* PowerPoint and Keynote re-wrap when edited. What native mode cannot carry keeps measured text, with a `native-body-fallback` finding.
|
|
228
|
+
*/
|
|
229
|
+
bodyText?: "measured" | "native";
|
|
230
|
+
/**
|
|
231
|
+
* `pptx`: a footer or header text with words around the slide number (`{{slide.number}} / {{deck.slideCount}}`). `static` (opf-pptx's
|
|
232
|
+
* default) writes the number as fixed text, so Keynote shows the words too; `live` keeps the slide-number field, which Keynote
|
|
233
|
+
* draws as the digit alone. A text that is only `{{slide.number}}` stays a live field in both.
|
|
234
|
+
*/
|
|
235
|
+
slideNumbers?: "static" | "live";
|
|
224
236
|
/** `YYYY-MM-DD`, for date fields that show today's date. Nothing reads a clock: without it such a field reports `date-needs-value`. */
|
|
225
237
|
date?: string;
|
|
226
238
|
/** The directory a presentation's relative image paths resolve against. Omitted: no local file is read (data URIs and remote references work as the engines handle them). */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { convertFiles, deckFileFormat, exportFormatOf, openDeck, planConversion, readInput, saveDeck, writePlanned } from './chunk-
|
|
1
|
+
export { convertFiles, deckFileFormat, exportFormatOf, openDeck, planConversion, readInput, saveDeck, writePlanned } from './chunk-AHKRJSCF.js';
|
|
2
2
|
import './chunk-MG34DCDB.js';
|
|
3
3
|
import './chunk-MAJHCGOW.js';
|
|
4
4
|
import './chunk-UJSAR6GF.js';
|
package/dist/docs.js
CHANGED
|
@@ -4,7 +4,7 @@ var docsData = Object.freeze([
|
|
|
4
4
|
"slug": "agent-skills",
|
|
5
5
|
"file": "docs/agent-skills.md",
|
|
6
6
|
"title": "AI agent skills for OPF",
|
|
7
|
-
"markdown": "# AI agent skills for OPF\n\nThe repository ships six reusable skills in `skills/`. Each folder has a `SKILL.md` entrypoint and optional references, assets, or scripts. `agents/openai.yaml` supplies Codex display metadata; the instructions themselves are Markdown and do not require a hosted service.\n\n| Skill | Use it for |\n| --- | --- |\n| [opf-author](../skills/opf-author/SKILL.md) | Turn briefs and source material into valid OPF content; includes a complete starter deck |\n| [opf-layout](../skills/opf-layout/SKILL.md) | Dynamic composition, nested groups, promoted regions, overflow repair, and pagination |\n| [opf-presets](../skills/opf-presets/SKILL.md) | Catalog discovery, design inheritance, gallery reuse, colors, themes, and fonts |\n| [opf-edit](../skills/opf-edit/SKILL.md) | Precise JSON Patch edits, undo, canvas/schema integration, and copy/import |\n| [opf-export](../skills/opf-export/SKILL.md) | Browser previews, SVG/PNG/PDF/PPTX, assets, fonts, and export verification |\n| [opf-inspect](../skills/opf-inspect/SKILL.md) | Exact schema fields, catalog IDs, validation errors, and reference warnings |\n\nLoad only the skills relevant to the request. They distinguish the portable format from current renderer/editor capabilities, and distinguish imported document instructions from the user's request. They do not authorize publishing, sending decks, or changing unrelated project configuration.\n\n## Use from a checkout\n\nAn agent can read the entrypoint directly, for example:\n\n> Use `skills/opf-author/SKILL.md` to create a decision brief in OPF, then validate it using `skills/opf-inspect/SKILL.md`.\n\nThe root `AGENTS.md` points repository agents to these entrypoints. Skills read the current project's schema and package exports instead of hardcoding a historical field count or assuming a public package has unreleased APIs.\n\n## Install in an agent environment\n\nPublished CLI 0.20.0 bundles all six complete skill folders. Use Node 24 for the current packages and this checkout. From your project directory:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills install\n```\n\nThe default installs copies into `.agents/skills` in the current project, suitable for agents including Codex. It does not change AGENTS.md or any agent configuration. No symlink privileges, paid service, API key or AI provider is required. npm downloads the CLI on first use; the installed CLI then installs its bundled skills without network access. The explicit 0.20.0 pin makes this installation repeatable and matches the [current package train](compatibility-matrix.md). For CLI source development, use `node packages/cli/dist/index.js skills install` after building.\n\n| Target | Project directory | Personal directory with `--global` |\n| --- | --- | --- |\n| Default / `--agent universal` | `.agents/skills` | `~/.agents/skills` |\n| `--agent codex` | `.agents/skills` | `~/.codex/skills` |\n| `--agent claude-code` | `.claude/skills` | `~/.claude/skills` |\n| `--agent cursor` | `.cursor/skills` | `~/.cursor/skills` |\n\nFor example, `npx @openpresentation/cli@0.20.0 skills install --agent codex --global` installs personal Codex skills. For another compatible agent use `--directory <its-skills-directory>`; this option cannot be combined with `--agent` or `--global`. Restart or reload your agent if its skill discovery requires it. A compatible client can invoke the installed skills with names such as `$opf-author` or `$opf-inspect`.\n\nInspect or update the same destination:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills status\nnpx @openpresentation/cli@0.20.0 skills update\n```\n\nSupply the same target options used for installation. `status` is read-only and compares against the invoked CLI's bundled version; it does not query npm for newer releases. Repeated installation is idempotent. Updates check every installed file before changing any skill. Modified, added, deleted or unmanaged files cause the command to stop and list the conflicting folders; keep your customizations, move those folders outside the active skills directory, then retry. There is no force-overwrite option. A successful update returns backup paths outside the active skills directory for recovering the previous managed versions. Keep those backups until you have reviewed the update. Do not run concurrent writers: the installer lock coordinates other installer runs, but cannot lock an external editor.\n\nNo skills are installed by the repository build. Manual installation remains supported: copy whole folders from `skills/`, including references and scripts, to your agent's skill directory. Each folder is self-contained. The managed installer treats existing manual copies as unmanaged and preserves them.\n\nThe inspection helper requires Node 24 and `@openpresentation/opf` in the current project. In this checkout, build with `pnpm build` first. For an installed skill used outside the checkout, either run from an npm project that has the package or set `OPF_ROOT` to the built OPF checkout. It does not install dependencies, fetch catalogs, or modify input files.\n\n## Local CLI\n\nThe [installable CLI](../packages/cli/README.md) complements these skills with `opf create`, `opf validate`, `opf edit`, and schema/catalog lookup. It runs on the core it depends on; the inspection skill helper instead resolves the host project's core package. Check versions when moving between them. From code, core's `@openpresentation/opf` offers `convert`, `open` and `save` in Node (see the opf-export skill).\n\n## Examples of requests\n\n- \u201CUse $opf-author to turn these notes into a five-slide decision brief. Keep every factual claim sourced.\u201D\n- \u201CUse $opf-layout to fix this overflow without losing any text or notes.\u201D\n- \u201CUse $opf-presets to apply our brand colors while preserving slide-specific overrides.\u201D\n- \u201CUse $opf-edit to replace one table and retain all other document fields.\u201D\n- \u201CUse $opf-export to export the same reviewed slides to SVG and PPTX.\u201D (PPTX text is editable one line at a time; the skill says so.)\n- \u201CUse $opf-inspect to explain which background forms the installed schema accepts.\u201D\n\n## Maintenance\n\n`pnpm test:skills` checks skill links, schema-valid examples, and the inspection helper's actual behavior, including a copied standalone skill and a package installed in a consumer project. Run the skill-creator frontmatter validator when editing skill metadata. Behavioral tests are not evidence that every renderer option is visually complete.\n\nWhen schema/package APIs change, update only the affected skill/reference and its executable examples. Keep option lists in the canonical schema and catalogs. The format package and skill folders are separate distribution surfaces: CLI 0.20.0 includes the six skills; the core `@openpresentation/opf` package does not install agent configuration. Repository skill prose can be newer than the immutable snapshot bundled in an already-published CLI. These source documentation corrections do not change the CLI 0.20.0 tarball; `skills status` and `skills update` compare against the invoked CLI's bundled snapshot.\n"
|
|
7
|
+
"markdown": "# AI agent skills for OPF\n\nThe repository ships six reusable skills in `skills/`. Each folder has a `SKILL.md` entrypoint and optional references, assets, or scripts. `agents/openai.yaml` supplies Codex display metadata; the instructions themselves are Markdown and do not require a hosted service.\n\n| Skill | Use it for |\n| --- | --- |\n| [opf-author](../skills/opf-author/SKILL.md) | Turn briefs and source material into valid OPF content; includes a complete starter deck |\n| [opf-layout](../skills/opf-layout/SKILL.md) | Dynamic composition, nested groups, promoted regions, overflow repair, and pagination |\n| [opf-presets](../skills/opf-presets/SKILL.md) | Catalog discovery, design inheritance, gallery reuse, colors, themes, and fonts |\n| [opf-edit](../skills/opf-edit/SKILL.md) | Precise JSON Patch edits, undo, canvas/schema integration, and copy/import |\n| [opf-export](../skills/opf-export/SKILL.md) | Browser previews, SVG/PNG/PDF/PPTX, assets, fonts, and export verification |\n| [opf-inspect](../skills/opf-inspect/SKILL.md) | Exact schema fields, catalog IDs, validation errors, and reference warnings |\n\nLoad only the skills relevant to the request. They distinguish the portable format from current renderer/editor capabilities, and distinguish imported document instructions from the user's request. They do not authorize publishing, sending decks, or changing unrelated project configuration.\n\n## Use from a checkout\n\nAn agent can read the entrypoint directly, for example:\n\n> Use `skills/opf-author/SKILL.md` to create a decision brief in OPF, then validate it using `skills/opf-inspect/SKILL.md`.\n\nThe root `AGENTS.md` points repository agents to these entrypoints. Skills read the current project's schema and package exports instead of hardcoding a historical field count or assuming a public package has unreleased APIs.\n\n## Install in an agent environment\n\nPublished CLI 0.20.0 bundles all six complete skill folders. Use Node 24 for the current packages and this checkout. From your project directory:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills install\n```\n\nThe default installs copies into `.agents/skills` in the current project, suitable for agents including Codex. It does not change AGENTS.md or any agent configuration. No symlink privileges, paid service, API key or AI provider is required. npm downloads the CLI on first use; the installed CLI then installs its bundled skills without network access. The explicit 0.20.0 pin makes this installation repeatable and matches the [current package train](compatibility-matrix.md). For CLI source development, use `node packages/cli/dist/index.js skills install` after building.\n\n| Target | Project directory | Personal directory with `--global` |\n| --- | --- | --- |\n| Default / `--agent universal` | `.agents/skills` | `~/.agents/skills` |\n| `--agent codex` | `.agents/skills` | `~/.codex/skills` |\n| `--agent claude-code` | `.claude/skills` | `~/.claude/skills` |\n| `--agent cursor` | `.cursor/skills` | `~/.cursor/skills` |\n\nFor example, `npx @openpresentation/cli@0.20.0 skills install --agent codex --global` installs personal Codex skills. For another compatible agent use `--directory <its-skills-directory>`; this option cannot be combined with `--agent` or `--global`. Restart or reload your agent if its skill discovery requires it. A compatible client can invoke the installed skills with names such as `$opf-author` or `$opf-inspect`.\n\nInspect or update the same destination:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills status\nnpx @openpresentation/cli@0.20.0 skills update\n```\n\nSupply the same target options used for installation. `status` is read-only and compares against the invoked CLI's bundled version; it does not query npm for newer releases. Repeated installation is idempotent. Updates check every installed file before changing any skill. Modified, added, deleted or unmanaged files cause the command to stop and list the conflicting folders; keep your customizations, move those folders outside the active skills directory, then retry. There is no force-overwrite option. A successful update returns backup paths outside the active skills directory for recovering the previous managed versions. Keep those backups until you have reviewed the update. Do not run concurrent writers: the installer lock coordinates other installer runs, but cannot lock an external editor.\n\nNo skills are installed by the repository build. Manual installation remains supported: copy whole folders from `skills/`, including references and scripts, to your agent's skill directory. Each folder is self-contained. The managed installer treats existing manual copies as unmanaged and preserves them.\n\nThe inspection helper requires Node 24 and `@openpresentation/opf` in the current project. In this checkout, build with `pnpm build` first. For an installed skill used outside the checkout, either run from an npm project that has the package or set `OPF_ROOT` to the built OPF checkout. It does not install dependencies, fetch catalogs, or modify input files.\n\n## Local CLI\n\nThe [installable CLI](../packages/cli/README.md) complements these skills with `opf create`, `opf validate`, `opf edit`, and schema/catalog lookup. It runs on the core it depends on; the inspection skill helper instead resolves the host project's core package. Check versions when moving between them. From code, core's `@openpresentation/opf` offers `convert`, `open` and `save` in Node (see the opf-export skill).\n\n## Examples of requests\n\n- \u201CUse $opf-author to turn these notes into a five-slide decision brief. Keep every factual claim sourced.\u201D\n- \u201CUse $opf-layout to fix this overflow without losing any text or notes.\u201D\n- \u201CUse $opf-presets to apply our brand colors while preserving slide-specific overrides.\u201D\n- \u201CUse $opf-edit to replace one table and retain all other document fields.\u201D\n- \u201CUse $opf-export to export the same reviewed slides to SVG and PPTX.\u201D (PPTX text is editable one line at a time unless you opt in to `--body-text native`, from opf-pptx and CLI 0.22.0; the skill says so.)\n- \u201CUse $opf-inspect to explain which background forms the installed schema accepts.\u201D\n\n## Maintenance\n\n`pnpm test:skills` checks skill links, schema-valid examples, and the inspection helper's actual behavior, including a copied standalone skill and a package installed in a consumer project. Run the skill-creator frontmatter validator when editing skill metadata. Behavioral tests are not evidence that every renderer option is visually complete.\n\nWhen schema/package APIs change, update only the affected skill/reference and its executable examples. Keep option lists in the canonical schema and catalogs. The format package and skill folders are separate distribution surfaces: CLI 0.20.0 includes the six skills; the core `@openpresentation/opf` package does not install agent configuration. Repository skill prose can be newer than the immutable snapshot bundled in an already-published CLI. These source documentation corrections do not change the CLI 0.20.0 tarball; `skills status` and `skills update` compare against the invoked CLI's bundled snapshot.\n"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"slug": "catalog-schema-reference",
|
|
@@ -28,13 +28,13 @@ var docsData = Object.freeze([
|
|
|
28
28
|
"slug": "cli",
|
|
29
29
|
"file": "docs/cli.md",
|
|
30
30
|
"title": "The `opf` CLI",
|
|
31
|
-
"markdown": '# The `opf` CLI\n\nThe CLI (`@openpresentation/cli`, binary `opf`, Node 22 or later; verified on Node 24) creates, checks, edits, converts and\ninspects decks (see [its README](../packages/cli/README.md)). `opf validate` is the one checker ([validate](validate.md)).\n`opf convert` is the one command that changes formats: a deck form, PDF, PPTX, PNG and SVG. Every command runs core\'s\nfunctions, the ones applications call from [`@openpresentation/opf`](node.md); the commands add the flags, the JSON report and\nthe exit codes.\n\nEverything is deterministic and local: no network, no model, no telemetry, no system fonts. The same document, options and\ninstalled package versions give the same bytes on every operating system.\n\n## Commands\n\n| Command | What it does |\n| --- | --- |\n| `opf create [output] [--title <text> \\| --example <slug>]` | A one-slide starter deck, or one of core\'s bundled examples (`opf catalog examples` lists them). |\n| `opf validate <file\\|dir\\|->...` | The one checker: format, references, policy, accessibility, layout, content. Several files, folders and stdin in one run; `--format json\\|text\\|github`. |\n| `opf convert <input> <output>` | Any change of format: `.json`, `.opf.yaml`, `.opf.md` (and a plain `.md` input), `.pptx`, `.pdf`, `.png`, `.svg`, `.zip`, or stdin and stdout. |\n| `opf edit <file> [output] --patch <patch>` | A JSON Patch, applied whole or not at all, the result checked. |\n| `opf format <file>... [output]` | Canonical key order and layout, in the form the file has. |\n| `opf diff <a> <b>` | What changed between two decks (`--format text\\|json\\|patch`). |\n| `opf merge <base> <ours> <theirs> [output]` | A three-way merge; conflicts are listed. |\n| `opf stats <file>` | Neutral facts about a deck (`--format json\\|text`). |\n| `opf paginate <input> [output]` | Overflowing slides split into continuation slides, measured with the renderer\'s fonts when it is installed. |\n| `opf embed <input> [output]` | The catalog records a deck references, copied into it. |\n| `opf fill <template> <data> [output]` | A template\'s variables filled: one deck per record (the output is a name pattern), or `--combine`. The second argument is always the data (`-` for stdin). |\n| `opf ingest <data> [output] --as table\\|chart` | CSV, TSV or JSON rows as a table or chart slide, alone or added `--into` a deck. |\n| `opf doctor [deck]` | Which formats this install can write, and the one command that installs what is missing. |\n| `opf schemas`, `opf schema [name] [pointer]` | The OPF schemas. |\n| `opf catalogs`, `opf catalog <kind> [id]`, `opf catalog examples [slug]` | The pptx.gallery catalog (`@openpresentation/gallery`) and the bundled example decks. |\n| `opf skills <install\\|update\\|status>` | The six bundled agent skills. |\n\n`opf <command> --help` (or `-h`, or `opf help <command>`) prints a command\'s flags and examples.\n\nRemoved in 0.18 (each is a usage error, exit 2, that names its replacement): `opf render`, `opf export`, `opf import`,\n`opf from-md`, `opf to-md`, `opf from-yaml` and `opf to-yaml` (all `opf convert`), `opf create --from` (`opf convert`), and the\nflags `--output`, `--out`, `--out-dir` and `--name` (a positional output), `--input-format` (`--from`), `--json` (reports are\nJSON), `--dry-run` (stdout), `--data` (fill\'s second argument) and the deck values of `--format` (`--to`).\n\n## One output convention\n\nEvery command that writes a deck takes the same arguments:\n\n| Form | Meaning |\n| --- | --- |\n| `opf embed deck.opf.json` | No output: the deck goes to stdout, the report to stderr. `-` says the same. |\n| `opf embed deck.opf.json out.opf.json` | An output after the inputs: written atomically (a temporary sibling, then a rename); an existing file needs `--force`. |\n| `opf embed deck.opf.json -i` | `-i` (`--in-place`) rewrites the input, refusing if it changed since it was read. Not for stdin, and not with an output. |\n| `opf embed deck.opf.json --check` | Writes nothing and exits 1 when the command would change something: `format --check` (not canonical), `embed --check` (not self-contained) and `paginate --check` (a slide overflows). |\n\nThe form written is the output\'s extension (`.json`, `.opf.yaml`/`.yml`, `.opf.md`), else `--to json|yaml|md`, else the form\nthat was read. An existing output is refused before anything is printed or written.\n\n`opf fill` writes one deck per record through a file name pattern: `{n}` is the record number (padded to the width of the largest, the rule of `opf convert`), `{column}` a slug of that\ncolumn\'s value (`opf fill t.opf.md clients.csv decks/qbr-{client}.opf.md`); `--combine` writes one deck with every record\'s slides.\n\n## What each flag means\n\n| Flag | Meaning |\n| --- | --- |\n| `--format json\\|text\\|github\\|patch` | The report format only: `json` (the default), `text`, `github` (validate\'s workflow annotations) or `patch` (diff\'s JSON Patch). |\n| `--from json\\|yaml\\|md` | The form of a deck input: stdin, and names that say none. Without it, a name ending `.json`, `.opf.yaml`, `.yaml`, `.yml` or `.opf.md` says the form, and text that starts with `{` or `[` is JSON; anything else is a usage error that asks for `--from yaml\\|md`. `opf convert` also takes `--from pptx`. |\n| `--to json\\|yaml\\|md` | The form of a deck output, for stdout and names that say none. `opf convert` also takes `pdf`, `pptx`, `png`, `svg` and `zip`. A `--to` that disagrees with the output\'s extension is refused. |\n| `--data-format csv\\|tsv\\|json` | The form of the data of `opf fill` and `opf ingest` (default: the extension, else JSON when it starts with `{` or `[`, else CSV). |\n| `--fail-on error\\|warning\\|info` | Findings at or above this severity fail the command (exit 1) and nothing is written (default `error`). |\n| `--force` | Replace an existing output. |\n\n## Install\n\n**Verified package set (2026-10-16):** CLI 0.20.0 with core 0.20.3, renderer\n0.20.2, PPTX/editor 0.20.3 and Gallery 3.0.0. Use the complete pinned\n[quickstart installation](quickstart.md#versions) and its required font/converter\npeers. [Artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md).\n\nDeck forms (JSON, YAML, Markdown) need nothing beyond the CLI. PDF, PNG and SVG need `@openpresentation/opf-render`; PPTX output\nand `.pptx` input also need `@openpresentation/opf-pptx`. Both are **optional peer dependencies** of the CLI and of core (decision\nRR-27, below), loaded the first time a command needs them, from beside the CLI first and from the working directory second.\n\n`opf doctor` says what this install can write and prints the one command that installs the rest:\n\n```sh\nopf doctor --format text # per format: ready, or what is missing; then one install command\nopf doctor deck.opf.md # JSON; with a deck, a PDF needs sharp only when the deck has pictures\n```\n\nThe command matches the package manager that runs the CLI (`npm_config_user_agent`: npm, pnpm, yarn or bun) and the way the CLI\nis installed: global (`npm install -g ...`), a project (`npm install ...`), or one npx run (`npx -p @openpresentation/cli -p ... opf <command>`).\nA conversion that misses a peer exits 2 with `code: "peer-not-installed"` (or `peer-too-old`), and its `install` field and message\ncarry that one command for the format asked for. What each output needs beside `@openpresentation/opf-render`:\n\n| Output | Also install |\n| --- | --- |\n| every drawn format (the office font pack) | `@expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2` |\n| `png` | `@resvg/resvg-js@^2.6.2 sharp@^0.35.5` |\n| `pdf` | nothing for text in the default vector mode; `pdf-lib@^1.17.1` for `--raster`; `sharp@^0.35.5` when the deck has pictures |\n| `svg` | the fonts only |\n| `pptx`, a `.pptx` input | `@openpresentation/opf-pptx` |\n\n## From code\n\nThe engine the commands run is core\'s `@openpresentation/opf` in Node, which an application imports instead of spawning `opf`.\nIn Node, `validate`, `stats`, `paginate`, `embed` and `edit` register the gallery (`@openpresentation/gallery`) when a call names none, as the commands\ndo, so `opf validate deck` and `validate(deck)` agree:\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf"); // opf convert deck.opf.md deck.pdf\nconst deck = await opf.open("deck.pptx"); // opf convert deck.pptx -\nconst { files } = await opf.convert(deck, { format: "png", scale: 2 }); // bytes, nothing written\nconst { presentation } = opf.edit(deck, patch); // opf edit deck --patch patch.json\nconst { decks } = opf.fill(template, csvText); // opf fill template data.csv out-{n}.opf.json\n```\n\nThe options are the flags in camel case (`--raster` is `raster: true`, `--fonts` is `fonts` (a folder or a list of folders),\n`--asset-dir` is `assetDir`, `--include-hidden` is `includeHidden`). The commands refuse an existing output without `--force`;\n`convert` replaces it unless `overwrite: false`. See [OPF files in Node](node.md).\n\n## `opf convert`\n\n```sh\nopf convert deck.opf.md deck.pdf\nopf convert deck.opf.md slides/deck.png --slides 1,3-5 --scale 2 # slides/deck-1.png, -3, -4, -5\nopf convert deck.opf.md "slides/slide-{n}.png" # slides/slide-1.png, slide-2.png, ... (always numbered)\nopf convert deck.opf.md slides.zip --to svg\nopf convert deck.pptx deck.opf.md --signals signals.json\nopf convert outline.md deck.opf.yaml # a plain .md input is OPF Markdown\nopf convert deck.opf.json deck.opf.yaml --schema-comment\nopf convert deck.opf.md - --to pdf > deck.pdf\n```\n\n| Output | Written as |\n| --- | --- |\n| `.pdf`, `.pptx` | one file |\n| `.png`, `.svg` | one file per slide beside the output, named after it: `slides/deck.png` writes `slides/deck-1.png`, `-2`, ... (the deck\'s slide numbers). One selected slide, or a one-slide deck, is written to the output itself. The extension is the format: `deck.png` is always PNG. With `{n}` in the output (`"slides/slide-{n}.png"`) the files are always numbered, even for one slide or one selected slide, and `{n}` is the deck\'s slide number (2 and 5 with `--slides 2,5`). `{n}` is the only placeholder: any other `{key}`, and `{n}` in `.pdf`, `.pptx`, `.zip` or a deck form, is a usage error (exit 2, `invalid-option`). |\n\nNumbers are padded with zeros to the width of the largest number written: `1`...`9` for slides 1 to 9, `01`...`99` up to 99, `001` from 100. With `--slides` the width comes from the largest slide selected (`--slides 2,5` gives `deck-2.png`, `deck-5.png`). The same rule pads `{n}` in `opf fill` (the largest record number) and the zip entries below.\n| `.zip` | one archive of the slides, its entries named as the plain `.png` and `.svg` files above (`slides.zip` holds `slides-1.png`, ...; always numbered, and no `{n}` in the archive\'s own name): PNG, or SVG with `--to svg`. Entries are stored with a fixed timestamp, so the archive is byte-identical everywhere. |\n| `.json`, `.opf.yaml`, `.yml`, `.opf.md` | the deck in that form |\n| `-` | stdout, with `--to`: one file (`pdf`, `pptx`, `zip`, a deck form, or one slide as `png` or `svg`) |\n\nA flag applies only when its format is involved; anywhere else it is a usage error (exit 2, `option-not-applicable`):\n\n| Involved | Flags |\n| --- | --- |\n| a Markdown input | `--split auto\\|rules\\|headings` (default `auto`: slides at `---` lines, and an outline with none at its `#` headings), `--title <text>` (the deck name unless the front matter sets one) |\n| a YAML input | `--aliases` (expand anchors, aliases and merge keys, at most 100) |\n| a `.pptx` input | `--signals <file>` (also write the raw per-shape layout and style signals, `fromPptx` with `signals: true`) |\n| a YAML output | `--schema-comment` (the `# yaml-language-server: $schema=...` line) |\n| a Markdown output | `--drop-unsupported` (leave out what the dialect has no syntax for and list it as loss, instead of embedding it as YAML in a fence) |\n| pdf, png, svg | `--slides 1,3-5` (one-based; `2-` to the end), `--include-hidden` |\n| pdf, png, svg, pptx | `--paginate`, `--date YYYY-MM-DD`, `--fonts <dir>` (repeatable), `--asset-dir <dir>` |\n| png, raster pdf | `--scale 0.1-8` (1 draws the 1280 x 720 reference slide at 1280 x 720 pixels) |\n| pdf | `--raster` (each page a picture instead of selectable vector text) |\n| svg | `--text fonts\\|system\\|paths` (embed the faces the slide uses, none, or draw glyph outlines) |\n| pptx | `--charts auto\\|native\\|picture`, `--images compatible\\|preserve`, `--provenance full\\|references-only\\|none` |\n\nA `.pptx` input is imported, checked and then written or drawn; `findings` holds both steps (`import/` rules from the importer).\nPer-slide images and the PDF skip slides marked `hidden: true` unless `--include-hidden`; slides named with `--slides` are always\nwritten, and the report lists the slides left out in `skippedHidden`. The PPTX keeps a hidden slide as a hidden slide.\n\n## The other commands\n\n- **`opf create`** writes a starter deck (`--title`) or a bundled example (`--example <slug>`, from `opf catalog examples`).\n- **`opf validate`** checks files, folders (their `*.opf.json`, `*.opf.yaml`, `*.opf.yml` and `*.opf.md`, sorted, skipping\n `node_modules` and dot folders) and `-`. One input prints its report; several print `{ command, ok, files: [reports], counts }`.\n The exit status is the worst over every file. `--format github` prints `::error`, `::warning` and `::notice` annotations with the\n file, line and column. See [validate](validate.md).\n- **`opf paginate`** measures the page breaks with the fonts `opf convert --paginate` draws with when `@openpresentation/opf-render`\n is installed (`layout: "measured"`); without it they are estimated (`layout: "estimated"`) and `hint` names the install command.\n- **`opf fill`** reads CSV, TSV or JSON data; a blank cell keeps the variable\'s declared value; an unfilled required variable fails\n unless `--partial`; `--examples` fills from the declared examples. The second argument is always the data, whatever its name\n (`-` reads it from stdin), and the third is the output; a template alone fills from its own values, to stdout.\n- **`opf ingest`** gives the same slide id and bytes for the same inputs; `--dataset <id>` stores the rows in the deck\'s datasets.\n\n## Reports and errors\n\nEvery command that reports prints one envelope:\n\n```json\n{ "command": "convert", "ok": true, "input": { "file": "/abs/deck.opf.md", "sha256": "\u2026", "bytes": 1234 },\n "outputs": [{ "file": "/abs/deck.pdf", "sha256": "\u2026", "bytes": 56789, "mediaType": "application/pdf" }],\n "findings": [], "counts": { "error": 0, "warning": 0, "info": 0 }, "format": "pdf", "\u2026": "\u2026" }\n```\n\n`input` is `null` when nothing was read and a list when several were (diff, merge, fill). The report is on stdout, or on stderr when\nstdout carries the document. Findings have the [`opf validate`](validate.md) shape (`ruleId`, `severity`, `category`, JSON Pointer\n`path`, `message`, `help`, and `location` when read from text); engine findings carry the prefix `render/`, `pptx/`, `pdf/`,\n`fonts/`, `import/` or `cli/`. The lookup commands (`schemas`, `schema`, `catalogs`, `catalog`, `--version`) print the data asked for.\n\nErrors are `{ command, ok: false, code, error, ... }` on stderr. The message states the fix: a missing argument names it and\nprints the usage, an unknown command, option or value suggests the nearest one ("Did you mean --check?"), and a removed command\nor flag names its replacement. Codes include `usage`, `unknown-command`, `unknown-option`, `removed-command`, `removed-option`,\n`missing-argument`, `extra-argument`, `missing-value`, `invalid-value`, `option-not-applicable`, `unknown-input-format`,\n`input-not-found`, `invalid-json`, `invalid-yaml`, `invalid-markdown`, `invalid-document`, `findings-at-fail-on`,\n`output-exists`, `merge-conflict`, `fill-failed`, `peer-not-installed` and `peer-too-old`.\n\nExit `0`: success (warnings allowed). Exit `1`: an invalid document, a finding at or above `--fail-on`, an existing output, a\nmerge conflict, or a `--check` that found a change. Exit `2`: usage, a read or I/O problem, text that is not valid JSON, YAML or\nMarkdown, or a missing or too-old peer. (`opf validate` reports text that does not parse as a finding, exit 1.)\n\n## Fonts\n\nThe renderer\'s bundled open font pack (the office pack: Carlito, Intos for Aptos, the open families font schemes select and the\nopen replacements the font policy routes proprietary families to) with the visual substitution policy, plus the files from\n`--fonts`. **System fonts are never loaded**, and nothing is downloaded. Font substitutions are listed under\n`fonts.substitutions` in the report. The PPTX keeps the font names the document chose.\n\nScripts beyond Latin, Greek and Cyrillic need the optional Noto script packages of the renderer (`@expo-google-fonts/noto-sans-jp`\nand so on); install the ones named in the `fonts/script-font-not-installed` diagnostic next to the CLI. A standalone SVG\n(`--text fonts`, the default) embeds the faces its text uses; `--text system` omits all font bytes; `--text paths` draws the text\nas glyph outlines (the real text kept invisible beside it for selection and search), so the SVG needs no font.\n\n## Images and assets\n\nRelative image paths and `file:` paths resolve against the input\'s folder (or `--asset-dir`). Only `.png`, `.jpg`, `.jpeg`,\n`.gif`, `.webp` and `.svg` files whose content matches are read, and only inside that folder (symlinks are resolved first). URLs\nare never fetched. For SVG and PNG an unreadable image draws the renderer\'s placeholder with an `unresolved-asset` or\n`cli/asset-blocked` warning; for PPTX an unreadable or blocked local path stops the export (`pptx/asset-unresolved`), and an\nunresolved remote URL becomes an unavailable-image placeholder with a `pptx/unresolved-asset` warning. Use `--fail-on warning` to\nreject that output before any file is written.\n\n## Markdown and YAML\n\nJSON stays the canonical form of a deck. YAML (`deck.opf.yaml`) and Markdown (`deck.opf.md`) are authoring forms of the same data,\nand every command that reads or writes a deck reads and writes all three:\n\n```sh\nopf convert deck.md deck.opf.json # Markdown in the OPF dialect (or an outline) to a deck\nopf convert deck.opf.json deck.opf.md # a deck as Markdown that reads back unchanged\nopf convert deck.opf.yaml deck.opf.json --aliases # strict YAML (--aliases expands anchors)\nopf validate deck.opf.yaml deck.opf.md # every command reads .yaml, .yml and .opf.md\nopf edit deck.opf.md --patch changes.json -i # the file stays Markdown\nopf format deck.opf.md --check # canonical Markdown\nopf validate - --from yaml < deck.txt # stdin and other names: --from\n```\n\nSee [Markdown and outlines](markdown.md) and [OPF as YAML](yaml.md). A syntax error exits 2 with the line and column (`opf\nvalidate` reports it as a `yaml/<rule>` or `markdown/<rule>` finding and exits 1); a command that rewrites a YAML file does not\nkeep its comments and says so on stderr, and one that has to put content into an `opf-slide`/`opf-block` fence says so too.\n\n## Runtime choices\n\n- **Peers, not bundled.** opf-render and opf-pptx are optional peer dependencies loaded lazily. opf-pptx pulls the native `sharp`\n engine, opf-render `resvg`, `fontkit` and the font packs (about 135 MB installed); bundling them would make the small CLI\n impossible to install offline or on a locked-down agent host, and validating or editing a deck would pay for it.\n- **JSON reports.** `--format text` gives human-readable output where a command has it; `--format github` gives CI annotations.\n- **No clock.** `--date` is explicit so a rerun tomorrow gives the same bytes.\n- **Fonts are the bundled pack plus `--fonts`.** Never system fonts (owner font policy).\n- **Stored zip entries.** Deflate output differs between zlib builds, which would make archive hashes host-dependent.\n\n## Catalog names\n\n`opf catalogs` lists canonical camelCase names such as `colorSchemes` and `fontSchemes`, and `examples`. `opf catalog` also\naccepts the website-style `color-schemes`, `font-schemes` and `chart-types` spellings.\n'
|
|
31
|
+
"markdown": '# The `opf` CLI\n\nThe CLI (`@openpresentation/cli`, binary `opf`, Node 22 or later; verified on Node 24) creates, checks, edits, converts and\ninspects decks (see [its README](../packages/cli/README.md)). `opf validate` is the one checker ([validate](validate.md)).\n`opf convert` is the one command that changes formats: a deck form, PDF, PPTX, PNG and SVG. Every command runs core\'s\nfunctions, the ones applications call from [`@openpresentation/opf`](node.md); the commands add the flags, the JSON report and\nthe exit codes.\n\nEverything is deterministic and local: no network, no model, no telemetry, no system fonts. The same document, options and\ninstalled package versions give the same bytes on every operating system.\n\n## Commands\n\n| Command | What it does |\n| --- | --- |\n| `opf create [output] [--title <text> \\| --example <slug>]` | A one-slide starter deck, or one of core\'s bundled examples (`opf catalog examples` lists them). |\n| `opf validate <file\\|dir\\|->...` | The one checker: format, references, policy, accessibility, layout, content. Several files, folders and stdin in one run; `--format json\\|text\\|github`. |\n| `opf convert <input> <output>` | Any change of format: `.json`, `.opf.yaml`, `.opf.md` (and a plain `.md` input), `.pptx`, `.pdf`, `.png`, `.svg`, `.zip`, or stdin and stdout. |\n| `opf edit <file> [output] --patch <patch>` | A JSON Patch, applied whole or not at all, the result checked. |\n| `opf format <file>... [output]` | Canonical key order and layout, in the form the file has. |\n| `opf diff <a> <b>` | What changed between two decks (`--format text\\|json\\|patch`). |\n| `opf merge <base> <ours> <theirs> [output]` | A three-way merge; conflicts are listed. |\n| `opf stats <file>` | Neutral facts about a deck (`--format json\\|text`). |\n| `opf paginate <input> [output]` | Overflowing slides split into continuation slides, measured with the renderer\'s fonts when it is installed. |\n| `opf embed <input> [output]` | The catalog records a deck references, copied into it. |\n| `opf fill <template> <data> [output]` | A template\'s variables filled: one deck per record (the output is a name pattern), or `--combine`. The second argument is always the data (`-` for stdin). |\n| `opf ingest <data> [output] --as table\\|chart` | CSV, TSV or JSON rows as a table or chart slide, alone or added `--into` a deck. |\n| `opf doctor [deck]` | Which formats this install can write, and the one command that installs what is missing. |\n| `opf schemas`, `opf schema [name] [pointer]` | The OPF schemas. |\n| `opf catalogs`, `opf catalog <kind> [id]`, `opf catalog examples [slug]` | The pptx.gallery catalog (`@openpresentation/gallery`) and the bundled example decks. |\n| `opf skills <install\\|update\\|status>` | The six bundled agent skills. |\n\n`opf <command> --help` (or `-h`, or `opf help <command>`) prints a command\'s flags and examples.\n\nRemoved in 0.18 (each is a usage error, exit 2, that names its replacement): `opf render`, `opf export`, `opf import`,\n`opf from-md`, `opf to-md`, `opf from-yaml` and `opf to-yaml` (all `opf convert`), `opf create --from` (`opf convert`), and the\nflags `--output`, `--out`, `--out-dir` and `--name` (a positional output), `--input-format` (`--from`), `--json` (reports are\nJSON), `--dry-run` (stdout), `--data` (fill\'s second argument) and the deck values of `--format` (`--to`).\n\n## One output convention\n\nEvery command that writes a deck takes the same arguments:\n\n| Form | Meaning |\n| --- | --- |\n| `opf embed deck.opf.json` | No output: the deck goes to stdout, the report to stderr. `-` says the same. |\n| `opf embed deck.opf.json out.opf.json` | An output after the inputs: written atomically (a temporary sibling, then a rename); an existing file needs `--force`. |\n| `opf embed deck.opf.json -i` | `-i` (`--in-place`) rewrites the input, refusing if it changed since it was read. Not for stdin, and not with an output. |\n| `opf embed deck.opf.json --check` | Writes nothing and exits 1 when the command would change something: `format --check` (not canonical), `embed --check` (not self-contained) and `paginate --check` (a slide overflows). |\n\nThe form written is the output\'s extension (`.json`, `.opf.yaml`/`.yml`, `.opf.md`), else `--to json|yaml|md`, else the form\nthat was read. An existing output is refused before anything is printed or written.\n\n`opf fill` writes one deck per record through a file name pattern: `{n}` is the record number (padded to the width of the largest, the rule of `opf convert`), `{column}` a slug of that\ncolumn\'s value (`opf fill t.opf.md clients.csv decks/qbr-{client}.opf.md`); `--combine` writes one deck with every record\'s slides.\n\n## What each flag means\n\n| Flag | Meaning |\n| --- | --- |\n| `--format json\\|text\\|github\\|patch` | The report format only: `json` (the default), `text`, `github` (validate\'s workflow annotations) or `patch` (diff\'s JSON Patch). |\n| `--from json\\|yaml\\|md` | The form of a deck input: stdin, and names that say none. Without it, a name ending `.json`, `.opf.yaml`, `.yaml`, `.yml` or `.opf.md` says the form, and text that starts with `{` or `[` is JSON; anything else is a usage error that asks for `--from yaml\\|md`. `opf convert` also takes `--from pptx`. |\n| `--to json\\|yaml\\|md` | The form of a deck output, for stdout and names that say none. `opf convert` also takes `pdf`, `pptx`, `png`, `svg` and `zip`. A `--to` that disagrees with the output\'s extension is refused. |\n| `--data-format csv\\|tsv\\|json` | The form of the data of `opf fill` and `opf ingest` (default: the extension, else JSON when it starts with `{` or `[`, else CSV). |\n| `--fail-on error\\|warning\\|info` | Findings at or above this severity fail the command (exit 1) and nothing is written (default `error`). |\n| `--force` | Replace an existing output. |\n\n## Install\n\n**Verified package set (2026-10-16):** CLI 0.20.0 with core 0.20.3, renderer\n0.20.2, PPTX/editor 0.20.3 and Gallery 3.0.0. Use the complete pinned\n[quickstart installation](quickstart.md#versions) and its required font/converter\npeers. [Artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md).\n\nDeck forms (JSON, YAML, Markdown) need nothing beyond the CLI. PDF, PNG and SVG need `@openpresentation/opf-render`; PPTX output\nand `.pptx` input also need `@openpresentation/opf-pptx`. Both are **optional peer dependencies** of the CLI and of core (decision\nRR-27, below), loaded the first time a command needs them, from beside the CLI first and from the working directory second.\n\n`opf doctor` says what this install can write and prints the one command that installs the rest:\n\n```sh\nopf doctor --format text # per format: ready, or what is missing; then one install command\nopf doctor deck.opf.md # JSON; with a deck, a PDF needs sharp only when the deck has pictures\n```\n\nThe command matches the package manager that runs the CLI (`npm_config_user_agent`: npm, pnpm, yarn or bun) and the way the CLI\nis installed: global (`npm install -g ...`), a project (`npm install ...`), or one npx run (`npx -p @openpresentation/cli -p ... opf <command>`).\nA conversion that misses a peer exits 2 with `code: "peer-not-installed"` (or `peer-too-old`), and its `install` field and message\ncarry that one command for the format asked for. What each output needs beside `@openpresentation/opf-render`:\n\n| Output | Also install |\n| --- | --- |\n| every drawn format (the office font pack) | `@expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2` |\n| `png` | `@resvg/resvg-js@^2.6.2 sharp@^0.35.5` |\n| `pdf` | nothing for text in the default vector mode; `pdf-lib@^1.17.1` for `--raster`; `sharp@^0.35.5` when the deck has pictures |\n| `svg` | the fonts only |\n| `pptx`, a `.pptx` input | `@openpresentation/opf-pptx` |\n\n## From code\n\nThe engine the commands run is core\'s `@openpresentation/opf` in Node, which an application imports instead of spawning `opf`.\nIn Node, `validate`, `stats`, `paginate`, `embed` and `edit` register the gallery (`@openpresentation/gallery`) when a call names none, as the commands\ndo, so `opf validate deck` and `validate(deck)` agree:\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf"); // opf convert deck.opf.md deck.pdf\nconst deck = await opf.open("deck.pptx"); // opf convert deck.pptx -\nconst { files } = await opf.convert(deck, { format: "png", scale: 2 }); // bytes, nothing written\nconst { presentation } = opf.edit(deck, patch); // opf edit deck --patch patch.json\nconst { decks } = opf.fill(template, csvText); // opf fill template data.csv out-{n}.opf.json\n```\n\nThe options are the flags in camel case (`--raster` is `raster: true`, `--fonts` is `fonts` (a folder or a list of folders),\n`--asset-dir` is `assetDir`, `--include-hidden` is `includeHidden`). The commands refuse an existing output without `--force`;\n`convert` replaces it unless `overwrite: false`. See [OPF files in Node](node.md).\n\n## `opf convert`\n\n```sh\nopf convert deck.opf.md deck.pdf\nopf convert deck.opf.md slides/deck.png --slides 1,3-5 --scale 2 # slides/deck-1.png, -3, -4, -5\nopf convert deck.opf.md "slides/slide-{n}.png" # slides/slide-1.png, slide-2.png, ... (always numbered)\nopf convert deck.opf.md slides.zip --to svg\nopf convert deck.pptx deck.opf.md --signals signals.json\nopf convert outline.md deck.opf.yaml # a plain .md input is OPF Markdown\nopf convert deck.opf.json deck.opf.yaml --schema-comment\nopf convert deck.opf.md - --to pdf > deck.pdf\n```\n\n| Output | Written as |\n| --- | --- |\n| `.pdf`, `.pptx` | one file |\n| `.png`, `.svg` | one file per slide beside the output, named after it: `slides/deck.png` writes `slides/deck-1.png`, `-2`, ... (the deck\'s slide numbers). One selected slide, or a one-slide deck, is written to the output itself. The extension is the format: `deck.png` is always PNG. With `{n}` in the output (`"slides/slide-{n}.png"`) the files are always numbered, even for one slide or one selected slide, and `{n}` is the deck\'s slide number (2 and 5 with `--slides 2,5`). `{n}` is the only placeholder: any other `{key}`, and `{n}` in `.pdf`, `.pptx`, `.zip` or a deck form, is a usage error (exit 2, `invalid-option`). |\n\nNumbers are padded with zeros to the width of the largest number written: `1`...`9` for slides 1 to 9, `01`...`99` up to 99, `001` from 100. With `--slides` the width comes from the largest slide selected (`--slides 2,5` gives `deck-2.png`, `deck-5.png`). The same rule pads `{n}` in `opf fill` (the largest record number) and the zip entries below.\n| `.zip` | one archive of the slides, its entries named as the plain `.png` and `.svg` files above (`slides.zip` holds `slides-1.png`, ...; always numbered, and no `{n}` in the archive\'s own name): PNG, or SVG with `--to svg`. Entries are stored with a fixed timestamp, so the archive is byte-identical everywhere. |\n| `.json`, `.opf.yaml`, `.yml`, `.opf.md` | the deck in that form |\n| `-` | stdout, with `--to`: one file (`pdf`, `pptx`, `zip`, a deck form, or one slide as `png` or `svg`) |\n\nA flag applies only when its format is involved; anywhere else it is a usage error (exit 2, `option-not-applicable`):\n\n| Involved | Flags |\n| --- | --- |\n| a Markdown input | `--split auto\\|rules\\|headings` (default `auto`: slides at `---` lines, and an outline with none at its `#` headings), `--title <text>` (the deck name unless the front matter sets one) |\n| a YAML input | `--aliases` (expand anchors, aliases and merge keys, at most 100) |\n| a `.pptx` input | `--signals <file>` (also write the raw per-shape layout and style signals, `fromPptx` with `signals: true`) |\n| a YAML output | `--schema-comment` (the `# yaml-language-server: $schema=...` line) |\n| a Markdown output | `--drop-unsupported` (leave out what the dialect has no syntax for and list it as loss, instead of embedding it as YAML in a fence) |\n| pdf, png, svg | `--slides 1,3-5` (one-based; `2-` to the end), `--include-hidden` |\n| pdf, png, svg, pptx | `--paginate`, `--date YYYY-MM-DD`, `--fonts <dir>` (repeatable), `--asset-dir <dir>` |\n| png, raster pdf | `--scale 0.1-8` (1 draws the 1280 x 720 reference slide at 1280 x 720 pixels) |\n| pdf | `--raster` (each page a picture instead of selectable vector text) |\n| svg | `--text fonts\\|system\\|paths` (embed the faces the slide uses, none, or draw glyph outlines) |\n| pptx | `--charts auto\\|native\\|picture`, `--images compatible\\|preserve`, `--provenance full\\|references-only\\|none`, `--body-text measured\\|native`, `--slide-numbers static\\|live` |\n\nA `.pptx` input is imported, checked and then written or drawn; `findings` holds both steps (`import/` rules from the importer).\nPer-slide images and the PDF skip slides marked `hidden: true` unless `--include-hidden`; slides named with `--slides` are always\nwritten, and the report lists the slides left out in `skippedHidden`. The PPTX keeps a hidden slide as a hidden slide.\n\n### PPTX text (RR-88, RR-91)\n\nBy default (`--body-text measured`) a wrapped paragraph becomes one text box per line, laid out exactly as the preview draws it, so editing a line in PowerPoint or Keynote does not reflow the rest. `--body-text native` opts in to whole paragraphs: a title that wraps is one Title placeholder, and a text, bullets or numbered-list region is one body that the application re-wraps when edited. What native mode cannot carry (groups, cards, nested or described lists, restarted numbering, titles with a hard break) stays measured and is reported as `native-body-fallback`. `--slide-numbers` decides a footer such as `{{slide.number}} / {{deck.slideCount}}`: `static` (the default) writes the number as fixed text, so Keynote shows the words around it, and `live` keeps the slide-number field, which Keynote draws as the digit alone because it drops the words next to a live number. A footer that is only `{{slide.number}}` stays a live field in both modes (the total is always fixed text). Both flags need an opf-pptx that reads them (0.22.0 or later); an older one is refused (`peer-too-old`) rather than ignoring them, and they are exit 2 for any other output.\n\n**Which to choose.** Pick `measured` (the default) when the deck is final and the PPTX is a delivery format: every line sits where the preview put it, and the deck is edited in OPF. Pick `native` when people will edit the text in PowerPoint or Keynote: a longer bullet wraps, a title keeps its placeholder (so outline view and slide titles work), and numbered lists renumber. Native text is re-wrapped by the application\'s own fonts and engine, so line breaks can differ from the preview; native wrapping is not yet qualified in every application, and anything it cannot carry (see above) stays measured with a `native-body-fallback` finding, so read the findings. `--slide-numbers static` is the default because Keynote replaces any paragraph that holds a live slide-number field with the number alone, so `{{slide.number}} / {{deck.slideCount}}` showed only the digit; the cost is that PowerPoint does not renumber a static number when you reorder slides there (choose `live` for that, and accept the digit-only footer in Keynote).\n\n## The other commands\n\n- **`opf create`** writes a starter deck (`--title`) or a bundled example (`--example <slug>`, from `opf catalog examples`).\n- **`opf validate`** checks files, folders (their `*.opf.json`, `*.opf.yaml`, `*.opf.yml` and `*.opf.md`, sorted, skipping\n `node_modules` and dot folders) and `-`. One input prints its report; several print `{ command, ok, files: [reports], counts }`.\n The exit status is the worst over every file. `--format github` prints `::error`, `::warning` and `::notice` annotations with the\n file, line and column. See [validate](validate.md).\n- **`opf paginate`** measures the page breaks with the fonts `opf convert --paginate` draws with when `@openpresentation/opf-render`\n is installed (`layout: "measured"`); without it they are estimated (`layout: "estimated"`) and `hint` names the install command.\n- **`opf fill`** reads CSV, TSV or JSON data; a blank cell keeps the variable\'s declared value; an unfilled required variable fails\n unless `--partial`; `--examples` fills from the declared examples. The second argument is always the data, whatever its name\n (`-` reads it from stdin), and the third is the output; a template alone fills from its own values, to stdout.\n- **`opf ingest`** gives the same slide id and bytes for the same inputs; `--dataset <id>` stores the rows in the deck\'s datasets.\n\n## Reports and errors\n\nEvery command that reports prints one envelope:\n\n```json\n{ "command": "convert", "ok": true, "input": { "file": "/abs/deck.opf.md", "sha256": "\u2026", "bytes": 1234 },\n "outputs": [{ "file": "/abs/deck.pdf", "sha256": "\u2026", "bytes": 56789, "mediaType": "application/pdf" }],\n "findings": [], "counts": { "error": 0, "warning": 0, "info": 0 }, "format": "pdf", "\u2026": "\u2026" }\n```\n\n`input` is `null` when nothing was read and a list when several were (diff, merge, fill). The report is on stdout, or on stderr when\nstdout carries the document. Findings have the [`opf validate`](validate.md) shape (`ruleId`, `severity`, `category`, JSON Pointer\n`path`, `message`, `help`, and `location` when read from text); engine findings carry the prefix `render/`, `pptx/`, `pdf/`,\n`fonts/`, `import/` or `cli/`. The lookup commands (`schemas`, `schema`, `catalogs`, `catalog`, `--version`) print the data asked for.\n\nErrors are `{ command, ok: false, code, error, ... }` on stderr. The message states the fix: a missing argument names it and\nprints the usage, an unknown command, option or value suggests the nearest one ("Did you mean --check?"), and a removed command\nor flag names its replacement. Codes include `usage`, `unknown-command`, `unknown-option`, `removed-command`, `removed-option`,\n`missing-argument`, `extra-argument`, `missing-value`, `invalid-value`, `option-not-applicable`, `unknown-input-format`,\n`input-not-found`, `invalid-json`, `invalid-yaml`, `invalid-markdown`, `invalid-document`, `findings-at-fail-on`,\n`output-exists`, `merge-conflict`, `fill-failed`, `peer-not-installed` and `peer-too-old`.\n\nExit `0`: success (warnings allowed). Exit `1`: an invalid document, a finding at or above `--fail-on`, an existing output, a\nmerge conflict, or a `--check` that found a change. Exit `2`: usage, a read or I/O problem, text that is not valid JSON, YAML or\nMarkdown, or a missing or too-old peer. (`opf validate` reports text that does not parse as a finding, exit 1.)\n\n## Fonts\n\nThe renderer\'s bundled open font pack (the office pack: Carlito, Intos for Aptos, the open families font schemes select and the\nopen replacements the font policy routes proprietary families to) with the visual substitution policy, plus the files from\n`--fonts`. **System fonts are never loaded**, and nothing is downloaded. Font substitutions are listed under\n`fonts.substitutions` in the report. The PPTX keeps the font names the document chose.\n\nScripts beyond Latin, Greek and Cyrillic need the optional Noto script packages of the renderer (`@expo-google-fonts/noto-sans-jp`\nand so on); install the ones named in the `fonts/script-font-not-installed` diagnostic next to the CLI. A standalone SVG\n(`--text fonts`, the default) embeds the faces its text uses; `--text system` omits all font bytes; `--text paths` draws the text\nas glyph outlines (the real text kept invisible beside it for selection and search), so the SVG needs no font.\n\n## Images and assets\n\nRelative image paths and `file:` paths resolve against the input\'s folder (or `--asset-dir`). Only `.png`, `.jpg`, `.jpeg`,\n`.gif`, `.webp` and `.svg` files whose content matches are read, and only inside that folder (symlinks are resolved first). URLs\nare never fetched. For SVG and PNG an unreadable image draws the renderer\'s placeholder with an `unresolved-asset` or\n`cli/asset-blocked` warning; for PPTX an unreadable or blocked local path stops the export (`pptx/asset-unresolved`), and an\nunresolved remote URL becomes an unavailable-image placeholder with a `pptx/unresolved-asset` warning. Use `--fail-on warning` to\nreject that output before any file is written.\n\n## Markdown and YAML\n\nJSON stays the canonical form of a deck. YAML (`deck.opf.yaml`) and Markdown (`deck.opf.md`) are authoring forms of the same data,\nand every command that reads or writes a deck reads and writes all three:\n\n```sh\nopf convert deck.md deck.opf.json # Markdown in the OPF dialect (or an outline) to a deck\nopf convert deck.opf.json deck.opf.md # a deck as Markdown that reads back unchanged\nopf convert deck.opf.yaml deck.opf.json --aliases # strict YAML (--aliases expands anchors)\nopf validate deck.opf.yaml deck.opf.md # every command reads .yaml, .yml and .opf.md\nopf edit deck.opf.md --patch changes.json -i # the file stays Markdown\nopf format deck.opf.md --check # canonical Markdown\nopf validate - --from yaml < deck.txt # stdin and other names: --from\n```\n\nSee [Markdown and outlines](markdown.md) and [OPF as YAML](yaml.md). A syntax error exits 2 with the line and column (`opf\nvalidate` reports it as a `yaml/<rule>` or `markdown/<rule>` finding and exits 1); a command that rewrites a YAML file does not\nkeep its comments and says so on stderr, and one that has to put content into an `opf-slide`/`opf-block` fence says so too.\n\n## Runtime choices\n\n- **Peers, not bundled.** opf-render and opf-pptx are optional peer dependencies loaded lazily. opf-pptx pulls the native `sharp`\n engine, opf-render `resvg`, `fontkit` and the font packs (about 135 MB installed); bundling them would make the small CLI\n impossible to install offline or on a locked-down agent host, and validating or editing a deck would pay for it.\n- **JSON reports.** `--format text` gives human-readable output where a command has it; `--format github` gives CI annotations.\n- **No clock.** `--date` is explicit so a rerun tomorrow gives the same bytes.\n- **Fonts are the bundled pack plus `--fonts`.** Never system fonts (owner font policy).\n- **Stored zip entries.** Deflate output differs between zlib builds, which would make archive hashes host-dependent.\n\n## Catalog names\n\n`opf catalogs` lists canonical camelCase names such as `colorSchemes` and `fontSchemes`, and `examples`. `opf catalog` also\naccepts the website-style `color-schemes`, `font-schemes` and `chart-types` spellings.\n'
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
"slug": "compatibility-matrix",
|
|
35
35
|
"file": "docs/compatibility-matrix.md",
|
|
36
36
|
"title": "Compatibility matrix",
|
|
37
|
-
"markdown": "# Compatibility matrix\n\nPublished registry evidence for the coordinated Node 24 toolchain. This matrix\nis the honest supported subset for [the developer quickstart](quickstart.md).\nIt is not universal Office parity and does not describe archived prototypes as\nshipped.\n\n**Verified artifacts (2026-10-17):** core 0.20.5, renderer/PPTX/editor 0.20.4,\nCLI 0.20.1 and Gallery 3.0.0.\n[Artifact verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) binds\nthe selected archives to their release commits and trusted publication checks.\nEarlier matrices and native observations retain their historical identities.\nComplete native compatibility remains a separate gate.\n\n## Published 0.20 contract\n\nLayout `areas` uses nested arrays of cell names. Repeated names span rectangular\nregions and `.` denotes an empty cell. Gallery owns every default record;\nGallery 3 is required for catalog compatibility generation 3, while per-kind\nschema versions remain v2. Core 0.19 and later consume the Gallery package and\ndo not export `@openpresentation/opf/catalog` or ship a catalog snapshot.\n\nThe 0.20 engines share measured quote allocation, the 32 reference pixel\nstarting size for lists without an authored size, neutral structural borders\nand default right alignment for numeric table columns. Core and CLI forward\nregistered catalogs during PPTX import. PPTX preserves stretch images with\nzero crop through native save/re-import. These published contracts do not\nimply universal Office or full-catalog visual parity.\n\n## Current published package contracts\n\n| Package | Version | Dependency contract |\n| --- | --- | --- |\n| Core | 0.20.5 | Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| CLI | 0.20.1 | Core ^0.20.5, Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| Renderer | 0.20.4 | Core ^0.20.5; optional pinned font/converter peers |\n| PPTX | 0.20.4 | Core ^0.20.5, Gallery ^3.0.0; optional renderer ^0.20.4 |\n| Editor | 0.20.4 | Core ^0.20.5; optional renderer ^0.20.4 |\n| Gallery | 3.0.0 | Independent owner of every default catalog record; 28 intent layouts |\n\n## Runtime\n\n| Requirement | Status |\n| --- | --- |\n| Node.js | `engines.node` is `>=22` on every package below; **Node 24** is the coordinated toolchain these records are verified on (the 0.12.2 set and later are tested on Node 22, 24 and 26) |\n| Package managers | npm for published installs; this repo uses pnpm 10.33.2 for core development |\n| Account / model / hosted API | Not required |\n| Operating systems | macOS, Linux, Windows for Node APIs; browser entrypoints are separate |\n\n## Historical coordinated 0.18 published packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/opf` | 0.18.4 | optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0`, which the Node build loads for `convert`, `open` and `save` |\n| `@openpresentation/cli` | 0.18.1 | `@openpresentation/opf@^0.18.3` (a regular dependency, no bundled copy); optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0` |\n| `@openpresentation/opf-render` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peers (RR-63): the converters `sharp`, `@resvg/resvg-js` and `pdf-lib` and the `@expo-google-fonts/*` font packages, which a host installs for the formats and fonts it uses |\n| `@openpresentation/opf-editor` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n| `@openpresentation/opf-pptx` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n\nThe 0.18 set is the consistent-API release ([design](programs/release-readiness/0.18-consistent-api.md)), breaking:\n- **One import (RR-70).** `import * as opf from \"@openpresentation/opf\"` works in every runtime through conditional\n exports, and `/node` is removed. `defaultCatalog` lives at `@openpresentation/opf/catalog` only.\n- **Names (RR-73) and options (RR-74).**\n - `ingest` replaces `importData` (and `opf ingest` replaces `opf import-data`).\n - `toSvg`, `toPng`, `toPdf` and `toHtml` work on decks.\n - The editor exports with `convert`.\n - `convert` takes `text`, `raster`, `charts`, `images`, `fonts` and `name`.\n- **Logos (RR-71).** Logos come from `organizations[].logo` (`full`, `stacked`, `icon`, `wordmark`, `onLight`/`onDark`),\n placed with `var:organization.logo.*`. `LogoSet` and zone `logo: true` are removed.\n- **PowerPoint (RR-72).** Shared furniture is written once on the PPTX slide master or layout.\n- **CLI (RR-75).** `opf convert` absorbs `render`, `export` and `import`. There is one output convention, plus\n `opf doctor` and a multi-file `validate`.\n\nSee the 0.18.0 and 0.18.1 entries of the [changelog](../CHANGELOG.md) and the 0.18.0 entry of the [CLI changelog](../packages/cli/CHANGELOG.md).\n\nCore starts at **0.18.1**. `opf-v0.18.0` was tagged, but its publish run failed before `npm publish`: a core test\nspawned the CLI, which the publish workflow had not built. So 0.18.1 is the first published 0.18 core.\n\nThe 0.17 set (core, editor and PPTX 0.17.0, renderer 0.17.1, CLI 0.17.1) was the lockstep minor after 0.16, also breaking:\n- **Furniture variables (FA-31):** header and footer text uses the `{{slide.number}}`, `{{slide.section}}` and\n `{{deck.slideCount}}` variables, and the `HeaderFooterItem` flags are removed.\n- **`@openpresentation/opf/node` (RR-62):** `convert`, `open` and `save` work from files and bytes. `parse` and\n `stringify` replace `readDeck` and `writeDeck`. `opf convert` is new, and `@openpresentation/cli/api` is removed.\n- **Fixes and additions:**\n - deterministic data import;\n - `validate` and `paginate` that plan script faces (RR-59);\n - the Arabic and Latin preview fixes;\n - `renderSvg(deck, { textAsPaths: true })` (RR-64 phase 1);\n - the Windows CLI path fixes (RR-66).\n\nSee the 0.17.0 entries of the [changelog](../CHANGELOG.md) and the 0.17.1 entry of the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nRenderer 0.17.1 is a patch on the 0.17 line: SVG never embeds a face whose OS/2 `fsType` forbids it (RR-76), embedded faces are cut to the glyphs a slide draws (RR-65, mean example slide 2.4 MB to 59 KB), and `textAsPaths` shapes with HarfBuzz and keeps an invisible text layer (RR-64 phases 2 and 3).\n\nThe CLI starts at **0.17.1**. `cli-v0.17.0` was tagged, but its publish run failed before `npm publish`, because the\nrelease commit carried a stale generated gallery tracker. So 0.17.1 is the first published 0.17 CLI.\n\nThe 0.16 set (core, CLI, renderer and editor 0.16.0, PPTX 0.16.1) was the lockstep minor after 0.15 (owner decision 2026-10-08: no 0.15.x patch train). It adds nested\nplaceholder groups in layout records (FA-26), `Table.alt` (FA-27), the `opf/language-tag` warning (FA-29), Markdown\ndecks (`.opf.md`) in every command with core `readDeck`/`writeDeck` (RR-60), SVGs that embed only the faces they draw\n(RR-61), the CLI's programmatic `@openpresentation/cli/api` (RR-62), a lighter renderer install with optional\nconverters and fonts and the `/png`, `/pdf` and `/html` subpaths (RR-63), and the developer-contract fixes (RR-59).\n0.15 documents are 0.16 documents. See the 0.16.0 entries of the [changelog](../CHANGELOG.md) and the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nThe 0.15 set (core 0.15.1, CLI 0.15.0, renderer, PPTX and editor 0.15.0) was the breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nSee the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore 0.15 started at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nwas the first published 0.15 core and the floor of every 0.15 sibling.\n\nOPF 0.15 is a breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nThe 0.15 set reads and writes only 0.15 documents; see the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore starts at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nis the first published 0.15 core and the floor of every 0.15 sibling.\n\nFrom CLI 0.16.0 the CLI depends on core (`@openpresentation/opf`, a regular dependency, not a bundled copy). CLI 0.16.0\nexposed `@openpresentation/cli/api`, and 0.17 removes it in favour of core's `@openpresentation/opf/node`. The renderer\nand PPTX stay optional peers. CLI 0.15.0 and earlier bundled their core.\n\nInstall the complete pinned set. A caret range starting at 0.10.1 does not\ninclude 0.11.x; old consumers can install a second core and do not establish\nColorRef preview/export support. The selected renderer 0.20.2 and PPTX/editor 0.20.3 require core ^0.20.2;\nCLI 0.20.0 accepts core ^0.20.1. Pin core 0.20.3 alongside them and\nverify one installed Core so preview and export use the same composition.\nCore 0.20.2 and later center metrics when alignment is omitted; explicit alignment remains\nauthoritative.\n\nShared header/footer geometry (`furniture-flow-v2`) is published. PPTX exports\neditable slide shapes tagged `OPF_FURNITURE_V1` with provenance for controlled\nreimport. Published PPTX through 0.11.8 draws every part that way, not as native\nOffice Header/Footer objects. PPTX 0.11.9 and later (RR-11) write the footer's\nfirst text, date and slide number as native `ftr`, `dt` and `sldNum` placeholders\n(with master/layout placeholders, `p:hf` flags and a notes-master flag) at the same\ngeometry and reads them back with or without provenance; the rest stays tagged\nshapes. Native PowerPoint acceptance remains [issue 87](https://github.com/OpenPresentation/opf/issues/87).\nPPTX 0.18 (RR-72) writes a tagged part that is the same on two or more slides once,\non the slide master or a shared layout (one per background tone for `onLight`/`onDark`\nlogos; a hidden footer uses a layout that hides the master's), and reads it back from\nthere ([rr-72-master-furniture.md](programs/release-readiness/rr-72-master-furniture.md)).\n\nThe [Windows native-picture checkpoint](evidence/windows-native-picture-20260921/README.md)\nand accepted [native B/C bundle](evidence/windows-native-edits-20260921/README.md)\nrecord finite picture/furniture edits, current-content provenance reimport and\nsafe fallback, production notes packaging and two controlled reordered-file\nrefusals. Core105 publishes that evidence; PPTX47 adds the tested harness, with\nno new package version. UI image replacement changes geometry, longer header\ntext clips, and duplicated tagged headers overlap. Refused workers retain their\nfailed cleanup state separately from later empty-workspace observations.\nThis is not general native layout/reflow fidelity.\n\nAccepted core106's [tab and font checkpoint](evidence/windows-native-tabs-fonts-20260921/README.md)\nrecords plain native tab target error **0.022655487060546875pt** and tab/literal\ndifference **0.022678375244140625pt**, both above the unchanged **0.02pt** gate.\nIts bounded four-face Carlito edit/save/reopen control passes exact text/style\npersistence, zero observed bounds drift, matching rasters and owned font cleanup.\nMixed-size table fidelity, physical glyph-font identity, fallback/synthesis and actual embedding remain\nopen; embedding was disabled for this control. The Windows supervisor retains sole\nOffice control. This documentation task reads evidence and makes no Office calls.\n\nThe later [read-only font inventory](evidence/windows-native-font-inventory-20260921/README.md)\nretains the four Carlito text styles but reports both Carlito and unexpected\nAptos in `Presentation.Fonts`. The native font allowlist fails, and no embedding\nwas attempted. The original parent report incorrectly fails cleanup because of\na Windows PowerShell 5.1 JSON-array parsing defect; raw stages and registration\nrows establish one owned close and four removals in a separately labeled offline\naudit. The raw failure remains intact. Collection names and flags do not identify\nthe physical font used for each glyph.\n\nThe [read-only mixed-table observation](evidence/windows-native-mixed-table-20260921/README.md)\nretains all 245 characters, one literal tab and five authored runs, with outer\ngeometry within 0.02pt and confirmed owned close/font cleanup. Native soft-line\nboundaries are 92/194 versus the estimated preview's 78/172, and native default\ntab spacing is 72pt. These finite content/style results do not pass table\nedit/save/reopen, browser/native raster agreement or physical glyph identity.\nThe accepted [nine-pair offline tab analysis](evidence/windows-native-tab-analysis-20260921/REPORT.md)\nfinds a 0.05pt-compatible pattern in the observed character starts, with finer saved\ntab coordinates. These inputs do not distinguish relative versus absolute placement\nor establish an internal engine cause. The 0.02pt native tab gate remains failed;\nthe separate 0.1px renderer gate is unchanged. Accepted core108 `9b277e1` and\ncore109 `b2711549` publish bounded evidence only. Windows-owned [core110](https://github.com/OpenPresentation/opf/pull/110)\nis merged as `4f7a4bd494f1a873319eff897423d301d1cfc9d6`, from reviewed fc3c36e\nwith four required PR checks passing. [Renderer30](https://github.com/OpenPresentation/opf-render/pull/30) is now merged\nas `c8d7d5ca1f67a7b39f70c7c4bd14577a865b175b`, with exact-head CI 35661051100\npassing. The supervisor reports postmerge 35661504472 also passed. Its companion\nsource preserves rich-tab advances/spans; the [accepted Windows wrap-up](handoff-windows-native-2026-09-21-wrap-up.md)\nrecords bounded source-linked rendering/browser checks and the original missing-test\nCI failure. These checks do not update the frozen registry consumer. Core111\n`3c5048522714365a41d9b5b9ba81620affae718b` publishes the font-inventory evidence\nabove with both PR workflows green; its postmerge workflows were started at the\nfinal notice, not recorded as passed. Package/lock/release/site pins, schema,\ngoldens and tolerances are unchanged. Native allowlist and physical-glyph/embedding\nacceptance remain open; no new package train or broad native pass is inferred.\n\n## Independently released packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/gallery` | 3.0.0 ([independent artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md); prior 2.0.0 and 1.0.0 remain historical) | nothing (its `GalleryCatalog` type has the shape of `@openpresentation/opf`'s `Catalog`) |\n\nThe gallery (RR-78) is released from its own repository, OpenPresentation/gallery, on its own version line (`vX.Y.Z`\ntags, `gallery-publish.yml` there), outside the lockstep set above and\nrecorded under `independentPackages` in `release-plan.json`: new catalog records are a minor release, a removed id or\na changed drawing a major one ([release process](release-process.md#the-gallery-lane-rr-78)). From OPF 0.19 core and the\nCLI consume the Gallery package (Gallery 2 in OPF 0.19; Gallery 3 in OPF 0.20), and core has no `/catalog` subpath; up to 0.18 core ships the same records as\n`@openpresentation/opf/catalog`.\n\n## Supported in this set\n\n| Capability | How | Notes |\n| --- | --- | --- |\n| JSON authoring | `*.opf.json` plus CLI `opf create` | Local files only |\n| Bundled examples catalog | `@openpresentation/opf/examples` | **127** decks; the quickstart JSON is a docs fixture, not a 128th catalog entry |\n| Validate | `validate` / `opf validate` | One read-only checker: format, references, policy, accessibility, layout and content findings ([guide](validate.md)); no network catalog fetch |\n| Color references | `ColorRef`, `variables`, `resolveColorRef` | Core schema/resolution, renderer preview and PPTX resolved colors are shipped. Supported theme-slot colors write native `schemeClr` references; arbitrary native Office recoloring and full visual fidelity remain separately scoped. |\n| Embed catalog records | `embed` / `opf embed` | Embeds every record a document references, in the group it resolves in, so it renders with no catalog registered; remote media and data remain explicit host concerns. |\n| Offline fonts | `loadFonts` (`/fonts-node`) | Bundled Roboto pack (`pack: 'base'`) or the office pack; hashed files; the handle is passed as `{ fonts }` to every deck-level call |\n| Composition | `composeSlide` | Includes shared headers/footers |\n| Pagination | `paginate` / `opf paginate` | Returns mappings; preserves source |\n| Edit + undo | `@openpresentation/opf-editor` `createEditorSession` | JSON Patch undo/redo |\n| JSON Patch CLI | `opf edit` | No persistent CLI undo history |\n| SVG preview | `toSvg(deck)` (every slide) / `toSvg(deck, n)` (slide n, counted from 1) (opf-render 0.18; `renderSvg` / `renderSlideSvg` before) | Local; the same `{ fonts }` as layout; `text: \"paths\"` draws the text as glyph outlines |\n| Convert, export and import files from code | `convert`, `open` and `save` of `@openpresentation/opf` in Node (core 0.18.0 and later; core 0.17 had them at `@openpresentation/opf/node`; CLI 0.16.0 had `exportDeck` / `importDeck` in `@openpresentation/cli/api`) | PDF, PNG, SVG and PPTX out, PPTX in, deck forms both ways, with the bundled fonts, no network and no clock; calls opf-render and opf-pptx as optional peers and throws `OPFExportError` / `OPFImportError` with the install command when one is missing. `opf convert` runs the same engine (CLI 0.18; `opf render`, `opf export` and `opf import` before it) ([OPF files in Node](node.md)) |\n| Read and write a deck in JSON, YAML or Markdown | `parse` / `stringify` (core 0.17.0 and later; `readDeck` / `writeDeck` in 0.16) | One entry point for the three text formats; `parse` throws `OPFValidationError` with located findings |\n| PNG | `toPng` (opf-render 0.18; `svgToPng` before) | Node raster of a deck or of SVG slides |\n| PDF | `toPdf` (opf-render 0.18; `svgToPdf` before) | opf-render 0.12.0 and later (RR-12, opf-render#90): **vector with selectable text by default** (embedded TrueType subsets of the supplied/bundled fonts, ToUnicode, links, metadata, tagged structure); `raster: true` keeps the image-per-slide output (`mode: \"raster\"` before 0.18). Renderers up to 0.11.9: raster-backed, not selectable text. Not a PDF/UA or PDF/A claim; see the renderer's `docs/evidence/rr-12-vector-pdf.md` for reader limits |\n| Editable PPTX export | `toPptx` | OPF \u2192 PPTX serialization. Text is one editable box per wrapped line, so edited text does not reflow in PowerPoint or Keynote, and a wrapped title is not the Title placeholder; an opt-in native body mode is in progress. Shared furniture is written on the master or layout; supported footer placeholders and tagged parts retain controlled re-import provenance. General native Office behavior remains separately scoped |\n| Agent skills | `opf skills install` | Offline after the CLI is installed |\n| Browser canvas | `@openpresentation/opf-editor/canvas` | Host must supply font bytes |\n\n## Public sites\n\nThe current coordinated adoption is recorded in the [final adoption note](evidence/rr-85-final-adoption-2026-10-16/README.md). The following source, CI and canonical production results are the historical September 21 checkpoint, recorded in the\n[font-readiness checkpoint](evidence/font-readiness-acceptance-20260921/README.md),\n[prior Inspector actions checkpoint](evidence/inspector-current-actions-20260921/README.md),\n[earlier publication checkpoint](evidence/inspector-share-acceptance-20260921/README.md),\n[source-preservation checkpoint](evidence/author-source-acceptance-20260921/README.md),\n[completion checkpoint](evidence/completion-acceptance-20260921/README.md),\n[earlier acceptance ledger](evidence/issue88-final-20260921/README.md) and\n[handoff](handoff-2026-09-21.md). [Issue88](https://github.com/OpenPresentation/opf/issues/88)\nremains open. Package adoption, deployed features and complete workflow\nacceptance are separate claims.\n\n| Surface | Deployed scope and acceptance | Source commit |\n| --- | --- | --- |\n| [openpresentation.org](https://www.openpresentation.org) | Current published guides, agent skills, JSON/preview workflow and downloads. Exact canonical deployment passes 321 checks across 11 pages and 18 raw resources, plus two browser flows for agent installation/navigation and JSON/SVG/PPTX downloads. Reviewed screenshots and output hashes match the accepted build. | `a85bcc77d899ce9ba1df659548be564142c16120` |\n| [pptx.dev](https://www.pptx.dev) `/inspector` and `/author` | App54 merged/live on exact READY production. Premerge Linux/Windows pass 704 units, 13 standalone controls and 39/39 browsers. Full canonical acceptance **fails (34/39 passed)** at five no-POST assertions; the bounded audit does not establish an introduced upload regression. Postmerge Linux 39/39 passes, Windows 38/39 fails initial font readiness. Preset Undo all and broader source writers remain unresolved. | `8f54228a9e38a1dcc0bf8188bcdd519795b3799a` |\n| [pptx.gallery](https://www.pptx.gallery) `/docs`, `/editor` and gallery pages | Published ColorRef/bundle guidance, Playground and Editor actions, and the canonical docs-to-editor flow are verified. | `f17e9ae5869669d5fbac3720f285652d0c37551c` |\n\nThe site uses documentation source `120a770`, whose tree matches accepted core\nPR98 commit `b1ff81db6f8714b0db1a98bde482ed8a64d0ccc9`. Core PR93/97/98/99/100/102/103 passed\npre-merge and post-merge CI. Accepted core102 is\n`578bcc6e0894129b00059258bd4ad1994a414baa`; its reviewed and accepted trees match,\nand all four required pre/post-merge runs passed on their first attempts. The\n[core102 receipts](evidence/inspector-share-acceptance-20260921/README.md#accepted-core102)\npin this documentation checkpoint without changing the site's older accepted\ndocumentation snapshot. The site's complete guides and raw resources match\nthe reviewed source; binary evidence remains linked and downloadable without\nbeing decoded into the AI-facing guide.\nCore104 `3d301f1` preserves exact postmerge OPF success and coordinated cancellation;\nit is not a complete green postmerge gate. Accepted descendant core105\n`84e914710520a7b0e777fce30e5758ee64a64924` preserves all 60 core104 evidence blobs\nand passes both exact-head workflows on their first attempts. [Compact receipts](evidence/inspector-current-actions-20260921/README.md#core-source-and-ci)\nkeep descendant acceptance separate from the canceled predecessor run.\nAccepted core106 `3847f712ccb2379952bcc8ab7c9fdbaedfd0a4ce` also passes both\npre/postmerge workflows on their first attempts. Its [compact receipt](evidence/inspector-current-actions-20260921/core106/acceptance-receipt.json)\nbinds the bounded native evidence above without completing general compatibility.\nAccepted core107 `5bc0d3f89414b382b2ce48452c7e56e5d66aaf74` has reviewed tree\n`ffdc678beadf0808bc717d67e7fc0a9ec4790127`. Both original postmerge push workflows\n**35652360502 / 35652360551** passed on attempt 1 under Node24.20.0.\n[Compact receipts](evidence/font-readiness-acceptance-20260921/README.md#core107-acceptance)\nretain earlier automatic premerge cancellations separately from the later automatic\nsuccessful pair; no rerun or accepted checkpoint relabels them.\n\nApp47 corrects the pre-app47\ncompletion adapter's rejected layout choices while preserving unchanged source\ntokens and undo history. Accepted commit `0f35352a1445f56ad4bb7c9f4c5609e01f2dd9ae`\nhas reviewed tree `203bdab509d05911f04f234d996f9c91f2b5e4f2`, green Linux/Windows\npre/post-merge CI and its exact READY canonical deployment. The historical App47 **23/24** production run passes all five new completion cases but still fails the existing\nLF Author third-popup assertion. This does not establish complete public-surface\nacceptance; the [historical App47 report](evidence/completion-acceptance-20260921/canonical/REPORT.md)\nand [earlier failed app45/app46 results](evidence/issue88-final-20260921/README.md)\nretain their evidence and unresolved causes.\n\nThe [source audit](evidence/completion-acceptance-20260921/source-preservation-audit/REPORT.md)\nidentified Author canvas/Copy/export and Inspector JSON-download normalization.\nMerged App53 at\n`e40c287b64fcbcfb85fb4a8a50641aea8e3e54a8` has the identical reviewed b33dc18\ntree and preserves those bounded raw-source\npaths and corrects order-only reimport history. A public Suggest-action guard\naddresses the observed stale Quick Input context competing with focused-editor\nCtrl+Space. Current local checks pass **627 unit tests and 29/29 browser cases\nin 88.78 seconds**, zero retries. First-attempt Linux/Windows application CI\npassed 627 unit tests and 29 browser cases per platform; artifact CI also passed.\nThe [exact READY canonical run](evidence/author-source-acceptance-20260921/canonical/REPORT.md) passes **29/29**, zero retries, with matching deployment receipts before and after. Postmerge application CI fails Linux **28/29** while Windows passes **29/29**; both pass 627 unit tests and separate artifact CI passes. The [Linux failure](evidence/author-source-acceptance-20260921/app53/postmerge-ci/README.md) stops before security assertions because five default-deck canvases remain after the shared-load toast. No rerun or canonical pass replaces that failed gate. The retained draft preview was READY but not browser-accepted.\n\nThe earlier **24/27** import-undo regression, **26/27** local popup failure and\nold-head Windows **26/27** shared-load failure remain historical evidence.\nThe fresh successful Windows job retained its sanitized timing artifact, but\nonly Author timings survived; the Inspector pagehide snapshot is missing. This\ndoes not explain or fix the old readiness delay. Phase4\ncaptured no post-fix stale-context overlap, so causal stress is inconclusive.\nThe existing suggestion-details pane remains clipped; visibility is not legibility.\nThe [postmerge trace diagnosis](evidence/author-source-acceptance-20260921/inspector-share-diagnosis/REPORT.md)\nproves wrong-document automatic share-hash publication during import.\nApp54 first corrected automatic\npublication at `60c91f6`: authoritative source/format is checked before and after\nencoding, load/navigation guards remain, and obsolete `import=hash:` is removed.\nURL transfer preserves semantics, not raw spelling. Local 659-unit/31-browser\nacceptance does not replace original first-attempt CI **35638158483**, which\npassed Linux **31/31** but failed Windows **30/31** at the unchanged 45-second\nAuthor readiness deadline. Both publication cases passed. The [frozen diagnosis](evidence/inspector-share-acceptance-20260921/app54/windows-timeout/REPORT.md)\nretains bounded slow-delivery observations with unknown cause. Late assertions\nare not an in-budget pass. Browser History tests permit prior accepted content\nuntil first new publication; held-promise controls establish the narrower race guard.\n\nThe product correction was added at `57e5e59fddbc94346f142dc12d86a916228bf2ae`, tree\n`d9c3aab543c6a886a85c6ec07dd55e5ab22590cd`. It guards current snapshots for\nexplicit Copy/JSON/Share/PPTX/PDF/Author/Deckchat actions. Pending public canvas\ndrafts commit before capture, pointer-blur rejection survives session recovery,\nand newer source/load/navigation/unmount/action invalidates late results. Raw\nCopy and accepted same-format JSON bytes are preserved; handoffs remain semantic.\nAlready-started clipboard/download effects cannot be retroactively canceled.\nThe integrated portable standalone startup verifies packaged runtime assets,\nfonts and traced schema inputs without changing dependencies or published pins.\n\nLocal runtime checks pass **692 unit tests**, **7 standalone controls**, focused\n**8/8** and full **39/39** browser cases, zero retries, with visual review. The\nimmutable 93-file app bundle\nretains stale-draft negative evidence and the initial candidate **7/8** result.\nThe latter did not establish accepted pasted source before releasing mocked PDF 401;\nfinal visible-code preconditions change no product bytes or budgets. PDF/Deckchat\nare locally mocked. CRLF ingress normalized 279 to 274 LF bytes; subsequent exact\nactions preserve the accepted buffer, not that ingress boundary.\n\n**The original 57e packaging gate failed.** First-attempt CI **35645493900** failed Linux and\nWindows typecheck on archived `.spec.ts` evidence copies after each platform\npassed 692 units and 7 standalone controls. The exact-head preview failed with\n`module_not_found`; precise Vercel compiler logs were unavailable. [The first-attempt receipt](evidence/inspector-current-actions-20260921/app54/first57-ci/REPORT.md)\nrecords skipped build/browser steps and no uploaded artifacts. The passing runtime build\npredates those copies. A byte-identical `.ts.txt` archive correction produced captured App54 head\n`6dfc2584698c1306505929a2bc3e427996d66563`, tree\n`f3279495f7685945b7541c90d24f7239407639a0`. Its final-tree local typecheck passes\nwith all 305 product/test inputs unchanged. [The corrected receipt](evidence/inspector-current-actions-20260921/app54/corrected6df/commit-receipt.json)\nbinds its 106-file evidence bundle. [Exact-head CI 35646213757](evidence/inspector-current-actions-20260921/app54/corrected6df/ci/REPORT.md)\npasses 692 units, seven standalone controls, typechecks and build on both platforms;\nLinux passes **39/39** browsers. Windows executes **zero browser tests** because\nstandalone startup fails with `EPERM` while statting its packaged React dependency\nlink. No timing or test-result artifacts were uploaded. The exact-head preview\nis READY, which is metadata only; at that 6df checkpoint App54 was unmerged and undeployed to production.\nThe [current ledger](evidence/inspector-current-actions-20260921/README.md)\nretains the separate 60c readiness, 57e packaging and 6df startup failures. Production then remained App53 `e40c287`, with its original\nfailed Linux postmerge gate preserved separately from canonical **29/29**.\nThe startup-link correction at 4338e57\nthen reached all browser cases: Linux **39/39**, Windows **38/39** in first-attempt\nCI **35649707689**, with 692 units and 13 standalone controls passing per platform.\nThe sole Windows failure was initial canvas-title visibility at five seconds,\nbefore any edit/recovery operation; correct incoming source remained at loading\nfonts. Late font acquisition and eleven incomplete responses at teardown do not\nestablish a permanent stall or a dominant cause. The [failed gate](evidence/font-readiness-acceptance-20260921/README.md#preserved-failed-windows-gate)\nis preserved.\n\nApp54 font-preparation revision `a4eb88ab7aa585c9efb91de4c190c1f1c0c7d0eb`, tree\n`031d893855a540fd2a4d2ec2162605817b8dde16`, overlaps font acquisition with converter\nwarmup while readiness still waits for both. The offline converter barrier and all\n**33 faces / 9,317,044 bytes**, manifest, substitution/measurement policy and\n`document.fonts.ready` gate remain unchanged. Fresh local Node24.21.0 checks pass\n**704 units, 13 standalone controls and 39/39 browsers**, zero retries, including\nunchanged offline export/reimport. Immutable app evidence\nretains reviewed images, exact source/output bindings and the original failed run.\n\n**The original a4eb gate failed:** first-attempt application **35654753237** passes\nLinux **39/39** but fails Windows **38/39**; both pass 704 units, 13 standalone\ncontrols, typecheck and build. The Open in Author URL assertion exceeds its existing\nfive-second deadline at `inspector-actions.spec.ts:176`; later source checks are\nnot reached. Original4338 font readiness passes in this run. No navigation cause\nor data-loss finding is established. The [frozen final audit](evidence/font-readiness-acceptance-20260921/app54/final-a4eb-ci/release-audit.json)\nretains exact source/artifact bindings and the original failed trace; that failure remains preserved.\nThe Python artifact workflow is not applicable under its full-PR path filters,\nnot a fresh pass. Exact-head preview is READY metadata only; an\nunauthenticated request redirects to sign-in, with no preview-browser acceptance.\nAt that a4eb capture App54 was unmerged and production remained App53\n`e40c287`. The separate suggestion-details candidate remains unreleased and supplies\nno acceptance here. No local/browser pass broadens native compatibility.\nThe [bounded navigation diagnosis](evidence/font-readiness-acceptance-20260921/README.md#author-navigation-diagnosis-and-prospective-policy)\nrecords Loading Author and a delayed successful script response: 1490 bytes inferred\nfrom ETag, 766 compressed bytes recorded, actual body absent. Later DOM does not\naccept unreached assertions or identify a cause. App54 accepted merge\n`8f54228a9e38a1dcc0bf8188bcdd519795b3799a` retains reviewed 79ba tree\n`3639d3c14daec94d13711fa2b10f2b927df45eca`, preregisters `waitForURL(load)` before\nthe real action, matching `page.goto` within the unchanged 45-second test and default\nfive-second content budgets. It retains all oracles but deliberately removes the\nincidental five-second navigation deadline. Fresh local **39/39**, zero retries,\npasses in 112.780048s with reviewed source/images and prepared-tree build/typecheck.\nThe immutable app bundle\nretains original failures. Units/standalone controls were not repeated locally;\nfresh first-attempt application **35659187971 passes 704 units, 13 standalone\ncontrols and 39/39 browsers on both Linux and Windows**. Exact-head preview was\nREADY/protected, not browser accepted. The identical reviewed tree is merged/live\nat 8f on READY deployment `dpl_H1FtXx1QSuGxn3MwzJwWJpGtRg8b`, but full canonical\nacceptance **fails (34/39 passed)** at five no-POST assertions observing Clerk environment\nPOSTs. The [safe audit](evidence/font-readiness-acceptance-20260921/app54/canonical8f/write-audit/REPORT.md.txt)\nrecords ten such requests, nine with HTTP200/zero-length bodies and one incomplete.\nNo fixture-content needle was detected in captured fields; uncaptured data remains\nunknown. Three final action page-error assertions were not reached; the two share\ncases passed theirs. The prior auth/config comparison is 28/29 identical, with only\npackage scripts changed. Production is kept without a rollback, test change or\nrerun; the strict gate remains failed. Raw authentication-bearing diagnostics\nremain private. Postmerge CI 35660464578\nfinishes failed: Linux 39/39 passes while Windows 38/39 fails, with 704 units/13 controls/typecheck/build\npassing on each. Windows fails initial gallery-rail title visibility after 5,000ms\nwith correct source, clean schema and Loading slide fonts; later editing/export/\nreimport checks were not reached. The [final audit](evidence/font-readiness-acceptance-20260921/app54/merged8f/postmerge-ci/REPORT.md.txt)\npreserves this separate failed gate without cause inference or a retry. No canonical\npass or general native/font acceptance is claimed. That September 21 checkpoint was paused; the user resumed work on September 29. See the [current source checkpoint](handoff-runtime-2026-09-29.md) for ongoing repairs and release holds.\n\nSeparate local negative controls confirmed that preset Undo all discarded New run\nand imported replacement documents. The guarded correction is now preserved in\ndraft app #58: independent\nreview and local Node 24 checks passed (716 units, 13 standalone controls and\n49 browsers without retries). Original Linux/Windows CI could not start because\nof the account payment/spending-limit restriction; no application CI or production\nacceptance is claimed. Unbusy asynchronous account replacements still need a\nsynchronous invalidation guard and held-response control.\nOther local source writers still require their separate preservation checks. These unresolved local findings and raw imported-file/account/agent/metadata boundaries remain outside\nApp53 and App54. See the [App53 ledger](evidence/author-source-acceptance-20260921/README.md)\nand its immutable application evidence links. Issue88 remains OPEN. Native/font\ncompatibility, required repair and release gates remain separate; geometry is\ndeferred as the coordinated set below. The unimplemented worker candidate remains in the [handoff](handoff-2026-09-21.md).\n\nThe five coordinated geometry drafts (core94, renderer27, editor25, PPTX42,\nsite40) remain unmerged. In particular, site40 is not independently shipped.\n\n## Explicitly not shipped\n\n| Topic | Tracker | Do not describe as done |\n| --- | --- | --- |\n| Linux vs Chromium native-width residual at the 0.1px gate | [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24) | Rounding that fixes Linux but breaks macOS is rejected |\n| General native PowerPoint fidelity beyond the supported footer placeholders | [opf#87](https://github.com/OpenPresentation/opf/issues/87) | Finite B/C and bounded Carlito edit controls above are accepted evidence, as is one finite mixed-size table edit/save/reopen ([evidence](evidence/windows-native-mixed-edit-20260922/README.md)). Tab tolerance, general mixed-size table layout and preview/native wrapping, physical glyph identity/fallback/synthesis, embedding and general layout/reflow fidelity remain open; supported native footer placeholders, self-import and tagged furniture do not certify arbitrary Office behavior |\n| Public-surface acceptance checklist | [opf#88](https://github.com/OpenPresentation/opf/issues/88) | Shipping features does not establish every acceptance item; use the checklist and deployment receipts |\n| HarfBuzz / prepared-glyph shaping | Archive branches `codex/archive-shaping-20260915` | Prototypes are preserved, not in npm |\n| Selectable vector PDF | [pdf plan](plans/pdf-export.md) | Shipped in opf-render 0.12.0 (opf-render#90; the browser download entry `@openpresentation/opf-render/export-browser` ships there too); PDF/UA, PDF/A and viewer coverage beyond pdf.js, PDFium and poppler remain open |\n| General SVG diagrams / Mermaid | [diagrams plan](plans/diagrams-svg.md) | Embedded SVG \u2260 native editable primitives |\n| Full visual editor / IME / bidi / repair loop | [developer adoption](plans/developer-adoption-20260915.md) | Schema support \u2260 WYSIWYG coverage |\n\nCLI 0.9.2 and earlier do not render or export PPTX; CLI 0.10.0 adds `opf render`, `opf export` and `opf import`\nthrough the optional peers opf-render and opf-pptx ([CLI reference](cli.md)). The Node `toPng` / `toPdf`\n(`svgToPng` / `svgToPdf` before 0.18) APIs stay Node-only; renderer 0.12.0 adds the separate `@openpresentation/opf-render/export-browser` entry for browsers.\nCore's file API (`open`, `save`, `convert`) is Node-only too: it reads files, font files and the engines' native packages. From\ncore 0.18 the root resolves to a browser-safe build in a browser or a worker, with the same names, where those three reject\nwith `node-only`; every other core entry is browser-safe. In a browser, use the engines' browser entries.\n\n## Predecessor notes\n\n| Older set | Relationship |\n| --- | --- |\n| core 0.17.0, CLI 0.17.1, renderer 0.17.1, PPTX 0.17.0, editor 0.17.0 | Previous coordinated set (FA-31 furniture variables, `@openpresentation/opf/node`, `parse`/`stringify`, `opf convert`). The 0.18 set is breaking: one root import replaces `/node`, the `toSvg`/`ingest` names and `convert` options, organization logos, and the consolidated CLI. |\n| core 0.16.0, CLI 0.16.0, renderer 0.16.0, PPTX 0.16.1, editor 0.16.0 | Previous coordinated set (Markdown decks, used-face SVG embedding, optional renderer converters, nested placeholder groups, `@openpresentation/cli/api`). The 0.17 set is breaking: header and footer flags become `{{slide.number}}`-style variables (FA-31), `readDeck`/`writeDeck` become `parse`/`stringify`, and `@openpresentation/cli/api` is replaced by `@openpresentation/opf/node`. |\n| core 0.15.1, CLI 0.15.0, renderer 0.15.0, PPTX 0.15.0, editor 0.15.0 | Previous coordinated set (OPF 0.15, format audit wave C; CLI 0.15.0 bundles core 0.15.1). The 0.16 set is additive for documents; the renderer's converters and font packages became optional peers (RR-63), so a 0.16 host installs the ones it uses, and the CLI depends on core instead of bundling it (RR-62). |\n| core 0.14.0, CLI 0.11.0, renderer 0.14.0, PPTX 0.14.0, editor 0.14.2 | Previous coordinated set (format audit waves A and B, RR-55 and RR-56; CLI 0.11.0 bundles core 0.13.0). The 0.15 set is breaking: a 0.14 document's catalog shape (`catalogs.<kind>.records`), catalog-id languages and `design.slideImage` are invalid in 0.15, and the 0.15 engines need the host to register the default catalog. |\n| core 0.13.0, CLI 0.11.0, renderer 0.13.1, PPTX 0.13.2, editor 0.12.1 | Previous coordinated set (chart and table data, RR-54). |\n| core 0.12.2, CLI 0.10.1, renderer 0.12.2, PPTX 0.12.4, editor 0.11.3 | Previous coordinated set (the first set whose `engines.node` is the open-ended `>=22`). The 0.13 set adds chart and table data (RR-54, [contract](chart-table-data.md)): strict chart numbers (a value is a finite number or a strict decimal string; `\"12%\"`, `\"$5\"` and `\"Q1\"` are a gap with a `chart-value-not-numeric` warning, never a guessed number), number formats (`DataColumn` `{ name, format }`), top-level `datasets` with `{ \"dataset\", \"fields\" }` references and `chart.mapping`, in core 0.13.0, previewed by renderer 0.13.0, exported and re-imported by PPTX 0.13.0 (`OPF_DATASETS_V1` and `OPF_DATA_V1` provenance), edited in the editor 0.12.x data grid and imported by CLI 0.11.0 (`opf import-data --dataset`). PPTX 0.13.0 also vendors pptxgenjs-plus 4.3.4 (was PptxGenJS 4.0.1) and PPTX 0.13.1 bundles for browsers again. Renderer 0.13.1 and PPTX 0.13.2 pin Sharp 0.35.5 (GHSA-wq5f-xc86-pv6w; output unchanged); editor 0.12.1 replaces 0.12.0, which was never published. Renderer, PPTX and editor raise their core floor to `^0.13.0` and the renderer peer to `^0.13.1` together. |\n| core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. PPTX 0.12.3 corrects the table range of a chart's embedded workbook (apostrophe-quoted sheet references and bubble-series references), which made Keynote drop category charts on import (opf-pptx#162, opf-pptx#163; every other part of the package is byte-identical). Core, renderer and editor are unchanged and keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. Core 0.12.1 deprecates the six plural audience ids (`executives`, `investors`, `customers`, `sales-team`, `marketing-team`, `regulators`) in favour of the singular ids: additive catalog data, validation warns and never errors, and no geometry moves, so renderer, PPTX and editor keep the core floor `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.1, editor 0.11.1 | Previous coordinated set (PowerPoint lists only the deck's fonts). PPTX 0.12.2 also names the deck's font in the theme's `Viet` (Vietnamese) and `Uigh` (Uyghur) per-language script entries, which PowerPoint applies to `vi-VN` and `ug` runs and which kept Office's Times New Roman and Arial for those two languages (RR-17; every other language is byte-identical). Editor 0.11.2 adds the `slide-sizes` and `purposes` switch dimensions, `SLIDE_SIZE_PRESETS` and the Slide size and Purpose selects in the Design panel (RR-41, additive API). Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.0, editor 0.11.0 | Previous coordinated set (the 0.12.0 train). PPTX 0.12.1 writes the deck's fonts so that PowerPoint's font list shows only the fonts the deck uses (FF-05: schema-order `presentation.xml`, an own notes theme, no east-asian or complex-script font on runs, and a theme east-asian slot that is never empty); editor 0.11.1 stops the restore prompt showing a literal `null`. Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.9, editor 0.10.6 | Previous coordinated set (native header/footer placeholders, SVG pictures, `fromPptx` import signals; the published 0.11.4 train measured in the font-fidelity program). Core 0.12.0 moves geometry (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored), so renderer 0.12.0, PPTX 0.12.0 and editor 0.11.0 raise their core floor to `^0.12.0` and the renderer peer of PPTX and editor to `^0.12.0` together; the set adds templates and variables, numbered lists, footnotes, citations and captions and chart options (additive schema), vector PDF with selectable text and the browser export entry, the `<opf-deck>` player, the editor's slide management, autosave, data grid, find and replace, image crop, Review panel and fill UI, and the shared JSON Patch module. CLI 0.10.0 bundles core 0.12.0 and adds `opf audit`, `from-md`, `to-md`, `diff`, `merge`, `format`, `render`, `export` and `import`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.8, editor 0.10.6 | Previous coordinated set (PPTX 0.11.8 re-imports wrapped rich text as one authored payload; CLI 0.9.2 bundles core 0.11.4). PPTX 0.11.9 writes a deck footer's first text, date and slide number as native PowerPoint Header & Footer placeholders (every export also carries the footer placeholders on its master, layout and notes master, so Insert > Header & Footer works), exports an SVG image as a native SVG picture over a PNG fallback (rasterized in Node by the optional opf-render peer or `options.svgRasterizer`) and adds the opt-in `fromPptx(bytes, {signals: true})` import signals; core floor `^0.11.4` unchanged. |\n| core 0.11.4, CLI 0.9.1, renderer 0.11.9, PPTX 0.11.7, editor 0.10.6 | Previous coordinated set (the design fields compose and export natively). PPTX 0.11.8 re-imports rich text that wraps over several native lines as one authored payload (the export records the line count; decks exported by 0.11.7 import as before) and keeps the core floor `^0.11.4`; CLI 0.9.2 bundles core 0.11.4 (CLI 0.9.1 bundled core 0.11.3) and still requires Node 24. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.6, editor 0.10.5 | Previous coordinated set (the native chartex export by default). Core 0.11.4 composes the design fields (logos on covers and section slides, `contentDirection`, `chartPrimary`, picture bullets, header and footer logos, the accent font), aligns a cover's tag and subtitle with its title, and sizes picture bullets and furniture images as PowerPoint does, so renderer, PPTX and editor raise their core floor to `^0.11.4` together; renderer 0.11.9 draws those fields and applies the tag contrast rule (FF-61: the tag draws in the text colour when the scheme primary is under 4.5:1); PPTX 0.11.7 exports them natively, writes every chart's text at the preview's size (FF-62: 12 pt, not 9 pt), writes slide sections as PowerPoint's section list and restores the authored form of a fresh export on import (a root payload returns as `slides.N.text`, `.items`, `.chart` ... rather than one typed block); editor 0.10.6 is a floor bump. CLI 0.9.1 still bundles core 0.11.3. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.5, editor 0.10.5 | Previous coordinated set (the slide tag draws in the scheme primary colour and PPTX writes it as `a:schemeClr accent1`; the playground loads its base faces through `extraLazyFonts`). PPTX 0.11.6 exports the treemap, histogram, pareto, box-and-whisker, waterfall and funnel charts as native chartex parts by default (`toPptx({chartex: 'auto'})`, confirmed in desktop PowerPoint; `world` stays a clustered column with `chart-data-adapted` because PowerPoint's map needs online geodata; pass `chartex: 'fallback'` for the previous output) and gives chartex text the deck's label colour and font. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.6, PPTX 0.11.4, editor 0.10.4 | Previous coordinated set (the 100-layout catalog and its geometry, category-axis label rotation, quote and slide image re-import). Renderer 0.11.7 adds the `extraLazyFonts` registry option and `splitStartupFaces` (a browser host can start with Roboto Regular alone and load its other base faces on demand); renderer 0.11.8 draws the slide tag in the scheme primary colour; PPTX 0.11.5 writes the tag run as `a:schemeClr accent1` where the deck theme holds the primary (the colour is unchanged); editor 0.10.5 loads its playground base faces through `extraLazyFonts`. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.5, PPTX 0.11.3, editor 0.10.3 | Previous coordinated set (native classic and chartex chart previews, opt-in chartex export, face-level lazy fonts and the font gate's render options). Core 0.11.3 adds the pinned pptx.gallery default catalog and the 70 legacy gallery layout ids (layouts 30 to 100; 25 carry a `composition` or `contentBox` contract, which moves geometry, so renderer, PPTX and editor raise their core floor to `^0.11.3` together); renderer 0.11.6 rotates and skips dense category-axis labels; PPTX 0.11.4 re-imports quote and slide image payloads and writes theme `a:ea`/`a:cs` only where a script font is selected; editor 0.10.4 is a floor bump; CLI 0.9.1 bundles core 0.11.3. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.1, PPTX 0.11.0, editor 0.10.0 | Previous coordinated set (lockstep floors, Intos and the open families, selected-name export). Renderer 0.11.2 adds script-face loading (`scripts: 'auto'`); PPTX 0.11.1 adds `design.watermark` export; editor 0.10.2 loads the fonts a document needs before every render (FF-41). Renderer 0.11.3 previews every kept classic chart type natively; PPTX 0.11.2 exports the native construct for each kept classic chart type (with `chart-data-adapted` diagnostics where data is adapted) and writes theme colour references for table and text colours. Renderer 0.11.4 previews the seven chartex chart types natively (the world map as a non-geographic tile grid), keeps the Latin Noto Sans replacement for script schemes under `scripts: 'auto'`, shapes Noto Sans Mongolian, and bundles Raleway and Playfair Display (94 lazy faces); PPTX 0.11.3 adds the opt-in `toPptx({chartex: 'native'})` export of the chartex chart types (the default output is unchanged) and always imports chartex charts. |\n| core 0.11.0, CLI 0.9.0, renderer 0.9.0, PPTX 0.9.1, editor 0.8.0 | Previous coordinated Node 24 set (ColorRef, shared furniture). Renderer and PPTX had different core floors from 0.10.x. |\n| core 0.10.0, renderer/PPTX/CLI 0.8.0, editor 0.7.0 | Previous coordinated Node 24 baseline. Lint and furniture landed across 0.10.0/0.8.0 then layout-placeholder fixes in 0.10.1/0.8.1/0.7.1. |\n| Node 20 | Not valid for these packages |\n| Node 22 / 26 | Declared by the current set (`engines.node` `>=22`) and tested, but Node 24 is the toolchain the evidence runs on. Releases up to core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.3 and editor 0.11.2 declared `24.x`, so npm on Node 22 or 26 silently picked an older release |\n\nDo not install sibling `../opf-render` dist folders when following the\nquickstart. Packed and registry consumers must resolve `@openpresentation/*`\nfrom npm.\n"
|
|
37
|
+
"markdown": "# Compatibility matrix\n\nPublished registry evidence for the coordinated Node 24 toolchain. This matrix\nis the honest supported subset for [the developer quickstart](quickstart.md).\nIt is not universal Office parity and does not describe archived prototypes as\nshipped.\n\n**Verified artifacts (2026-10-17):** core 0.20.5, renderer/PPTX/editor 0.20.4,\nCLI 0.20.1 and Gallery 3.0.0.\n[Artifact verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) binds\nthe selected archives to their release commits and trusted publication checks.\nEarlier matrices and native observations retain their historical identities.\nComplete native compatibility remains a separate gate.\n\n## Published 0.20 contract\n\nLayout `areas` uses nested arrays of cell names. Repeated names span rectangular\nregions and `.` denotes an empty cell. Gallery owns every default record;\nGallery 3 is required for catalog compatibility generation 3, while per-kind\nschema versions remain v2. Core 0.19 and later consume the Gallery package and\ndo not export `@openpresentation/opf/catalog` or ship a catalog snapshot.\n\nThe 0.20 engines share measured quote allocation, the 32 reference pixel\nstarting size for lists without an authored size, neutral structural borders\nand default right alignment for numeric table columns. Core and CLI forward\nregistered catalogs during PPTX import. PPTX preserves stretch images with\nzero crop through native save/re-import. These published contracts do not\nimply universal Office or full-catalog visual parity.\n\n## Current published package contracts\n\n| Package | Version | Dependency contract |\n| --- | --- | --- |\n| Core | 0.20.5 | Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| CLI | 0.20.1 | Core ^0.20.5, Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| Renderer | 0.20.4 | Core ^0.20.5; optional pinned font/converter peers |\n| PPTX | 0.20.4 | Core ^0.20.5, Gallery ^3.0.0; optional renderer ^0.20.4 |\n| Editor | 0.20.4 | Core ^0.20.5; optional renderer ^0.20.4 |\n| Gallery | 3.0.0 | Independent owner of every default catalog record; 28 intent layouts |\n\n## Runtime\n\n| Requirement | Status |\n| --- | --- |\n| Node.js | `engines.node` is `>=22` on every package below; **Node 24** is the coordinated toolchain these records are verified on (the 0.12.2 set and later are tested on Node 22, 24 and 26) |\n| Package managers | npm for published installs; this repo uses pnpm 10.33.2 for core development |\n| Account / model / hosted API | Not required |\n| Operating systems | macOS, Linux, Windows for Node APIs; browser entrypoints are separate |\n\n## Historical coordinated 0.18 published packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/opf` | 0.18.4 | optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0`, which the Node build loads for `convert`, `open` and `save` |\n| `@openpresentation/cli` | 0.18.1 | `@openpresentation/opf@^0.18.3` (a regular dependency, no bundled copy); optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0` |\n| `@openpresentation/opf-render` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peers (RR-63): the converters `sharp`, `@resvg/resvg-js` and `pdf-lib` and the `@expo-google-fonts/*` font packages, which a host installs for the formats and fonts it uses |\n| `@openpresentation/opf-editor` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n| `@openpresentation/opf-pptx` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n\nThe 0.18 set is the consistent-API release ([design](programs/release-readiness/0.18-consistent-api.md)), breaking:\n- **One import (RR-70).** `import * as opf from \"@openpresentation/opf\"` works in every runtime through conditional\n exports, and `/node` is removed. `defaultCatalog` lives at `@openpresentation/opf/catalog` only.\n- **Names (RR-73) and options (RR-74).**\n - `ingest` replaces `importData` (and `opf ingest` replaces `opf import-data`).\n - `toSvg`, `toPng`, `toPdf` and `toHtml` work on decks.\n - The editor exports with `convert`.\n - `convert` takes `text`, `raster`, `charts`, `images`, `fonts` and `name`.\n- **Logos (RR-71).** Logos come from `organizations[].logo` (`full`, `stacked`, `icon`, `wordmark`, `onLight`/`onDark`),\n placed with `var:organization.logo.*`. `LogoSet` and zone `logo: true` are removed.\n- **PowerPoint (RR-72).** Shared furniture is written once on the PPTX slide master or layout.\n- **CLI (RR-75).** `opf convert` absorbs `render`, `export` and `import`. There is one output convention, plus\n `opf doctor` and a multi-file `validate`.\n\nSee the 0.18.0 and 0.18.1 entries of the [changelog](../CHANGELOG.md) and the 0.18.0 entry of the [CLI changelog](../packages/cli/CHANGELOG.md).\n\nCore starts at **0.18.1**. `opf-v0.18.0` was tagged, but its publish run failed before `npm publish`: a core test\nspawned the CLI, which the publish workflow had not built. So 0.18.1 is the first published 0.18 core.\n\nThe 0.17 set (core, editor and PPTX 0.17.0, renderer 0.17.1, CLI 0.17.1) was the lockstep minor after 0.16, also breaking:\n- **Furniture variables (FA-31):** header and footer text uses the `{{slide.number}}`, `{{slide.section}}` and\n `{{deck.slideCount}}` variables, and the `HeaderFooterItem` flags are removed.\n- **`@openpresentation/opf/node` (RR-62):** `convert`, `open` and `save` work from files and bytes. `parse` and\n `stringify` replace `readDeck` and `writeDeck`. `opf convert` is new, and `@openpresentation/cli/api` is removed.\n- **Fixes and additions:**\n - deterministic data import;\n - `validate` and `paginate` that plan script faces (RR-59);\n - the Arabic and Latin preview fixes;\n - `renderSvg(deck, { textAsPaths: true })` (RR-64 phase 1);\n - the Windows CLI path fixes (RR-66).\n\nSee the 0.17.0 entries of the [changelog](../CHANGELOG.md) and the 0.17.1 entry of the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nRenderer 0.17.1 is a patch on the 0.17 line: SVG never embeds a face whose OS/2 `fsType` forbids it (RR-76), embedded faces are cut to the glyphs a slide draws (RR-65, mean example slide 2.4 MB to 59 KB), and `textAsPaths` shapes with HarfBuzz and keeps an invisible text layer (RR-64 phases 2 and 3).\n\nThe CLI starts at **0.17.1**. `cli-v0.17.0` was tagged, but its publish run failed before `npm publish`, because the\nrelease commit carried a stale generated gallery tracker. So 0.17.1 is the first published 0.17 CLI.\n\nThe 0.16 set (core, CLI, renderer and editor 0.16.0, PPTX 0.16.1) was the lockstep minor after 0.15 (owner decision 2026-10-08: no 0.15.x patch train). It adds nested\nplaceholder groups in layout records (FA-26), `Table.alt` (FA-27), the `opf/language-tag` warning (FA-29), Markdown\ndecks (`.opf.md`) in every command with core `readDeck`/`writeDeck` (RR-60), SVGs that embed only the faces they draw\n(RR-61), the CLI's programmatic `@openpresentation/cli/api` (RR-62), a lighter renderer install with optional\nconverters and fonts and the `/png`, `/pdf` and `/html` subpaths (RR-63), and the developer-contract fixes (RR-59).\n0.15 documents are 0.16 documents. See the 0.16.0 entries of the [changelog](../CHANGELOG.md) and the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nThe 0.15 set (core 0.15.1, CLI 0.15.0, renderer, PPTX and editor 0.15.0) was the breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nSee the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore 0.15 started at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nwas the first published 0.15 core and the floor of every 0.15 sibling.\n\nOPF 0.15 is a breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nThe 0.15 set reads and writes only 0.15 documents; see the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore starts at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nis the first published 0.15 core and the floor of every 0.15 sibling.\n\nFrom CLI 0.16.0 the CLI depends on core (`@openpresentation/opf`, a regular dependency, not a bundled copy). CLI 0.16.0\nexposed `@openpresentation/cli/api`, and 0.17 removes it in favour of core's `@openpresentation/opf/node`. The renderer\nand PPTX stay optional peers. CLI 0.15.0 and earlier bundled their core.\n\nInstall the complete pinned set. A caret range starting at 0.10.1 does not\ninclude 0.11.x; old consumers can install a second core and do not establish\nColorRef preview/export support. The selected renderer 0.20.2 and PPTX/editor 0.20.3 require core ^0.20.2;\nCLI 0.20.0 accepts core ^0.20.1. Pin core 0.20.3 alongside them and\nverify one installed Core so preview and export use the same composition.\nCore 0.20.2 and later center metrics when alignment is omitted; explicit alignment remains\nauthoritative.\n\nShared header/footer geometry (`furniture-flow-v2`) is published. PPTX exports\neditable slide shapes tagged `OPF_FURNITURE_V1` with provenance for controlled\nreimport. Published PPTX through 0.11.8 draws every part that way, not as native\nOffice Header/Footer objects. PPTX 0.11.9 and later (RR-11) write the footer's\nfirst text, date and slide number as native `ftr`, `dt` and `sldNum` placeholders\n(with master/layout placeholders, `p:hf` flags and a notes-master flag) at the same\ngeometry and reads them back with or without provenance; the rest stays tagged\nshapes. Native PowerPoint acceptance remains [issue 87](https://github.com/OpenPresentation/opf/issues/87).\nPPTX 0.18 (RR-72) writes a tagged part that is the same on two or more slides once,\non the slide master or a shared layout (one per background tone for `onLight`/`onDark`\nlogos; a hidden footer uses a layout that hides the master's), and reads it back from\nthere ([rr-72-master-furniture.md](programs/release-readiness/rr-72-master-furniture.md)).\n\nThe [Windows native-picture checkpoint](evidence/windows-native-picture-20260921/README.md)\nand accepted [native B/C bundle](evidence/windows-native-edits-20260921/README.md)\nrecord finite picture/furniture edits, current-content provenance reimport and\nsafe fallback, production notes packaging and two controlled reordered-file\nrefusals. Core105 publishes that evidence; PPTX47 adds the tested harness, with\nno new package version. UI image replacement changes geometry, longer header\ntext clips, and duplicated tagged headers overlap. Refused workers retain their\nfailed cleanup state separately from later empty-workspace observations.\nThis is not general native layout/reflow fidelity.\n\nAccepted core106's [tab and font checkpoint](evidence/windows-native-tabs-fonts-20260921/README.md)\nrecords plain native tab target error **0.022655487060546875pt** and tab/literal\ndifference **0.022678375244140625pt**, both above the unchanged **0.02pt** gate.\nIts bounded four-face Carlito edit/save/reopen control passes exact text/style\npersistence, zero observed bounds drift, matching rasters and owned font cleanup.\nMixed-size table fidelity, physical glyph-font identity, fallback/synthesis and actual embedding remain\nopen; embedding was disabled for this control. The Windows supervisor retains sole\nOffice control. This documentation task reads evidence and makes no Office calls.\n\nThe later [read-only font inventory](evidence/windows-native-font-inventory-20260921/README.md)\nretains the four Carlito text styles but reports both Carlito and unexpected\nAptos in `Presentation.Fonts`. The native font allowlist fails, and no embedding\nwas attempted. The original parent report incorrectly fails cleanup because of\na Windows PowerShell 5.1 JSON-array parsing defect; raw stages and registration\nrows establish one owned close and four removals in a separately labeled offline\naudit. The raw failure remains intact. Collection names and flags do not identify\nthe physical font used for each glyph.\n\nThe [read-only mixed-table observation](evidence/windows-native-mixed-table-20260921/README.md)\nretains all 245 characters, one literal tab and five authored runs, with outer\ngeometry within 0.02pt and confirmed owned close/font cleanup. Native soft-line\nboundaries are 92/194 versus the estimated preview's 78/172, and native default\ntab spacing is 72pt. These finite content/style results do not pass table\nedit/save/reopen, browser/native raster agreement or physical glyph identity.\nThe accepted [nine-pair offline tab analysis](evidence/windows-native-tab-analysis-20260921/REPORT.md)\nfinds a 0.05pt-compatible pattern in the observed character starts, with finer saved\ntab coordinates. These inputs do not distinguish relative versus absolute placement\nor establish an internal engine cause. The 0.02pt native tab gate remains failed;\nthe separate 0.1px renderer gate is unchanged. Accepted core108 `9b277e1` and\ncore109 `b2711549` publish bounded evidence only. Windows-owned [core110](https://github.com/OpenPresentation/opf/pull/110)\nis merged as `4f7a4bd494f1a873319eff897423d301d1cfc9d6`, from reviewed fc3c36e\nwith four required PR checks passing. [Renderer30](https://github.com/OpenPresentation/opf-render/pull/30) is now merged\nas `c8d7d5ca1f67a7b39f70c7c4bd14577a865b175b`, with exact-head CI 35661051100\npassing. The supervisor reports postmerge 35661504472 also passed. Its companion\nsource preserves rich-tab advances/spans; the [accepted Windows wrap-up](handoff-windows-native-2026-09-21-wrap-up.md)\nrecords bounded source-linked rendering/browser checks and the original missing-test\nCI failure. These checks do not update the frozen registry consumer. Core111\n`3c5048522714365a41d9b5b9ba81620affae718b` publishes the font-inventory evidence\nabove with both PR workflows green; its postmerge workflows were started at the\nfinal notice, not recorded as passed. Package/lock/release/site pins, schema,\ngoldens and tolerances are unchanged. Native allowlist and physical-glyph/embedding\nacceptance remain open; no new package train or broad native pass is inferred.\n\n## Independently released packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/gallery` | 3.0.0 ([independent artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md); prior 2.0.0 and 1.0.0 remain historical) | nothing (its `GalleryCatalog` type has the shape of `@openpresentation/opf`'s `Catalog`) |\n\nThe gallery (RR-78) is released from its own repository, OpenPresentation/gallery, on its own version line (`vX.Y.Z`\ntags, `gallery-publish.yml` there), outside the lockstep set above and\nrecorded under `independentPackages` in `release-plan.json`: new catalog records are a minor release, a removed id or\na changed drawing a major one ([release process](release-process.md#the-gallery-lane-rr-78)). From OPF 0.19 core and the\nCLI consume the Gallery package (Gallery 2 in OPF 0.19; Gallery 3 in OPF 0.20), and core has no `/catalog` subpath; up to 0.18 core ships the same records as\n`@openpresentation/opf/catalog`.\n\n## Supported in this set\n\n| Capability | How | Notes |\n| --- | --- | --- |\n| JSON authoring | `*.opf.json` plus CLI `opf create` | Local files only |\n| Bundled examples catalog | `@openpresentation/opf/examples` | **127** decks; the quickstart JSON is a docs fixture, not a 128th catalog entry |\n| Validate | `validate` / `opf validate` | One read-only checker: format, references, policy, accessibility, layout and content findings ([guide](validate.md)); no network catalog fetch |\n| Color references | `ColorRef`, `variables`, `resolveColorRef` | Core schema/resolution, renderer preview and PPTX resolved colors are shipped. Supported theme-slot colors write native `schemeClr` references; arbitrary native Office recoloring and full visual fidelity remain separately scoped. |\n| Embed catalog records | `embed` / `opf embed` | Embeds every record a document references, in the group it resolves in, so it renders with no catalog registered; remote media and data remain explicit host concerns. |\n| Offline fonts | `loadFonts` (`/fonts-node`) | Bundled Roboto pack (`pack: 'base'`) or the office pack; hashed files; the handle is passed as `{ fonts }` to every deck-level call |\n| Composition | `composeSlide` | Includes shared headers/footers |\n| Pagination | `paginate` / `opf paginate` | Returns mappings; preserves source |\n| Edit + undo | `@openpresentation/opf-editor` `createEditorSession` | JSON Patch undo/redo |\n| JSON Patch CLI | `opf edit` | No persistent CLI undo history |\n| SVG preview | `toSvg(deck)` (every slide) / `toSvg(deck, n)` (slide n, counted from 1) (opf-render 0.18; `renderSvg` / `renderSlideSvg` before) | Local; the same `{ fonts }` as layout; `text: \"paths\"` draws the text as glyph outlines |\n| Convert, export and import files from code | `convert`, `open` and `save` of `@openpresentation/opf` in Node (core 0.18.0 and later; core 0.17 had them at `@openpresentation/opf/node`; CLI 0.16.0 had `exportDeck` / `importDeck` in `@openpresentation/cli/api`) | PDF, PNG, SVG and PPTX out, PPTX in, deck forms both ways, with the bundled fonts, no network and no clock; calls opf-render and opf-pptx as optional peers and throws `OPFExportError` / `OPFImportError` with the install command when one is missing. `opf convert` runs the same engine (CLI 0.18; `opf render`, `opf export` and `opf import` before it) ([OPF files in Node](node.md)) |\n| Read and write a deck in JSON, YAML or Markdown | `parse` / `stringify` (core 0.17.0 and later; `readDeck` / `writeDeck` in 0.16) | One entry point for the three text formats; `parse` throws `OPFValidationError` with located findings |\n| PNG | `toPng` (opf-render 0.18; `svgToPng` before) | Node raster of a deck or of SVG slides |\n| PDF | `toPdf` (opf-render 0.18; `svgToPdf` before) | opf-render 0.12.0 and later (RR-12, opf-render#90): **vector with selectable text by default** (embedded TrueType subsets of the supplied/bundled fonts, ToUnicode, links, metadata, tagged structure); `raster: true` keeps the image-per-slide output (`mode: \"raster\"` before 0.18). Renderers up to 0.11.9: raster-backed, not selectable text. Not a PDF/UA or PDF/A claim; see the renderer's `docs/evidence/rr-12-vector-pdf.md` for reader limits |\n| Editable PPTX export | `toPptx` | OPF \u2192 PPTX serialization. Text is one editable box per wrapped line, so edited text does not reflow in PowerPoint or Keynote, and a wrapped title is not the Title placeholder; the opt-in `bodyText: \"native\"` mode (CLI `--body-text native`, from opf-pptx and CLI 0.22.0) writes whole paragraphs, a wrapped title as the Title placeholder. Shared furniture is written on the master or layout; supported footer placeholders and tagged parts retain controlled re-import provenance. General native Office behavior remains separately scoped |\n| Agent skills | `opf skills install` | Offline after the CLI is installed |\n| Browser canvas | `@openpresentation/opf-editor/canvas` | Host must supply font bytes |\n\n## Public sites\n\nThe current coordinated adoption is recorded in the [final adoption note](evidence/rr-85-final-adoption-2026-10-16/README.md). The following source, CI and canonical production results are the historical September 21 checkpoint, recorded in the\n[font-readiness checkpoint](evidence/font-readiness-acceptance-20260921/README.md),\n[prior Inspector actions checkpoint](evidence/inspector-current-actions-20260921/README.md),\n[earlier publication checkpoint](evidence/inspector-share-acceptance-20260921/README.md),\n[source-preservation checkpoint](evidence/author-source-acceptance-20260921/README.md),\n[completion checkpoint](evidence/completion-acceptance-20260921/README.md),\n[earlier acceptance ledger](evidence/issue88-final-20260921/README.md) and\n[handoff](handoff-2026-09-21.md). [Issue88](https://github.com/OpenPresentation/opf/issues/88)\nremains open. Package adoption, deployed features and complete workflow\nacceptance are separate claims.\n\n| Surface | Deployed scope and acceptance | Source commit |\n| --- | --- | --- |\n| [openpresentation.org](https://www.openpresentation.org) | Current published guides, agent skills, JSON/preview workflow and downloads. Exact canonical deployment passes 321 checks across 11 pages and 18 raw resources, plus two browser flows for agent installation/navigation and JSON/SVG/PPTX downloads. Reviewed screenshots and output hashes match the accepted build. | `a85bcc77d899ce9ba1df659548be564142c16120` |\n| [pptx.dev](https://www.pptx.dev) `/inspector` and `/author` | App54 merged/live on exact READY production. Premerge Linux/Windows pass 704 units, 13 standalone controls and 39/39 browsers. Full canonical acceptance **fails (34/39 passed)** at five no-POST assertions; the bounded audit does not establish an introduced upload regression. Postmerge Linux 39/39 passes, Windows 38/39 fails initial font readiness. Preset Undo all and broader source writers remain unresolved. | `8f54228a9e38a1dcc0bf8188bcdd519795b3799a` |\n| [pptx.gallery](https://www.pptx.gallery) `/docs`, `/editor` and gallery pages | Published ColorRef/bundle guidance, Playground and Editor actions, and the canonical docs-to-editor flow are verified. | `f17e9ae5869669d5fbac3720f285652d0c37551c` |\n\nThe site uses documentation source `120a770`, whose tree matches accepted core\nPR98 commit `b1ff81db6f8714b0db1a98bde482ed8a64d0ccc9`. Core PR93/97/98/99/100/102/103 passed\npre-merge and post-merge CI. Accepted core102 is\n`578bcc6e0894129b00059258bd4ad1994a414baa`; its reviewed and accepted trees match,\nand all four required pre/post-merge runs passed on their first attempts. The\n[core102 receipts](evidence/inspector-share-acceptance-20260921/README.md#accepted-core102)\npin this documentation checkpoint without changing the site's older accepted\ndocumentation snapshot. The site's complete guides and raw resources match\nthe reviewed source; binary evidence remains linked and downloadable without\nbeing decoded into the AI-facing guide.\nCore104 `3d301f1` preserves exact postmerge OPF success and coordinated cancellation;\nit is not a complete green postmerge gate. Accepted descendant core105\n`84e914710520a7b0e777fce30e5758ee64a64924` preserves all 60 core104 evidence blobs\nand passes both exact-head workflows on their first attempts. [Compact receipts](evidence/inspector-current-actions-20260921/README.md#core-source-and-ci)\nkeep descendant acceptance separate from the canceled predecessor run.\nAccepted core106 `3847f712ccb2379952bcc8ab7c9fdbaedfd0a4ce` also passes both\npre/postmerge workflows on their first attempts. Its [compact receipt](evidence/inspector-current-actions-20260921/core106/acceptance-receipt.json)\nbinds the bounded native evidence above without completing general compatibility.\nAccepted core107 `5bc0d3f89414b382b2ce48452c7e56e5d66aaf74` has reviewed tree\n`ffdc678beadf0808bc717d67e7fc0a9ec4790127`. Both original postmerge push workflows\n**35652360502 / 35652360551** passed on attempt 1 under Node24.20.0.\n[Compact receipts](evidence/font-readiness-acceptance-20260921/README.md#core107-acceptance)\nretain earlier automatic premerge cancellations separately from the later automatic\nsuccessful pair; no rerun or accepted checkpoint relabels them.\n\nApp47 corrects the pre-app47\ncompletion adapter's rejected layout choices while preserving unchanged source\ntokens and undo history. Accepted commit `0f35352a1445f56ad4bb7c9f4c5609e01f2dd9ae`\nhas reviewed tree `203bdab509d05911f04f234d996f9c91f2b5e4f2`, green Linux/Windows\npre/post-merge CI and its exact READY canonical deployment. The historical App47 **23/24** production run passes all five new completion cases but still fails the existing\nLF Author third-popup assertion. This does not establish complete public-surface\nacceptance; the [historical App47 report](evidence/completion-acceptance-20260921/canonical/REPORT.md)\nand [earlier failed app45/app46 results](evidence/issue88-final-20260921/README.md)\nretain their evidence and unresolved causes.\n\nThe [source audit](evidence/completion-acceptance-20260921/source-preservation-audit/REPORT.md)\nidentified Author canvas/Copy/export and Inspector JSON-download normalization.\nMerged App53 at\n`e40c287b64fcbcfb85fb4a8a50641aea8e3e54a8` has the identical reviewed b33dc18\ntree and preserves those bounded raw-source\npaths and corrects order-only reimport history. A public Suggest-action guard\naddresses the observed stale Quick Input context competing with focused-editor\nCtrl+Space. Current local checks pass **627 unit tests and 29/29 browser cases\nin 88.78 seconds**, zero retries. First-attempt Linux/Windows application CI\npassed 627 unit tests and 29 browser cases per platform; artifact CI also passed.\nThe [exact READY canonical run](evidence/author-source-acceptance-20260921/canonical/REPORT.md) passes **29/29**, zero retries, with matching deployment receipts before and after. Postmerge application CI fails Linux **28/29** while Windows passes **29/29**; both pass 627 unit tests and separate artifact CI passes. The [Linux failure](evidence/author-source-acceptance-20260921/app53/postmerge-ci/README.md) stops before security assertions because five default-deck canvases remain after the shared-load toast. No rerun or canonical pass replaces that failed gate. The retained draft preview was READY but not browser-accepted.\n\nThe earlier **24/27** import-undo regression, **26/27** local popup failure and\nold-head Windows **26/27** shared-load failure remain historical evidence.\nThe fresh successful Windows job retained its sanitized timing artifact, but\nonly Author timings survived; the Inspector pagehide snapshot is missing. This\ndoes not explain or fix the old readiness delay. Phase4\ncaptured no post-fix stale-context overlap, so causal stress is inconclusive.\nThe existing suggestion-details pane remains clipped; visibility is not legibility.\nThe [postmerge trace diagnosis](evidence/author-source-acceptance-20260921/inspector-share-diagnosis/REPORT.md)\nproves wrong-document automatic share-hash publication during import.\nApp54 first corrected automatic\npublication at `60c91f6`: authoritative source/format is checked before and after\nencoding, load/navigation guards remain, and obsolete `import=hash:` is removed.\nURL transfer preserves semantics, not raw spelling. Local 659-unit/31-browser\nacceptance does not replace original first-attempt CI **35638158483**, which\npassed Linux **31/31** but failed Windows **30/31** at the unchanged 45-second\nAuthor readiness deadline. Both publication cases passed. The [frozen diagnosis](evidence/inspector-share-acceptance-20260921/app54/windows-timeout/REPORT.md)\nretains bounded slow-delivery observations with unknown cause. Late assertions\nare not an in-budget pass. Browser History tests permit prior accepted content\nuntil first new publication; held-promise controls establish the narrower race guard.\n\nThe product correction was added at `57e5e59fddbc94346f142dc12d86a916228bf2ae`, tree\n`d9c3aab543c6a886a85c6ec07dd55e5ab22590cd`. It guards current snapshots for\nexplicit Copy/JSON/Share/PPTX/PDF/Author/Deckchat actions. Pending public canvas\ndrafts commit before capture, pointer-blur rejection survives session recovery,\nand newer source/load/navigation/unmount/action invalidates late results. Raw\nCopy and accepted same-format JSON bytes are preserved; handoffs remain semantic.\nAlready-started clipboard/download effects cannot be retroactively canceled.\nThe integrated portable standalone startup verifies packaged runtime assets,\nfonts and traced schema inputs without changing dependencies or published pins.\n\nLocal runtime checks pass **692 unit tests**, **7 standalone controls**, focused\n**8/8** and full **39/39** browser cases, zero retries, with visual review. The\nimmutable 93-file app bundle\nretains stale-draft negative evidence and the initial candidate **7/8** result.\nThe latter did not establish accepted pasted source before releasing mocked PDF 401;\nfinal visible-code preconditions change no product bytes or budgets. PDF/Deckchat\nare locally mocked. CRLF ingress normalized 279 to 274 LF bytes; subsequent exact\nactions preserve the accepted buffer, not that ingress boundary.\n\n**The original 57e packaging gate failed.** First-attempt CI **35645493900** failed Linux and\nWindows typecheck on archived `.spec.ts` evidence copies after each platform\npassed 692 units and 7 standalone controls. The exact-head preview failed with\n`module_not_found`; precise Vercel compiler logs were unavailable. [The first-attempt receipt](evidence/inspector-current-actions-20260921/app54/first57-ci/REPORT.md)\nrecords skipped build/browser steps and no uploaded artifacts. The passing runtime build\npredates those copies. A byte-identical `.ts.txt` archive correction produced captured App54 head\n`6dfc2584698c1306505929a2bc3e427996d66563`, tree\n`f3279495f7685945b7541c90d24f7239407639a0`. Its final-tree local typecheck passes\nwith all 305 product/test inputs unchanged. [The corrected receipt](evidence/inspector-current-actions-20260921/app54/corrected6df/commit-receipt.json)\nbinds its 106-file evidence bundle. [Exact-head CI 35646213757](evidence/inspector-current-actions-20260921/app54/corrected6df/ci/REPORT.md)\npasses 692 units, seven standalone controls, typechecks and build on both platforms;\nLinux passes **39/39** browsers. Windows executes **zero browser tests** because\nstandalone startup fails with `EPERM` while statting its packaged React dependency\nlink. No timing or test-result artifacts were uploaded. The exact-head preview\nis READY, which is metadata only; at that 6df checkpoint App54 was unmerged and undeployed to production.\nThe [current ledger](evidence/inspector-current-actions-20260921/README.md)\nretains the separate 60c readiness, 57e packaging and 6df startup failures. Production then remained App53 `e40c287`, with its original\nfailed Linux postmerge gate preserved separately from canonical **29/29**.\nThe startup-link correction at 4338e57\nthen reached all browser cases: Linux **39/39**, Windows **38/39** in first-attempt\nCI **35649707689**, with 692 units and 13 standalone controls passing per platform.\nThe sole Windows failure was initial canvas-title visibility at five seconds,\nbefore any edit/recovery operation; correct incoming source remained at loading\nfonts. Late font acquisition and eleven incomplete responses at teardown do not\nestablish a permanent stall or a dominant cause. The [failed gate](evidence/font-readiness-acceptance-20260921/README.md#preserved-failed-windows-gate)\nis preserved.\n\nApp54 font-preparation revision `a4eb88ab7aa585c9efb91de4c190c1f1c0c7d0eb`, tree\n`031d893855a540fd2a4d2ec2162605817b8dde16`, overlaps font acquisition with converter\nwarmup while readiness still waits for both. The offline converter barrier and all\n**33 faces / 9,317,044 bytes**, manifest, substitution/measurement policy and\n`document.fonts.ready` gate remain unchanged. Fresh local Node24.21.0 checks pass\n**704 units, 13 standalone controls and 39/39 browsers**, zero retries, including\nunchanged offline export/reimport. Immutable app evidence\nretains reviewed images, exact source/output bindings and the original failed run.\n\n**The original a4eb gate failed:** first-attempt application **35654753237** passes\nLinux **39/39** but fails Windows **38/39**; both pass 704 units, 13 standalone\ncontrols, typecheck and build. The Open in Author URL assertion exceeds its existing\nfive-second deadline at `inspector-actions.spec.ts:176`; later source checks are\nnot reached. Original4338 font readiness passes in this run. No navigation cause\nor data-loss finding is established. The [frozen final audit](evidence/font-readiness-acceptance-20260921/app54/final-a4eb-ci/release-audit.json)\nretains exact source/artifact bindings and the original failed trace; that failure remains preserved.\nThe Python artifact workflow is not applicable under its full-PR path filters,\nnot a fresh pass. Exact-head preview is READY metadata only; an\nunauthenticated request redirects to sign-in, with no preview-browser acceptance.\nAt that a4eb capture App54 was unmerged and production remained App53\n`e40c287`. The separate suggestion-details candidate remains unreleased and supplies\nno acceptance here. No local/browser pass broadens native compatibility.\nThe [bounded navigation diagnosis](evidence/font-readiness-acceptance-20260921/README.md#author-navigation-diagnosis-and-prospective-policy)\nrecords Loading Author and a delayed successful script response: 1490 bytes inferred\nfrom ETag, 766 compressed bytes recorded, actual body absent. Later DOM does not\naccept unreached assertions or identify a cause. App54 accepted merge\n`8f54228a9e38a1dcc0bf8188bcdd519795b3799a` retains reviewed 79ba tree\n`3639d3c14daec94d13711fa2b10f2b927df45eca`, preregisters `waitForURL(load)` before\nthe real action, matching `page.goto` within the unchanged 45-second test and default\nfive-second content budgets. It retains all oracles but deliberately removes the\nincidental five-second navigation deadline. Fresh local **39/39**, zero retries,\npasses in 112.780048s with reviewed source/images and prepared-tree build/typecheck.\nThe immutable app bundle\nretains original failures. Units/standalone controls were not repeated locally;\nfresh first-attempt application **35659187971 passes 704 units, 13 standalone\ncontrols and 39/39 browsers on both Linux and Windows**. Exact-head preview was\nREADY/protected, not browser accepted. The identical reviewed tree is merged/live\nat 8f on READY deployment `dpl_H1FtXx1QSuGxn3MwzJwWJpGtRg8b`, but full canonical\nacceptance **fails (34/39 passed)** at five no-POST assertions observing Clerk environment\nPOSTs. The [safe audit](evidence/font-readiness-acceptance-20260921/app54/canonical8f/write-audit/REPORT.md.txt)\nrecords ten such requests, nine with HTTP200/zero-length bodies and one incomplete.\nNo fixture-content needle was detected in captured fields; uncaptured data remains\nunknown. Three final action page-error assertions were not reached; the two share\ncases passed theirs. The prior auth/config comparison is 28/29 identical, with only\npackage scripts changed. Production is kept without a rollback, test change or\nrerun; the strict gate remains failed. Raw authentication-bearing diagnostics\nremain private. Postmerge CI 35660464578\nfinishes failed: Linux 39/39 passes while Windows 38/39 fails, with 704 units/13 controls/typecheck/build\npassing on each. Windows fails initial gallery-rail title visibility after 5,000ms\nwith correct source, clean schema and Loading slide fonts; later editing/export/\nreimport checks were not reached. The [final audit](evidence/font-readiness-acceptance-20260921/app54/merged8f/postmerge-ci/REPORT.md.txt)\npreserves this separate failed gate without cause inference or a retry. No canonical\npass or general native/font acceptance is claimed. That September 21 checkpoint was paused; the user resumed work on September 29. See the [current source checkpoint](handoff-runtime-2026-09-29.md) for ongoing repairs and release holds.\n\nSeparate local negative controls confirmed that preset Undo all discarded New run\nand imported replacement documents. The guarded correction is now preserved in\ndraft app #58: independent\nreview and local Node 24 checks passed (716 units, 13 standalone controls and\n49 browsers without retries). Original Linux/Windows CI could not start because\nof the account payment/spending-limit restriction; no application CI or production\nacceptance is claimed. Unbusy asynchronous account replacements still need a\nsynchronous invalidation guard and held-response control.\nOther local source writers still require their separate preservation checks. These unresolved local findings and raw imported-file/account/agent/metadata boundaries remain outside\nApp53 and App54. See the [App53 ledger](evidence/author-source-acceptance-20260921/README.md)\nand its immutable application evidence links. Issue88 remains OPEN. Native/font\ncompatibility, required repair and release gates remain separate; geometry is\ndeferred as the coordinated set below. The unimplemented worker candidate remains in the [handoff](handoff-2026-09-21.md).\n\nThe five coordinated geometry drafts (core94, renderer27, editor25, PPTX42,\nsite40) remain unmerged. In particular, site40 is not independently shipped.\n\n## Explicitly not shipped\n\n| Topic | Tracker | Do not describe as done |\n| --- | --- | --- |\n| Linux vs Chromium native-width residual at the 0.1px gate | [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24) | Rounding that fixes Linux but breaks macOS is rejected |\n| General native PowerPoint fidelity beyond the supported footer placeholders | [opf#87](https://github.com/OpenPresentation/opf/issues/87) | Finite B/C and bounded Carlito edit controls above are accepted evidence, as is one finite mixed-size table edit/save/reopen ([evidence](evidence/windows-native-mixed-edit-20260922/README.md)). Tab tolerance, general mixed-size table layout and preview/native wrapping, physical glyph identity/fallback/synthesis, embedding and general layout/reflow fidelity remain open; supported native footer placeholders, self-import and tagged furniture do not certify arbitrary Office behavior |\n| Public-surface acceptance checklist | [opf#88](https://github.com/OpenPresentation/opf/issues/88) | Shipping features does not establish every acceptance item; use the checklist and deployment receipts |\n| HarfBuzz / prepared-glyph shaping | Archive branches `codex/archive-shaping-20260915` | Prototypes are preserved, not in npm |\n| Selectable vector PDF | [pdf plan](plans/pdf-export.md) | Shipped in opf-render 0.12.0 (opf-render#90; the browser download entry `@openpresentation/opf-render/export-browser` ships there too); PDF/UA, PDF/A and viewer coverage beyond pdf.js, PDFium and poppler remain open |\n| General SVG diagrams / Mermaid | [diagrams plan](plans/diagrams-svg.md) | Embedded SVG \u2260 native editable primitives |\n| Full visual editor / IME / bidi / repair loop | [developer adoption](plans/developer-adoption-20260915.md) | Schema support \u2260 WYSIWYG coverage |\n\nCLI 0.9.2 and earlier do not render or export PPTX; CLI 0.10.0 adds `opf render`, `opf export` and `opf import`\nthrough the optional peers opf-render and opf-pptx ([CLI reference](cli.md)). The Node `toPng` / `toPdf`\n(`svgToPng` / `svgToPdf` before 0.18) APIs stay Node-only; renderer 0.12.0 adds the separate `@openpresentation/opf-render/export-browser` entry for browsers.\nCore's file API (`open`, `save`, `convert`) is Node-only too: it reads files, font files and the engines' native packages. From\ncore 0.18 the root resolves to a browser-safe build in a browser or a worker, with the same names, where those three reject\nwith `node-only`; every other core entry is browser-safe. In a browser, use the engines' browser entries.\n\n## Predecessor notes\n\n| Older set | Relationship |\n| --- | --- |\n| core 0.17.0, CLI 0.17.1, renderer 0.17.1, PPTX 0.17.0, editor 0.17.0 | Previous coordinated set (FA-31 furniture variables, `@openpresentation/opf/node`, `parse`/`stringify`, `opf convert`). The 0.18 set is breaking: one root import replaces `/node`, the `toSvg`/`ingest` names and `convert` options, organization logos, and the consolidated CLI. |\n| core 0.16.0, CLI 0.16.0, renderer 0.16.0, PPTX 0.16.1, editor 0.16.0 | Previous coordinated set (Markdown decks, used-face SVG embedding, optional renderer converters, nested placeholder groups, `@openpresentation/cli/api`). The 0.17 set is breaking: header and footer flags become `{{slide.number}}`-style variables (FA-31), `readDeck`/`writeDeck` become `parse`/`stringify`, and `@openpresentation/cli/api` is replaced by `@openpresentation/opf/node`. |\n| core 0.15.1, CLI 0.15.0, renderer 0.15.0, PPTX 0.15.0, editor 0.15.0 | Previous coordinated set (OPF 0.15, format audit wave C; CLI 0.15.0 bundles core 0.15.1). The 0.16 set is additive for documents; the renderer's converters and font packages became optional peers (RR-63), so a 0.16 host installs the ones it uses, and the CLI depends on core instead of bundling it (RR-62). |\n| core 0.14.0, CLI 0.11.0, renderer 0.14.0, PPTX 0.14.0, editor 0.14.2 | Previous coordinated set (format audit waves A and B, RR-55 and RR-56; CLI 0.11.0 bundles core 0.13.0). The 0.15 set is breaking: a 0.14 document's catalog shape (`catalogs.<kind>.records`), catalog-id languages and `design.slideImage` are invalid in 0.15, and the 0.15 engines need the host to register the default catalog. |\n| core 0.13.0, CLI 0.11.0, renderer 0.13.1, PPTX 0.13.2, editor 0.12.1 | Previous coordinated set (chart and table data, RR-54). |\n| core 0.12.2, CLI 0.10.1, renderer 0.12.2, PPTX 0.12.4, editor 0.11.3 | Previous coordinated set (the first set whose `engines.node` is the open-ended `>=22`). The 0.13 set adds chart and table data (RR-54, [contract](chart-table-data.md)): strict chart numbers (a value is a finite number or a strict decimal string; `\"12%\"`, `\"$5\"` and `\"Q1\"` are a gap with a `chart-value-not-numeric` warning, never a guessed number), number formats (`DataColumn` `{ name, format }`), top-level `datasets` with `{ \"dataset\", \"fields\" }` references and `chart.mapping`, in core 0.13.0, previewed by renderer 0.13.0, exported and re-imported by PPTX 0.13.0 (`OPF_DATASETS_V1` and `OPF_DATA_V1` provenance), edited in the editor 0.12.x data grid and imported by CLI 0.11.0 (`opf import-data --dataset`). PPTX 0.13.0 also vendors pptxgenjs-plus 4.3.4 (was PptxGenJS 4.0.1) and PPTX 0.13.1 bundles for browsers again. Renderer 0.13.1 and PPTX 0.13.2 pin Sharp 0.35.5 (GHSA-wq5f-xc86-pv6w; output unchanged); editor 0.12.1 replaces 0.12.0, which was never published. Renderer, PPTX and editor raise their core floor to `^0.13.0` and the renderer peer to `^0.13.1` together. |\n| core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. PPTX 0.12.3 corrects the table range of a chart's embedded workbook (apostrophe-quoted sheet references and bubble-series references), which made Keynote drop category charts on import (opf-pptx#162, opf-pptx#163; every other part of the package is byte-identical). Core, renderer and editor are unchanged and keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. Core 0.12.1 deprecates the six plural audience ids (`executives`, `investors`, `customers`, `sales-team`, `marketing-team`, `regulators`) in favour of the singular ids: additive catalog data, validation warns and never errors, and no geometry moves, so renderer, PPTX and editor keep the core floor `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.1, editor 0.11.1 | Previous coordinated set (PowerPoint lists only the deck's fonts). PPTX 0.12.2 also names the deck's font in the theme's `Viet` (Vietnamese) and `Uigh` (Uyghur) per-language script entries, which PowerPoint applies to `vi-VN` and `ug` runs and which kept Office's Times New Roman and Arial for those two languages (RR-17; every other language is byte-identical). Editor 0.11.2 adds the `slide-sizes` and `purposes` switch dimensions, `SLIDE_SIZE_PRESETS` and the Slide size and Purpose selects in the Design panel (RR-41, additive API). Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.0, editor 0.11.0 | Previous coordinated set (the 0.12.0 train). PPTX 0.12.1 writes the deck's fonts so that PowerPoint's font list shows only the fonts the deck uses (FF-05: schema-order `presentation.xml`, an own notes theme, no east-asian or complex-script font on runs, and a theme east-asian slot that is never empty); editor 0.11.1 stops the restore prompt showing a literal `null`. Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.9, editor 0.10.6 | Previous coordinated set (native header/footer placeholders, SVG pictures, `fromPptx` import signals; the published 0.11.4 train measured in the font-fidelity program). Core 0.12.0 moves geometry (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored), so renderer 0.12.0, PPTX 0.12.0 and editor 0.11.0 raise their core floor to `^0.12.0` and the renderer peer of PPTX and editor to `^0.12.0` together; the set adds templates and variables, numbered lists, footnotes, citations and captions and chart options (additive schema), vector PDF with selectable text and the browser export entry, the `<opf-deck>` player, the editor's slide management, autosave, data grid, find and replace, image crop, Review panel and fill UI, and the shared JSON Patch module. CLI 0.10.0 bundles core 0.12.0 and adds `opf audit`, `from-md`, `to-md`, `diff`, `merge`, `format`, `render`, `export` and `import`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.8, editor 0.10.6 | Previous coordinated set (PPTX 0.11.8 re-imports wrapped rich text as one authored payload; CLI 0.9.2 bundles core 0.11.4). PPTX 0.11.9 writes a deck footer's first text, date and slide number as native PowerPoint Header & Footer placeholders (every export also carries the footer placeholders on its master, layout and notes master, so Insert > Header & Footer works), exports an SVG image as a native SVG picture over a PNG fallback (rasterized in Node by the optional opf-render peer or `options.svgRasterizer`) and adds the opt-in `fromPptx(bytes, {signals: true})` import signals; core floor `^0.11.4` unchanged. |\n| core 0.11.4, CLI 0.9.1, renderer 0.11.9, PPTX 0.11.7, editor 0.10.6 | Previous coordinated set (the design fields compose and export natively). PPTX 0.11.8 re-imports rich text that wraps over several native lines as one authored payload (the export records the line count; decks exported by 0.11.7 import as before) and keeps the core floor `^0.11.4`; CLI 0.9.2 bundles core 0.11.4 (CLI 0.9.1 bundled core 0.11.3) and still requires Node 24. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.6, editor 0.10.5 | Previous coordinated set (the native chartex export by default). Core 0.11.4 composes the design fields (logos on covers and section slides, `contentDirection`, `chartPrimary`, picture bullets, header and footer logos, the accent font), aligns a cover's tag and subtitle with its title, and sizes picture bullets and furniture images as PowerPoint does, so renderer, PPTX and editor raise their core floor to `^0.11.4` together; renderer 0.11.9 draws those fields and applies the tag contrast rule (FF-61: the tag draws in the text colour when the scheme primary is under 4.5:1); PPTX 0.11.7 exports them natively, writes every chart's text at the preview's size (FF-62: 12 pt, not 9 pt), writes slide sections as PowerPoint's section list and restores the authored form of a fresh export on import (a root payload returns as `slides.N.text`, `.items`, `.chart` ... rather than one typed block); editor 0.10.6 is a floor bump. CLI 0.9.1 still bundles core 0.11.3. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.5, editor 0.10.5 | Previous coordinated set (the slide tag draws in the scheme primary colour and PPTX writes it as `a:schemeClr accent1`; the playground loads its base faces through `extraLazyFonts`). PPTX 0.11.6 exports the treemap, histogram, pareto, box-and-whisker, waterfall and funnel charts as native chartex parts by default (`toPptx({chartex: 'auto'})`, confirmed in desktop PowerPoint; `world` stays a clustered column with `chart-data-adapted` because PowerPoint's map needs online geodata; pass `chartex: 'fallback'` for the previous output) and gives chartex text the deck's label colour and font. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.6, PPTX 0.11.4, editor 0.10.4 | Previous coordinated set (the 100-layout catalog and its geometry, category-axis label rotation, quote and slide image re-import). Renderer 0.11.7 adds the `extraLazyFonts` registry option and `splitStartupFaces` (a browser host can start with Roboto Regular alone and load its other base faces on demand); renderer 0.11.8 draws the slide tag in the scheme primary colour; PPTX 0.11.5 writes the tag run as `a:schemeClr accent1` where the deck theme holds the primary (the colour is unchanged); editor 0.10.5 loads its playground base faces through `extraLazyFonts`. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.5, PPTX 0.11.3, editor 0.10.3 | Previous coordinated set (native classic and chartex chart previews, opt-in chartex export, face-level lazy fonts and the font gate's render options). Core 0.11.3 adds the pinned pptx.gallery default catalog and the 70 legacy gallery layout ids (layouts 30 to 100; 25 carry a `composition` or `contentBox` contract, which moves geometry, so renderer, PPTX and editor raise their core floor to `^0.11.3` together); renderer 0.11.6 rotates and skips dense category-axis labels; PPTX 0.11.4 re-imports quote and slide image payloads and writes theme `a:ea`/`a:cs` only where a script font is selected; editor 0.10.4 is a floor bump; CLI 0.9.1 bundles core 0.11.3. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.1, PPTX 0.11.0, editor 0.10.0 | Previous coordinated set (lockstep floors, Intos and the open families, selected-name export). Renderer 0.11.2 adds script-face loading (`scripts: 'auto'`); PPTX 0.11.1 adds `design.watermark` export; editor 0.10.2 loads the fonts a document needs before every render (FF-41). Renderer 0.11.3 previews every kept classic chart type natively; PPTX 0.11.2 exports the native construct for each kept classic chart type (with `chart-data-adapted` diagnostics where data is adapted) and writes theme colour references for table and text colours. Renderer 0.11.4 previews the seven chartex chart types natively (the world map as a non-geographic tile grid), keeps the Latin Noto Sans replacement for script schemes under `scripts: 'auto'`, shapes Noto Sans Mongolian, and bundles Raleway and Playfair Display (94 lazy faces); PPTX 0.11.3 adds the opt-in `toPptx({chartex: 'native'})` export of the chartex chart types (the default output is unchanged) and always imports chartex charts. |\n| core 0.11.0, CLI 0.9.0, renderer 0.9.0, PPTX 0.9.1, editor 0.8.0 | Previous coordinated Node 24 set (ColorRef, shared furniture). Renderer and PPTX had different core floors from 0.10.x. |\n| core 0.10.0, renderer/PPTX/CLI 0.8.0, editor 0.7.0 | Previous coordinated Node 24 baseline. Lint and furniture landed across 0.10.0/0.8.0 then layout-placeholder fixes in 0.10.1/0.8.1/0.7.1. |\n| Node 20 | Not valid for these packages |\n| Node 22 / 26 | Declared by the current set (`engines.node` `>=22`) and tested, but Node 24 is the toolchain the evidence runs on. Releases up to core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.3 and editor 0.11.2 declared `24.x`, so npm on Node 22 or 26 silently picked an older release |\n\nDo not install sibling `../opf-render` dist folders when following the\nquickstart. Packed and registry consumers must resolve `@openpresentation/*`\nfrom npm.\n"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"slug": "content-item-design-overrides",
|
|
@@ -286,7 +286,7 @@ var docsData = Object.freeze([
|
|
|
286
286
|
"slug": "quickstart",
|
|
287
287
|
"file": "docs/quickstart.md",
|
|
288
288
|
"title": "Developer quickstart",
|
|
289
|
-
"markdown": "# Developer quickstart\n\nA new developer can install the **published** OPF packages into a fresh Node 24\nproject and author, validate, compose, paginate, edit with undo, preview and export\na representative deck. No account, model call, or sibling repository checkout\nis required.\n\nThis is a documented supported subset, not universal Office or all-feature\nparity. See the [compatibility matrix](compatibility-matrix.md) for what is\nshipped versus deferred.\n\n## Versions\n\n**Verified artifacts (2026-10-17):** core **0.20.5**, CLI **0.20.1**,\nrenderer **0.20.4**, PPTX **0.20.4**, editor **0.20.4** and Gallery **3.0.0**.\n[Artifact identities and verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) records\nthe releases selected in `release-plan.json`; artifact verification is separate\nfrom the consumer and native checks below. All declare\n`engines.node: >=22`; Node 24 is the toolchain this quickstart is verified on.\n\n```sh\nnode -v # 24.x (verified); 22 and later are declared\nnpm install @openpresentation/opf@0.20.5 \\\n @openpresentation/opf-render@0.20.4 \\\n @openpresentation/opf-editor@0.20.4 \\\n @openpresentation/opf-pptx@0.20.4 \\\n @openpresentation/cli@0.20.1 \\\n @openpresentation/gallery@3.0.0\n# opf-render 0.20 keeps its converters and font packages as optional peers: install the ones you use\n# (here the base font pack, PNG and PDF; add @expo-google-fonts/* office or script packs as needed)\nnpm install @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 \\\n @resvg/resvg-js@^2.6.2 pdf-lib@^1.17.1 sharp@^0.35.5\n```\n\nA missing converter or font package is a typed error (`converter-missing`, `font-resource-unavailable`) whose message\nnames the `npm install` command, so a host that only previews SVG in a browser installs none of them.\n\nCopy [`docs/quickstart/developer-quickstart.opf.json`](quickstart/developer-quickstart.opf.json)\ninto that project as `deck.opf.json`. That file is a docs fixture, not one of\nthe 127 decks in `@openpresentation/opf/examples`. Verify the install came from the registry\n(`package-lock.json` `resolved` URLs start with `https://registry.npmjs.org/`)\nand that you did not add `file:` dependencies on this repository.\n\nThe [format card](format-card.md) describes ColorRef, named variables and the\ncurrent source contract. `opf embed` embeds the catalog records a deck uses for\nportable offline authoring; it does not download remote assets. Keep the\nColorRef docs fixture outside the 127-deck example/golden corpus in this update.\n\n## The short path\n\nWith core 0.18.0 and later, three calls of `@openpresentation/opf` cover files in a Node application (core 0.17 had them at `@openpresentation/opf/node`):\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.md', 'deck.pdf'); // also .pptx, .png (one per slide), .svg, .zip and the deck forms\nconst deck = await opf.open('deck.opf.md'); // .opf.md, .opf.yaml, .opf.json, or a .pptx to import\ndeck.slides.push({ title: 'Q4' });\nawait opf.save(deck, 'deck.opf.md');\n```\n\nThe CLI does the same from a shell: `opf convert deck.opf.md deck.pdf`. The sections below take the pieces one at a time.\n\n## Author and validate\n\n```sh\nnpx --no-install opf --version\nnpx --no-install opf validate deck.opf.json\nnpx --no-install opf validate deck.opf.json --format text\n```\n\nThe CLI depends on core, which holds the schema, catalogs and the checker. Validation never renders; `opf convert` produces PDF, PPTX, PNG and SVG and reads `.pptx` files through the optional peers `@openpresentation/opf-render` and\n`@openpresentation/opf-pptx` (see [the CLI reference](cli.md); `opf doctor` says what is installed).\n`opf --version` reports the CLI and the installed core. Successful validation is not\nvisual verification.\n\nLibrary equivalents:\n\n```js\nimport { readFile } from 'node:fs/promises';\nimport { validate } from '@openpresentation/opf';\n\nconst source = await readFile('deck.opf.json', 'utf8');\nconst document = JSON.parse(source);\nconsole.log(validate(document)); // a parsed document: findings of every category\nconsole.log(validate(source)); // JSON text: the same findings with line and column\n```\n\n## Offline fonts, composition, pagination\n\n```js\nimport { paginate, resolveSlideContext } from '@openpresentation/opf';\nimport { composeSlide } from '@openpresentation/opf/composition';\nimport { loadFonts } from '@openpresentation/opf-render/fonts-node';\n\nconst fonts = await loadFonts({ pack: 'base' });\nconst { slide, options } = resolveSlideContext(document, 0, { fonts });\nconst geometry = composeSlide(slide, options);\nconst { presentation, pages } = paginate(document, { fonts });\n```\n\n`loadFonts({ pack: 'base' })` loads the bundled Roboto faces for\n`design.fontScheme: 'roboto'`. It returns the fonts handle: every deck-level\nverb (`paginate`, `toSvg`, `toPng`, `toPdf`, `toPptx`) takes it as\n`{ fonts }` and reads what it needs from it (the `textMeasurement`, the faces\nto embed, the font files). `resolveSlideContext(document, index, { fonts, catalogs })` resolves\none slide's layout, canvas, theme and font families (slide design, then deck\ndesign, then theme, then the engine default font scheme) into the options `composeSlide`\ntakes, so you never look up a font scheme yourself; a reference that resolves\nnowhere comes back as an `unresolved-reference` diagnostic. It also returns\n`slide`: the slide with `{{slide.number}}`, `{{slide.section}}` and\n`{{deck.slideCount}}` substituted for `options.slideNumber` and\n`options.slideCount`; compose and draw that slide. `paginate`\ndoes the same for every slide. The helper does not install system fonts or\nchange the authored scheme. Reuse the same `fonts` for SVG preview and PPTX\nexport.\n\nShared headers and footers use `furniture-flow-v2`. Body content stays between\n`geometry.furniture.headerBottom` and `geometry.furniture.footerTop`. A zone\nwrites generated values as variables in its `text`, such as\n`\"{{slide.number}} / {{deck.slideCount}}\"` or `\"{{organization.name}}\"`.\n\nPagination returns a new presentation plus source mappings. It preserves\nauthored text, whitespace and reading order; it does not drop overflowed\ncontent.\n\n```sh\nnpx --no-install opf paginate deck.opf.json paginated.opf.json\n```\n\n## Edit with undo\n\n```js\nimport { createEditorSession } from '@openpresentation/opf-editor';\n\nconst editor = createEditorSession(document, { rejectInvalid: true });\nconst original = editor.presentation.slides[0].title;\neditor.set('slides.0.title', 'Edited title');\neditor.undo();\n// original title, including whitespace, is restored\n```\n\nThe CLI can apply JSON Patch edits (`opf edit`) but has no persistent undo\nhistory. Use the editor session or version control for undo.\n\n## Preview and export\n\n`@openpresentation/opf` in Node (core 0.18.0 and later) reads and writes files with the bundled fonts and loads the render and\nPPTX engines you installed:\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.json', 'deck.pdf');\nawait opf.convert('deck.opf.json', 'slides/deck.png', { scale: 2 }); // slides/deck-1.png, -2, ... (or \"slides/slide-{n}.png\")\nawait opf.convert('deck.opf.json', 'deck.pptx');\nconst back = await opf.open('deck.pptx'); // PowerPoint to OPF\nconst { files } = await opf.convert(back, { format: 'svg' }); // bytes, nothing written\n```\n\n`convert` checks the deck (format and references), draws with the renderer's bundled open fonts and no system fonts,\nreads images next to the input file, never reads a clock (pass `date: 'YYYY-MM-DD'` for a date field) and writes each file\natomically. A missing engine throws `OPFExportError` with code `peer-not-installed` and the install command. The options and\nerrors are in [OPF files in Node](node.md).\n\n### Advanced: the engines directly\n\n`convert` calls these. Use them when you need the pieces: your own fonts handle, one SVG per slide in a browser,\nor options the function does not expose.\n\n```js\nimport { toSvg, toPng, toPdf } from '@openpresentation/opf-render';\nimport { toPptx } from '@openpresentation/opf-pptx';\n\nconst svgs = toSvg(presentation, { fonts }); // one SVG per slide\nconst png = await toPng(svgs[0], { fonts });\nconst pdf = await toPdf(svgs, { fonts });\nconst pptx = await toPptx(presentation, { fonts });\n```\n\n`toSvg` is the local preview of every slide (`toSvg(presentation, 3, { fonts })` draws slide 3 as one string). PNG rasterizes that SVG.\nPDF (opf-render 0.12.0 and later) converts the same SVG to **vector paths with\nselectable, searchable text** in embedded font subsets, with no second layout pass;\npass `{ raster: true }` for the image-per-slide output that renderers up to 0.11.9\nalways wrote. Pass the same fonts handle as for PNG; vector PDF never uses system\nfonts.\n`toPptx` is the supported PowerPoint export from OPF. **Its text is editable\nline by line, not paragraph by paragraph:** it writes one text box for each\nwrapped line, so text you add or lengthen in PowerPoint or Keynote does not\nreflow into the next line, and a title that wraps is not the Title placeholder.\nFor heavy text changes, edit the OPF source and export again. An opt-in native\nbody mode is in progress and is not published yet. Shared\nheaders and footers are written on the slide master or layout. The retained\n[Windows sample](evidence/rr-20-windows-current-2026-10-14/README.md) covers\n17 selected native cases and four actual UI operations; 41 of 58 cases were\nnot executed natively. This quickstart verifies published Node APIs and\nexports. It does not certify every Office operation or global native fidelity.\n\nBrowser preview uses the same SVG core plus\n`@openpresentation/opf-render/fonts-browser` and\n`@openpresentation/opf-editor/canvas`. Load the same font bytes the Node helper\nresolved. Do not fetch fonts from the network at render time.\n\n## Prove it\n\nFrom this repository, after a normal `pnpm install`:\n\n```sh\nnode scripts/test-developer-quickstart.mjs\n```\n\nThat script creates an empty temp project, installs the published versions from\nthe npm registry, copies this example, and asserts validate, offline\nfonts, furniture composition, pagination, undo, SVG, PNG, PDF and PPTX.\nIt fails if any package is a `file:` or workspace link.\n\n## What this does not cover\n\n- Renderer native-width residuals:\n [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24)\n- Native PowerPoint open/edit/save/reopen and real Office Header/Footer (`p:hf`):\n [opf#87](https://github.com/OpenPresentation/opf/issues/87)\n- Remaining GitHub [issue 88](https://github.com/OpenPresentation/opf/issues/88)\n checklist (the Inspector overlay/json-options, gallery Playground+Editor\n links, and Header & footer playground example are already live on\n production; the issue stays open)\n- Archived font-shaping prototypes (not in the published runtime)\n- PDF/UA or PDF/A conformance, general SVG diagrams, and Mermaid\n"
|
|
289
|
+
"markdown": "# Developer quickstart\n\nA new developer can install the **published** OPF packages into a fresh Node 24\nproject and author, validate, compose, paginate, edit with undo, preview and export\na representative deck. No account, model call, or sibling repository checkout\nis required.\n\nThis is a documented supported subset, not universal Office or all-feature\nparity. See the [compatibility matrix](compatibility-matrix.md) for what is\nshipped versus deferred.\n\n## Versions\n\n**Verified artifacts (2026-10-17):** core **0.20.5**, CLI **0.20.1**,\nrenderer **0.20.4**, PPTX **0.20.4**, editor **0.20.4** and Gallery **3.0.0**.\n[Artifact identities and verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) records\nthe releases selected in `release-plan.json`; artifact verification is separate\nfrom the consumer and native checks below. All declare\n`engines.node: >=22`; Node 24 is the toolchain this quickstart is verified on.\n\n```sh\nnode -v # 24.x (verified); 22 and later are declared\nnpm install @openpresentation/opf@0.20.5 \\\n @openpresentation/opf-render@0.20.4 \\\n @openpresentation/opf-editor@0.20.4 \\\n @openpresentation/opf-pptx@0.20.4 \\\n @openpresentation/cli@0.20.1 \\\n @openpresentation/gallery@3.0.0\n# opf-render 0.20 keeps its converters and font packages as optional peers: install the ones you use\n# (here the base font pack, PNG and PDF; add @expo-google-fonts/* office or script packs as needed)\nnpm install @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 \\\n @resvg/resvg-js@^2.6.2 pdf-lib@^1.17.1 sharp@^0.35.5\n```\n\nA missing converter or font package is a typed error (`converter-missing`, `font-resource-unavailable`) whose message\nnames the `npm install` command, so a host that only previews SVG in a browser installs none of them.\n\nCopy [`docs/quickstart/developer-quickstart.opf.json`](quickstart/developer-quickstart.opf.json)\ninto that project as `deck.opf.json`. That file is a docs fixture, not one of\nthe 127 decks in `@openpresentation/opf/examples`. Verify the install came from the registry\n(`package-lock.json` `resolved` URLs start with `https://registry.npmjs.org/`)\nand that you did not add `file:` dependencies on this repository.\n\nThe [format card](format-card.md) describes ColorRef, named variables and the\ncurrent source contract. `opf embed` embeds the catalog records a deck uses for\nportable offline authoring; it does not download remote assets. Keep the\nColorRef docs fixture outside the 127-deck example/golden corpus in this update.\n\n## The short path\n\nWith core 0.18.0 and later, three calls of `@openpresentation/opf` cover files in a Node application (core 0.17 had them at `@openpresentation/opf/node`):\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.md', 'deck.pdf'); // also .pptx, .png (one per slide), .svg, .zip and the deck forms\nconst deck = await opf.open('deck.opf.md'); // .opf.md, .opf.yaml, .opf.json, or a .pptx to import\ndeck.slides.push({ title: 'Q4' });\nawait opf.save(deck, 'deck.opf.md');\n```\n\nThe CLI does the same from a shell: `opf convert deck.opf.md deck.pdf`. The sections below take the pieces one at a time.\n\n## Author and validate\n\n```sh\nnpx --no-install opf --version\nnpx --no-install opf validate deck.opf.json\nnpx --no-install opf validate deck.opf.json --format text\n```\n\nThe CLI depends on core, which holds the schema, catalogs and the checker. Validation never renders; `opf convert` produces PDF, PPTX, PNG and SVG and reads `.pptx` files through the optional peers `@openpresentation/opf-render` and\n`@openpresentation/opf-pptx` (see [the CLI reference](cli.md); `opf doctor` says what is installed).\n`opf --version` reports the CLI and the installed core. Successful validation is not\nvisual verification.\n\nLibrary equivalents:\n\n```js\nimport { readFile } from 'node:fs/promises';\nimport { validate } from '@openpresentation/opf';\n\nconst source = await readFile('deck.opf.json', 'utf8');\nconst document = JSON.parse(source);\nconsole.log(validate(document)); // a parsed document: findings of every category\nconsole.log(validate(source)); // JSON text: the same findings with line and column\n```\n\n## Offline fonts, composition, pagination\n\n```js\nimport { paginate, resolveSlideContext } from '@openpresentation/opf';\nimport { composeSlide } from '@openpresentation/opf/composition';\nimport { loadFonts } from '@openpresentation/opf-render/fonts-node';\n\nconst fonts = await loadFonts({ pack: 'base' });\nconst { slide, options } = resolveSlideContext(document, 0, { fonts });\nconst geometry = composeSlide(slide, options);\nconst { presentation, pages } = paginate(document, { fonts });\n```\n\n`loadFonts({ pack: 'base' })` loads the bundled Roboto faces for\n`design.fontScheme: 'roboto'`. It returns the fonts handle: every deck-level\nverb (`paginate`, `toSvg`, `toPng`, `toPdf`, `toPptx`) takes it as\n`{ fonts }` and reads what it needs from it (the `textMeasurement`, the faces\nto embed, the font files). `resolveSlideContext(document, index, { fonts, catalogs })` resolves\none slide's layout, canvas, theme and font families (slide design, then deck\ndesign, then theme, then the engine default font scheme) into the options `composeSlide`\ntakes, so you never look up a font scheme yourself; a reference that resolves\nnowhere comes back as an `unresolved-reference` diagnostic. It also returns\n`slide`: the slide with `{{slide.number}}`, `{{slide.section}}` and\n`{{deck.slideCount}}` substituted for `options.slideNumber` and\n`options.slideCount`; compose and draw that slide. `paginate`\ndoes the same for every slide. The helper does not install system fonts or\nchange the authored scheme. Reuse the same `fonts` for SVG preview and PPTX\nexport.\n\nShared headers and footers use `furniture-flow-v2`. Body content stays between\n`geometry.furniture.headerBottom` and `geometry.furniture.footerTop`. A zone\nwrites generated values as variables in its `text`, such as\n`\"{{slide.number}} / {{deck.slideCount}}\"` or `\"{{organization.name}}\"`.\n\nPagination returns a new presentation plus source mappings. It preserves\nauthored text, whitespace and reading order; it does not drop overflowed\ncontent.\n\n```sh\nnpx --no-install opf paginate deck.opf.json paginated.opf.json\n```\n\n## Edit with undo\n\n```js\nimport { createEditorSession } from '@openpresentation/opf-editor';\n\nconst editor = createEditorSession(document, { rejectInvalid: true });\nconst original = editor.presentation.slides[0].title;\neditor.set('slides.0.title', 'Edited title');\neditor.undo();\n// original title, including whitespace, is restored\n```\n\nThe CLI can apply JSON Patch edits (`opf edit`) but has no persistent undo\nhistory. Use the editor session or version control for undo.\n\n## Preview and export\n\n`@openpresentation/opf` in Node (core 0.18.0 and later) reads and writes files with the bundled fonts and loads the render and\nPPTX engines you installed:\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.json', 'deck.pdf');\nawait opf.convert('deck.opf.json', 'slides/deck.png', { scale: 2 }); // slides/deck-1.png, -2, ... (or \"slides/slide-{n}.png\")\nawait opf.convert('deck.opf.json', 'deck.pptx');\nconst back = await opf.open('deck.pptx'); // PowerPoint to OPF\nconst { files } = await opf.convert(back, { format: 'svg' }); // bytes, nothing written\n```\n\n`convert` checks the deck (format and references), draws with the renderer's bundled open fonts and no system fonts,\nreads images next to the input file, never reads a clock (pass `date: 'YYYY-MM-DD'` for a date field) and writes each file\natomically. A missing engine throws `OPFExportError` with code `peer-not-installed` and the install command. The options and\nerrors are in [OPF files in Node](node.md).\n\n### Advanced: the engines directly\n\n`convert` calls these. Use them when you need the pieces: your own fonts handle, one SVG per slide in a browser,\nor options the function does not expose.\n\n```js\nimport { toSvg, toPng, toPdf } from '@openpresentation/opf-render';\nimport { toPptx } from '@openpresentation/opf-pptx';\n\nconst svgs = toSvg(presentation, { fonts }); // one SVG per slide\nconst png = await toPng(svgs[0], { fonts });\nconst pdf = await toPdf(svgs, { fonts });\nconst pptx = await toPptx(presentation, { fonts });\n```\n\n`toSvg` is the local preview of every slide (`toSvg(presentation, 3, { fonts })` draws slide 3 as one string). PNG rasterizes that SVG.\nPDF (opf-render 0.12.0 and later) converts the same SVG to **vector paths with\nselectable, searchable text** in embedded font subsets, with no second layout pass;\npass `{ raster: true }` for the image-per-slide output that renderers up to 0.11.9\nalways wrote. Pass the same fonts handle as for PNG; vector PDF never uses system\nfonts.\n`toPptx` is the supported PowerPoint export from OPF. **Its text is editable\nline by line, not paragraph by paragraph:** it writes one text box for each\nwrapped line, so text you add or lengthen in PowerPoint or Keynote does not\nreflow into the next line, and a title that wraps is not the Title placeholder.\nFor heavy text changes, edit the OPF source and export again. From opf-pptx and\nCLI 0.22.0, `opf convert deck.opf.json deck.pptx --body-text native` (`bodyText:\n'native'` in `toPptx`) is an opt-in mode that writes whole paragraphs and binds a\nwrapped title to the Title placeholder; see [when to choose it](cli.md#pptx-text-rr-88-rr-91). Shared\nheaders and footers are written on the slide master or layout. The retained\n[Windows sample](evidence/rr-20-windows-current-2026-10-14/README.md) covers\n17 selected native cases and four actual UI operations; 41 of 58 cases were\nnot executed natively. This quickstart verifies published Node APIs and\nexports. It does not certify every Office operation or global native fidelity.\n\nBrowser preview uses the same SVG core plus\n`@openpresentation/opf-render/fonts-browser` and\n`@openpresentation/opf-editor/canvas`. Load the same font bytes the Node helper\nresolved. Do not fetch fonts from the network at render time.\n\n## Prove it\n\nFrom this repository, after a normal `pnpm install`:\n\n```sh\nnode scripts/test-developer-quickstart.mjs\n```\n\nThat script creates an empty temp project, installs the published versions from\nthe npm registry, copies this example, and asserts validate, offline\nfonts, furniture composition, pagination, undo, SVG, PNG, PDF and PPTX.\nIt fails if any package is a `file:` or workspace link.\n\n## What this does not cover\n\n- Renderer native-width residuals:\n [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24)\n- Native PowerPoint open/edit/save/reopen and real Office Header/Footer (`p:hf`):\n [opf#87](https://github.com/OpenPresentation/opf/issues/87)\n- Remaining GitHub [issue 88](https://github.com/OpenPresentation/opf/issues/88)\n checklist (the Inspector overlay/json-options, gallery Playground+Editor\n links, and Header & footer playground example are already live on\n production; the issue stays open)\n- Archived font-shaping prototypes (not in the published runtime)\n- PDF/UA or PDF/A conformance, general SVG diagrams, and Mermaid\n"
|
|
290
290
|
},
|
|
291
291
|
{
|
|
292
292
|
"slug": "release-process",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { SchemaEntry, SchemaName, audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './schemas.js';
|
|
2
|
-
import { C as ConvertInput, a as ConvertOptions, b as ConvertResult, c as ConvertFormat, O as OpenOptions, S as SaveOptions, d as SaveResult } from './conversion-
|
|
3
|
-
export { e as ConvertedFile, E as ExportFormat, F as FontsHandle, f as OPFApiError, g as OPFExportError, h as OPFImportError, i as SlideSelection, p as parseSlideSelection } from './conversion-
|
|
2
|
+
import { C as ConvertInput, a as ConvertOptions, b as ConvertResult, c as ConvertFormat, O as OpenOptions, S as SaveOptions, d as SaveResult } from './conversion-C_MOkYGH.js';
|
|
3
|
+
export { e as ConvertedFile, E as ExportFormat, F as FontsHandle, f as OPFApiError, g as OPFExportError, h as OPFImportError, i as SlideSelection, p as parseSlideSelection } from './conversion-C_MOkYGH.js';
|
|
4
4
|
import { v as validate$1 } from './validator-Cflv4F9e.js';
|
|
5
5
|
export { C as CATALOG_SCHEMA, a as CatalogDisplayKind, b as CatalogRecordKind, D as DEFAULT_VALIDATION_THRESHOLDS, O as OPFValidationError, c as assertValid, d as assertValidCatalogRecord, e as catalogDisplayKinds, f as catalogSchemaNames, g as findValidationRule, h as validateCatalogRecord, i as validationRules } from './validator-Cflv4F9e.js';
|
|
6
6
|
import { R as ResolveVariablesOptions, h as VariableDiagnostic } from './validation-types-B21fZfb2.js';
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var stats2 = (presentation2, options) => stats(presentation2, withDefaultCatalog
|
|
|
31
31
|
var paginate2 = (input, options) => paginate(input, withDefaultCatalog(options));
|
|
32
32
|
var embed2 = (document, options) => embed(document, withDefaultCatalog(options));
|
|
33
33
|
var edit2 = (deck, patch, options) => edit(deck, patch, withDefaultCatalog(options));
|
|
34
|
-
var engine = () => import('./conversion-
|
|
34
|
+
var engine = () => import('./conversion-LBQMY7LX.js');
|
|
35
35
|
var rethrow = (error, fallback) => {
|
|
36
36
|
if (error instanceof OPFValidationError) throw error;
|
|
37
37
|
throw asApiError(OPFApiError, error, fallback);
|
package/dist/node-engine.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { b as DeckReport, c as DeckReportOptions, r as readDeckReport } from './deck-report-vnWmXBjq.js';
|
|
2
|
-
import { F as FontsHandle, j as EmbeddedFace, R as Renderer, k as Reporter, P as Peer, l as PptxModule, f as OPFApiError } from './conversion-
|
|
3
|
-
export { m as ConversionPlan, D as Diagnostic, n as EXPORT_FORMATS, o as EXPORT_TEXT, q as ExportContext, r as ExportFile, s as ExportFontSummary, E as ExportFormat, t as ExportOptions, u as ExportResult, v as ExportRun, w as ExportText, I as ImportOptions, x as ImportResult, g as OPFExportError, h as OPFImportError, y as PEER_RANGES, z as PPTX_PACKAGE, A as PreparedExport, B as RENDER_EXTRAS, G as RENDER_PACKAGE, H as ReportFinding, J as ResolvedExportOptions, i as SlideSelection, K as Source, V as VERSIONS, L as asApiError, M as assetRoot, N as checkDate, Q as checkOptions, T as checkRenamedOptions, U as checkScale, W as deckFileFormat, X as exportFormatOf, Y as finishReport, Z as importPresentation, _ as loadPptx, $ as loadRenderer, a0 as locatePackage, a1 as missingPeerFrom, p as parseSlideSelection, a2 as planConversion, a3 as prepareExport, a4 as reaches, a5 as readInput, a6 as reportThrown, a7 as resolveExportOptions, a8 as runExport, a9 as runPreparedExport, aa as writePlanned } from './conversion-
|
|
2
|
+
import { F as FontsHandle, j as EmbeddedFace, R as Renderer, k as Reporter, P as Peer, l as PptxModule, f as OPFApiError } from './conversion-C_MOkYGH.js';
|
|
3
|
+
export { m as ConversionPlan, D as Diagnostic, n as EXPORT_FORMATS, o as EXPORT_TEXT, q as ExportContext, r as ExportFile, s as ExportFontSummary, E as ExportFormat, t as ExportOptions, u as ExportResult, v as ExportRun, w as ExportText, I as ImportOptions, x as ImportResult, g as OPFExportError, h as OPFImportError, y as PEER_RANGES, z as PPTX_PACKAGE, A as PreparedExport, B as RENDER_EXTRAS, G as RENDER_PACKAGE, H as ReportFinding, J as ResolvedExportOptions, i as SlideSelection, K as Source, V as VERSIONS, L as asApiError, M as assetRoot, N as checkDate, Q as checkOptions, T as checkRenamedOptions, U as checkScale, W as deckFileFormat, X as exportFormatOf, Y as finishReport, Z as importPresentation, _ as loadPptx, $ as loadRenderer, a0 as locatePackage, a1 as missingPeerFrom, p as parseSlideSelection, a2 as planConversion, a3 as prepareExport, a4 as reaches, a5 as readInput, a6 as reportThrown, a7 as resolveExportOptions, a8 as runExport, a9 as runPreparedExport, aa as writePlanned } from './conversion-C_MOkYGH.js';
|
|
4
4
|
import { C as Catalog } from './catalog-refs-CSHevLn1.js';
|
|
5
5
|
import { e as PresentationPaginationResult } from './pagination-C4jzAtFy.js';
|
|
6
6
|
import './presentation-fN6tORPZ.js';
|
package/dist/node-engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { loadRenderer, listFontDirectories, leaseSharedFonts, prepareFonts } from './chunk-
|
|
2
|
-
export { EXPORT_FORMATS, EXPORT_TEXT, PEER_RANGES, PPTX_PACKAGE, RENDER_EXTRAS, RENDER_PACKAGE, Reporter, VERSIONS, assetRoot, checkDate, checkDestinations, checkOptions, checkOutputPattern, checkRenamedOptions, checkScale, createZip, deckFileFormat, deckStem, embeddedFor, exportFormatOf, finishReport, importPresentation, leaseSharedFonts, listFontDirectories, loadPptx, loadRenderer, localAssetProblems, locatePackage, missingPeerFrom, padNumber, planConversion, pointerOf, prepareExport, prepareFonts, publishNew, reaches, readInput, reportThrown, resolveExportOptions, runExport, runImport, runPreparedExport, stemOf, writeFiles, writePlanned } from './chunk-
|
|
1
|
+
import { loadRenderer, listFontDirectories, leaseSharedFonts, prepareFonts } from './chunk-AHKRJSCF.js';
|
|
2
|
+
export { EXPORT_FORMATS, EXPORT_TEXT, PEER_RANGES, PPTX_PACKAGE, RENDER_EXTRAS, RENDER_PACKAGE, Reporter, VERSIONS, assetRoot, checkDate, checkDestinations, checkOptions, checkOutputPattern, checkRenamedOptions, checkScale, createZip, deckFileFormat, deckStem, embeddedFor, exportFormatOf, finishReport, importPresentation, leaseSharedFonts, listFontDirectories, loadPptx, loadRenderer, localAssetProblems, locatePackage, missingPeerFrom, padNumber, planConversion, pointerOf, prepareExport, prepareFonts, publishNew, reaches, readInput, reportThrown, resolveExportOptions, runExport, runImport, runPreparedExport, stemOf, writeFiles, writePlanned } from './chunk-AHKRJSCF.js';
|
|
3
3
|
import { defaultCatalogs } from './chunk-MG34DCDB.js';
|
|
4
4
|
export { defaultCatalogs, galleryMismatch } from './chunk-MG34DCDB.js';
|
|
5
5
|
import { OPFApiError } from './chunk-MAJHCGOW.js';
|
package/dist/repo-readme.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/generated/repo-readme.ts
|
|
2
|
-
var repoReadmeRaw = '# Open Presentation Format (OPF)\n\n[](https://www.npmjs.com/package/@openpresentation/opf)\n[](https://www.npmjs.com/package/@openpresentation/opf)\n[](./LICENSE)\n\nPublic npm package: [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf) (`npm install @openpresentation/opf`).\n\nOpen Presentation Format is the portable, human-readable JSON document format for slide decks.\n\nThis repository is the canonical home for the OPF **spec**, **JSON Schemas**, **catalog presets**, examples, generated developer types, local validation tooling, and integration docs for the render/edit/convert toolkit. OpenPresentation publishes open-source code and documentation only; it does not provide hosted APIs, hosted rendering functions, queues, storage, authentication, jobs, previews, SLAs, telemetry, or managed infrastructure.\n\nFor AI agents, use the [OPF skill set](docs/agent-skills.md) for authoring, layout, presets, editing, export, and schema inspection.\n\nPublished CLI 0.18.0 installs all six skills into your project with `npx @openpresentation/cli@0.18.0 skills install`. It uses local copies, preserves existing instructions and refuses to overwrite customized skills. See the [installation and update guide](docs/agent-skills.md) for personal or agent-specific targets and source development.\n\nFor a fresh Node 24 project that installs **published** packages (not this\ncheckout), follow the [developer quickstart](docs/quickstart.md) and the\n[compatibility matrix](docs/compatibility-matrix.md).\n\nFor LLM authoring, start with [the authoring guide](docs/llm-authoring.md), [dynamic composition](docs/dynamic-composition.md), and [local ecosystem verification](docs/ecosystem-development.md).\n\n## File naming\n\nOpen Presentation Format documents are JSON files. Use `*.opf.json` for complete OPF presentation documents, for example `board-review.opf.json` or `deck.opf.json`.\n\nAvoid using bare `*.opf` for OPF JSON. The `.opf` extension is already used by other document and project formats, while `.opf.json` keeps the OPF identity and still makes the underlying JSON format clear to editors, validators, agents, and version-control tooling.\n\n## Naming and reuse\n\nThe format name is **Open Presentation Format**. The schemas, catalogs, packages, and local tooling in this repository are free and open source under the MIT license, so third-party tools may read, write, validate, render, convert, and describe support for Open Presentation Format without adopting any product-specific branding.\n\n## Why OPF\n\n`.pptx` is a zipped bundle of XML. Humans can\'t diff it, LLMs can\'t read or write it reliably, and git can\'t track it meaningfully. Every change looks like a binary blob.\n\nOPF is plain JSON. A human can open it in an editor. A model can read and write it without guessing at schema-by-example. Decks live in git like the rest of your work.\n\nThat\'s the shift that lets LLMs actually *author* decks. When the format stops fighting them, models can do the work that matters \u2014 narrative structure, persuasive framing, data analysis, chart recommendations, ruthless revision passes \u2014 instead of wrestling with `<p:sp>` tags.\n\nAnd they don\'t start from a blank canvas. [pptx.gallery](https://pptx.gallery) is the human-browsable reference for OPF catalog presets: layouts, themes, color schemes, font schemes, chart types, narratives, audiences, purposes, tones, languages, and social platforms.\n\n## Start in three steps\n\n1. **Install the coordinated published packages** on Node 24. See [the developer quickstart](docs/quickstart.md) for the currently recorded registry pin set: core 0.20.3, renderer 0.20.2, PPTX/editor 0.20.3, CLI 0.20.0 and Gallery 3.0.0. The default catalog is supplied by `@openpresentation/gallery@3.0.0`; see the [compatibility matrix](docs/compatibility-matrix.md) for feature and native limits.\n2. **Author, validate, paginate, preview and export.** New to OPF? Run the [first deck](docs/first-deck/README.md) end to end first. Copy [`docs/quickstart/developer-quickstart.opf.json`](./docs/quickstart/developer-quickstart.opf.json) and run the commands in that guide. From code, `convert("deck.opf.md", "deck.pdf")` of `@openpresentation/opf` is the simple path to a PDF, PNG, SVG or PPTX file. `validate` / `opf validate` is one local checker for format, references, accessibility, layout and content ([guide](docs/validate.md)), not visual verification.\n3. **Know the limits.** PPTX export writes one text box per wrapped line, so text edited in PowerPoint or Keynote does not reflow (see [rich text](docs/rich-text.md)), and fonts are named, not embedded. The [compatibility matrix](docs/compatibility-matrix.md) lists shipped APIs versus renderer issue 24, native PowerPoint issue 87, and other deferred work. Browse presets at [pptx.gallery](https://pptx.gallery).\n\nYour deck can live in git from the first commit. After installing dependencies and supplying referenced assets, these commands run locally without a model provider, account or hosted OPF API.\n\n## JavaScript and TypeScript\n\nThe canonical JavaScript/TypeScript package is published at [`packages/javascript`](./packages/javascript) as [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf). The schema is pre-stable (0.x \u2014 expect breaking changes between minor versions until 1.0). Its responsibility is local and format-level only:\n\n- export the canonical schemas from [`spec/`](./spec)\n- register the pptx.gallery catalog of [`@openpresentation/gallery`](https://github.com/OpenPresentation/gallery) (a dependency, released on its own version line) as the Node defaults, as the CLI does; a browser host registers it itself\n- export a typed raw spec file manifest for package-addressable `spec/` content\n- generate TypeScript types, with `Presentation` as the top-level type\n- validate OPF JSON and catalog records locally\n\nIn Node (and Bun and Deno) its root also reads and writes files (`convert`, `open`, `save`) and draws them through the optional engines below; in a browser or a worker the same import is a browser-safe build with the same names, where those three reject with `node-only`. Every other entry runs in a browser. It does not generate content with AI, fetch remote catalogs, call hosted APIs, or provide managed services. Render/edit/convert packages live in separate MIT repos that depend on `@openpresentation/opf`. The format package also exposes pure composition geometry so those packages share layout behavior.\n\n## Toolkit libraries\n\nOPF is six packages. **Core** (`@openpresentation/opf`) is the format and its API: it reads and writes the text formats (JSON, YAML, Markdown) and validates, and in Node reads and writes files, through the same import. **The gallery** (`@openpresentation/gallery`) is the pptx.gallery catalog of layouts, themes, colour and font schemes and the other records, released on its own version line; core depends on it and registers it in Node. **The CLI** (`@openpresentation/cli`) is the `opf` command. **Render** (`opf-render`), **PPTX** (`opf-pptx`) and **the editor** (`opf-editor`) are the engines: drawing, PowerPoint and editing, installed as needed. The engines and the editor live in sibling repositories. See [ecosystem development](docs/ecosystem-development.md) for coordinated builds and verification, and [dynamic composition](docs/dynamic-composition.md) for portable layout rules.\n\n| Repo | Role | Boundary |\n|---|---|---|\n| [`gallery`](https://github.com/OpenPresentation/gallery) (`@openpresentation/gallery`) | The pptx.gallery catalog | Catalog records only, no runtime dependency; minor releases add records, a changed or removed record is a major release |\n| `cli` (`@openpresentation/cli`, this repository) | `opf` command | Runs core\'s Node engine: reads and writes files and calls the engines below as optional peers |\n| `opf-render` | OPF to SVG/PNG/PDF | Local and embeddable rendering library |\n| `opf-editor` | WYSIWYG bindings/components | Headless editor primitives plus optional UI components |\n| `opf-pptx` | OPF to PPTX and PPTX to OPF | Pure local import/export library for browser and server use where supported |\n\nThese repos provide OSS primitives only. Downstream applications own hosting, auth, storage, collaboration, queues, previews, analytics, support, and workflow UX.\n\n## Usage\n\nInstall from npm:\n\n```sh\npnpm add @openpresentation/opf\n# or: npm install @openpresentation/opf\n```\n\nTo work on the package itself, clone this repo and build the workspace:\n\n```sh\npnpm install\npnpm build\n```\n\nUse the format package from JavaScript or TypeScript:\n\n```ts\nimport { presentation, validate } from "@openpresentation/opf";\nimport { gallery } from "@openpresentation/gallery";\n\nimport type { Presentation } from "@openpresentation/opf";\n\nconst deck: Presentation = {\n name: "Quarterly Review",\n slides: [{ title: "Quarterly Review", items: ["Revenue", "Product", "Hiring"] }],\n};\n\nconst report = validate(deck);\nconsole.log(report.valid); // no finding has severity "error"\nconsole.log(report.findings); // every finding: rule id, severity, category, JSON Pointer path, message, fixes\n// Core ships no catalog records; in Node, validate registers the gallery when a call names no catalogs, and a host may name its own.\nconsole.log(validate({ ...deck, design: { theme: "classic" } }, { catalogs: [gallery] }).valid);\nconsole.log(Object.keys(gallery.audiences).length);\n```\n\nConvert, open and save files in Node with `@openpresentation/opf`, the same import a browser uses (the engines `@openpresentation/opf-render` and `@openpresentation/opf-pptx` are optional peers, installed as needed; see [OPF files in Node](docs/node.md)):\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf");\nconst deck = await opf.open("deck.opf.md");\ndeck.slides.push({ title: "Q4" });\nawait opf.save(deck, "deck.opf.md");\nconst { files } = await opf.convert(deck, { format: "pptx" }); // bytes, nothing written\n```\n\nUse focused imports when you only need one surface:\n\n```ts\nimport { presentation } from "@openpresentation/opf/schemas";\nimport { specFileEntries } from "@openpresentation/opf/spec-files";\nimport { validate } from "@openpresentation/opf/validator";\nimport type { Presentation } from "@openpresentation/opf/types";\n```\n\nUse raw JSON when an engine or resolver needs package-addressable files:\n\n```ts\nimport presentationSchema from "@openpresentation/opf/spec/schemas/opf.schema.json" with {\n type: "json",\n};\n```\n\nUse the [installable OPF CLI](./packages/cli/README.md) to create, validate, and edit files locally:\n\n```sh\npnpm --filter @openpresentation/cli build\nnode packages/cli/dist/index.js create deck.opf.json --title "Decision brief"\nnode packages/cli/dist/index.js validate deck.opf.json\nnode packages/cli/dist/index.js edit deck.opf.json --patch changes.json --in-place\n```\n\nSee [CSV and JSON data import](./docs/data-import.md) for editable tables and charts in the editor, CLI, and package API, and [content conversions](./docs/conversions.md) for the pure converters (`@openpresentation/opf/convert`) that change a block\'s kind, nest list items, restructure a slide and split or merge slides, and [Markdown and outlines](./docs/markdown.md) for the deterministic Markdown dialect (`@openpresentation/opf/markdown`, `opf convert deck.md deck.opf.json`) that reads and writes a whole deck as text.\n\n[OPF as YAML](./docs/yaml.md) describes the strict YAML form of a deck (`@openpresentation/opf/yaml`, `opf convert deck.opf.json deck.opf.yaml`, `.opf.yaml` files in every command, editor autocomplete); JSON stays the canonical form. A deck can equally be a Markdown file, `deck.opf.md`: every command reads and writes it (see [Markdown decks in every command](./docs/markdown.md#markdown-decks-in-every-command)), and `parse` / `stringify` read and write a deck in JSON, YAML or Markdown with one call.\n\nSee [Templates and variables](./docs/templates-and-variables.md) for fillable OPF templates: typed variables, `{{id}}` tokens, `opf fill` and `resolveVariables`.\n\n## Layout\n\n| Path | Contents |\n|---|---|\n| [`spec/schemas/opf.schema.json`](./spec/schemas/opf.schema.json) | Canonical JSON Schema for top-level OPF `Presentation` documents. |\n| [`docs/how-opf-works.md`](./docs/how-opf-works.md) | Conceptual introduction: the document model, content shapes, catalog resolution, and the validation philosophy. Start here. |\n| [`docs/design-resolution.md`](./docs/design-resolution.md) | The design precedence algorithm (slide design \u2192 deck design \u2192 resolved theme \u2192 engine defaults) with worked examples. |\n| [`docs/schema-reference.md`](./docs/schema-reference.md) | Author-facing reference for top-level OPF fields and every presentation schema `$defs` object/type. |\n| [`docs/slide-sizes.md`](./docs/slide-sizes.md) | OPF\'s slide-size presets in inches, points and EMU, how they compare with PowerPoint\'s presets, and what PPTX export and import do with them. |\n| [`docs/catalog-schema-reference.md`](./docs/catalog-schema-reference.md) | Author-facing reference for every companion catalog schema. |\n| [`docs/content-payloads.md`](./docs/content-payloads.md) | Author-facing notes for slide and region content payloads, including chart and table object shapes. |\n| [`docs/footnotes-citations-captions.md`](./docs/footnotes-citations-captions.md) | Citations (`cite`), inline footnotes, the deck `references` list, captions on images, charts, tables and videos, and how every engine draws them. |\n| [`docs/examples.md`](./docs/examples.md) | Guide to the expanded scenario-oriented examples under `examples/gallery/`. |\n| [`docs/live-editor.md`](./docs/live-editor.md) | Browser canvas, live OPF editing, font loading, published packages, and current fidelity limits. |\n| [`docs/release-process.md`](./docs/release-process.md) | Maintainer runbook for tagging, trusted npm publishing, verification, and GitHub release notes. |\n| [`spec/schemas/*.schema.json`](./spec/schemas) | Companion schemas for catalog records and sub-objects. |\n| [`@openpresentation/gallery`](https://github.com/OpenPresentation/gallery) | The independently published default catalog and layout previews. |\n| [`spec/openapi.yaml`](./spec/openapi.yaml) | Optional reference OpenAPI contract for downstream services that choose to expose OPF over HTTP. OpenPresentation does not host this API. |\n| [`examples/technical/`](./examples/technical) | Focused OPF fixtures for validator, renderer, catalog-resolution, design, content-payload, and region behavior. |\n| [`examples/gallery/`](./examples/gallery) | Broader OPF example decks organized by industry, function, education, government, presentation type, international, and design/media scenarios. |\n| [`packages/javascript/`](./packages/javascript) | Public pre-stable source for `@openpresentation/opf`. |\n| [`packages/cli/`](./packages/cli) | Installable local CLI for creating, validating, editing, and inspecting OPF. |\n| [`legacy/`](./legacy) | Tombstone for service-specific clients, CLIs, tool integrations, and workflows removed from the OpenPresentation OSS repo. |\n\n## OpenPresentation Boundary\n\nOpenPresentation defines the format, bundled presets, local validation, examples, docs, and planned local render/edit/convert libraries. It does not provide hosted functions or managed product surfaces.\n\nFuture non-JavaScript OPF packages should follow the same local-first boundary: Python and Go packages should expose schemas, types/models, catalogs, validation, and package-addressable assets. Future toolkit packages should expose embeddable library APIs with no required network calls, hosted callbacks, hidden telemetry, or managed infrastructure assumptions.\n\nThe published JavaScript package copies package-addressable OPF schemas, catalogs, reference assets, and the optional reference `spec/openapi.yaml` from `spec/`. It intentionally remains `@openpresentation/opf` instead of introducing a separate `@openpresentation/opf-spec` package so downstream imports can advance by semver-pinning one canonical package.\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n';
|
|
2
|
+
var repoReadmeRaw = '# Open Presentation Format (OPF)\n\n[](https://www.npmjs.com/package/@openpresentation/opf)\n[](https://www.npmjs.com/package/@openpresentation/opf)\n[](./LICENSE)\n\nPublic npm package: [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf) (`npm install @openpresentation/opf`).\n\nOpen Presentation Format is the portable, human-readable JSON document format for slide decks.\n\nThis repository is the canonical home for the OPF **spec**, **JSON Schemas**, **catalog presets**, examples, generated developer types, local validation tooling, and integration docs for the render/edit/convert toolkit. OpenPresentation publishes open-source code and documentation only; it does not provide hosted APIs, hosted rendering functions, queues, storage, authentication, jobs, previews, SLAs, telemetry, or managed infrastructure.\n\nFor AI agents, use the [OPF skill set](docs/agent-skills.md) for authoring, layout, presets, editing, export, and schema inspection.\n\nPublished CLI 0.18.0 installs all six skills into your project with `npx @openpresentation/cli@0.18.0 skills install`. It uses local copies, preserves existing instructions and refuses to overwrite customized skills. See the [installation and update guide](docs/agent-skills.md) for personal or agent-specific targets and source development.\n\nFor a fresh Node 24 project that installs **published** packages (not this\ncheckout), follow the [developer quickstart](docs/quickstart.md) and the\n[compatibility matrix](docs/compatibility-matrix.md).\n\nFor LLM authoring, start with [the authoring guide](docs/llm-authoring.md), [dynamic composition](docs/dynamic-composition.md), and [local ecosystem verification](docs/ecosystem-development.md).\n\n## File naming\n\nOpen Presentation Format documents are JSON files. Use `*.opf.json` for complete OPF presentation documents, for example `board-review.opf.json` or `deck.opf.json`.\n\nAvoid using bare `*.opf` for OPF JSON. The `.opf` extension is already used by other document and project formats, while `.opf.json` keeps the OPF identity and still makes the underlying JSON format clear to editors, validators, agents, and version-control tooling.\n\n## Naming and reuse\n\nThe format name is **Open Presentation Format**. The schemas, catalogs, packages, and local tooling in this repository are free and open source under the MIT license, so third-party tools may read, write, validate, render, convert, and describe support for Open Presentation Format without adopting any product-specific branding.\n\n## Why OPF\n\n`.pptx` is a zipped bundle of XML. Humans can\'t diff it, LLMs can\'t read or write it reliably, and git can\'t track it meaningfully. Every change looks like a binary blob.\n\nOPF is plain JSON. A human can open it in an editor. A model can read and write it without guessing at schema-by-example. Decks live in git like the rest of your work.\n\nThat\'s the shift that lets LLMs actually *author* decks. When the format stops fighting them, models can do the work that matters \u2014 narrative structure, persuasive framing, data analysis, chart recommendations, ruthless revision passes \u2014 instead of wrestling with `<p:sp>` tags.\n\nAnd they don\'t start from a blank canvas. [pptx.gallery](https://pptx.gallery) is the human-browsable reference for OPF catalog presets: layouts, themes, color schemes, font schemes, chart types, narratives, audiences, purposes, tones, languages, and social platforms.\n\n## Start in three steps\n\n1. **Install the coordinated published packages** on Node 24. See [the developer quickstart](docs/quickstart.md) for the currently recorded registry pin set: core 0.20.3, renderer 0.20.2, PPTX/editor 0.20.3, CLI 0.20.0 and Gallery 3.0.0. The default catalog is supplied by `@openpresentation/gallery@3.0.0`; see the [compatibility matrix](docs/compatibility-matrix.md) for feature and native limits.\n2. **Author, validate, paginate, preview and export.** New to OPF? Run the [first deck](docs/first-deck/README.md) end to end first. Copy [`docs/quickstart/developer-quickstart.opf.json`](./docs/quickstart/developer-quickstart.opf.json) and run the commands in that guide. From code, `convert("deck.opf.md", "deck.pdf")` of `@openpresentation/opf` is the simple path to a PDF, PNG, SVG or PPTX file. `validate` / `opf validate` is one local checker for format, references, accessibility, layout and content ([guide](docs/validate.md)), not visual verification.\n3. **Know the limits.** PPTX export writes one text box per wrapped line, so text edited in PowerPoint or Keynote does not reflow (see [rich text](docs/rich-text.md)) unless you opt in to `--body-text native` (opf-pptx and CLI 0.22.0 or later), and fonts are named, not embedded. The [compatibility matrix](docs/compatibility-matrix.md) lists shipped APIs versus renderer issue 24, native PowerPoint issue 87, and other deferred work. Browse presets at [pptx.gallery](https://pptx.gallery).\n\nYour deck can live in git from the first commit. After installing dependencies and supplying referenced assets, these commands run locally without a model provider, account or hosted OPF API.\n\n## JavaScript and TypeScript\n\nThe canonical JavaScript/TypeScript package is published at [`packages/javascript`](./packages/javascript) as [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf). The schema is pre-stable (0.x \u2014 expect breaking changes between minor versions until 1.0). Its responsibility is local and format-level only:\n\n- export the canonical schemas from [`spec/`](./spec)\n- register the pptx.gallery catalog of [`@openpresentation/gallery`](https://github.com/OpenPresentation/gallery) (a dependency, released on its own version line) as the Node defaults, as the CLI does; a browser host registers it itself\n- export a typed raw spec file manifest for package-addressable `spec/` content\n- generate TypeScript types, with `Presentation` as the top-level type\n- validate OPF JSON and catalog records locally\n\nIn Node (and Bun and Deno) its root also reads and writes files (`convert`, `open`, `save`) and draws them through the optional engines below; in a browser or a worker the same import is a browser-safe build with the same names, where those three reject with `node-only`. Every other entry runs in a browser. It does not generate content with AI, fetch remote catalogs, call hosted APIs, or provide managed services. Render/edit/convert packages live in separate MIT repos that depend on `@openpresentation/opf`. The format package also exposes pure composition geometry so those packages share layout behavior.\n\n## Toolkit libraries\n\nOPF is six packages. **Core** (`@openpresentation/opf`) is the format and its API: it reads and writes the text formats (JSON, YAML, Markdown) and validates, and in Node reads and writes files, through the same import. **The gallery** (`@openpresentation/gallery`) is the pptx.gallery catalog of layouts, themes, colour and font schemes and the other records, released on its own version line; core depends on it and registers it in Node. **The CLI** (`@openpresentation/cli`) is the `opf` command. **Render** (`opf-render`), **PPTX** (`opf-pptx`) and **the editor** (`opf-editor`) are the engines: drawing, PowerPoint and editing, installed as needed. The engines and the editor live in sibling repositories. See [ecosystem development](docs/ecosystem-development.md) for coordinated builds and verification, and [dynamic composition](docs/dynamic-composition.md) for portable layout rules.\n\n| Repo | Role | Boundary |\n|---|---|---|\n| [`gallery`](https://github.com/OpenPresentation/gallery) (`@openpresentation/gallery`) | The pptx.gallery catalog | Catalog records only, no runtime dependency; minor releases add records, a changed or removed record is a major release |\n| `cli` (`@openpresentation/cli`, this repository) | `opf` command | Runs core\'s Node engine: reads and writes files and calls the engines below as optional peers |\n| `opf-render` | OPF to SVG/PNG/PDF | Local and embeddable rendering library |\n| `opf-editor` | WYSIWYG bindings/components | Headless editor primitives plus optional UI components |\n| `opf-pptx` | OPF to PPTX and PPTX to OPF | Pure local import/export library for browser and server use where supported |\n\nThese repos provide OSS primitives only. Downstream applications own hosting, auth, storage, collaboration, queues, previews, analytics, support, and workflow UX.\n\n## Usage\n\nInstall from npm:\n\n```sh\npnpm add @openpresentation/opf\n# or: npm install @openpresentation/opf\n```\n\nTo work on the package itself, clone this repo and build the workspace:\n\n```sh\npnpm install\npnpm build\n```\n\nUse the format package from JavaScript or TypeScript:\n\n```ts\nimport { presentation, validate } from "@openpresentation/opf";\nimport { gallery } from "@openpresentation/gallery";\n\nimport type { Presentation } from "@openpresentation/opf";\n\nconst deck: Presentation = {\n name: "Quarterly Review",\n slides: [{ title: "Quarterly Review", items: ["Revenue", "Product", "Hiring"] }],\n};\n\nconst report = validate(deck);\nconsole.log(report.valid); // no finding has severity "error"\nconsole.log(report.findings); // every finding: rule id, severity, category, JSON Pointer path, message, fixes\n// Core ships no catalog records; in Node, validate registers the gallery when a call names no catalogs, and a host may name its own.\nconsole.log(validate({ ...deck, design: { theme: "classic" } }, { catalogs: [gallery] }).valid);\nconsole.log(Object.keys(gallery.audiences).length);\n```\n\nConvert, open and save files in Node with `@openpresentation/opf`, the same import a browser uses (the engines `@openpresentation/opf-render` and `@openpresentation/opf-pptx` are optional peers, installed as needed; see [OPF files in Node](docs/node.md)):\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf");\nconst deck = await opf.open("deck.opf.md");\ndeck.slides.push({ title: "Q4" });\nawait opf.save(deck, "deck.opf.md");\nconst { files } = await opf.convert(deck, { format: "pptx" }); // bytes, nothing written\n```\n\nUse focused imports when you only need one surface:\n\n```ts\nimport { presentation } from "@openpresentation/opf/schemas";\nimport { specFileEntries } from "@openpresentation/opf/spec-files";\nimport { validate } from "@openpresentation/opf/validator";\nimport type { Presentation } from "@openpresentation/opf/types";\n```\n\nUse raw JSON when an engine or resolver needs package-addressable files:\n\n```ts\nimport presentationSchema from "@openpresentation/opf/spec/schemas/opf.schema.json" with {\n type: "json",\n};\n```\n\nUse the [installable OPF CLI](./packages/cli/README.md) to create, validate, and edit files locally:\n\n```sh\npnpm --filter @openpresentation/cli build\nnode packages/cli/dist/index.js create deck.opf.json --title "Decision brief"\nnode packages/cli/dist/index.js validate deck.opf.json\nnode packages/cli/dist/index.js edit deck.opf.json --patch changes.json --in-place\n```\n\nSee [CSV and JSON data import](./docs/data-import.md) for editable tables and charts in the editor, CLI, and package API, and [content conversions](./docs/conversions.md) for the pure converters (`@openpresentation/opf/convert`) that change a block\'s kind, nest list items, restructure a slide and split or merge slides, and [Markdown and outlines](./docs/markdown.md) for the deterministic Markdown dialect (`@openpresentation/opf/markdown`, `opf convert deck.md deck.opf.json`) that reads and writes a whole deck as text.\n\n[OPF as YAML](./docs/yaml.md) describes the strict YAML form of a deck (`@openpresentation/opf/yaml`, `opf convert deck.opf.json deck.opf.yaml`, `.opf.yaml` files in every command, editor autocomplete); JSON stays the canonical form. A deck can equally be a Markdown file, `deck.opf.md`: every command reads and writes it (see [Markdown decks in every command](./docs/markdown.md#markdown-decks-in-every-command)), and `parse` / `stringify` read and write a deck in JSON, YAML or Markdown with one call.\n\nSee [Templates and variables](./docs/templates-and-variables.md) for fillable OPF templates: typed variables, `{{id}}` tokens, `opf fill` and `resolveVariables`.\n\n## Layout\n\n| Path | Contents |\n|---|---|\n| [`spec/schemas/opf.schema.json`](./spec/schemas/opf.schema.json) | Canonical JSON Schema for top-level OPF `Presentation` documents. |\n| [`docs/how-opf-works.md`](./docs/how-opf-works.md) | Conceptual introduction: the document model, content shapes, catalog resolution, and the validation philosophy. Start here. |\n| [`docs/design-resolution.md`](./docs/design-resolution.md) | The design precedence algorithm (slide design \u2192 deck design \u2192 resolved theme \u2192 engine defaults) with worked examples. |\n| [`docs/schema-reference.md`](./docs/schema-reference.md) | Author-facing reference for top-level OPF fields and every presentation schema `$defs` object/type. |\n| [`docs/slide-sizes.md`](./docs/slide-sizes.md) | OPF\'s slide-size presets in inches, points and EMU, how they compare with PowerPoint\'s presets, and what PPTX export and import do with them. |\n| [`docs/catalog-schema-reference.md`](./docs/catalog-schema-reference.md) | Author-facing reference for every companion catalog schema. |\n| [`docs/content-payloads.md`](./docs/content-payloads.md) | Author-facing notes for slide and region content payloads, including chart and table object shapes. |\n| [`docs/footnotes-citations-captions.md`](./docs/footnotes-citations-captions.md) | Citations (`cite`), inline footnotes, the deck `references` list, captions on images, charts, tables and videos, and how every engine draws them. |\n| [`docs/examples.md`](./docs/examples.md) | Guide to the expanded scenario-oriented examples under `examples/gallery/`. |\n| [`docs/live-editor.md`](./docs/live-editor.md) | Browser canvas, live OPF editing, font loading, published packages, and current fidelity limits. |\n| [`docs/release-process.md`](./docs/release-process.md) | Maintainer runbook for tagging, trusted npm publishing, verification, and GitHub release notes. |\n| [`spec/schemas/*.schema.json`](./spec/schemas) | Companion schemas for catalog records and sub-objects. |\n| [`@openpresentation/gallery`](https://github.com/OpenPresentation/gallery) | The independently published default catalog and layout previews. |\n| [`spec/openapi.yaml`](./spec/openapi.yaml) | Optional reference OpenAPI contract for downstream services that choose to expose OPF over HTTP. OpenPresentation does not host this API. |\n| [`examples/technical/`](./examples/technical) | Focused OPF fixtures for validator, renderer, catalog-resolution, design, content-payload, and region behavior. |\n| [`examples/gallery/`](./examples/gallery) | Broader OPF example decks organized by industry, function, education, government, presentation type, international, and design/media scenarios. |\n| [`packages/javascript/`](./packages/javascript) | Public pre-stable source for `@openpresentation/opf`. |\n| [`packages/cli/`](./packages/cli) | Installable local CLI for creating, validating, editing, and inspecting OPF. |\n| [`legacy/`](./legacy) | Tombstone for service-specific clients, CLIs, tool integrations, and workflows removed from the OpenPresentation OSS repo. |\n\n## OpenPresentation Boundary\n\nOpenPresentation defines the format, bundled presets, local validation, examples, docs, and planned local render/edit/convert libraries. It does not provide hosted functions or managed product surfaces.\n\nFuture non-JavaScript OPF packages should follow the same local-first boundary: Python and Go packages should expose schemas, types/models, catalogs, validation, and package-addressable assets. Future toolkit packages should expose embeddable library APIs with no required network calls, hosted callbacks, hidden telemetry, or managed infrastructure assumptions.\n\nThe published JavaScript package copies package-addressable OPF schemas, catalogs, reference assets, and the optional reference `spec/openapi.yaml` from `spec/`. It intentionally remains `@openpresentation/opf` instead of introducing a separate `@openpresentation/opf-spec` package so downstream imports can advance by semver-pinning one canonical package.\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n';
|
|
3
3
|
|
|
4
4
|
// src/repo-readme.ts
|
|
5
5
|
var repoReadme = repoReadmeRaw;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpresentation/opf",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.2",
|
|
4
4
|
"description": "Canonical Open Presentation Format schemas, TypeScript types, local validation and composition; in Node, the file API and the CLI defaults register the pptx.gallery catalog of @openpresentation/gallery.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Open Presentation",
|
|
@@ -157,8 +157,8 @@
|
|
|
157
157
|
"@expo-google-fonts/roboto": "0.4.3",
|
|
158
158
|
"@expo-google-fonts/roboto-mono": "0.4.2",
|
|
159
159
|
"@expo-google-fonts/tinos": "0.4.2",
|
|
160
|
-
"@openpresentation/opf-pptx": "0.
|
|
161
|
-
"@openpresentation/opf-render": "0.
|
|
160
|
+
"@openpresentation/opf-pptx": "0.22.0",
|
|
161
|
+
"@openpresentation/opf-render": "0.22.2",
|
|
162
162
|
"@resvg/resvg-js": "2.6.2",
|
|
163
163
|
"@types/node": "^24.13.4",
|
|
164
164
|
"json-schema-to-typescript": "^16.0.0",
|