@openpresentation/opf 0.26.10 → 0.27.0
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/README.md +6 -0
- package/dist/browser.js +20 -17
- package/dist/{chunk-H7XMO36O.js → chunk-353VBJ6K.js} +5 -5
- package/dist/chunk-43JWUIGF.js +8 -0
- package/dist/chunk-4ACS57MG.js +236 -0
- package/dist/{chunk-F4HFDP6I.js → chunk-4ZM5G3XX.js} +1 -1
- package/dist/{chunk-NVT2YDVC.js → chunk-7XUEKIXH.js} +2 -2
- package/dist/{chunk-Y25DYRME.js → chunk-ABFDFZIW.js} +1 -1
- package/dist/{chunk-A54NGT2K.js → chunk-APLNGCEZ.js} +2 -2
- package/dist/{chunk-G5WJMJV3.js → chunk-CXPBU5W4.js} +1 -1
- package/dist/{chunk-PHP45MO7.js → chunk-EO3JWEO5.js} +1 -1
- package/dist/chunk-GJO5NUVT.js +1 -0
- package/dist/chunk-GM7WFPGG.js +10 -0
- package/dist/{chunk-3S2XVDPN.js → chunk-GNKESMZT.js} +6 -6
- package/dist/chunk-JBARNYFR.js +23 -0
- package/dist/{chunk-TKQ2VFV5.js → chunk-JSHMFTSY.js} +1 -1
- package/dist/{chunk-VZBFQLCA.js → chunk-KZPZRHL6.js} +6 -6
- package/dist/{chunk-XRMV2GMB.js → chunk-LRTQXGJO.js} +236 -144
- package/dist/{chunk-EQ6S5SEZ.js → chunk-O3SA5SJ4.js} +1 -1
- package/dist/{chunk-GIWNHRTX.js → chunk-OSM3LZ2Y.js} +4 -4
- package/dist/{chunk-IVPA6NG6.js → chunk-PAGVSTVF.js} +348 -97
- package/dist/{chunk-Y4TZCE7R.js → chunk-SYW4BCGZ.js} +2 -2
- package/dist/{chunk-D25BNPTW.js → chunk-TMREBZBQ.js} +4 -4
- package/dist/{chunk-77KL2PMM.js → chunk-WJ4XTCOD.js} +2 -2
- package/dist/{chunk-L52N5FZZ.js → chunk-ZGTOZA2S.js} +6 -6
- package/dist/{composition-DIftf6iP.d.ts → composition-BGmpe5e4.d.ts} +51 -5
- package/dist/composition.d.ts +1 -1
- package/dist/composition.js +2 -1
- package/dist/{conversion-ChmGA8Ul.d.ts → conversion-8SQD4dqu.d.ts} +6 -6
- package/dist/conversion-LN6PFRZL.js +24 -0
- package/dist/convert.d.ts +2 -2
- package/dist/convert.js +9 -7
- package/dist/data.d.ts +1 -1
- package/dist/data.js +3 -2
- package/dist/{deck-report-5vwMGcQz.d.ts → deck-report-BaKgPh_N.d.ts} +2 -2
- package/dist/deck.d.ts +6 -6
- package/dist/deck.js +13 -11
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +6 -4
- package/dist/docs.js +6 -4
- package/dist/examples.d.ts +1 -1
- package/dist/examples.js +2 -0
- package/dist/{finding-l9dlQ2NF.d.ts → finding-Bh56sGtF.d.ts} +1 -1
- package/dist/font-policy.js +1 -0
- package/dist/format.js +3 -2
- package/dist/ids.d.ts +113 -0
- package/dist/ids.js +6 -0
- package/dist/index.d.ts +15 -12
- package/dist/index.js +24 -21
- package/dist/markdown.d.ts +4 -4
- package/dist/markdown.js +10 -8
- package/dist/node-engine.d.ts +8 -8
- package/dist/node-engine.js +24 -21
- package/dist/{pagination-D_kJe4jH.d.ts → pagination-BsUTsQvk.d.ts} +1 -1
- package/dist/pagination.d.ts +2 -2
- package/dist/pagination.js +6 -4
- package/dist/patch.d.ts +87 -4
- package/dist/patch.js +5 -3
- package/dist/precompiled.d.ts +2 -0
- package/dist/precompiled.js +7 -0
- package/dist/{presentation-Bwo8Ko6D.d.ts → presentation-C-nHZyMw.d.ts} +1 -1
- package/dist/repo-readme.js +2 -0
- package/dist/schema-engine-ajv.js +37 -0
- package/dist/schema-engine-precompiled.js +3 -0
- package/dist/schema.js +5 -3
- package/dist/schemas.js +2 -1
- package/dist/source.d.ts +1 -1
- package/dist/source.js +12 -10
- package/dist/spec/schemas/opf.schema.json +1 -1
- package/dist/spec-files.js +1 -0
- package/dist/symbol-font-encodings.js +2 -0
- package/dist/types.d.ts +2 -2
- package/dist/{validation-types-3ARvTsB0.d.ts → validation-types-6_jEaHeE.d.ts} +2 -2
- package/dist/validator.d.ts +5 -5
- package/dist/validator.js +5 -3
- package/dist/yaml.d.ts +4 -4
- package/dist/yaml.js +9 -7
- package/package.json +29 -8
- package/dist/conversion-IVKCKMCN.js +0 -21
package/dist/docs.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import './chunk-GM7WFPGG.js';
|
|
2
|
+
|
|
1
3
|
// src/generated/docs.ts
|
|
2
4
|
var docsData = Object.freeze([
|
|
3
5
|
{
|
|
@@ -214,7 +216,7 @@ var docsData = Object.freeze([
|
|
|
214
216
|
"slug": "live-editor",
|
|
215
217
|
"file": "docs/live-editor.md",
|
|
216
218
|
"title": "Browser preview and live editing",
|
|
217
|
-
"markdown": "# Browser preview and live editing\n\nEditor 0.26.1 provides an embeddable SVG canvas in `@openpresentation/opf-editor/canvas`. OPF JSON remains the document; the canvas writes validated JSON Patch operations through an `EditorSession`. Draft edits render with the same SVG engine used for standalone previews. Completed edits produce one undoable change.\n\nThe published canvas covers the interactions below; complete PowerPoint feature coverage remains separate work. \u201CPixel perfect\u201D is a fidelity target with specific prerequisites and remaining gaps described below.\n\n## Install the published packages\n\nUse Node 24 with Core, editor, PPTX, renderer and Gallery 0.26.1. This is the current published setup; the [quickstart](quickstart.md) is the shared command-line first run:\n\n```sh\nnpm install --save-exact @openpresentation/opf@0.26.1 @openpresentation/opf-render@0.26.1 @openpresentation/opf-editor@0.26.1 @openpresentation/opf-pptx@0.26.1 @openpresentation/gallery@0.26.1\n```\n\nNo paid service or provider account is required. The six agent skills install with `npx @openpresentation/cli@0.20.0 skills install`. See the [quickstart](quickstart.md) for an installed-package workflow and the [compatibility matrix](compatibility-matrix.md) for separately scoped browser and native evidence.\n\nThe editor exports in the browser (SVG through the renderer's browser entries, PPTX through opf-pptx). To export a saved presentation on a server, call `convert(\"deck.opf.json\", \"deck.pdf\")` (or `convert(presentation, { format: \"pdf\" | \"png\" | \"svg\" | \"pptx\" })` for the bytes) from `@openpresentation/opf` in Node, which draws with the same engines and the bundled fonts; `open(\"deck.pptx\")` reads a PowerPoint file back into a presentation for the editor ([OPF files in Node](node.md)).\n\nFor source builds and the Gallery host bundle, see [ecosystem development](ecosystem-development.md#editor-host-bundle-and-font-provenance).\n\n## Embed in any browser application\n\nMount after the host DOM exists. The container controls width; the slide retains its aspect ratio. React and Svelte applications can mount this framework-independent API in their normal client lifecycle and destroy it on unmount.\n\n```js\nimport { createCanvasEditor } from '@openpresentation/opf-editor/canvas';\nimport { saveDocument } from '@openpresentation/opf-editor';\nimport { loadFonts } from '@openpresentation/opf-render/fonts-browser';\nimport { gallery } from '@openpresentation/gallery';\n\n// Copy these licensed font files into your application's static assets first.\n// Use pinned, static faces; include every weight/style required by your deck.\nconst fonts = await loadFonts({ faces: [\n { url: '/fonts/Roboto-Regular.ttf', family: 'Roboto', weight: 400 },\n { url: '/fonts/Roboto-Bold.ttf', family: 'Roboto', weight: 700 },\n { url: '/fonts/RobotoMono-Regular.ttf', family: 'Roboto Mono', weight: 400 },\n] });\n\nconst container = document.querySelector('#slide');\nif (!(container instanceof HTMLElement)) throw new Error('Missing slide container.');\nconst canvas = createCanvasEditor(container, {\n presentation: {\n design: { theme: 'classic', fontScheme: 'roboto' },\n slides: [{ title: 'An editable presentation', text: 'Click to edit.' }],\n },\n catalogs: [gallery], // core ships no records: register the catalog the theme and font scheme above come from\n fonts,\n onCommit: ({ editor }) => {\n const { document: updatedOPF, unresolved } = saveDocument(editor);\n // Host owns storage. Surface unresolved catalog references before saving.\n if (unresolved.length) console.warn(unresolved);\n console.log(updatedOPF);\n },\n onError: error => console.error(error.message),\n});\nawait canvas.ready;\n\n// JSON or LLM patches also update the slide automatically.\ncanvas.editor.set('slides.0.title', 'Changes from another control');\ncanvas.editor.undo();\n\n// On unmount:\n// canvas.destroy();\n// fonts.dispose();\n```\n\n`loadFonts` accepts explicit font-file URLs or `Uint8Array` data (`faces`). It uses the same bytes for Fontkit measurement and browser `FontFace` registration, awaits loading, reports failures, and exposes `dispose()` for its owned font faces; the handle's `pending` and `ensure` load the script and vendored faces a document needs, and the canvas waits for them before it renders. Cross-origin font URLs need CORS access. Load fonts once and share the handle between canvases. The canvas does not fetch fonts or catalog sources itself.\n\nFor standalone SVG export, pass the handle as `{ fonts }` to `toSvg` (`toSvg(deck, n, ...)` is slide n, `toSvg(deck, ...)` every slide); the export carries the font bytes and supplied license metadata. In a running browser canvas the registered fonts are already available, so embedding those bytes into every draft is unnecessary.\n\n```js\nimport { toSvg } from '@openpresentation/opf-render/svg';\nconst svg = toSvg(canvas.editor.presentation, 1, { fonts, catalogs: canvas.editor.catalogs }); // slide 1: slides count from 1\n```\n\nThe explicit `/svg` entry is browser safe. Browser-aware bundlers also select it for the renderer's root import. The Node root entry additionally supplies `toPng` and `toPdf`; those functions are not browser APIs.\n\n## Save, undo and host state\n\nBefore saving, exporting, importing or navigating, call `canvas.commit()`. A `false` result means the active draft could not be committed: keep it visible, show the error and stop that action. `canvas.editor.presentation` is the committed document, not necessarily the text currently being edited. A source editor must preserve invalid raw text separately instead of silently replacing it with the last valid preview.\n\n```js\nfunction documentForDownload() {\n if (!canvas.commit()) throw new Error('Finish or cancel the current edit first.');\n const result = saveDocument(canvas.editor);\n if (result.unresolved.length) throw new Error('Resolve catalog references before sharing.');\n return result.document; // Referenced catalog records are embedded; the session is unchanged.\n}\nconst unsubscribe = canvas.editor.subscribe(({ type, snapshot }) => {\n // Runs for patch, undo, redo, restore and catalog changes, not just canvas commits.\n console.log(type, snapshot.canUndo, snapshot.canRedo, snapshot.validation);\n});\n// Host teardown: unsubscribe(); canvas.destroy(); fonts.dispose();\n```\n\nUndo and redo belong to that same session (`canvas.editor.undo()` / `redo()`). Subscribe when synchronizing another control or a save indicator; `onCommit` alone does not cover external edits, undo or restore. Dispose subscriptions and canvases on unmount, and dispose a shared fonts handle only after its last consumer is gone.\n\nLocal persistence is optional. [`createPersistence` from `/persistence`](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/README.md#autosave-and-restore-rr-22) supports restore offers and explicit `flush()`. Commit the canvas yourself before an explicit save or flush; persistence stores the session, not an invalid raw source draft. Show unavailable-storage errors and retain a download path. Call `markSaved()` only after a real host save or OPF download; a preview or PowerPoint export does not preserve the complete OPF source. Destroy the persistence controller on teardown. It is not a server or collaboration service.\n\n## Export results and diagnostics\n\nIn a browser, `toSvg` returns SVG strings and `toPptx` returns bytes; pass the same `fonts` and `catalogs`, surface `onDiagnostic` notifications and catch rejected conversions. When importing with `fromPptx(bytes, { signals: true })`, inspect both `presentation` and `signals` before replacing the session. Native conversion can change unsupported features. In Node, Core `convert` instead returns `{ files, findings }`; [the Node guide](node.md) explains findings, errors and file publication. Do not treat a successful conversion as a claim of complete native fidelity.\n\n## Exact package references\n\n- [Editor session types](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/src/index.d.ts), [canvas lifecycle](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/src/canvas.d.ts), [portable saving](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/src/catalogs.d.ts).\n- [Renderer entries and optional peers](https://github.com/OpenPresentation/opf-render/blob/opf-render-v0.20.2/README.md#install-what-to-add-for-what-rr-63).\n- [PowerPoint export/import types](https://github.com/OpenPresentation/opf-pptx/blob/opf-pptx-v0.20.3/src/index.d.ts).\n\n## Editing behavior\n\n| Content or action | Current behavior |\n| --- | --- |\n| Titles, subtitles, plain text, simple numeric values | One click enters editing with the caret at the clicked character (editor 0.10.2); press-drag selects a range; while editing, double-click selects a word and triple-click a paragraph. Focus a target and press Enter, Space or F2 to edit with all text selected. See *Text entry gestures* below. |\n| Table headers and string/number cells | Inline editing; numeric cells keep their numeric type. |\n| Lists, charts, metrics, quotes, code, timelines, rich text payloads | Select the object and edit its existing scalar fields in a floating form; valid drafts render immediately. A timeline event's `status` (`done`, `current`, `planned`) is a menu, not free text. |\n| Images | Edit source/alt fields; replace with a local PNG/JPEG/GIF/WebP file up to 20 MB. External sources still require a host image resolver. |\n| Collections | Add or remove the last item, subject to OPF schema validation. Empty structured collections may need authoring through source. |\n| Dynamic layout | Text edits recompose the slide through shared geometry; row/column/grid controls remain in the demo inspector. |\n| Undo and cancellation | Blur or Ctrl/Cmd+Enter commits plain text; Escape cancels; property forms have Apply/Cancel. |\n| Changes elsewhere | Unrelated edits are preserved; a changed selected payload cancels the stale local draft instead of overwriting it. This is conflict protection, not a distributed collaboration protocol. |\n| JSON editing | The demo Source view previews valid JSON beside the source; Apply records the document replacement. Invalid drafts retain the last valid preview. |\n\n`createCanvasEditor` accepts an existing `editor` session or a `presentation`, plus `slideIndex`, `fonts` (the renderer's fonts handle), `renderOptions`, `textEntry` (`'click'` by default, or `'dblclick'`), an optional empty `propertiesContainer` to dock forms outside the slide, and callbacks `onSelect`, `onDraft`, `onCommit`, `onCancel`, `onRender`, and `onError`. The returned object exposes `editor`, `ready`, `select`, `beginEdit`, `editProperties`, `commit`, `cancel`, `setSlide`, `setRenderOptions`, `setLayoutEditing`, `render`, and `destroy`. `commit()` and setters return false if a draft cannot be committed. Avoid using public `render(presentation)` as a second source of truth; normal document changes should flow through the session.\n\n## Text entry gestures\n\nEditor 0.10.2 follows the PowerPoint and Google Slides convention. Hover outlines a text target. A single press (mouse, pen, or a touch tap) on editable text selects the box, starts inline editing and puts the caret at the nearest character boundary to the pointer, including in wrapped, multi-line, centered, right-aligned, right-to-left and CJK text. Press and drag selects the range from the press point to the release point and never moves the box. While editing, a native double-click selects a word, a triple-click a line or paragraph, and a click elsewhere moves the caret. Clicking a different text target commits the current edit (an invalid edit still refuses) and enters the new target in the same click. Rich text uses the same gestures through its own pointer mapping.\n\nKeyboard entry keeps the replace convention: focus a target and press Enter, Space or F2 to edit with **all** text selected; `canvas.beginEdit(path)` does the same. Escape leaves editing and keeps the box selected. Images, video, charts and other non-text targets are unchanged: a click selects and a double-click opens their properties. Layout handles and block controls keep their own pointer handling.\n\n`createCanvasEditor(container, { textEntry: 'dblclick' })` keeps the older two-step gesture (a click selects, a double-click enters), but the double-click now places the caret at the pointer instead of selecting everything. Tests and hosts that used `dblclick()` and then relied on all text being selected should enter with the keyboard (focus the target, press Enter) or select explicitly; on the default canvas `dblclick()` now places the caret and selects the word under it. Carets are resolved from the traced SVG glyphs (each rendered line carries its source range) and converted to offsets in the input value, so CRLF sources, tabs and wrapped whitespace map exactly; real operating-system IME and bidi caret behavior are not verified.\n\n## Fidelity contract and remaining work\n\nThe same document, renderer version, dimensions, font bytes, and measurement provider produce the same SVG geometry in read and edit modes. Inline editing retains the actual SVG glyphs beneath a transparent native input; the input supplies the caret and selection. Browser regression checks compare draft text positions to standalone SVG rendering.\n\nThat is not a promise of identical raster pixels across browser engines, operating systems, or PowerPoint. Native caret/selection wrapping can differ from shaped SVG text, especially for mixed scripts, rich text, or unusual font features. Browser anti-aliasing and native PowerPoint typography also differ. Without a measurement provider the renderer uses deterministic estimates, which are not sufficient for a high-fidelity claim.\n\nStill needed for the requested complete editor:\n\n1. Continuous mixed-style typing and calibrated caret positioning, bidi/IME/vertical-script coverage. Rich text selection, formatting, links, and selected-text replacement are available through the [SVG formatting toolbar and range API](rich-text.md).\n2. More placement constraints and specialized interactions for fixed promoted regions and individual object geometry. **Add content** and **Arrange** already support the insertion, duplication and deletion described below, track resizing, sibling block dragging, and moving complete blocks between existing groups or slides.\n3. Full visual implementations for specialized charts, media playback, image crops/effects, theme chrome, and every catalog preset. Generic property editing does not imply complete renderer support.\n4. Approved screenshot baselines across representative fonts/layouts/browsers, vertical metric tests, and native PPTX comparison/embedding work.\n5. Broader font-family/script coverage and independently loadable font packs; the current base and Office substitute packs do not cover every requested font. Published packages, documentation examples and installed-package browser CI already exist.\n\nGoogle Fonts supports browser loading through its CSS API, and its repository permits self-hosting subject to each font's license. The OPF fidelity path uses pinned files for reproducibility instead of depending on whichever variant a hosted stylesheet returns. Keep the font's accompanying license. Sources: [Google Fonts CSS API](https://developers.google.com/fonts/docs/css2), [Google Fonts files and licenses](https://github.com/google/fonts/blob/main/README.md).\n\nThe [font roadmap](plans/font-roadmap.md) covers the starter Office substitutes and remaining families.\n\n## Verification\n\n`pnpm demo:editor` builds the playground and `/canvas-tests.html`. The browser harness exercises real font registration, live drafts, text-position parity, one-step undo, cancellation, external edit conflicts, number validation, table cells, structured payloads, collection changes, and cleanup. Node tests cover escaped field paths, typed values, immutable drafts, font loader failures and aborts. The renderer's 126-deck corpus and the coordinated CI's pinned furniture PNG baseline are separate checks. Current installed-package and browser results are recorded in the [compatibility matrix](compatibility-matrix.md); neither those checks nor historical rasters establish general native Office parity.\n\n## Copy, paste, files, and galleries\n\nThe demo's **Copy OPF** dialog exports the whole presentation, the current slide with its design/catalogs/assets, or the selected JSON value. Choose readable JSON, compact JSON, or a Markdown code block for an LLM. The slide toolbar and selection inspector offer direct shortcuts. If clipboard permission is unavailable, **Select all** provides a manual copy fallback.\n\n**Add OPF** accepts a document, one slide, a slide array, a JSON value, or a single fenced JSON/OPF block. Paste into its text box, choose a `.opf`/`.json` file, drop a file on the editor, or load a public JSON URL. Preview first, then insert after the current slide, open a presentation, or replace selected content. Imports are validated and create one undo step. Normal copy/paste inside text fields remains native. Outside text fields, Cmd/Ctrl+V opens import review; Cmd/Ctrl+Shift+C opens Copy OPF; Cmd/Ctrl+O opens file import.\n\n**Browse galleries** includes 854 examples generated from the sibling PPTX.gallery checkout and a separate live PPTX.gallery registry. Search by name, category, or description. Select an entry to preview, copy its OPF, or insert it. **Manage galleries** adds/removes custom registry URLs; custom sources persist in this browser's local storage. Host defaults are defined in `opf-editor/examples/galleries.json`. The bundled snapshot is regenerated by `pnpm demo:editor`; it does not update in the background. Some presets are minimal definition examples rather than completed presentation slides.\n\nPublic PPTX.gallery detail links for layouts, colors, typography, themes, charts, backgrounds, narratives, blocks, and image treatments can be entered in the URL tab. Other sites should expose a direct OPF document or a registry JSON endpoint. Cross-origin servers must enable CORS. Requests omit credentials and referrers, are cancelable, and cap responses at 20 MB. A registry item's URL must stay on the configured origin; explicitly load another origin's URL when intended. The editor does not scrape arbitrary HTML pages or automatically load external fonts/catalog sources.\n\nA custom registry can mix inline OPF and relative document URLs:\n\n```json\n{\n \"name\": \"Team slides\",\n \"items\": [\n { \"id\": \"intro\", \"name\": \"Introduction\", \"category\": \"Team\", \"opf\": { \"slides\": [{ \"title\": \"Hello\" }] } },\n { \"id\": \"metrics\", \"name\": \"Metrics\", \"opfUrl\": \"./metrics.opf.json\" }\n ]\n}\n```\n\nImported documents should embed the catalog records and assets they use. Inserting slides copies their records with core `copySlides`: groups match by `source`, an identical record is reused, a differing `custom` record is renamed `<id>-2` and a differing catalog revision moves into `custom` as `<id>-2`, so the inserted slides look the same and the existing slides stay intact. It does not merge presentation-level speakers, organizations, or narrative metadata into the current deck. Open as a presentation to retain the complete source document.\n\nThe reusable npm APIs are browser-safe and independent of the demo UI:\n\n```js\nimport { parseOpfTransfer, serializeOpfTransfer, prepareOpfImport } from '@openpresentation/opf-editor/transfer';\nimport { loadOpfGallery, loadOpfGalleryItem } from '@openpresentation/opf-editor/galleries';\n\nconst markdown = serializeOpfTransfer(editor.presentation, {\n scope: 'slide', slideIndex: 0, format: 'markdown',\n});\nconst parsed = parseOpfTransfer(markdown);\nconst result = prepareOpfImport(editor.presentation, parsed, {\n mode: 'insert', slideIndex: 0,\n});\n// Host previews result.presentation before applying this single undoable change.\neditor.applyPatch([{ op: 'replace', path: '', value: result.presentation }], {\n source: 'import', rejectInvalid: true,\n});\n\nconst gallery = await loadOpfGallery('https://example.com/registry.json');\nconst presentation = await loadOpfGalleryItem(gallery.items[0], { gallery: gallery.url });\n```\n\nBoth gallery functions accept an `AbortSignal` and an injected `fetch` for host integrations and tests. Import/copy tests cover format round trips, invalid inputs, conflicting IDs and references, source isolation, and one-step undo; the generated 854-example snapshot is checked through insertion and SVG rendering.\n\n## All OPF properties\n\n**All properties** opens the schema-driven workspace beside a live SVG preview. Use Presentation, Current slide, Selection, or Design to navigate; add optional fields, select structured value forms, edit arrays/maps, and Apply a validated change with one undo step. Click content in the preview to locate its field. Nonvisual metadata remains part of the OPF document. A dirty draft must be applied or discarded before closing.\n\nThe `/schema` and `/schema-inspector` npm exports provide the reusable model and DOM inspector. `createSchemaInspector(container, {editor, path, onDraft})` exposes `navigate`, `commit`, `reset`, `destroy`, and read-only `presentation`/`dirty` getters. Use `onDraft` to render valid previews. The companion gallery `/spec` reference indexes the same 604 property definitions, and `/editor` embeds the shared browser build.\n\nSee [spec coverage](plans/spec-editor-coverage.md) for the distinction between complete field discovery and the remaining WYSIWYG rendering work.\n\n## Create, duplicate and delete content\n\nUse **Add content** in the editor toolbar, or the canvas button in Arrange mode. Choose a content kind, destination and insertion position. Starter content covers text, lists, charts, tables, metrics, quotes, code, timelines and groups. Image insertion accepts a local PNG/JPEG/WebP file or a source; local files are embedded as data URLs. Video insertion stores a source, but playback and native video export remain separate work. Source URLs and asset references still need the host's supported asset-resolution behavior.\n\nArrange handles also offer **Duplicate**, **Delete**, **Add after** and, for groups, **Add inside**. Duplication copies the complete content subtree while retaining asset references. Deletion prunes empty ancestor groups or their named region, preserving the slide and its metadata. Removing the last root block leaves a valid empty slide. Every operation preflights the complete candidate with the shared renderer and commits one undo step; stale forms are dismissed and strict overflow fails before mutation.\n\nAdding to implicit root content or a named-region leaf converts existing payloads into explicit blocks in the renderer's canonical field order. Headings, notes, design, metadata and neighboring regions stay intact. Named regions are kept in their existing positions; choose one as the destination. Existing composition weights remain attached to positions, so insertion/deletion can change which content occupies a weighted slot.\n\n```js\nimport {\n prepareBlockInsert, prepareBlockDuplicate, prepareBlockRemove, createContentBlock,\n listBlockContainers,\n} from '@openpresentation/opf-editor/layout';\nconst containers = listBlockContainers(editor.presentation, {includeImplicit: true});\nconst prepared = prepareBlockInsert(editor.presentation, containers[0].path,\n createContentBlock('table')); // omit index to append\n// Render prepared.presentation with your intended fonts before applying.\neditor.applyPatch(prepared.patches, {rejectInvalid: true});\n// Duplicate/remove take complete paths such as /slides/0/blocks/1.\n// canvas.openInsertMenu(containerPath?, index?) opens the browser palette.\n```\n\nThe headless helpers return `{presentation, patches, path, changed}` and include expected-value guards. Preserve those guards when applying patches. They need no browser, AI provider, account or hosted service. These helpers are published in editor 0.8.0; use the coordinated versions above and check installed exports when working with older packages. `/create-tests.html` and its installed-package equivalent exercise creation, image bytes, regions, duplication, deletion, strict-fit rejection, keyboard focus and undo.\n"
|
|
219
|
+
"markdown": "# Browser preview and live editing\n\nEditor 0.26.1 provides an embeddable SVG canvas in `@openpresentation/opf-editor/canvas`. OPF JSON remains the document; the canvas writes validated JSON Patch operations through an `EditorSession`. Draft edits render with the same SVG engine used for standalone previews. Completed edits produce one undoable change.\n\nThe published canvas covers the interactions below; complete PowerPoint feature coverage remains separate work. \u201CPixel perfect\u201D is a fidelity target with specific prerequisites and remaining gaps described below.\n\n\n## The whole editor in one call\n\n`@openpresentation/opf-editor/workspace` mounts everything the sections below build by hand (the canvas, the source pane, the slide navigator and thumbnails, Present, Export, the sorter, the outline, Layout Studio, Review, Find and autosave) over one session and one fonts handle: `createOpfEditor(element, { document, catalogs, fonts })`. `@openpresentation/opf-editor/react` has `<OpfEditor>` and the hooks `useOpfEditor`, `useOpfDocument` and `useOpfSelection` for React 18 and 19. The chrome reads `--opf-ui-*` tokens that a `theme` option sets. See [Embedding the editor](../packages/editor/docs/embedding.md) and [Theming the editor chrome](../packages/editor/docs/theming.md).\n\n## Install the published packages\n\nUse Node 24 with Core, editor, PPTX, renderer and Gallery 0.26.1. This is the current published setup; the [quickstart](quickstart.md) is the shared command-line first run:\n\n```sh\nnpm install --save-exact @openpresentation/opf@0.26.1 @openpresentation/opf-render@0.26.1 @openpresentation/opf-editor@0.26.1 @openpresentation/opf-pptx@0.26.1 @openpresentation/gallery@0.26.1\n```\n\nNo paid service or provider account is required. The six agent skills install with `npx @openpresentation/cli@0.20.0 skills install`. See the [quickstart](quickstart.md) for an installed-package workflow and the [compatibility matrix](compatibility-matrix.md) for separately scoped browser and native evidence.\n\nThe editor exports in the browser (SVG through the renderer's browser entries, PPTX through opf-pptx). To export a saved presentation on a server, call `convert(\"deck.opf.json\", \"deck.pdf\")` (or `convert(presentation, { format: \"pdf\" | \"png\" | \"svg\" | \"pptx\" })` for the bytes) from `@openpresentation/opf` in Node, which draws with the same engines and the bundled fonts; `open(\"deck.pptx\")` reads a PowerPoint file back into a presentation for the editor ([OPF files in Node](node.md)).\n\nFor source builds and the Gallery host bundle, see [ecosystem development](ecosystem-development.md#editor-host-bundle-and-font-provenance).\n\n## Embed in any browser application\n\nMount after the host DOM exists. The container controls width; the slide retains its aspect ratio. React and Svelte applications can mount this framework-independent API in their normal client lifecycle and destroy it on unmount.\n\n```js\nimport { createCanvasEditor } from '@openpresentation/opf-editor/canvas';\nimport { saveDocument } from '@openpresentation/opf-editor';\nimport { loadFonts } from '@openpresentation/opf-render/fonts-browser';\nimport { gallery } from '@openpresentation/gallery';\n\n// Copy these licensed font files into your application's static assets first.\n// Use pinned, static faces; include every weight/style required by your deck.\nconst fonts = await loadFonts({ faces: [\n { url: '/fonts/Roboto-Regular.ttf', family: 'Roboto', weight: 400 },\n { url: '/fonts/Roboto-Bold.ttf', family: 'Roboto', weight: 700 },\n { url: '/fonts/RobotoMono-Regular.ttf', family: 'Roboto Mono', weight: 400 },\n] });\n\nconst container = document.querySelector('#slide');\nif (!(container instanceof HTMLElement)) throw new Error('Missing slide container.');\nconst canvas = createCanvasEditor(container, {\n presentation: {\n design: { theme: 'classic', fontScheme: 'roboto' },\n slides: [{ title: 'An editable presentation', text: 'Click to edit.' }],\n },\n catalogs: [gallery], // core ships no records: register the catalog the theme and font scheme above come from\n fonts,\n onCommit: ({ editor }) => {\n const { document: updatedOPF, unresolved } = saveDocument(editor);\n // Host owns storage. Surface unresolved catalog references before saving.\n if (unresolved.length) console.warn(unresolved);\n console.log(updatedOPF);\n },\n onError: error => console.error(error.message),\n});\nawait canvas.ready;\n\n// JSON or LLM patches also update the slide automatically.\ncanvas.editor.set('slides.0.title', 'Changes from another control');\ncanvas.editor.undo();\n\n// On unmount:\n// canvas.destroy();\n// fonts.dispose();\n```\n\n`loadFonts` accepts explicit font-file URLs or `Uint8Array` data (`faces`). It uses the same bytes for Fontkit measurement and browser `FontFace` registration, awaits loading, reports failures, and exposes `dispose()` for its owned font faces; the handle's `pending` and `ensure` load the script and vendored faces a document needs, and the canvas waits for them before it renders. Cross-origin font URLs need CORS access. Load fonts once and share the handle between canvases. The canvas does not fetch fonts or catalog sources itself.\n\nFor standalone SVG export, pass the handle as `{ fonts }` to `toSvg` (`toSvg(deck, n, ...)` is slide n, `toSvg(deck, ...)` every slide); the export carries the font bytes and supplied license metadata. In a running browser canvas the registered fonts are already available, so embedding those bytes into every draft is unnecessary.\n\n```js\nimport { toSvg } from '@openpresentation/opf-render/svg';\nconst svg = toSvg(canvas.editor.presentation, 1, { fonts, catalogs: canvas.editor.catalogs }); // slide 1: slides count from 1\n```\n\nThe explicit `/svg` entry is browser safe. Browser-aware bundlers also select it for the renderer's root import. The Node root entry additionally supplies `toPng` and `toPdf`; those functions are not browser APIs.\n\n## Save, undo and host state\n\nBefore saving, exporting, importing or navigating, call `canvas.commit()`. A `false` result means the active draft could not be committed: keep it visible, show the error and stop that action. `canvas.editor.presentation` is the committed document, not necessarily the text currently being edited. A source editor must preserve invalid raw text separately instead of silently replacing it with the last valid preview.\n\n```js\nfunction documentForDownload() {\n if (!canvas.commit()) throw new Error('Finish or cancel the current edit first.');\n const result = saveDocument(canvas.editor);\n if (result.unresolved.length) throw new Error('Resolve catalog references before sharing.');\n return result.document; // Referenced catalog records are embedded; the session is unchanged.\n}\nconst unsubscribe = canvas.editor.subscribe(({ type, snapshot }) => {\n // Runs for patch, undo, redo, restore and catalog changes, not just canvas commits.\n console.log(type, snapshot.canUndo, snapshot.canRedo, snapshot.validation);\n});\n// Host teardown: unsubscribe(); canvas.destroy(); fonts.dispose();\n```\n\nUndo and redo belong to that same session (`canvas.editor.undo()` / `redo()`). Subscribe when synchronizing another control or a save indicator; `onCommit` alone does not cover external edits, undo or restore. Dispose subscriptions and canvases on unmount, and dispose a shared fonts handle only after its last consumer is gone.\n\nLocal persistence is optional. [`createPersistence` from `/persistence`](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/README.md#autosave-and-restore-rr-22) supports restore offers and explicit `flush()`. Commit the canvas yourself before an explicit save or flush; persistence stores the session, not an invalid raw source draft. Show unavailable-storage errors and retain a download path. Call `markSaved()` only after a real host save or OPF download; a preview or PowerPoint export does not preserve the complete OPF source. Destroy the persistence controller on teardown. It is not a server or collaboration service.\n\n## Export results and diagnostics\n\nIn a browser, `toSvg` returns SVG strings and `toPptx` returns bytes; pass the same `fonts` and `catalogs`, surface `onDiagnostic` notifications and catch rejected conversions. When importing with `fromPptx(bytes, { signals: true })`, inspect both `presentation` and `signals` before replacing the session. Native conversion can change unsupported features. In Node, Core `convert` instead returns `{ files, findings }`; [the Node guide](node.md) explains findings, errors and file publication. Do not treat a successful conversion as a claim of complete native fidelity.\n\n## Exact package references\n\n- [Editor session types](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/src/index.d.ts), [canvas lifecycle](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/src/canvas.d.ts), [portable saving](https://github.com/OpenPresentation/opf-editor/blob/opf-editor-v0.20.3/src/catalogs.d.ts).\n- [Renderer entries and optional peers](https://github.com/OpenPresentation/opf-render/blob/opf-render-v0.20.2/README.md#install-what-to-add-for-what-rr-63).\n- [PowerPoint export/import types](https://github.com/OpenPresentation/opf-pptx/blob/opf-pptx-v0.20.3/src/index.d.ts).\n\n## Editing behavior\n\n| Content or action | Current behavior |\n| --- | --- |\n| Titles, subtitles, plain text, simple numeric values | One click enters editing with the caret at the clicked character (editor 0.10.2); press-drag selects a range; while editing, double-click selects a word and triple-click a paragraph. Focus a target and press Enter, Space or F2 to edit with all text selected. See *Text entry gestures* below. |\n| Table headers and string/number cells | Inline editing; numeric cells keep their numeric type. |\n| Lists, charts, metrics, quotes, code, timelines, rich text payloads | Select the object and edit its existing scalar fields in a floating form; valid drafts render immediately. A timeline event's `status` (`done`, `current`, `planned`) is a menu, not free text. |\n| Images | Edit source/alt fields; replace with a local PNG/JPEG/GIF/WebP file up to 20 MB. External sources still require a host image resolver. |\n| Collections | Add or remove the last item, subject to OPF schema validation. Empty structured collections may need authoring through source. |\n| Dynamic layout | Text edits recompose the slide through shared geometry; row/column/grid controls remain in the demo inspector. |\n| Undo and cancellation | Blur or Ctrl/Cmd+Enter commits plain text; Escape cancels; property forms have Apply/Cancel. |\n| Changes elsewhere | Unrelated edits are preserved; a changed selected payload cancels the stale local draft instead of overwriting it. This is conflict protection, not a distributed collaboration protocol. |\n| JSON editing | The demo Source view previews valid JSON beside the source; Apply records the document replacement. Invalid drafts retain the last valid preview. |\n\n`createCanvasEditor` accepts an existing `editor` session or a `presentation`, plus `slideIndex`, `fonts` (the renderer's fonts handle), `renderOptions`, `textEntry` (`'click'` by default, or `'dblclick'`), an optional empty `propertiesContainer` to dock forms outside the slide, and callbacks `onSelect`, `onDraft`, `onCommit`, `onCancel`, `onRender`, and `onError`. The returned object exposes `editor`, `ready`, `select`, `beginEdit`, `editProperties`, `commit`, `cancel`, `setSlide`, `setRenderOptions`, `setLayoutEditing`, `render`, and `destroy`. `commit()` and setters return false if a draft cannot be committed. Avoid using public `render(presentation)` as a second source of truth; normal document changes should flow through the session.\n\n## Text entry gestures\n\nEditor 0.10.2 follows the PowerPoint and Google Slides convention. Hover outlines a text target. A single press (mouse, pen, or a touch tap) on editable text selects the box, starts inline editing and puts the caret at the nearest character boundary to the pointer, including in wrapped, multi-line, centered, right-aligned, right-to-left and CJK text. Press and drag selects the range from the press point to the release point and never moves the box. While editing, a native double-click selects a word, a triple-click a line or paragraph, and a click elsewhere moves the caret. Clicking a different text target commits the current edit (an invalid edit still refuses) and enters the new target in the same click. Rich text uses the same gestures through its own pointer mapping.\n\nKeyboard entry keeps the replace convention: focus a target and press Enter, Space or F2 to edit with **all** text selected; `canvas.beginEdit(path)` does the same. Escape leaves editing and keeps the box selected. Images, video, charts and other non-text targets are unchanged: a click selects and a double-click opens their properties. Layout handles and block controls keep their own pointer handling.\n\n`createCanvasEditor(container, { textEntry: 'dblclick' })` keeps the older two-step gesture (a click selects, a double-click enters), but the double-click now places the caret at the pointer instead of selecting everything. Tests and hosts that used `dblclick()` and then relied on all text being selected should enter with the keyboard (focus the target, press Enter) or select explicitly; on the default canvas `dblclick()` now places the caret and selects the word under it. Carets are resolved from the traced SVG glyphs (each rendered line carries its source range) and converted to offsets in the input value, so CRLF sources, tabs and wrapped whitespace map exactly; real operating-system IME and bidi caret behavior are not verified.\n\n## Fidelity contract and remaining work\n\nThe same document, renderer version, dimensions, font bytes, and measurement provider produce the same SVG geometry in read and edit modes. Inline editing retains the actual SVG glyphs beneath a transparent native input; the input supplies the caret and selection. Browser regression checks compare draft text positions to standalone SVG rendering.\n\nThat is not a promise of identical raster pixels across browser engines, operating systems, or PowerPoint. Native caret/selection wrapping can differ from shaped SVG text, especially for mixed scripts, rich text, or unusual font features. Browser anti-aliasing and native PowerPoint typography also differ. Without a measurement provider the renderer uses deterministic estimates, which are not sufficient for a high-fidelity claim.\n\nStill needed for the requested complete editor:\n\n1. Continuous mixed-style typing and calibrated caret positioning, bidi/IME/vertical-script coverage. Rich text selection, formatting, links, and selected-text replacement are available through the [SVG formatting toolbar and range API](rich-text.md).\n2. More placement constraints and specialized interactions for fixed promoted regions and individual object geometry. **Add content** and **Arrange** already support the insertion, duplication and deletion described below, track resizing, sibling block dragging, and moving complete blocks between existing groups or slides.\n3. Full visual implementations for specialized charts, media playback, image crops/effects, theme chrome, and every catalog preset. Generic property editing does not imply complete renderer support.\n4. Approved screenshot baselines across representative fonts/layouts/browsers, vertical metric tests, and native PPTX comparison/embedding work.\n5. Broader font-family/script coverage and independently loadable font packs; the current base and Office substitute packs do not cover every requested font. Published packages, documentation examples and installed-package browser CI already exist.\n\nGoogle Fonts supports browser loading through its CSS API, and its repository permits self-hosting subject to each font's license. The OPF fidelity path uses pinned files for reproducibility instead of depending on whichever variant a hosted stylesheet returns. Keep the font's accompanying license. Sources: [Google Fonts CSS API](https://developers.google.com/fonts/docs/css2), [Google Fonts files and licenses](https://github.com/google/fonts/blob/main/README.md).\n\nThe [font roadmap](plans/font-roadmap.md) covers the starter Office substitutes and remaining families.\n\n## Verification\n\n`pnpm demo:editor` builds the playground and `/canvas-tests.html`. The browser harness exercises real font registration, live drafts, text-position parity, one-step undo, cancellation, external edit conflicts, number validation, table cells, structured payloads, collection changes, and cleanup. Node tests cover escaped field paths, typed values, immutable drafts, font loader failures and aborts. The renderer's 126-deck corpus and the coordinated CI's pinned furniture PNG baseline are separate checks. Current installed-package and browser results are recorded in the [compatibility matrix](compatibility-matrix.md); neither those checks nor historical rasters establish general native Office parity.\n\n## Copy, paste, files, and galleries\n\nThe demo's **Copy OPF** dialog exports the whole presentation, the current slide with its design/catalogs/assets, or the selected JSON value. Choose readable JSON, compact JSON, or a Markdown code block for an LLM. The slide toolbar and selection inspector offer direct shortcuts. If clipboard permission is unavailable, **Select all** provides a manual copy fallback.\n\n**Add OPF** accepts a document, one slide, a slide array, a JSON value, or a single fenced JSON/OPF block. Paste into its text box, choose a `.opf`/`.json` file, drop a file on the editor, or load a public JSON URL. Preview first, then insert after the current slide, open a presentation, or replace selected content. Imports are validated and create one undo step. Normal copy/paste inside text fields remains native. Outside text fields, Cmd/Ctrl+V opens import review; Cmd/Ctrl+Shift+C opens Copy OPF; Cmd/Ctrl+O opens file import.\n\n**Browse galleries** includes 854 examples generated from the sibling PPTX.gallery checkout and a separate live PPTX.gallery registry. Search by name, category, or description. Select an entry to preview, copy its OPF, or insert it. **Manage galleries** adds/removes custom registry URLs; custom sources persist in this browser's local storage. Host defaults are defined in `opf-editor/examples/galleries.json`. The bundled snapshot is regenerated by `pnpm demo:editor`; it does not update in the background. Some presets are minimal definition examples rather than completed presentation slides.\n\nPublic PPTX.gallery detail links for layouts, colors, typography, themes, charts, backgrounds, narratives, blocks, and image treatments can be entered in the URL tab. Other sites should expose a direct OPF document or a registry JSON endpoint. Cross-origin servers must enable CORS. Requests omit credentials and referrers, are cancelable, and cap responses at 20 MB. A registry item's URL must stay on the configured origin; explicitly load another origin's URL when intended. The editor does not scrape arbitrary HTML pages or automatically load external fonts/catalog sources.\n\nA custom registry can mix inline OPF and relative document URLs:\n\n```json\n{\n \"name\": \"Team slides\",\n \"items\": [\n { \"id\": \"intro\", \"name\": \"Introduction\", \"category\": \"Team\", \"opf\": { \"slides\": [{ \"title\": \"Hello\" }] } },\n { \"id\": \"metrics\", \"name\": \"Metrics\", \"opfUrl\": \"./metrics.opf.json\" }\n ]\n}\n```\n\nImported documents should embed the catalog records and assets they use. Inserting slides copies their records with core `copySlides`: groups match by `source`, an identical record is reused, a differing `custom` record is renamed `<id>-2` and a differing catalog revision moves into `custom` as `<id>-2`, so the inserted slides look the same and the existing slides stay intact. It does not merge presentation-level speakers, organizations, or narrative metadata into the current deck. Open as a presentation to retain the complete source document.\n\nThe reusable npm APIs are browser-safe and independent of the demo UI:\n\n```js\nimport { parseOpfTransfer, serializeOpfTransfer, prepareOpfImport } from '@openpresentation/opf-editor/transfer';\nimport { loadOpfGallery, loadOpfGalleryItem } from '@openpresentation/opf-editor/galleries';\n\nconst markdown = serializeOpfTransfer(editor.presentation, {\n scope: 'slide', slideIndex: 0, format: 'markdown',\n});\nconst parsed = parseOpfTransfer(markdown);\nconst result = prepareOpfImport(editor.presentation, parsed, {\n mode: 'insert', slideIndex: 0,\n});\n// Host previews result.presentation before applying this single undoable change.\neditor.applyPatch([{ op: 'replace', path: '', value: result.presentation }], {\n source: 'import', rejectInvalid: true,\n});\n\nconst gallery = await loadOpfGallery('https://example.com/registry.json');\nconst presentation = await loadOpfGalleryItem(gallery.items[0], { gallery: gallery.url });\n```\n\nBoth gallery functions accept an `AbortSignal` and an injected `fetch` for host integrations and tests. Import/copy tests cover format round trips, invalid inputs, conflicting IDs and references, source isolation, and one-step undo; the generated 854-example snapshot is checked through insertion and SVG rendering.\n\n## All OPF properties\n\n**All properties** opens the schema-driven workspace beside a live SVG preview. Use Presentation, Current slide, Selection, or Design to navigate; add optional fields, select structured value forms, edit arrays/maps, and Apply a validated change with one undo step. Click content in the preview to locate its field. Nonvisual metadata remains part of the OPF document. A dirty draft must be applied or discarded before closing.\n\nThe `/schema` and `/schema-inspector` npm exports provide the reusable model and DOM inspector. `createSchemaInspector(container, {editor, path, onDraft})` exposes `navigate`, `commit`, `reset`, `destroy`, and read-only `presentation`/`dirty` getters. Use `onDraft` to render valid previews. The companion gallery `/spec` reference indexes the same 604 property definitions, and `/editor` embeds the shared browser build.\n\nSee [spec coverage](plans/spec-editor-coverage.md) for the distinction between complete field discovery and the remaining WYSIWYG rendering work.\n\n## Create, duplicate and delete content\n\nUse **Add content** in the editor toolbar, or the canvas button in Arrange mode. Choose a content kind, destination and insertion position. Starter content covers text, lists, charts, tables, metrics, quotes, code, timelines and groups. Image insertion accepts a local PNG/JPEG/WebP file or a source; local files are embedded as data URLs. Video insertion stores a source, but playback and native video export remain separate work. Source URLs and asset references still need the host's supported asset-resolution behavior.\n\nArrange handles also offer **Duplicate**, **Delete**, **Add after** and, for groups, **Add inside**. Duplication copies the complete content subtree while retaining asset references. Deletion prunes empty ancestor groups or their named region, preserving the slide and its metadata. Removing the last root block leaves a valid empty slide. Every operation preflights the complete candidate with the shared renderer and commits one undo step; stale forms are dismissed and strict overflow fails before mutation.\n\nAdding to implicit root content or a named-region leaf converts existing payloads into explicit blocks in the renderer's canonical field order. Headings, notes, design, metadata and neighboring regions stay intact. Named regions are kept in their existing positions; choose one as the destination. Existing composition weights remain attached to positions, so insertion/deletion can change which content occupies a weighted slot.\n\n```js\nimport {\n prepareBlockInsert, prepareBlockDuplicate, prepareBlockRemove, createContentBlock,\n listBlockContainers,\n} from '@openpresentation/opf-editor/layout';\nconst containers = listBlockContainers(editor.presentation, {includeImplicit: true});\nconst prepared = prepareBlockInsert(editor.presentation, containers[0].path,\n createContentBlock('table')); // omit index to append\n// Render prepared.presentation with your intended fonts before applying.\neditor.applyPatch(prepared.patches, {rejectInvalid: true});\n// Duplicate/remove take complete paths such as /slides/0/blocks/1.\n// canvas.openInsertMenu(containerPath?, index?) opens the browser palette.\n```\n\nThe headless helpers return `{presentation, patches, path, changed}` and include expected-value guards. Preserve those guards when applying patches. They need no browser, AI provider, account or hosted service. These helpers are published in editor 0.8.0; use the coordinated versions above and check installed exports when working with older packages. `/create-tests.html` and its installed-package equivalent exercise creation, image bytes, regions, duplication, deletion, strict-fit rejection, keyboard focus and undo.\n"
|
|
218
220
|
},
|
|
219
221
|
{
|
|
220
222
|
"slug": "llm-authoring",
|
|
@@ -250,7 +252,7 @@ var docsData = Object.freeze([
|
|
|
250
252
|
"slug": "node",
|
|
251
253
|
"file": "docs/node.md",
|
|
252
254
|
"title": "OPF files in Node: `@openpresentation/opf`",
|
|
253
|
-
"markdown": '# OPF files in Node: `@openpresentation/opf`\n\n`@openpresentation/opf` is one import for every runtime. In Node (and Bun and Deno) it is core plus the file API: it converts,\nopens and saves OPF files, and draws them as PDF, PNG, SVG and PPTX through the optional engines. In a browser or a worker the\nsame import gives the browser-safe build, with the same names, where only the file functions refuse.\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\nThe namespace is all of core (`validate`, `parse`, `stringify`, `paginate`, `parseSlideSelection`, the types and the error\nclasses), so one import covers an application. The pptx.gallery catalog is the package `@openpresentation/gallery` (`gallery`, a\ndependency of core, RR-78); in Node, `open`, `save` and `convert` register it when no `catalogs` are passed. A gallery that targets\nanother catalog record schema than core reads (`CATALOG_SCHEMA`) is refused with `gallery-schema-mismatch`.\n\n## The CLI\'s verbs, with the CLI\'s defaults\n\nOPF 0.18 gives the Node build the verbs of the `opf` command, so a script calls them instead of spawning `opf`:\n\n| Function | Command | In Node, when `catalogs` is omitted |\n| --- | --- | --- |\n| `validate(deck, options?)` | `opf validate` | the gallery is registered, so `opf validate deck` and `validate(deck)` report the same findings |\n| `stats(deck, options?)` | `opf stats` | the gallery |\n| `paginate(deck, options?)` | `opf paginate` | the gallery. Pass `fonts` (opf-render\'s `loadFonts()`) for measured page breaks; without it the result says `layout: "estimated"`. `opf paginate` measures with the office pack, prepared once per process, as `convert` does for an export with `paginate: true` |\n| `embed(deck, options?)` | `opf embed` | the gallery |\n| `edit(deck, patch, options?)` | `opf edit` | the gallery for the check. Applies a JSON Patch whole or not at all (`OPFPatchError`), checks the result (`OPFValidationError`; `validate: false` skips it) and returns `{ presentation, findings, inverse }` |\n| `fill(template, data?, options?)` | `opf fill` | no catalog: `data` is CSV, TSV or JSON text, a record or a list of records; returns `{ decks, complete, unfilled, diagnostics }`, and `presentation` with `combine: true`. `fillRecords(text)` reads the records alone |\n| `diff(a, b)`, `merge(base, ours, theirs)` | `opf diff`, `opf merge` | no catalog |\n\nAn explicit `catalogs: []` registers none. The browser build exports the same functions and registers no catalog (FA-21): a\nbrowser host passes `{ catalogs: [gallery] }` itself (`import { gallery } from "@openpresentation/gallery"`). Only `open`, `save` and `convert` are Node only.\n\nA shorter name is available as an npm alias, if you want one: `npm i opf@npm:@openpresentation/opf` installs the same package\nas `opf`, so `import * as opf from "opf"` works too. The docs keep the full name.\n\n## One import for every runtime\n\nOPF 0.18 removed the `@openpresentation/opf/node` subpath of 0.17: import `@openpresentation/opf` instead, with the same names\n(OPF 0.19 removed `@openpresentation/opf/catalog` and `defaultCatalog`, with no alias: import `gallery` from\n`@openpresentation/gallery`).\nThe package\'s conditional exports pick the build:\n\n| Condition | Build | `open`, `save`, `convert` |\n| --- | --- | --- |\n| `bun`, `deno`, `node` | the full build (`dist/index.js`) | work on files, and `convert` also returns bytes |\n| `workerd` (Cloudflare), `worker`, `browser`, `default` | the browser-safe build (`dist/browser.js`) | reject with `OPFApiError` code `node-only` |\n\n- **One type surface.** Both builds share `dist/index.d.ts` (the `types` condition, first in the map), so TypeScript sees the\n same declarations under `NodeNext` and `Bundler` resolution; the file functions are documented there as Node only. The\n declarations need no Node types.\n- **The browser-safe build** imports no Node builtin, no file system and neither engine (`scripts/check-browser-safe.mjs`\n bundles it for the browser and worker conditions). Its `node-only` messages name the alternative: `parse(text, { filename })`\n for `open`, `stringify(deck, { filename })` for `save`, and for `convert` the deck forms through `parse` and `stringify` and\n drawing through opf-render\'s `/export-browser` entry. The in-memory `convert(deck, { format })` is Node only in 0.18 too; a\n later release adds a browser implementation through `/export-browser`.\n- **The Node build** loads its file engine on the first file call, so importing the root costs no more than core.\n- **Bundlers** pick the browser build for a browser target and the full build for a Node target. A bundler that sets neither\n condition gets `default`, the browser-safe build: for a Node bundle, add the `node` condition (Rollup\'s node-resolve\n `exportConditions: ["node"]`, esbuild\'s `platform: "node"`).\n\n## The stack\n\nOPF is six packages. **Core** (`@openpresentation/opf`) is the format and its API: schemas, types, `validate`, `parse` and\n`stringify`, composition and pagination, and in Node the files. **The gallery** (`@openpresentation/gallery`) is the pptx.gallery\ncatalog, a dependency of core released on its own version line. **The CLI** (`@openpresentation/cli`) is the `opf` command;\nits `convert` command runs core\'s Node engine (through `@openpresentation/opf/internal/engine`,\nwhich the package exports under the `node` condition for the CLI only; it is not an application API). **opf-render**,\n**opf-pptx** and **opf-editor** are the engines: drawing (SVG, PNG, PDF), PowerPoint (export and import) and the editor.\n\n## Install\n\n```sh\nnpm install @openpresentation/opf @openpresentation/opf-render @openpresentation/opf-pptx \\\n @resvg/resvg-js sharp pdf-lib \\\n @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 \\\n @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 \\\n @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2\n```\n\n`@openpresentation/opf-render` and `@openpresentation/opf-pptx` are optional peer dependencies of core: npm does not install them\nfor you, and an application that only reads, validates or converts between deck forms needs neither. Core loads them the\nfirst time a call needs them, from core\'s own install location and then from the working directory; a missing one throws\n`peer-not-installed` with the install command. What each output needs is listed in [the CLI reference](cli.md#install): PNG\nneeds `@resvg/resvg-js` and `sharp`, raster PDF `pdf-lib`, pictures in a PDF `sharp`, every format the office font pack, and\n`.pptx` input or output `@openpresentation/opf-pptx`.\n\n## `convert(input, output, options?)`\n\n```ts\nawait opf.convert("deck.opf.md", "deck.pdf");\nawait opf.convert("deck.opf.md", "deck.pptx");\nawait opf.convert("deck.opf.md", "slides/deck.png"); // one PNG per slide: slides/deck-1.png, -2, ...\nawait opf.convert("deck.opf.md", "slides/slide-{n}.png"); // always numbered: slides/slide-1.png, slide-2.png, ...\nawait opf.convert("deck.pptx", "deck.opf.yaml"); // import\nawait opf.convert("deck.opf.json", "deck.opf.md"); // change the deck form\nawait opf.convert("deck.opf.md", "deck.png", { slides: "1-3", scale: 2 });\nawait opf.convert("deck.opf.md", "slides.zip", { format: "svg" });\n```\n\n`input` is a file path, a presentation object or the bytes of a `.pptx` file (`Uint8Array`). The formats come from the names:\n\n| Input | Read as |\n| --- | --- |\n| `.pptx`, PPTX bytes | imported through opf-pptx |\n| `.opf.md` | a Markdown deck |\n| `.yaml`, `.yml` | a YAML deck |\n| `.json` | a JSON deck |\n\n| Output | Written as |\n| --- | --- |\n| `.pdf`, `.pptx` | one file |\n| `.png`, `.svg` | one file per slide, named after the output: `slides/deck.png` gives `slides/deck-1.png`, `-2`, ... (the numbers are the deck\'s slide numbers, padded with zeros to the width of the largest number written: `deck-01.png` from ten slides, `deck-001.png` from a hundred; `slides: "2,5"` gives `deck-2.png` and `deck-5.png`). When one slide is selected (or the deck has one), it is written to the output name itself, as `opf convert deck.opf.json slide.png --slides 2` does. With `{n}` in the output (`"slides/slide-{n}.png"`) the files are always numbered, even for one slide, and `{n}` is the slide number padded by the same rule. `{n}` is the only placeholder: any other `{key}`, and `{n}` in a `.pdf`, `.pptx`, `.zip` or deck-form output, rejects with `invalid-option`. |\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; the archive\'s own name takes no `{n}`). The slides are PNG unless `format: "svg"`. |\n| `.opf.md`, `.yaml`, `.yml`, `.json` | the deck in that form |\n\nAny other extension, and a plain `.md` file, rejects with `invalid-option`, which names the supported extensions. A `format`\nthat disagrees with the output\'s extension is refused too; it is only needed for a `.zip`.\n\n**Without an output path**, pass the format in the options: `convert(input, { format })` writes nothing and returns the files\nwith their names and bytes. `name` sets the base name of the files; without it they follow the deck: its `filename`, else its\nslugified `name`, else the input file\'s stem (`Q4-Review.pdf`, `Q4-Review-1.png`; padded like the files above, `Q4-Review-01.png` from ten slides, `deck.opf.yaml`). `zip: true` returns one archive of\nthe PNG or SVG slides; with an output path a `.zip` name makes the archive instead.\n\n**Options** are the flags of `opf convert` in camel case: `slides` (`3`, `"1,3-5"` or `[1, 3]`, read by `parseSlideSelection`), `includeHidden`, `paginate`, `scale`\n(0.1 to 8, PNG and raster PDF), `raster` (`true` draws a PDF as a picture per page; vector text is the default), `text` (an SVG\ncarries `"fonts"`, the default, which embeds the faces the slide uses; `"system"`, none, for a page that has the fonts; or\n`"paths"`, glyph outlines, so the file needs no font), `charts` (`"auto"`, `"native"` or `"picture"`), `provenance` and `images`\n(`"compatible"` or `"preserve"`) for PPTX, `date` (`YYYY-MM-DD` for date fields; nothing reads a clock), `catalogs` (the default\ncatalog when omitted), `fonts` (a prepared `loadFonts()` handle of `@openpresentation/opf-render/fonts-node`, or a folder or a list\nof folders of `.ttf`/`.otf` files), `assetDir`, plus `signals` (a `.pptx` input: also return the importer\'s per-shape\nsignals), and for output without a path `zip` and `name`, and `overwrite`. An option that does not apply to the pair rejects with\n`invalid-option`: `scale` for a deck output, `slides` for a PPTX, `raster` for anything but a PDF, `text` for anything but an SVG,\n`zip` or `name` with an output path. The 0.17 names (`pdfMode`, `svgFonts`, `chartex`, `imageFormat`, `fontDirs`, `filename`) were\nrenamed in 0.18 and are refused with the new name.\n\n**Writing.** Inputs are checked and all results are produced and staged before destination files are published. A preflight,\nrender or staging failure publishes no destination files, though parent folders may have been created. Each final file is\npublished atomically from a temporary sibling, so no partial file has the final name. Multiple files are not one transaction:\na later publication I/O failure can leave earlier complete outputs in place. Keep that possibility in your recovery logic.\nExisting regular outputs are replaced by default, as `save` and `fs.writeFile` do: running the same `convert` again rebuilds\nthe file. `overwrite: false` checks for existing outputs during preflight and reports `output-exists`, the rule the `opf`\ncommands apply without `--force`. A symlink, a directory or another non-regular file is refused (`output-not-file`). The result\nis `{ files: [{ name, path, type, bytes, ... }], findings }`, with the slide number, id and size of each picture and the pages of a PDF.\n\n**Images.** When `input` is a path, a deck\'s relative image paths resolve against the input file\'s folder, from any working\ndirectory; `assetDir` replaces that folder. Only image files inside it are read, and URLs are never fetched.\n\n**Chaining.** A `.pptx` input is imported, checked and then exported or written. `findings` holds both steps: the importer\'s\ndiagnostics carry the `import/` rule prefix, the format and references check `opf/`, and the engines `render/`, `pptx/`,\n`pdf/`, `fonts/` and `cli/`.\n\n## `open(pathOrBytes, options?)`\n\nReturns the presentation itself. A `.opf.md`, `.yaml`/`.yml` or `.json` file is read in the form its extension names and\nchecked for format and references; a `.pptx` path or its bytes are imported. An invalid deck throws `OPFValidationError`,\nwhose findings are located by line and column in the file; a PowerPoint file that cannot be imported throws `OPFImportError`.\nWarnings are not returned: `validate` reports them, and `convert("deck.pptx", "deck.opf.yaml")` returns what an import could\nnot keep. `catalogs` (default: the default catalog) is where references resolve.\n\n## `save(deck, path, options?)`\n\nWrites the deck in the form the file name names (`.opf.md`, `.yaml`/`.yml` or `.json`), atomically, creating folders and\nreplacing the file. It checks format and references first and refuses an invalid deck with `OPFValidationError`; `validate:\nfalse` writes work in progress, as an editor\'s autosave does (the YAML and Markdown writers still refuse a document that is\nnot well-formed OPF). `schemaComment: true` starts a YAML file with the editor modeline. Returns `{ path, format }`.\n\n## Errors\n\nEvery error has a `code`; branch on it, never on the message.\n\n| Class | Codes |\n| --- | --- |\n| `OPFApiError` (the base class) | `node-only` (the browser-safe build), `invalid-option`, `input-not-found`, `input-unreadable`, `invalid-presentation` (a deck written as a deck), `output-exists`, `output-not-file`, `output-unwritable` |\n| `OPFExportError` | the export step: `peer-not-installed` (the message carries the install command; `details` the package and range), `peer-too-old`, `peer-load-failed`, `invalid-option`, `invalid-presentation`, `no-slides`, `all-slides-hidden`, `export-failed`, a font code |\n| `OPFImportError` | the import step and `open` of a PowerPoint file: `peer-not-installed`, `peer-too-old`, `peer-load-failed`, `import-failed`, `invalid-presentation` |\n| `OPFValidationError` | `open` and `save` of an invalid deck; also thrown by core\'s `parse` and `assertValid` |\n\n`findings` on an error holds the located findings of the failed step.\n\n## Fonts, determinism and the network\n\nDrawing uses the renderer\'s bundled open font pack (the office pack with visual substitution) plus the files in the `fonts` folders, never\nsystem fonts. The pack is prepared once per process and reused by every call that passes no `fonts` (with `fonts` folders, once per\nlist of files); a deck that draws a script beyond Latin, Greek and Cyrillic gets a handle with its script\nfaces. Concurrent calls share the one preparation and draw one at a time on it. The output is the same bytes as with a handle\nof the caller\'s own. Nothing is fetched and no clock is read: the same input, options and installed versions give the same\nbytes on every machine.\n\n## Reading and writing text without files\n\nCore reads and writes deck text in every runtime: `parse(text, { filename?, format?, catalogs? })` returns the\npresentation and throws `OPFValidationError` on syntax, schema or reference errors (findings located by line and column);\n`stringify(deck, { format? | filename? })` returns text; `validate(text)` returns the full report, warnings included.\n\n## Slide selections\n\n`parseSlideSelection(selection, total, label?)` (both builds) turns a selection into slide numbers, one-based, ascending and\nwithout repeats: `3`, `[1, 3]`, `"1,3-5"`, `"2-"` (to the end) and `"-3"` (from the start). It throws `OPFApiError`\n`invalid-option` for a malformed selection, a number that is not a whole number from 1, a reversed range or a slide past the\nend, and `no-slides` when `total` is 0. `convert` and the opf CLI\'s `--slides` read selections with it, and an engine can share it.\n'
|
|
255
|
+
"markdown": '# OPF files in Node: `@openpresentation/opf`\n\n`@openpresentation/opf` is one import for every runtime. In Node (and Bun and Deno) it is core plus the file API: it converts,\nopens and saves OPF files, and draws them as PDF, PNG, SVG and PPTX through the optional engines. In a browser or a worker the\nsame import gives the browser-safe build, with the same names, where only the file functions refuse.\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\nThe namespace is all of core (`validate`, `parse`, `stringify`, `paginate`, `parseSlideSelection`, the types and the error\nclasses), so one import covers an application. The pptx.gallery catalog is the package `@openpresentation/gallery` (`gallery`, a\ndependency of core, RR-78); in Node, `open`, `save` and `convert` register it when no `catalogs` are passed. A gallery that targets\nanother catalog record schema than core reads (`CATALOG_SCHEMA`) is refused with `gallery-schema-mismatch`.\n\n## The CLI\'s verbs, with the CLI\'s defaults\n\nOPF 0.18 gives the Node build the verbs of the `opf` command, so a script calls them instead of spawning `opf`:\n\n| Function | Command | In Node, when `catalogs` is omitted |\n| --- | --- | --- |\n| `validate(deck, options?)` | `opf validate` | the gallery is registered, so `opf validate deck` and `validate(deck)` report the same findings |\n| `stats(deck, options?)` | `opf stats` | the gallery |\n| `paginate(deck, options?)` | `opf paginate` | the gallery. Pass `fonts` (opf-render\'s `loadFonts()`) for measured page breaks; without it the result says `layout: "estimated"`. `opf paginate` measures with the office pack, prepared once per process, as `convert` does for an export with `paginate: true` |\n| `embed(deck, options?)` | `opf embed` | the gallery |\n| `edit(deck, patch, options?)` | `opf edit` | the gallery for the check. Applies a JSON Patch whole or not at all (`OPFPatchError`), checks the result (`OPFValidationError`; `validate: false` skips it) and returns `{ presentation, findings, inverse }` |\n| `fill(template, data?, options?)` | `opf fill` | no catalog: `data` is CSV, TSV or JSON text, a record or a list of records; returns `{ decks, complete, unfilled, diagnostics }`, and `presentation` with `combine: true`. `fillRecords(text)` reads the records alone |\n| `diff(a, b)`, `merge(base, ours, theirs)` | `opf diff`, `opf merge` | no catalog |\n\nAn explicit `catalogs: []` registers none. The browser build exports the same functions and registers no catalog (FA-21): a\nbrowser host passes `{ catalogs: [gallery] }` itself (`import { gallery } from "@openpresentation/gallery"`). Only `open`, `save` and `convert` are Node only.\n\nA shorter name is available as an npm alias, if you want one: `npm i opf@npm:@openpresentation/opf` installs the same package\nas `opf`, so `import * as opf from "opf"` works too. The docs keep the full name.\n\n## One import for every runtime\n\nOPF 0.18 removed the `@openpresentation/opf/node` subpath of 0.17: import `@openpresentation/opf` instead, with the same names\n(OPF 0.19 removed `@openpresentation/opf/catalog` and `defaultCatalog`, with no alias: import `gallery` from\n`@openpresentation/gallery`).\nThe package\'s conditional exports pick the build:\n\n| Condition | Build | `open`, `save`, `convert` |\n| --- | --- | --- |\n| `bun`, `deno`, `node` | the full build (`dist/index.js`) | work on files, and `convert` also returns bytes |\n| `workerd` (Cloudflare), `worker`, `browser`, `default` | the browser-safe build (`dist/browser.js`) | reject with `OPFApiError` code `node-only` |\n\nThe condition also decides how `validate` runs the JSON Schemas; see [Edge runtimes](#edge-runtimes-validators-that-never-compile-code).\n\n- **One type surface.** Both builds share `dist/index.d.ts` (the `types` condition, first in the map), so TypeScript sees the\n same declarations under `NodeNext` and `Bundler` resolution; the file functions are documented there as Node only. The\n declarations need no Node types.\n- **The browser-safe build** imports no Node builtin, no file system and neither engine (`scripts/check-browser-safe.mjs`\n bundles it for the browser and worker conditions). Its `node-only` messages name the alternative: `parse(text, { filename })`\n for `open`, `stringify(deck, { filename })` for `save`, and for `convert` the deck forms through `parse` and `stringify` and\n drawing through opf-render\'s `/export-browser` entry. The in-memory `convert(deck, { format })` is Node only in 0.18 too; a\n later release adds a browser implementation through `/export-browser`.\n- **The Node build** loads its file engine on the first file call, so importing the root costs no more than core.\n- **Bundlers** pick the browser build for a browser target and the full build for a Node target. A bundler that sets neither\n condition gets `default`, the browser-safe build: for a Node bundle, add the `node` condition (Rollup\'s node-resolve\n `exportConditions: ["node"]`, esbuild\'s `platform: "node"`).\n\n## Edge runtimes: validators that never compile code\n\n`validate` checks a document against the JSON Schemas with Ajv, and Ajv compiles each schema to JavaScript with `new Function` the\nfirst time it is used. Cloudflare Workers, Vercel Edge and a page under a Content-Security-Policy without `unsafe-eval` refuse\ncode generation from strings (Workers: "Code generation from strings disallowed for this context", while a request is handled), so\non those hosts the package ships the same validators compiled at build time (Ajv\'s standalone code generation, `scripts/validators-codegen.mjs`).\nThey give the same verdicts and the same findings: `test/precompiled-validators.test.mjs` compares them with Ajv on every example deck,\non catalog records and on random mutants of both, and on every `oneOf` and `anyOf` branch of the schemas.\n\nThe choice is made by the package\'s export conditions, through the internal `#schema-engine` import (package.json `imports`), so a\nhost needs no alias, no build step and no option:\n\n| Condition | Validators |\n| --- | --- |\n| `workerd` (Cloudflare Workers), `worker`, `edge-light` (Vercel Edge) | precompiled; no code is generated at runtime |\n| `node`, `bun`, `deno`, `browser`, `default` | Ajv, compiled on the first `validate` of the process |\n\nWrangler and OpenNext set `workerd`, `worker` and `browser` when they bundle a Worker, and the Edge runtime of Next.js sets `edge-light`,\nso a Worker needs nothing from you. Two cases do not get them by condition. Code that runs on a worker but was bundled for Node (a\nNext.js route handler that Turbopack or webpack resolved with the `node` condition before OpenNext packaged it, for one), and a browser\npage under a strict Content-Security-Policy. Import the precompiled validators once, before the first `validate`:\n\n```ts\nimport "@openpresentation/opf/precompiled"; // installs the build-time validators for this process; it exports nothing\n```\n\nThe price is size: the precompiled validators add about 78 KB gzipped (1.4 MB minified, mostly generated code) to the browser-safe\nbuild, which is why the browser keeps Ajv by default. In the package they are one minified chunk of `dist/` (1.5 MB, 121 KB gzipped;\nthe schemas are shared with the rest of the package, not copied), which adds about 125 KB to the tarball. On the first `validate` they save the time Ajv spends compiling the schemas\n(the first `validate` took about 100 ms with Ajv and about 10 ms precompiled in Node). A schema you compile yourself with Ajv is not covered: the precompiled build validates the OPF schemas that\nship in the package.\n\nThe `workerd` CI lane (`packages/javascript/test/workerd.test.mjs`, in the core job of `ci.yml`) bundles the package the way Wrangler\ndoes, runs it in the real `workerd` runtime from the `workerd` npm package (no Cloudflare account, no login), validates the example\ndecks, and requires the findings to equal Node\'s. A control in the same file bundles a Worker without the worker conditions and\nrequires workerd to refuse it, so the lane fails if the default validators go back to runtime compilation. Fonts, rasterizing and the\nin-memory `convert` are not part of this lane yet (CE-02, CE-03 in `docs/programs/cloud-editor/`).\n\n## The stack\n\nOPF is six packages. **Core** (`@openpresentation/opf`) is the format and its API: schemas, types, `validate`, `parse` and\n`stringify`, composition and pagination, and in Node the files. **The gallery** (`@openpresentation/gallery`) is the pptx.gallery\ncatalog, a dependency of core released on its own version line. **The CLI** (`@openpresentation/cli`) is the `opf` command;\nits `convert` command runs core\'s Node engine (through `@openpresentation/opf/internal/engine`,\nwhich the package exports under the `node` condition for the CLI only; it is not an application API). **opf-render**,\n**opf-pptx** and **opf-editor** are the engines: drawing (SVG, PNG, PDF), PowerPoint (export and import) and the editor.\n\n## Install\n\n```sh\nnpm install @openpresentation/opf @openpresentation/opf-render @openpresentation/opf-pptx \\\n @resvg/resvg-js sharp pdf-lib \\\n @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 \\\n @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 \\\n @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2\n```\n\n`@openpresentation/opf-render` and `@openpresentation/opf-pptx` are optional peer dependencies of core: npm does not install them\nfor you, and an application that only reads, validates or converts between deck forms needs neither. Core loads them the\nfirst time a call needs them, from core\'s own install location and then from the working directory; a missing one throws\n`peer-not-installed` with the install command. What each output needs is listed in [the CLI reference](cli.md#install): PNG\nneeds `@resvg/resvg-js` and `sharp`, raster PDF `pdf-lib`, pictures in a PDF `sharp`, every format the office font pack, and\n`.pptx` input or output `@openpresentation/opf-pptx`.\n\n## `convert(input, output, options?)`\n\n```ts\nawait opf.convert("deck.opf.md", "deck.pdf");\nawait opf.convert("deck.opf.md", "deck.pptx");\nawait opf.convert("deck.opf.md", "slides/deck.png"); // one PNG per slide: slides/deck-1.png, -2, ...\nawait opf.convert("deck.opf.md", "slides/slide-{n}.png"); // always numbered: slides/slide-1.png, slide-2.png, ...\nawait opf.convert("deck.pptx", "deck.opf.yaml"); // import\nawait opf.convert("deck.opf.json", "deck.opf.md"); // change the deck form\nawait opf.convert("deck.opf.md", "deck.png", { slides: "1-3", scale: 2 });\nawait opf.convert("deck.opf.md", "slides.zip", { format: "svg" });\n```\n\n`input` is a file path, a presentation object or the bytes of a `.pptx` file (`Uint8Array`). The formats come from the names:\n\n| Input | Read as |\n| --- | --- |\n| `.pptx`, PPTX bytes | imported through opf-pptx |\n| `.opf.md` | a Markdown deck |\n| `.yaml`, `.yml` | a YAML deck |\n| `.json` | a JSON deck |\n\n| Output | Written as |\n| --- | --- |\n| `.pdf`, `.pptx` | one file |\n| `.png`, `.svg` | one file per slide, named after the output: `slides/deck.png` gives `slides/deck-1.png`, `-2`, ... (the numbers are the deck\'s slide numbers, padded with zeros to the width of the largest number written: `deck-01.png` from ten slides, `deck-001.png` from a hundred; `slides: "2,5"` gives `deck-2.png` and `deck-5.png`). When one slide is selected (or the deck has one), it is written to the output name itself, as `opf convert deck.opf.json slide.png --slides 2` does. With `{n}` in the output (`"slides/slide-{n}.png"`) the files are always numbered, even for one slide, and `{n}` is the slide number padded by the same rule. `{n}` is the only placeholder: any other `{key}`, and `{n}` in a `.pdf`, `.pptx`, `.zip` or deck-form output, rejects with `invalid-option`. |\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; the archive\'s own name takes no `{n}`). The slides are PNG unless `format: "svg"`. |\n| `.opf.md`, `.yaml`, `.yml`, `.json` | the deck in that form |\n\nAny other extension, and a plain `.md` file, rejects with `invalid-option`, which names the supported extensions. A `format`\nthat disagrees with the output\'s extension is refused too; it is only needed for a `.zip`.\n\n**Without an output path**, pass the format in the options: `convert(input, { format })` writes nothing and returns the files\nwith their names and bytes. `name` sets the base name of the files; without it they follow the deck: its `filename`, else its\nslugified `name`, else the input file\'s stem (`Q4-Review.pdf`, `Q4-Review-1.png`; padded like the files above, `Q4-Review-01.png` from ten slides, `deck.opf.yaml`). `zip: true` returns one archive of\nthe PNG or SVG slides; with an output path a `.zip` name makes the archive instead.\n\n**Options** are the flags of `opf convert` in camel case: `slides` (`3`, `"1,3-5"` or `[1, 3]`, read by `parseSlideSelection`), `includeHidden`, `paginate`, `scale`\n(0.1 to 8, PNG and raster PDF), `raster` (`true` draws a PDF as a picture per page; vector text is the default), `text` (an SVG\ncarries `"fonts"`, the default, which embeds the faces the slide uses; `"system"`, none, for a page that has the fonts; or\n`"paths"`, glyph outlines, so the file needs no font), `charts` (`"auto"`, `"native"` or `"picture"`), `provenance` and `images`\n(`"compatible"` or `"preserve"`) for PPTX, `date` (`YYYY-MM-DD` for date fields; nothing reads a clock), `catalogs` (the default\ncatalog when omitted), `fonts` (a prepared `loadFonts()` handle of `@openpresentation/opf-render/fonts-node`, or a folder or a list\nof folders of `.ttf`/`.otf` files), `assetDir`, plus `signals` (a `.pptx` input: also return the importer\'s per-shape\nsignals), and for output without a path `zip` and `name`, and `overwrite`. An option that does not apply to the pair rejects with\n`invalid-option`: `scale` for a deck output, `slides` for a PPTX, `raster` for anything but a PDF, `text` for anything but an SVG,\n`zip` or `name` with an output path. The 0.17 names (`pdfMode`, `svgFonts`, `chartex`, `imageFormat`, `fontDirs`, `filename`) were\nrenamed in 0.18 and are refused with the new name.\n\n**Writing.** Inputs are checked and all results are produced and staged before destination files are published. A preflight,\nrender or staging failure publishes no destination files, though parent folders may have been created. Each final file is\npublished atomically from a temporary sibling, so no partial file has the final name. Multiple files are not one transaction:\na later publication I/O failure can leave earlier complete outputs in place. Keep that possibility in your recovery logic.\nExisting regular outputs are replaced by default, as `save` and `fs.writeFile` do: running the same `convert` again rebuilds\nthe file. `overwrite: false` checks for existing outputs during preflight and reports `output-exists`, the rule the `opf`\ncommands apply without `--force`. A symlink, a directory or another non-regular file is refused (`output-not-file`). The result\nis `{ files: [{ name, path, type, bytes, ... }], findings }`, with the slide number, id and size of each picture and the pages of a PDF.\n\n**Images.** When `input` is a path, a deck\'s relative image paths resolve against the input file\'s folder, from any working\ndirectory; `assetDir` replaces that folder. Only image files inside it are read, and URLs are never fetched.\n\n**Chaining.** A `.pptx` input is imported, checked and then exported or written. `findings` holds both steps: the importer\'s\ndiagnostics carry the `import/` rule prefix, the format and references check `opf/`, and the engines `render/`, `pptx/`,\n`pdf/`, `fonts/` and `cli/`.\n\n## `open(pathOrBytes, options?)`\n\nReturns the presentation itself. A `.opf.md`, `.yaml`/`.yml` or `.json` file is read in the form its extension names and\nchecked for format and references; a `.pptx` path or its bytes are imported. An invalid deck throws `OPFValidationError`,\nwhose findings are located by line and column in the file; a PowerPoint file that cannot be imported throws `OPFImportError`.\nWarnings are not returned: `validate` reports them, and `convert("deck.pptx", "deck.opf.yaml")` returns what an import could\nnot keep. `catalogs` (default: the default catalog) is where references resolve.\n\n## `save(deck, path, options?)`\n\nWrites the deck in the form the file name names (`.opf.md`, `.yaml`/`.yml` or `.json`), atomically, creating folders and\nreplacing the file. It checks format and references first and refuses an invalid deck with `OPFValidationError`; `validate:\nfalse` writes work in progress, as an editor\'s autosave does (the YAML and Markdown writers still refuse a document that is\nnot well-formed OPF). `schemaComment: true` starts a YAML file with the editor modeline. Returns `{ path, format }`.\n\n## Errors\n\nEvery error has a `code`; branch on it, never on the message.\n\n| Class | Codes |\n| --- | --- |\n| `OPFApiError` (the base class) | `node-only` (the browser-safe build), `invalid-option`, `input-not-found`, `input-unreadable`, `invalid-presentation` (a deck written as a deck), `output-exists`, `output-not-file`, `output-unwritable` |\n| `OPFExportError` | the export step: `peer-not-installed` (the message carries the install command; `details` the package and range), `peer-too-old`, `peer-load-failed`, `invalid-option`, `invalid-presentation`, `no-slides`, `all-slides-hidden`, `export-failed`, a font code |\n| `OPFImportError` | the import step and `open` of a PowerPoint file: `peer-not-installed`, `peer-too-old`, `peer-load-failed`, `import-failed`, `invalid-presentation` |\n| `OPFValidationError` | `open` and `save` of an invalid deck; also thrown by core\'s `parse` and `assertValid` |\n\n`findings` on an error holds the located findings of the failed step.\n\n## Fonts, determinism and the network\n\nDrawing uses the renderer\'s bundled open font pack (the office pack with visual substitution) plus the files in the `fonts` folders, never\nsystem fonts. The pack is prepared once per process and reused by every call that passes no `fonts` (with `fonts` folders, once per\nlist of files); a deck that draws a script beyond Latin, Greek and Cyrillic gets a handle with its script\nfaces. Concurrent calls share the one preparation and draw one at a time on it. The output is the same bytes as with a handle\nof the caller\'s own. Nothing is fetched and no clock is read: the same input, options and installed versions give the same\nbytes on every machine.\n\n## Reading and writing text without files\n\nCore reads and writes deck text in every runtime: `parse(text, { filename?, format?, catalogs? })` returns the\npresentation and throws `OPFValidationError` on syntax, schema or reference errors (findings located by line and column);\n`stringify(deck, { format? | filename? })` returns text; `validate(text)` returns the full report, warnings included.\n\n## Slide selections\n\n`parseSlideSelection(selection, total, label?)` (both builds) turns a selection into slide numbers, one-based, ascending and\nwithout repeats: `3`, `[1, 3]`, `"1,3-5"`, `"2-"` (to the end) and `"-3"` (from the start). It throws `OPFApiError`\n`invalid-option` for a malformed selection, a number that is not a whole number from 1, a reversed range or a slide past the\nend, and `no-slides` when `total` is 0. `convert` and the opf CLI\'s `--slides` read selections with it, and an engine can share it.\n'
|
|
254
256
|
},
|
|
255
257
|
{
|
|
256
258
|
"slug": "numbered-lists",
|
|
@@ -268,7 +270,7 @@ var docsData = Object.freeze([
|
|
|
268
270
|
"slug": "patch-diff-merge-format",
|
|
269
271
|
"file": "docs/patch-diff-merge-format.md",
|
|
270
272
|
"title": "Patch, diff, merge and format",
|
|
271
|
-
"markdown": '# Patch, diff, merge and format\n\nStatus: in core 0.12.0 (RR-31) as the `@openpresentation/opf/patch`, `/diff` and `/format` entrypoints; the CLI commands `opf diff`, `opf merge` and `opf format` are in CLI 0.10.0 and later, not in CLI 0.9.2 (which has `opf edit`, JSON Patch). Core 0.11.4 and earlier lack the entrypoints. Check `opf --help` and the installed package exports before relying on them.\n\nFour deterministic, local pieces share one implementation: no network, no model call, no clock, no randomness. The same inputs always give the same output.\n\n| Piece | Core entrypoint | CLI |\n| --- | --- | --- |\n| JSON Patch (RFC 6902) with inverse patches | `@openpresentation/opf/patch` | `opf edit` |\n| Semantic diff of two documents | `@openpresentation/opf/diff` | `opf diff` |\n| Three-way merge with conflict objects | `@openpresentation/opf/diff` | `opf merge` |\n| Canonical key order and layout | `@openpresentation/opf/format` | `opf format` |\n\n## One patch module\n\n`opf edit`, the editor session (`@openpresentation/opf-editor`, undo/redo) and the diff output all use `@openpresentation/opf/patch`. There is no second implementation.\n\n```ts\nimport { applyPatch, applyPatchWithInverse, invertPatch, OPFPatchError } from "@openpresentation/opf/patch";\n\nconst next = applyPatch(presentation, [\n { op: "test", path: "/slides/0/id", value: "intro" },\n { op: "replace", path: "/slides/0/title", value: "Welcome" },\n { op: "move", from: "/slides/3", path: "/slides/1" },\n]);\n// applyPatch clones: `presentation` is never changed, and any failure applies nothing.\n\nconst { presentation: after, inverse } = applyPatchWithInverse(presentation, patch);\napplyPatch(after, inverse); // deep-equals `presentation`: this is the undo patch\n```\n\n- All six RFC 6902 operations: `add`, `remove`, `replace`, `move`, `copy`, `test`. Unknown members of an operation are ignored (RFC 6902 section 4); unknown `op` values are errors.\n- Pointers are strict RFC 6901. `-` is accepted only as the final token of an `add`, `move` target or `copy` target. Array indexes are canonical decimal (`01`, `-1` and `1.0` are errors). `parsePointer`, `formatPointer`, `escapePointerToken`, `pointerFromPath` (a pointer, an OPF dotted path such as `slides.0.title`, or a segment array) and `splitPointer` give every caller the same path handling; build pointers with `formatPointer` rather than joining unescaped keys.\n- Errors are `OPFPatchError` with a stable `code` (`patch-test-failed`, `patch-path-missing`, `patch-parent-missing`, `invalid-array-index`, `invalid-json-pointer`, `invalid-patch`, `invalid-patch-operation`, `unsupported-patch-operation`, `patch-invalid-move`, `patch-root-remove`, `patch-invalid-document`), the failing operation\'s `index` and `path`, and a message that starts with `Operation <n>:`.\n- `test` fails on a missing path, and compares by JSON value: member order is irrelevant, `0` equals `-0`, arrays compare in order. `move` onto itself is a no-op; moving a value into its own descendant is an error; removing the document root is an error. A `__proto__` key is data and never reaches a prototype.\n- Optional schema validation of the result: `applyPatch(presentation, patch, { validate: true })` (or a custom validator; add `strict: true` to reject warnings) throws `OPFPatchValidationError` with the full validation report when the final document is invalid. Intermediate states are not validated, so a patch may pass through an invalid state. `opf edit` validates the saved document itself, as before.\n- Inverse patches: `applyPatchWithInverse` and `invertPatch` return operations that restore the input exactly, in reverse order, with concrete array indexes (the inverse of an `add` at `-` removes the real index). `test` has no inverse. The editor stores these in its undo stack.\n\n## Diff\n\n```ts\nimport { diff, formatDiffReport } from "@openpresentation/opf/diff";\n\nconst result = diff(a, b);\nresult.equal; // true when nothing differs\nresult.patch; // RFC 6902 patch: applyPatch(a, result.patch) deep-equals b\nresult.changes; // typed, categorised changes\nresult.slides; // how each slide was matched, and its status\nresult.summary; // counts by type, category and slide status\nformatDiffReport(result); // plain-text report\n```\n\n**Matching.** Arrays are matched element by element, in this order, and the same rule applies to slides, blocks, list items, table rows and every other array:\n\n1. a shared string `id`;\n2. identical content (an order-preserving match first, then anywhere, so a moved slide with no `id` is still found);\n3. content similarity for objects and arrays: the share of scalar leaves with the same path and value, with partial credit for text that shares words. The default threshold is 0.5 (`threshold` option); two elements that both carry an `id` and differ in it must reach 0.8, so an unrelated added slide is not mistaken for a renamed one;\n4. leftover primitives between matched neighbours pair by position (an edited bullet is one `replace`).\n\nTies break by index, so the result never depends on iteration order or object key order. Arrays too large for the similarity pass (over 250,000 element pairs) fall back to id and equality matching.\n\n**Moves.** Matched elements that are out of order are found with a longest in-order chain, so moving one slide is one `move` operation and one `moved` change, not a remove and an add.\n\n**Changes.** Each change has a `type` (`added`, `removed`, `changed`, `moved`), a `category` (`slide`, `block`, `field`, `design`, `metadata`, `assets`, `catalogs`, `variables`, `narrative`, `extensions`), a JSON Pointer in A (`aPath`) and/or B (`bPath`), the `before` and `after` values, and the `slide` it belongs to (`id`, `title`, `aIndex`, `bIndex`). Root keys other than `slides`, `design`, `assets`, `catalogs`, `variables`, `narrative` and `extensions` are `metadata`; a slide\'s own `design` is `design`; adding, removing or moving a `blocks` element or a region payload (`left`, `center+right`, `top:left`, ...) is `block`; everything else inside a slide is `field`.\n\n**The patch** applies removals (highest index first), then moves, then insertions in ascending order, then edits inside matched elements at their final positions, so every path is valid when its operation runs.\n\n```text\n$ opf diff before.opf.json after.opf.json\n7 changes (slides: 1 added, 1 removed, 1 moved, 2 modified; metadata: 1; design: 1)\n\nMetadata\n ~ name "Deck" -> "Renamed"\n\nDesign\n ~ design.theme "bold" -> "minimal"\n\nSlides\n ~ slide #1 "End" (id end)\n + blocks[1] chart\n ~ slide #2 "Welcome" (id intro) (moved from #1)\n ~ title "Intro" -> "Welcome"\n + slide #3 "New" (id new)\n - slide #2 "Old" (id old)\n```\n\nCLI: `opf diff <a|-> <b|-> [--format text|json|patch] [--exit-code] [--threshold <0-1>]`. `--format json` prints the report `{command, ok, input, outputs, findings, counts, equal, summary, slides, changes, patch}` (CLI 0.18; `{equal, summary, slides, changes, patch}` before); `--format patch` prints only the patch (feed it to `opf edit --patch`). Exit 0 unless `--exit-code` is given and the documents differ (then 1); usage and I/O errors exit 2. `-` reads one side from stdin.\n\n## Merge\n\n```ts\nimport { merge } from "@openpresentation/opf/diff";\n\nconst { merged, conflicts, clean, applied } = merge(base, ours, theirs, { prefer: "ours" });\n```\n\nChanges in different places merge automatically; the same change on both sides is applied once. Arrays merge element by element with the diff matcher:\n\n- insertions from both sides are kept (ours first where both inserted at the same position); an identical insertion, or the same `id` inserted twice, appears once (same `id` with different content merges field by field);\n- a deletion of an untouched element wins; a deletion against an edit is a conflict;\n- a move on one side is applied on top of the other side\'s edits; the same move on both sides agrees; different moves of the same element conflict.\n\n**Conflicts are never silent.** Each is an object with `kind` (`modify-modify`, `modify-delete`, `delete-modify`, `add-add`, `move-move`), `path` (JSON Pointer in the merged document), `base`, `ours` and `theirs` values (absent when that side deleted the value; `deletedBy` names the deleting side), `resolution` (which side the merged document took), the `slide` it is inside, and a `message`. The merged document always holds one side\'s value at a conflict (`prefer`, default `ours`), and the conflict keeps the other side\'s value, so neither side is dropped without a record. `clean` is `true` only when there are no conflicts. `applied` counts the places taken from only one side or identical on both.\n\nThe merge result is not schema-validated by the library; `opf merge` validates it before writing, because two valid documents can merge into an invalid one (for example duplicate ids).\n\nCLI: `opf merge <base> <ours> <theirs> [output|-] [-i] [--force] [--prefer ours|theirs] [--report <file>] [--threshold <0-1>] [--fail-on <level>]`. With conflicts and no `--prefer`, nothing is written, the conflict report goes to stderr as JSON and the exit code is 1. With `--prefer`, the chosen side\'s value is written, the report lists every conflict, and the exit code is 0. `--report <file>` saves the `{clean, conflicts, applied}` summary. `-i` (`--in-place`) rewrites the *ours* file (like `git merge-file`) with the same guard as `opf edit`. The usual output rules apply: stdout without an output, `--force` to replace a file.\n\n## Format\n\n```ts\nimport { format, sortPresentationKeys, isFormatted } from "@openpresentation/opf/format";\n\nconst text = format(sourceTextOrDocument);\n```\n\n- **Key order** follows the property declaration order of the OPF schema at every depth (through `$ref`, `oneOf`, `anyOf`, `allOf` and `then`/`else`): `$schema`, `name`, `description`, ..., `design`, `variables`, `narrative`, `slides`, `assets`, `catalogs`, `extensions` at the root, `id`, `layout`, `title`, ... in a slide. Keys the schema does not declare (extensions, catalog records, custom colours, unknown fields) follow the declared ones in their original relative order, so formatting never reorders user data that has no canonical order. Array order and every value are unchanged.\n- **Layout** is two-space indentation (`indent` 0 to 8), LF line endings (`eol: "crlf"` for Windows checkouts that use `core.autocrlf`), no BOM, one trailing newline. Formatting is idempotent.\n- It does not validate: any JSON document can be formatted, and formatting never changes validity.\n\nCLI: `opf format <file|-> [output|-]` or `opf format <file>... (-i | --check)`, with `[--indent <0-8>] [--eol lf|crlf|preserve]`. One file with no output prints the canonical text to stdout; an output must name the same form (`opf convert` changes forms). `--check` reports `checked`, `formatted` and `unformatted` and exits 1 if any file would change (use it in CI); `-i` rewrites only changed files atomically; several files need `--check` or `-i`. `--eol preserve` keeps each file\'s own line endings.\n\n## What this does not do\n\n- It is structural, not visual: a diff of two decks says nothing about whether either composes, overflows or exports well.\n- Merge treats text as a value: two edits to different words of the same string are a conflict, not a text merge.\n- Similarity matching is a heuristic with a documented threshold. When it matters, give slides stable ids; ids are always trusted over content.\n'
|
|
273
|
+
"markdown": '# Patch, diff, merge and format\n\nStatus: in core 0.12.0 (RR-31) as the `@openpresentation/opf/patch`, `/diff` and `/format` entrypoints; the CLI commands `opf diff`, `opf merge` and `opf format` are in CLI 0.10.0 and later, not in CLI 0.9.2 (which has `opf edit`, JSON Patch). Core 0.11.4 and earlier lack the entrypoints. Check `opf --help` and the installed package exports before relying on them.\n\nFour deterministic, local pieces share one implementation: no network, no model call, no clock, no randomness. The same inputs always give the same output.\n\n| Piece | Core entrypoint | CLI |\n| --- | --- | --- |\n| JSON Patch (RFC 6902) with inverse patches | `@openpresentation/opf/patch` | `opf edit` |\n| Semantic diff of two documents | `@openpresentation/opf/diff` | `opf diff` |\n| Three-way merge with conflict objects | `@openpresentation/opf/diff` | `opf merge` |\n| Canonical key order and layout | `@openpresentation/opf/format` | `opf format` |\n| Ids, id-addressed paths, document hash, patch scope | `@openpresentation/opf/ids` (the patch parts also on `/patch`) | |\n\n## One patch module\n\n`opf edit`, the editor session (`@openpresentation/opf-editor`, undo/redo) and the diff output all use `@openpresentation/opf/patch`. There is no second implementation.\n\n```ts\nimport { applyPatch, applyPatchWithInverse, invertPatch, OPFPatchError } from "@openpresentation/opf/patch";\n\nconst next = applyPatch(presentation, [\n { op: "test", path: "/slides/0/id", value: "intro" },\n { op: "replace", path: "/slides/0/title", value: "Welcome" },\n { op: "move", from: "/slides/3", path: "/slides/1" },\n]);\n// applyPatch clones: `presentation` is never changed, and any failure applies nothing.\n\nconst { presentation: after, inverse } = applyPatchWithInverse(presentation, patch);\napplyPatch(after, inverse); // deep-equals `presentation`: this is the undo patch\n```\n\n- All six RFC 6902 operations: `add`, `remove`, `replace`, `move`, `copy`, `test`. Unknown members of an operation are ignored (RFC 6902 section 4); unknown `op` values are errors.\n- Pointers are strict RFC 6901. `-` is accepted only as the final token of an `add`, `move` target or `copy` target. Array indexes are canonical decimal (`01`, `-1` and `1.0` are errors). `parsePointer`, `formatPointer`, `escapePointerToken`, `pointerFromPath` (a pointer, an OPF dotted path such as `slides.0.title`, or a segment array) and `splitPointer` give every caller the same path handling; build pointers with `formatPointer` rather than joining unescaped keys.\n- Errors are `OPFPatchError` with a stable `code` (`patch-test-failed`, `patch-path-missing`, `patch-parent-missing`, `invalid-array-index`, `invalid-json-pointer`, `invalid-patch`, `invalid-patch-operation`, `unsupported-patch-operation`, `patch-invalid-move`, `patch-root-remove`, `patch-invalid-document`, `patch-id-not-found`, `patch-id-ambiguous`, `invalid-expect-hash`, `patch-hash-mismatch`), the failing operation\'s `index` and `path`, and a message that starts with `Operation <n>:`.\n- `test` fails on a missing path, and compares by JSON value: member order is irrelevant, `0` equals `-0`, arrays compare in order. `move` onto itself is a no-op; moving a value into its own descendant is an error; removing the document root is an error. A `__proto__` key is data and never reaches a prototype.\n- Optional schema validation of the result: `applyPatch(presentation, patch, { validate: true })` (or a custom validator; add `strict: true` to reject warnings) throws `OPFPatchValidationError` with the full validation report when the final document is invalid. Intermediate states are not validated, so a patch may pass through an invalid state. `opf edit` validates the saved document itself, as before.\n- Inverse patches: `applyPatchWithInverse` and `invertPatch` return operations that restore the input exactly, in reverse order, with concrete array indexes (the inverse of an `add` at `-` removes the real index). `test` has no inverse. The editor stores these in its undo stack.\n\n## Ids, id-addressed paths, hashes and patch scope\n\nIndexes move: `/slides/3/blocks/1` is another block once a slide is inserted above it. Anything that edits with other people or other programs (remote patches, comments, review, an agent\'s proposal) needs paths that name the thing, and a way to say "only if nothing changed". Slides and content payloads (blocks, groups, regions) already have an optional `id`, unique among slide and payload ids; these pieces build on it. They live in `@openpresentation/opf/ids` and on the package root (the pointer, hash and `expectHash` parts are also on `/patch`); all of them are deterministic, offline and browser-safe.\n\n### `assignIds`\n\n```ts\nimport { assignIds } from "@openpresentation/opf/ids";\n\nconst { document, patch, assigned } = assignIds(presentation);\n// document: a copy where every slide and payload has an id\n// patch: the same change as `add` operations (`/slides/0/id`, ...), for an editor to apply as one undoable edit\n// assigned: [{ pointer: "/slides/0", kind: "slide", id: "slide-1" }, ...]\n```\n\nMissing ids are filled in document order (slides, then each slide\'s blocks, nested groups and promoted regions), as `slide-1`, `slide-2`, ... and `block-1`, `block-2`, ..., skipping ids already in use, so the same document always gets the same ids and the result is valid against the schema. An existing id is never changed. Pass `generate({ kind, pointer, node, parentId })` to choose the ids yourself (a UUID, a slug of the title); it must return a non-empty string no slide or payload uses, or `assignIds` throws `OPFIdError` (`invalid-id`, `duplicate-id`). The id is added as the node\'s last member; `format` puts it in canonical order. List items, table rows and runs have no id.\n\n### Id-addressed paths\n\nIn an array, the pointer token `@intro` is the element whose `id` is `intro`: `/slides/@kpis/blocks/@note/text`. It is a valid RFC 6901 pointer, so every JSON Pointer tool can carry it; it resolves in OPF\'s own `readPointer`, `getAtPointer`, `applyPatch` (every operation, `from` and `path`) and the editor\'s `get`, `set` and `applyPatch`.\n\n```ts\nimport { applyPatch, resolveIdPointer, toIdPointer, resolveIdPatch, toIdPatch } from "@openpresentation/opf/patch";\n\nresolveIdPointer(doc, "/slides/@kpis/blocks/@note/text"); // "/slides/1/blocks/1/text"\ntoIdPointer(doc, "/slides/1/blocks/1/text"); // "/slides/@kpis/blocks/@note/text"\napplyPatch(doc, [{ op: "replace", path: "/slides/@kpis/blocks/@note/text", value: "Edited" }]);\ntoIdPatch(doc, patch); // every index that names an element with an id, operation by operation\nresolveIdPatch(doc, patch); // the reverse: every id token as the index it has when its operation runs\n```\n\n- **Why `@`.** It is legal in a pointer token and in a URI fragment (`#/slides/@intro`) without percent-encoding; `{id:intro}` would need `%7B` and `%7D` there. Arrays have no other use for a token that is not an index, and the token is read as an id only where the value is an array, so an object key such as an extension\'s `@context` is still a plain key. An id that begins with `@` is written `@@id`; `/` and `~` in an id escape as in any pointer (`~1`, `~0`).\n- **Resolution.** `resolveIdPointer` throws `patch-id-not-found` for an id no element has and `patch-id-ambiguous` for an id several elements of the array share (an invalid document; `readPointer` reports it as not found). A path that does not exist is not an error: only the id tokens are resolved. `toIdPointer` writes an id token only where the element\'s `id` is a non-empty string no other element of the array has; other indexes stay.\n- **`move` and insertion.** An id as the last token of `add`, `copy` or `move` inserts before that element (an index does the same). A `move` resolves its destination after the value has left, as RFC 6902 reads an index. The `patch` and `inverse` that `applyPatchWithInverse` returns keep the paths as written and in index form respectively: an inverse never depends on ids.\n- **`toIdPatch`** writes each operation against the document it applies to, so a host that syncs by id sends a patch that still means the same thing after the receiver has reordered. Where an operation inserts, the last token stays an index (or `-`): there is no element yet to name; its ancestors are named by the ids they have before the insertion.\n\n### `documentHash` and `expectHash`\n\n```ts\nimport { documentHash, applyPatch, OPFPatchHashError } from "@openpresentation/opf/patch";\n\nconst hash = documentHash(doc); // "sha256:" + 64 hex digits\napplyPatch(doc, patch, { expectHash: hash }); // throws OPFPatchHashError (code patch-hash-mismatch) when doc is not the document hashed; nothing is applied\n```\n\nThe hash is SHA-256 over the document\'s canonical JSON: the members of every object sorted by UTF-16 code unit at every depth (RFC 8785), no whitespace. Two documents hash alike exactly when `jsonEqual` calls them equal: key order, a JSON, YAML or Markdown round trip, and formatting do not matter; any content change, including array order, does. (`format` is not the canonical form here: it keeps keys the schema does not declare in their written order, which is right for a file and wrong for an identity.) The digest is pure JavaScript and synchronous, with no Node import and no Web Crypto, so it is the same in Node, browsers and edge runtimes and can be checked inside the synchronous `applyPatch`. `expectHash` also takes the bare 64-digit hex; a value that is not a hash throws `invalid-expect-hash`. `edit(deck, patch, { expectHash })` takes it too. A server that holds the document answers a client\'s patch with `expectHash` as optimistic concurrency, without writing its own etag code. This is not the CLI\'s `--expect-sha256`, which is the digest of a file\'s bytes.\n\n### `touches`\n\n```ts\nimport { touches } from "@openpresentation/opf/ids";\n\nconst scope = touches(patch, document);\nscope.writes; // [{ operation: 1, op: "replace", pointer: "/slides/1/title", idPointer: "/slides/@kpis/title", kind: "slide", ids: ["kpis"], slideIds: ["kpis"] }]\nscope.reads; // `test`, and the source of `copy`\nscope.ids, scope.slideIds, scope.kinds;\n```\n\nWhat a patch reads and writes, for conflict detection and permission checks. With the `document` the patch was made for, the patch is walked operation by operation, so every `pointer` is the index path as it is when its operation runs, every `idPointer` is its id form, and a `remove`, `replace` or `move` of a slide or group lists the ids inside it. Without a document, the pointers are as written and `ids` holds the id tokens and the ids of the values the operations insert. A patch that does not apply throws the error `applyPatch` throws. `kind` is the part of the deck a location belongs to: `document`, `slide-order` (the `slides` array, or a slide added, removed or moved), `slide`, `block` (a payload or the `blocks` array that holds it), `design`, `assets`, `catalogs` or `metadata`.\n\nTwo patches made against the same document conflict when a write of one has the same `idPointer` as, or sits inside or above, a location the other reads or writes, or the other destroys what is in its `ids`. A `move` changes where an element sits, not what is inside it: do not count the descendants of a `move` location. A permission check reads `kind`, `slideIds` and `ids` of `writes` (moves included). What counts as a conflict and who may touch what stays with the host; the library only reports the scope.\n\nThe editor reports the same patches by id (`createEditorSession(doc, { idPaths: true })`), accepts `expectHash` on `applyPatch` and assigns ids as one edit (`session.assignIds()`); see the editor README.\n\n## Diff\n\n```ts\nimport { diff, formatDiffReport } from "@openpresentation/opf/diff";\n\nconst result = diff(a, b);\nresult.equal; // true when nothing differs\nresult.patch; // RFC 6902 patch: applyPatch(a, result.patch) deep-equals b\nresult.changes; // typed, categorised changes\nresult.slides; // how each slide was matched, and its status\nresult.summary; // counts by type, category and slide status\nformatDiffReport(result); // plain-text report\n```\n\n**Matching.** Arrays are matched element by element, in this order, and the same rule applies to slides, blocks, list items, table rows and every other array:\n\n1. a shared string `id`;\n2. identical content (an order-preserving match first, then anywhere, so a moved slide with no `id` is still found);\n3. content similarity for objects and arrays: the share of scalar leaves with the same path and value, with partial credit for text that shares words. The default threshold is 0.5 (`threshold` option); two elements that both carry an `id` and differ in it must reach 0.8, so an unrelated added slide is not mistaken for a renamed one;\n4. leftover primitives between matched neighbours pair by position (an edited bullet is one `replace`).\n\nTies break by index, so the result never depends on iteration order or object key order. Arrays too large for the similarity pass (over 250,000 element pairs) fall back to id and equality matching.\n\n**Moves.** Matched elements that are out of order are found with a longest in-order chain, so moving one slide is one `move` operation and one `moved` change, not a remove and an add.\n\n**Changes.** Each change has a `type` (`added`, `removed`, `changed`, `moved`), a `category` (`slide`, `block`, `field`, `design`, `metadata`, `assets`, `catalogs`, `variables`, `narrative`, `extensions`), a JSON Pointer in A (`aPath`) and/or B (`bPath`), the `before` and `after` values, and the `slide` it belongs to (`id`, `title`, `aIndex`, `bIndex`). Root keys other than `slides`, `design`, `assets`, `catalogs`, `variables`, `narrative` and `extensions` are `metadata`; a slide\'s own `design` is `design`; adding, removing or moving a `blocks` element or a region payload (`left`, `center+right`, `top:left`, ...) is `block`; everything else inside a slide is `field`.\n\n**The patch** applies removals (highest index first), then moves, then insertions in ascending order, then edits inside matched elements at their final positions, so every path is valid when its operation runs.\n\n```text\n$ opf diff before.opf.json after.opf.json\n7 changes (slides: 1 added, 1 removed, 1 moved, 2 modified; metadata: 1; design: 1)\n\nMetadata\n ~ name "Deck" -> "Renamed"\n\nDesign\n ~ design.theme "bold" -> "minimal"\n\nSlides\n ~ slide #1 "End" (id end)\n + blocks[1] chart\n ~ slide #2 "Welcome" (id intro) (moved from #1)\n ~ title "Intro" -> "Welcome"\n + slide #3 "New" (id new)\n - slide #2 "Old" (id old)\n```\n\nCLI: `opf diff <a|-> <b|-> [--format text|json|patch] [--exit-code] [--threshold <0-1>]`. `--format json` prints the report `{command, ok, input, outputs, findings, counts, equal, summary, slides, changes, patch}` (CLI 0.18; `{equal, summary, slides, changes, patch}` before); `--format patch` prints only the patch (feed it to `opf edit --patch`). Exit 0 unless `--exit-code` is given and the documents differ (then 1); usage and I/O errors exit 2. `-` reads one side from stdin.\n\n## Merge\n\n```ts\nimport { merge } from "@openpresentation/opf/diff";\n\nconst { merged, conflicts, clean, applied } = merge(base, ours, theirs, { prefer: "ours" });\n```\n\nChanges in different places merge automatically; the same change on both sides is applied once. Arrays merge element by element with the diff matcher:\n\n- insertions from both sides are kept (ours first where both inserted at the same position); an identical insertion, or the same `id` inserted twice, appears once (same `id` with different content merges field by field);\n- a deletion of an untouched element wins; a deletion against an edit is a conflict;\n- a move on one side is applied on top of the other side\'s edits; the same move on both sides agrees; different moves of the same element conflict.\n\n**Conflicts are never silent.** Each is an object with `kind` (`modify-modify`, `modify-delete`, `delete-modify`, `add-add`, `move-move`), `path` (JSON Pointer in the merged document), `base`, `ours` and `theirs` values (absent when that side deleted the value; `deletedBy` names the deleting side), `resolution` (which side the merged document took), the `slide` it is inside, and a `message`. The merged document always holds one side\'s value at a conflict (`prefer`, default `ours`), and the conflict keeps the other side\'s value, so neither side is dropped without a record. `clean` is `true` only when there are no conflicts. `applied` counts the places taken from only one side or identical on both.\n\nThe merge result is not schema-validated by the library; `opf merge` validates it before writing, because two valid documents can merge into an invalid one (for example duplicate ids).\n\nCLI: `opf merge <base> <ours> <theirs> [output|-] [-i] [--force] [--prefer ours|theirs] [--report <file>] [--threshold <0-1>] [--fail-on <level>]`. With conflicts and no `--prefer`, nothing is written, the conflict report goes to stderr as JSON and the exit code is 1. With `--prefer`, the chosen side\'s value is written, the report lists every conflict, and the exit code is 0. `--report <file>` saves the `{clean, conflicts, applied}` summary. `-i` (`--in-place`) rewrites the *ours* file (like `git merge-file`) with the same guard as `opf edit`. The usual output rules apply: stdout without an output, `--force` to replace a file.\n\n## Format\n\n```ts\nimport { format, sortPresentationKeys, isFormatted } from "@openpresentation/opf/format";\n\nconst text = format(sourceTextOrDocument);\n```\n\n- **Key order** follows the property declaration order of the OPF schema at every depth (through `$ref`, `oneOf`, `anyOf`, `allOf` and `then`/`else`): `$schema`, `name`, `description`, ..., `design`, `variables`, `narrative`, `slides`, `assets`, `catalogs`, `extensions` at the root, `id`, `layout`, `title`, ... in a slide. Keys the schema does not declare (extensions, catalog records, custom colours, unknown fields) follow the declared ones in their original relative order, so formatting never reorders user data that has no canonical order. Array order and every value are unchanged.\n- **Layout** is two-space indentation (`indent` 0 to 8), LF line endings (`eol: "crlf"` for Windows checkouts that use `core.autocrlf`), no BOM, one trailing newline. Formatting is idempotent.\n- It does not validate: any JSON document can be formatted, and formatting never changes validity.\n\nCLI: `opf format <file|-> [output|-]` or `opf format <file>... (-i | --check)`, with `[--indent <0-8>] [--eol lf|crlf|preserve]`. One file with no output prints the canonical text to stdout; an output must name the same form (`opf convert` changes forms). `--check` reports `checked`, `formatted` and `unformatted` and exits 1 if any file would change (use it in CI); `-i` rewrites only changed files atomically; several files need `--check` or `-i`. `--eol preserve` keeps each file\'s own line endings.\n\n## What this does not do\n\n- It is structural, not visual: a diff of two decks says nothing about whether either composes, overflows or exports well.\n- Merge treats text as a value: two edits to different words of the same string are a conflict, not a text merge.\n- Similarity matching is a heuristic with a documented threshold. When it matters, give slides stable ids (`assignIds`); ids are always trusted over content.\n- `touches` and the hash do not transform or rebase a patch over another: that is a later item of the cloud-editor program. They give a host what it needs to detect that a rebase is required.\n'
|
|
272
274
|
},
|
|
273
275
|
{
|
|
274
276
|
"slug": "pr-backlog-2026-09-09",
|
|
@@ -298,7 +300,7 @@ var docsData = Object.freeze([
|
|
|
298
300
|
"slug": "schema-reference",
|
|
299
301
|
"file": "docs/schema-reference.md",
|
|
300
302
|
"title": "OPF Presentation Schema Reference",
|
|
301
|
-
"markdown": "# OPF Presentation Schema Reference\n\nThis reference documents the author-facing shape of a complete `*.opf.json` presentation document. It summarizes the canonical schema in `spec/schemas/opf.schema.json`; the schema remains the source of truth for validators.\n\n## Document Contract\n\n- Schema id: `https://openpresentation.org/schema/opf/v1`\n- Required top-level fields: `slides`\n- Additional top-level fields: not allowed\n\n## Top-Level Fields\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `$schema` | no | `const:\"https://openpresentation.org/schema/opf/v1\"` | Optional OPF schema version. When omitted, validators and engines should assume the latest supported OPF schema. |\n| `name` | no | `string` | Display name of the presentation for GUI/TUI lists, library/search indexing, OS-level metadata, and default export filenames. This is deck identity, not slide content. Use slides[].title and slides[].subtitle for text... |\n| `description` | no | `string` | Free-form prose describing what this presentation is about. Used by agents and humans as a deck-level summary; complements purpose (the goal) and narrative (the structured storyline). Round-trips to OOXML 'docProps/co... |\n| `filename` | no | `string` | Optional base filename for exports (without extension). The opf CLI (convert) and the editor strip a trailing .pptx, .pdf, .png, or .svg (case-insensitive) and append the target format's extension. When omitted, they... |\n| `organization` | no | `oneOf:ref:Organization / array<ref:Organization>` | Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) su... |\n| `speaker` | no | `oneOf:ref:Speaker / array<ref:Speaker>` | Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' read it, 'speakers' lists every name, and 'speaker.<id... |\n| `author` | no | `oneOf:string / array<string>` | Optional credit for the person who authored or contributed to the deck, distinct from speaker. Array form supports multiple contributors. Round-trips to OOXML 'docProps/core.xml' as '<dc:creator>' (semicolon-joined wh... |\n| `audience` | no | `oneOf:string / ref:Audience / array<oneOf:string / ref:Audience>` | Intended audiences for the presentation. Accepts any of: - A single string: an audiences reference ('executive', or 'acme:board' for a record of the catalogs.acme group), or free-form text ('Series B investors'). A st... |\n| `purpose` | no | `oneOf:string / ref:Purpose` | Primary goal of the presentation. Accepts either: - A string: a purposes reference ('decide', or 'acme:fundraise' for a record of the catalogs.acme group), or a free-form goal ('Raise a Series B round of $30M'). A str... |\n| `language` | no | `oneOf:ref:LanguageTag / ref:Language` | Language of the presentation content, an engine vocabulary: a BCP-47 language tag, validated directly with no catalog lookup. Accepts either: - A BCP-47 tag ('en-US', 'en-GB', 'ja', 'zh-Hans', 'ar-SA'). - An inline La... |\n| `tone` | no | `oneOf:ref:CatalogReference / ref:Tone` | Desired tone for the presentation. Accepts either: - A tones reference: a bare id ('formal') or 'name:id' for a record of a named catalogs group. - An inline Tone object for custom tone metadata or overrides on a refe... |\n| `takeaway` | no | `oneOf:string / array<string>` | Audience-facing takeaway the presentation should leave behind. Array form supports multiple takeaways. Deck-level intent used by AI to seed and pressure-test slide content. |\n| `duration` | no | `integer` | Target presentation duration, as an integer number of minutes. The opf-render presenter view counts the elapsed time against it, and core validate warns when it lies outside the resolved narrative's 'duration' range.... |\n| `tags` | no | `array<string>` | Free-form labels used for categorization, search, and filtering. Lowercase kebab-case is recommended for consistency across a deck library. |\n| `design` | no | `ref:Design` | Optional design system covering theme, color scheme, font scheme, dimensions, background, logo, watermark, header, and footer applied to the deck. When omitted, engines use their default design configuration. |\n| `variables` | no | `ref:Variables` | Optional named variables: deck colors referenced as 'var:<id>' (the original use), and typed content variables (text, number, date, image, url, list) referenced inline as '{{<id>}}' or whole as 'var:<id>'. Variables a... |\n| `template` | no | `boolean` | Marks this document as a template: an incomplete OPF file. A template declares variables (top-level 'variables') and references them from content, and may leave required variables unfilled; validation then reports the... |\n| `narrative` | no | `ref:CatalogReference` | The deck's narrative plan, by reference: a bare narratives id ('classic-story') or 'name:id' for a record of a named catalogs group ('acme:founder-pitch'). The deck holds only this pointer; the plan (the arc, its beat... |\n| `slides` | yes | `array<ref:Slide>` | Ordered array of slides that make up the presentation. |\n| `references` | no | `array<ref:Reference>` | Sources that text runs cite with 'cite'. Ids are unique. A cited reference is listed in the footnote area of every slide that cites it, with a marker number assigned per deck in order of first use; a reference no run... |\n| `datasets` | no | `ref:Datasets` | Optional shared data tables, keyed by id. A chart ('chart.data': { \"dataset\": \"<id>\" }) or a table ('table': { \"dataset\": \"<id>\" }) references one instead of holding its own copy; engines inline the reference before c... |\n| `assets` | no | `ref:Assets` | Optional reusable asset registry for images, data files, videos, documents, fonts, and other resources referenced elsewhere in the deck via 'asset:<id>' strings. |\n| `catalogs` | no | `ref:Catalogs` | The catalog records this document embeds, grouped by the catalog they came from: 'default' (the catalog bare ids come from), 'custom' (records the document defines itself) and any number of named catalogs ('acme', ref... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows; ignored by the engine but preserved across read/write round-trips. |\n\n## Object And Type Reference\n\n### Assets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Reusable asset registry for resources used by slides, charts, metadata, and design. Keys are stable asset ids referenced elsewhere as 'asset:<id>'. Each asset can be a source string or an object with src plus optional metadata.\n\n_No named properties._\n\n\n### Asset\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: Reusable or inline resource. A string is shorthand for { \"src\": value }. Source strings accept 'asset:<id>' references, HTTPS URLs, data URIs, relative paths resolved against the OPF file location, or local filesystem paths. Use object form when metadata such as alt text, title or mediaType matters.\n\n_No named properties._\n\n\n### Audience\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline audience metadata for the presentation. Use 'id' to reference an audiences record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline audience.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional audiences reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable audience name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the audience. |\n| `description` | no | `string` | Longer prose describing the audience and how to address them. |\n| `seniority` | no | `enum:ic \\| manager \\| director \\| vp \\| c-suite \\| mixed` | Typical seniority level of the audience. |\n| `technicalFluency` | no | `enum:low \\| medium \\| high \\| mixed` | Typical technical fluency of the audience. |\n| `decisionPower` | no | `enum:informational \\| advisory \\| decision-maker` | Whether the audience is expected to be informed, advise, or decide. |\n| `attentionBudgetMinutes` | no | `number` | Realistic upper bound on focused attention for a single presentation, in minutes. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Purpose\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline purpose metadata for the presentation. Use 'id' to reference a purposes record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline purpose.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional purposes reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable purpose name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the purpose. |\n| `description` | no | `string` | Longer prose describing when to use this purpose and how it should shape a deck. |\n| `outcome` | no | `string` | Desired audience outcome after the presentation. |\n| `successCriteria` | no | `array<string>` | Observable signals that the deck accomplished this purpose. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Language\n\n- Type: `object`\n- Required fields: `bcp47`\n- Purpose: Inline language metadata for the presentation: a BCP-47 'bcp47' tag plus overrides of what engines know about it. Languages are an engine vocabulary, not catalog records, so there is no 'id'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | no | `string` | Human-readable language name. |\n| `bcp47` | yes | `ref:LanguageTag` | BCP-47 language tag used for locale-aware rendering, proofing, and accessibility metadata. Use 'en-GB' for UK English; 'en-UK' is not a valid BCP-47 region form. |\n| `ooxmlLang` | no | `string` | Culture tag for OOXML text-run language attributes (a:rPr/@lang, a:endParaRPr/@lang), in the language-[Script-]REGION form Office recognizes (e.g. 'ja-JP', 'ar-SA', 'ms-MY', 'nb-NO', 'fil-PH', 'zh-CN'). Without it, en... |\n| `code` | no | `string` | ISO 639-3 or 639-2 language code carried for engines that prefer ISO codes. |\n| `direction` | no | `enum:ltr \\| rtl` | Base text direction for the language. When omitted, engines derive it from the script: Arabic (Arab), Hebrew (Hebr), Syriac (Syrc), Thaana (Thaa), N'Ko (Nkoo), Adlam (Adlm), Samaritan (Samr), Mandaic (Mand) and Hanifi... |\n| `script` | no | `string` | ISO 15924 script code of the language's writing system. The script selects the OOXML font slot the language's text uses: East Asian scripts (Hans, Hant, Hani, Jpan, Kore, Hang, Hira, Kana, Bopo, Yiii) use the eastAsia... |\n| `fontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') for this language's script slot when targeting PowerPoint output. Its major/minor families fill the language's script slot (eastAsian or complexScript, chosen by 'script'... |\n| `googleFontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') used in place of 'fontScheme' when resolving script fonts for Google Slides output. |\n| `summary` | no | `string` | One-sentence note about coverage or font defaults. |\n| `description` | no | `string` | Longer prose describing the language record and any font-pairing rationale. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Tone\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline tone metadata for the presentation. Use 'id' to reference a tones record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline tone.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional tones reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable tone name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the tone. |\n| `description` | no | `string` | Longer prose describing the tone and the kinds of decks it suits. |\n| `voiceCues` | no | `array<string>` | Short directives that shape AI generation toward this tone. |\n| `avoid` | no | `array<string>` | Anti-patterns that AI generation should not produce when this tone is active. |\n| `samplePhrases` | no | `array<string>` | Short example phrases that exemplify this tone. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids this tone pairs well with, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Organization\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the cover and section logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'organization.<id>.<field>'), and every logo a slide-scoped image ('var:organization.logo.icon', 'var:organization...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the organization, used to reference it from Speaker.organizationId and to address it in built-in variables as 'organization.<id>.<field>'. Must be unique among organizations (a duplicate is a val... |\n| `name` | yes | `string` | Display name. The built-in variable 'organization.name' (primary organization) or 'organization.<id>.name' carries it into any string, such as a header or footer text of '{{organization.name}}'. |\n| `legalName` | no | `string` | Optional legal entity name when it differs from the display name. Never drawn automatically; available as the built-in variable 'organization.legalName'. |\n| `logo` | no | `oneOf:ref:Asset / ref:OrganizationLogo` | The organization's logo. A path, URL or Asset is one logo for every shape; an object gives up to four shapes (full, stacked, icon, wordmark), each a path or Asset, or { onLight, onDark } for the two backgrounds. A pat... |\n| `domain` | no | `string` | Bare internet domain for the organization. Never drawn automatically and not used to look up assets; available as the built-in variable 'organization.domain'. |\n| `email` | no | `string` | General contact email for the organization. Never drawn automatically; available as the built-in variable 'organization.email'. |\n| `phone` | no | `string` | Main contact phone number for the organization. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'organization.phone'. |\n| `tagline` | no | `string` | Short tagline. Never drawn automatically (cover slides do not show it); available as the built-in variable 'organization.tagline', for example in a footer 'text' of '{{organization.tagline}}'. |\n| `role` | no | `enum:primary \\| partner \\| client \\| sponsor \\| host` | Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (cover and section logo, the 'socials' header/footer field, and the 'organization.<field>' built-... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the organization. The primary organization's socials render in header/footer zones that set socials: true; otherwise they are authoring metadata. |\n\n\n### Speaker\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, a header or footer text included, and 'var:speaker.photo' as a whole image field). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the speaker. Must be unique among speakers (a duplicate is a validation error). Addresses this speaker in built-in variables as 'speaker.<id>.<field>', such as '{{speaker.alice.title}}'. |\n| `name` | yes | `string` | Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; every name is listed by 'speakers'. |\n| `title` | no | `string` | Role or title. Built-in variable 'speaker.title' (or 'speaker.<id>.title'), for example in a footer text of '{{speaker.name}}, {{speaker.title}}'. |\n| `photo` | no | `ref:Asset` | Source for the speaker's headshot image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are JPG or PNG; SVG is not appropr... |\n| `email` | no | `string` | Contact email. Never drawn automatically; available as the built-in variable 'speaker.email'. |\n| `phone` | no | `string` | Contact phone number for the speaker. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'speaker.phone'. |\n| `bio` | no | `string` | Short biographical paragraph. Never drawn automatically; available as the built-in variable 'speaker.bio', for a bio or 'about the speaker' slide you write. |\n| `organizationId` | no | `string` | Reference to an Organization.id in organization; it must name an existing organization (a validation error otherwise). Attribution metadata for hosts: no engine draws it or changes which organization is primary becaus... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the speaker. Authoring metadata only: nothing draws speaker socials and there is no built-in variable for them (the 'socials' header/footer field draws the primary organizatio... |\n\n\n### Socials\n\n- Type: `object`\n- Required fields: none\n- Purpose: Social media handles or URLs, keyed by platform. The keys are an engine vocabulary validated directly (no catalog lookup): engines link a handle with the platform's URL pattern and handle prefix (spec/reference/engine-vocabularies.json). Each value is a string, either a full URL or a platform handle (e.g., '@acme'). Brand colours and icons are catalog display metadata for authoring UIs; engines render the profile URL, not icons or brand colours. Used both for organization and speaker records.\n\n_No named properties._\n\n\n### Design\n\n- Type: `object`\n- Required fields: none\n- Purpose: Visual design system applied to the presentation; individual slides may override fields via Slide.design.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `theme` | no | `ref:CatalogReference` | Theme for the deck: a themes reference, a bare id ('minimal') or 'name:id' ('acme:brand'). The theme supplies the default colour scheme, font scheme, background and dimensions; design.colorScheme, design.fontScheme, d... |\n| `colorScheme` | no | `oneOf:string / ref:ColorScheme` | Color scheme for the presentation. Accepts two forms: - A colorSchemes reference: a bare id ('cool-horizon') or 'name:id' ('acme:ocean'). - Object form: a ColorScheme with an optional 'id' reference as the base plus s... |\n| `fontScheme` | no | `oneOf:string / ref:FontScheme` | Font scheme for heading, body, accent, and code text. Accepts two forms: - A fontSchemes reference: a bare id ('aptos') or 'name:id' ('acme:inter'). - Object form: a FontScheme with an optional 'id' reference as the b... |\n| `dimensions` | no | `oneOf:ref:DimensionPreset / ref:Dimensions` | Slide dimensions and aspect ratio. String shorthand such as 'widescreen' is equivalent to { preset: 'widescreen' }. |\n| `background` | no | `oneOf:ref:BackgroundShortcut / ref:Background` | Default slide background applied across the deck unless overridden on a slide (slides.N.design.background); the resolved theme's background is the fallback. A background is the only canvas fill and never moves content... |\n| `variant` | no | `oneOf:ref:ThemeBackgroundSlot / ref:ColorVariant` | Optional color variant, as a background slot ('light1', 'light2', 'dark1', 'dark2'; the same as { background: slot }) or the object form, which is needed only for an accent order: which slot of the color scheme is the... |\n| `logo` | no | `oneOf:string / const:false` | Which logo covers, section dividers and picture bullets draw. Logos live on the organization (Organization.logo); without this field covers and sections draw the primary organization's full logo and picture bullets it... |\n| `watermark` | no | `oneOf:const:false / ref:Asset / ref:Watermark` | Optional decorative watermark applied across slides: an image or a text stamp, in the fixed frame and at the opacity described on Watermark. Use false to suppress an inherited watermark in slide-level design; a string... |\n| `header` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated header furniture rendered outside the main slide content. Use false to suppress an inherited header. |\n| `footer` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated footer furniture rendered outside the main slide content. Use false to suppress an inherited footer. |\n| `titleAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for the title heading area (the title and the tag) in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.titleAlignment, then the layout... |\n| `contentAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for body/content regions in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.contentAlignment, then the layout record's design.content... |\n| `contentBox` | no | `boolean` | Whether body/content regions are rendered inside a visible card or surface. Effective value: the slide's design, then the deck's design, then the theme's design.contentBox, then the layout record's design.contentBox,... |\n| `contentDirection` | no | `enum:horizontal \\| vertical` | Axis along which parallel body content is arranged: 'vertical' is a column, 'horizontal' a row. Effective value: the slide's design, then the deck's design, then the theme's design.contentDirection, then the layout re... |\n| `chartPrimary` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's design.chartPrimary. It applies to automatic slides only; a template layout says t... |\n| `imageFit` | no | `enum:cover \\| contain \\| stretch` | Default fit of image blocks (and Slide.image) that set no fit of their own: 'cover' fills the frame and crops what overflows around the block's focus, 'contain' shows the whole picture centered in the frame, 'stretch'... |\n| `listBullet` | no | `enum:character \\| image` | Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the theme's design.listBullet, then the layout record's design.listBullet, then 'character'. 'character' (the... |\n| `mirror` | no | `boolean` | Draw the slide's template layout mirrored (OPF 0.19): each row's cells and the column sizes reversed, so a 'beside' layout puts its picture, chart or table at the end. Logical: a right-to-left deck mirrors on top. Bin... |\n\n\n### ColorScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Color palette used by the design system. Every slot and role is a hex color ('#RGB', '#RRGGBB' or '#RRGGBBAA'); a color-scheme name or a 'var:<id>' reference is not valid here, because those resolve through the scheme. The slot fields (accent1-accent6, dark1, dark2, light1, light2, hyperlink, followedHyperlink) mirror color-scheme.schema.json (https://openpresentation.org/schema/opf-color-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Colour-scheme reference (a bare id or 'name:id') resolved as the base; slot and role overrides on the surrounding ColorScheme object take precedence over the resolved scheme. |\n| `accent1` | no | `ref:HexColor` | Accent 1 color (hex). Mirrors the OOXML accent1 slot. |\n| `accent2` | no | `ref:HexColor` | Accent 2 color (hex). Mirrors the OOXML accent2 slot. |\n| `accent3` | no | `ref:HexColor` | Accent 3 color (hex). Mirrors the OOXML accent3 slot. |\n| `accent4` | no | `ref:HexColor` | Accent 4 color (hex). Mirrors the OOXML accent4 slot. |\n| `accent5` | no | `ref:HexColor` | Accent 5 color (hex). Mirrors the OOXML accent5 slot. |\n| `accent6` | no | `ref:HexColor` | Accent 6 color (hex). Mirrors the OOXML accent6 slot. |\n| `dark1` | no | `ref:HexColor` | Dark 1 color (hex). Typically the deepest neutral; OOXML dark1. |\n| `dark2` | no | `ref:HexColor` | Dark 2 color (hex). Secondary dark; OOXML dark2. |\n| `light1` | no | `ref:HexColor` | Light 1 color (hex). Typically the slide canvas; OOXML lt1. |\n| `light2` | no | `ref:HexColor` | Light 2 color (hex). Secondary light surface; OOXML lt2. |\n| `hyperlink` | no | `ref:HexColor` | Hyperlink color (hex). OOXML hlink. Link runs with no color of their own are drawn underlined in it, in the preview and in PowerPoint, unless it has under 4.5:1 contrast against the slide background, then in the slide... |\n| `followedHyperlink` | no | `ref:HexColor` | Followed-hyperlink color (hex). OOXML folHlink. |\n| `primary` | no | `ref:HexColor` | Abstract role: primary brand color (hex). The engine maps this onto an OOXML accent slot when serializing. |\n| `secondary` | no | `ref:HexColor` | Abstract role: secondary brand color (hex). |\n| `accent` | no | `ref:HexColor` | Abstract role: accent color used for highlights and emphasis (hex). |\n| `background` | no | `ref:HexColor` | Abstract role: default slide background color (hex), used when the design names no single-color background (and for gradient and picture backgrounds). Overrides light1 as that default. |\n| `surface` | no | `ref:HexColor` | Abstract role: color for elevated surfaces such as cards and panels (hex). Overrides the default of light2 (dark2 on a dark slide). |\n| `text` | no | `ref:HexColor` | Abstract role: primary body text color (hex). Overrides dark1 on a light slide only; a dark slide always uses light1. |\n| `textSecondary` | no | `ref:HexColor` | Abstract role: secondary or muted text color used for captions and supporting copy (hex). Overrides the default of dark2 (light2 on a dark slide). |\n\n\n### FontScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Typography selections used by the design system. The pair fields (major, minor) and refinement fields (type, app, languageFamily) mirror font-scheme.schema.json (https://openpresentation.org/schema/opf-font-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel models are supported and may be mixed: - OOXML pairs (major, minor) - heading and body family names that round-trip directly to PowerPoint majorFont/minorFont entries. - Abstract roles...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') resolved as the base; field overrides on the surrounding FontScheme object take precedence over the resolved scheme. |\n| `major` | no | `string` | Heading (major) font family mirrors the OOXML majorFont entry. Pairs with 'minor'. |\n| `minor` | no | `string` | Body (minor) font family mirrors the OOXML minorFont entry. Pairs with 'major'. |\n| `eastAsian` | no | `object` | East Asian script fonts. Maps to the OOXML a:ea element of majorFont (major) and minorFont (minor), and to run-level a:ea. When set, they fill the eastAsian slot for every language; when omitted, the slot comes from t... |\n| `complexScript` | no | `object` | Complex-script fonts (for example Arabic, Hebrew, Indic and Thai). Maps to the OOXML a:cs element of majorFont (major) and minorFont (minor), and to run-level a:cs. When set, they fill the complexScript slot for every... |\n| `type` | no | `enum:sans-serif \\| serif \\| monospace` | High-level typographic class of the scheme. |\n| `app` | no | `enum:powerpoint \\| google-slides` | Target application this font pairing is intended for. Metadata for pickers and catalog filters: no engine changes its output by it. |\n| `languageFamily` | no | `enum:latin \\| ea \\| cs \\| eastAsian \\| complexScript` | Font-language family this scheme is intended for: 'latin' for Latin-script content, 'ea' (or 'eastAsian', the same value) for East Asian scripts, 'cs' (or 'complexScript', the same value) for Complex Scripts. The long... |\n| `heading` | no | `string` | Abstract role: font family name used for slide titles and headings. Maps onto the OOXML major slot when serializing. |\n| `body` | no | `string` | Abstract role: font family name used for body copy. Maps onto the OOXML minor slot when serializing. |\n| `accent` | no | `string` | Abstract role: font family name used for accent text. When set, the slide tag (eyebrow) and the quote body use this family instead of the body and heading families; nothing else changes. resolveFontFamilies() returns... |\n| `code` | no | `string` | Abstract role: monospaced font family name used for code blocks and for inline code runs (TextRun.code). No direct OOXML slot. Resolution: this override, then the resolved font-scheme record's 'code' (for example Cons... |\n\n\n### SlideDesign\n\n- Type: `allOf:ref:Design + schema`\n- Required fields: none\n- Purpose: A slide's design: every Design field except dimensions. A PPTX has one slide size, so the size is set once, on the deck's design.dimensions (or its theme), and a slide's design cannot set it. A slide-level theme whose resolved dimensions differ from the deck's is a validate warning (opf/slide-theme-dimensions); exporting such a deck to PPTX fails with mixed-slide-dimensions.\n\n_No named properties._\n\n\n### DimensionPreset\n\n- Type: `enum:16:9 | 4:3 | 16:10 | 1:1 | 4:5 | 9:16 | letter | a4 | widescreen | standard`\n- Required fields: none\n- Purpose: Named preset; sets aspect ratio and physical size. 16:9 ('widescreen'): 13.333 x 7.5 in, PowerPoint's Widescreen. 4:3 ('standard'): 10 x 7.5 in. 16:10: 10 x 6.25 in. letter (11 x 8.5 in) and a4 (11.69 x 8.27 in) are paper sizes, not PowerPoint's Letter Paper or A4 Paper. 1:1, 4:5 and 9:16 are 7.5 in on the short edge.\n\n_No named properties._\n\n\n### Dimensions\n\n- Type: `object`\n- Required fields: none\n- Purpose: Slide dimensions: a preset, or custom inches that override it. PowerPoint's Letter Paper (10 x 7.5 in), A4 Paper (10.8333 x 7.5 in) and On-screen Show (16:9) (10 x 5.625 in) are not OPF presets; use widthInches and heightInches. docs/slide-sizes.md compares every preset.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `preset` | no | `ref:DimensionPreset` | |\n| `widthInches` | no | `number` | Custom slide width in inches; overrides the preset width when provided. |\n| `heightInches` | no | `number` | Custom slide height in inches; overrides the preset height when provided. |\n\n\n### ThemeBackgroundSlot\n\n- Type: `enum:light1 | light2 | dark1 | dark2`\n- Required fields: none\n- Purpose: PowerPoint theme-controlled slide background slot from the active color scheme. These are slots, not assumptions about actual colors: light1 is usually white and dark1 is usually black by convention, but the color scheme controls the real values.\n\n_No named properties._\n\n\n### HexColor\n\n- Type: `string`\n- Required fields: none\n- Purpose: Hex color shorthand accepted by selected string fields.\n\n_No named properties._\n\n\n### ColorRef\n\n- Type: `anyOf:ref:HexColor / enum:accent1 | accent2 | accent3 | accent4 | accent5 | accent6 | dark1 | dark2 | light1 | light2 | hyperlink | followedHyperlink | primary | secondary | accent | background | surface | surfaceAlt | text | textSecondary / string`\n- Required fields: none\n- Purpose: A color value or reference, enforced on styled table cell fill and text colors, cell border colors, solid background colors, gradient stop colors and pattern foreground and background colors. Three forms: - Literal hex: '#RGB', '#RRGGBB', or '#RRGGBBAA'. - Color-scheme name, resolved through the effective color scheme after design resolution: an OOXML slot ('accent1'-'accent6', 'dark1', 'dark2', 'light1', 'light2', 'hyperlink', 'followedHyperlink') or an abstract role ('primary', 'secondary',...\n\n_No named properties._\n\n\n### Variables\n\n- Type: `object`\n- Required fields: none\n- Purpose: Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: ex...\n\n_No named properties._\n\n\n### Variable\n\n- Type: `oneOf:ref:HexColor / ref:ColorVariable / ref:TextVariable / ref:NumberVariable / ref:DateVariable / ref:ImageVariable / ref:UrlVariable / ref:ListVariable`\n- Required fields: none\n- Purpose: A single named variable: a hex string (shorthand for a color variable) or an object whose 'type' is color, text, number, date, image, url or list. 'value' is the current value and is optional: a variable with no value is unfilled, which a template allows and a normal deck does not. 'example' only illustrates the slot (fill forms, template previews) and never reaches output.\n\n_No named properties._\n\n\n### ColorVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A named color. Content color fields reference it as 'var:<id>'. Colors resolve at render time through the ordinary color-reference path, so a color variable keeps working as before.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"color\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:HexColor` | Hex color this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:HexColor` | Illustrative color shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### TextVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: Text content. Plain string or rich TextRun[]. Insert it inline as '{{<id>}}' inside any string (rich text is flattened to plain text there), or reference it whole as 'var:<id>' in a field that accepts string or TextRun[] (the rich runs are kept).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"text\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `oneOf:string / array<ref:TextRun>` | Text this variable resolves to: a plain string, or TextRun[] for rich text. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `oneOf:string / array<ref:TextRun>` | Illustrative text shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### NumberVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A number. '{{<id>}}' inserts it as text using 'format'; 'var:<id>' as a whole field supplies the number itself (chart values, font sizes).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"number\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `number` | Number this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `number` | Illustrative number shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `ref:NumberFormat` | Display pattern used by '{{<id>}}' (the shared NumberFormat syntax). A literal prefix, a numeric part of '#', '0', ',' and '.', and a literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits afte... |\n\n\n### NumberFormat\n\n- Type: `string`\n- Required fields: none\n- Purpose: Number display pattern, shared by NumberVariable.format, data column formats (DataColumn.format) and table cell formats (StyledTableCell.format). An optional literal prefix, a numeric part of '#', '0', ',' and '.', and an optional literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the prefix or suffix multiplies the value by 100. English separators only. A format applies only to number values; st...\n\n_No named properties._\n\n\n### DateVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A calendar date as an ISO YYYY-MM-DD string. No time zone and no clock are involved. '{{<id>}}' inserts it formatted with 'format'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"date\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | ISO calendar date (YYYY-MM-DD) this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative ISO date shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `string` | Date display pattern, the same LDML-style tokens as header/footer dateFormat: yyyy, yy, MMMM, MMM, MM, M, dd, d, EEEE, EEE and quoted literals. English names. Default 'MMMM d, yyyy'. |\n\n\n### ImageVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: An image source: any Asset (an 'asset:<id>' reference, HTTPS URL, data URI, relative or local path, or an object with src, alt and metadata). Reference it whole as 'var:<id>' in an image, asset or src field; '{{<id>}}' inserts the source string.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"image\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:Asset` | Image source this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:Asset` | Illustrative image source shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### UrlVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A link target (http, https, mailto or tel). Use it as '{{<id>}}' inside a link string or reference it whole as 'var:<id>' in a link field.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"url\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | Link target this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative link shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### ListVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A list of strings, for bullets and list items. A whole-string array element 'var:<id>' splices every entry into the array in place; a whole field 'var:<id>' becomes the array; '{{<id>}}' joins the entries with ', ' (or the separator after a pipe: '{{<id>|; }}').\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"list\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `array<string>` | Entries this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `array<string>` | Illustrative entries shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### BackgroundShortcut\n\n- Type: `oneOf:ref:ThemeBackgroundSlot / ref:HexColor / ref:ImageSource`\n- Required fields: none\n- Purpose: String shorthand for a background. Theme slots ('light1', 'light2', 'dark1', 'dark2') are equivalent to { type: 'theme', slot: value }; hex colors are equivalent to { type: 'solid', color: value }; an image source (starting with 'asset:', 'https://', 'data:', './' or '../') is equivalent to { type: 'image', src: value }, a cover image. Any other string is invalid.\n\n_No named properties._\n\n\n### ImageSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: An image source in a string shorthand: an 'asset:<id>' reference, an HTTPS URL, a data URI, or a relative path starting with './' or '../' (resolved against the OPF file location).\n\n_No named properties._\n\n\n### ColorVariant\n\n- Type: `object`\n- Required fields: `background`\n- Purpose: A variant of the deck's single color scheme, as a mapping over its twelve slots (PowerPoint's clrMap): 'background' is the slot the slide background and the role bg1 take, and with it the other roles: light1 maps bg1 = light1, tx1 = dark1, bg2 = light2, tx2 = dark2; dark1 maps bg1 = dark1, tx1 = light1, bg2 = dark2, tx2 = light2; light2 and dark2 make that slot both the background and the alternate surface (light2: bg1 = bg2 = light2, tx1 = dark1, tx2 = dark2; dark2: bg1 = bg2 = dark2, tx1 =...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `background` | yes | `ref:ThemeBackgroundSlot` | The scheme slot that is the background (bg1) of the variant. |\n| `accents` | no | `array<enum:accent1 \\| accent2 \\| accent3 \\| accent4 \\| accent5 \\| accent6>` | Optional accent order: a permutation of accent1 to accent6. Entry i is the scheme slot drawn as accent i + 1, so primary, secondary and accent (the first three) and the chart series colors follow it. Default: the sche... |\n\n\n### Background\n\n- Type: `oneOf:ref:ThemeBackground / ref:SolidBackground / ref:GradientBackground / ref:ImageBackground / ref:PatternBackground`\n- Required fields: none\n- Purpose: Background fill applied to slides. Theme backgrounds preserve PowerPoint's color-scheme background choice; other variants represent fixed background fills.\n\n_No named properties._\n\n\n### ThemeBackground\n\n- Type: `object`\n- Required fields: `type`, `slot`\n- Purpose: Theme-controlled PowerPoint slide background. The slot is resolved through the active color scheme and remains theme-aware.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"theme\"` | Theme-controlled background fill. |\n| `slot` | yes | `ref:ThemeBackgroundSlot` | |\n\n\n### SolidBackground\n\n- Type: `object`\n- Required fields: `type`, `color`\n- Purpose: Fixed solid slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"solid\"` | Fixed solid background fill. |\n| `color` | yes | `ref:ColorRef` | Fixed solid fill color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef, resolved against the effective color scheme and the deck variables). Use { type: 'theme', slot: ...... |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### GradientBackground\n\n- Type: `object`\n- Required fields: `type`, `gradient`\n- Purpose: Fixed gradient slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `tone` | no | `enum:dark \\| light` | Whether the background is dark or light, which picks the color map (dark1 or light1) so text, lines and neutrals read on it. Without a tone a picture or gradient takes the deck's variant, else counts as light. |\n| `type` | yes | `const:\"gradient\"` | Fixed gradient background fill. |\n| `gradient` | yes | `object` | Gradient fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### ImageBackground\n\n- Type: `object`\n- Required fields: `type`, `src`\n- Purpose: Picture background: the image fills the whole canvas behind everything and never moves content (to push content aside, use an image block with placement). The string shorthand 'asset:x' (or any image source) is { type: 'image', src: 'asset:x' }, a cover image. Paint order: the colour scheme's default slide background, the picture, then the overlay, then furniture and content.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `tone` | no | `enum:dark \\| light` | Whether the background is dark or light, which picks the color map (dark1 or light1) so text, lines and neutrals read on it. Without a tone a picture or gradient takes the deck's variant, else counts as light. |\n| `type` | yes | `const:\"image\"` | Picture background. |\n| `src` | yes | `string` | Source for the background image: an 'asset:<id>' reference, HTTPS URL, data URI, or a relative path resolved against the OPF file location. |\n| `alt` | no | `string` | Alternative text. A background with alt is meaningful: PowerPoint export draws it as a full-slide picture at the back that carries the text, so screen readers see it. Without alt it is decorative and exports as the na... |\n| `fit` | no | `enum:cover \\| contain \\| stretch \\| tile` | How the picture fills the canvas: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered over the colour scheme's default background; 'stretch' scales it to the canvas exact... |\n| `focus` | no | `ref:ImageFocus` | Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `opacity` | no | `number` | Picture opacity from 0 (fully transparent) to 1 (fully opaque); the overlay keeps its own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Grayscale or duotone treatment of the picture's pixels, as on an image block: recolor and opacity apply to the pixels, then the overlay is drawn above. |\n| `overlay` | no | `ref:Overlay` | Scrim over the whole picture or a band along one edge (edge, size), beneath furniture and content, for example to keep a title readable over a busy photo. |\n\n\n### ImageFocus\n\n- Type: `object`\n- Required fields: `x`, `y`\n- Purpose: A point of a picture as fractions of its width and height, from the top-left corner. A cover fit centers this point in the frame as far as the picture still covers the frame, so it always stays in view: { x: 0.5, y: 0.5 } (the default) is a center crop, { x: 0, y: 0 } keeps the top-left corner.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `x` | yes | `number` | Horizontal position, 0 (left edge) to 1 (right edge). |\n| `y` | yes | `number` | Vertical position, 0 (top edge) to 1 (bottom edge). |\n\n\n### Overlay\n\n- Type: `object`\n- Required fields: `color`, `opacity`\n- Purpose: Solid scrim over a picture: the whole frame in the frame's shape, or a band along one edge. Shared by image backgrounds and image blocks. Drawn directly above its picture and beneath headings and content; PowerPoint export writes one native shape above the picture.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Overlay fill color: a hex color (eight-digit hex keeps its alpha), a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `opacity` | yes | `number` | Overlay fill opacity. |\n| `edge` | no | `enum:top \\| bottom \\| left \\| right` | Cover only a band along this edge of the frame, for example a caption strip. Backgrounds and rectangle image frames only; on another shape the overlay is not drawn and composition reports unsupported-image-treatment.... |\n| `size` | no | `number` | Band share of the frame for an edge overlay. Default 0.3. Ignored without edge. |\n\n\n### ImagePlacement\n\n- Type: `object`\n- Required fields: `edge`\n- Purpose: Bleeds an image block to one edge of the slide: the image takes a band along that edge, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. Only top-level blocks (slides.N.blocks.I) can be placed, at most one per edge. Template layout regions do not supply image placement; set it on the image block itself. Bands are taken in block order, each spanning the free area left by earlier ones. Placement is the only mechanism that moves content asi...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `edge` | yes | `enum:left \\| right \\| top \\| bottom` | The slide edge the image bleeds to. |\n| `size` | no | `number` | Share of the slide width (left, right) or height (top, bottom) given to the band. Default 0.5. |\n| `inset` | no | `boolean` | Draw the frame inside the slide padding on every side of the band, like a card, instead of edge to edge. Default false. |\n\n\n### ImageBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: Solid line along the image frame's shape, centered on the outline with a miter join, like a native picture line. A thick dark border on a rounded portrait frame gives a device bezel.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Line color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `width` | yes | `number` | Line width in reference pixels at a 720-pixel short edge. 0 removes the line. |\n\n\n### ImageRecolor\n\n- Type: `oneOf:const:\"grayscale\" / object`\n- Required fields: none\n- Purpose: Color treatment for the image pixels. Luminance uses Rec. 601 weights (0.299, 0.587, 0.114) on sRGB values.\n\n_No named properties._\n\n\n### PatternBackground\n\n- Type: `object`\n- Required fields: `type`, `pattern`\n- Purpose: Fixed pattern slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"pattern\"` | Fixed pattern background fill. |\n| `pattern` | yes | `object` | Pattern fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### OrganizationLogo\n\n- Type: `object`\n- Required fields: none\n- Purpose: An organization's logo in up to four shapes. Each shape is a path or Asset, or { onLight, onDark } for the two backgrounds. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon; within a shape a missing onLight or onDark uses the other. Shapes are placed with slide-scoped references such as 'var:organization.logo.icon'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `full` | no | `ref:LogoSource` | The full logo (lockup): mark and name side by side. Drawn on cover and section slides, and placed by 'var:organization.logo'. |\n| `stacked` | no | `ref:LogoSource` | The stacked lockup: mark above the name, for portrait or square slots. Placed by 'var:organization.logo.stacked'. |\n| `icon` | no | `ref:LogoSource` | The mark or symbol without the name, for tight spaces such as header and footer zones and picture bullets. Placed by 'var:organization.logo.icon'. |\n| `wordmark` | no | `ref:LogoSource` | The name set in the brand's lettering, without the mark. Placed by 'var:organization.logo.wordmark'. |\n\n\n### LogoSource\n\n- Type: `oneOf:ref:Asset / object`\n- Required fields: none\n- Purpose: One logo shape: a path or Asset for every background, or { onLight, onDark }. onLight is drawn on light backgrounds (usually dark artwork) and onDark on dark backgrounds (usually light artwork), by each slide's background; a missing one uses the other.\n\n_No named properties._\n\n\n### Watermark\n\n- Type: `oneOf:schema / schema`\n- Required fields: `opacity`\n- Purpose: Decorative watermark: an image ('src') or a text stamp ('text'), exactly one of the two, with its opacity. An image is drawn once per slide, contained and centered in a fixed frame (the middle 40% of the slide width and height, from 30% to 70% on each axis). A text watermark is one line of text in the heading font and the theme text color at the given opacity, centered on the slide and rotated 30 degrees counterclockwise (rising to the right), sized to span at most 70% of the slide width and...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | no | `string` | Source for the watermark image: an 'asset:<id>' reference, HTTPS URL, data URI, relative path or local path, as for Asset.src. Exactly one of src and text is set. |\n| `text` | no | `string` | Text for a text watermark, such as DRAFT or CONFIDENTIAL; line breaks are drawn as spaces. Exactly one of src and text is set. |\n| `opacity` | yes | `number` | Watermark opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### HeaderFooter\n\n- Type: `object`\n- Required fields: none\n- Purpose: Repeated header or footer content split into left, center, and right zones. Header/footer content is slide furniture, separate from the main slide content payloads.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `left` | no | `ref:HeaderFooterItem` | Left-aligned header/footer content. |\n| `center` | no | `ref:HeaderFooterItem` | Centered header/footer content. |\n| `right` | no | `ref:HeaderFooterItem` | Right-aligned header/footer content. |\n\n\n### HeaderFooterItem\n\n- Type: `object`\n- Required fields: none\n- Purpose: One header/footer zone. Every configured field renders, side by side in one row in the order image, text, socials, date (mirrored in a right-to-left deck), aligned to the zone's edge (left, center or right) and vertically centered on each other; a line break inside text or socials ('\\n') stacks lines within that part. When the parts are wider than the zone their text wraps, and a row that cannot fit even so is reported as overflow. Generated values such as the slide number, the slide count, t...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | no | `string` | Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-i... |\n| `image` | no | `ref:Asset` | Image rendered first in this zone's row, such as a logo, partner mark, certification badge, or icon: a path, URL or Asset, or an organization logo reference ('var:organization.logo.icon', 'var:organization.<id>.logo.w... |\n| `date` | no | `oneOf:boolean / string` | true renders the current date: the renderer or exporter must be given an explicit ISO date by its host (core never reads a clock). PPTX export writes a native date field only for a supported dateFormat whose complete... |\n| `dateFormat` | no | `string` | Date pattern for date. Tokens: yyyy (2026), yy (26), MMMM (April), MMM (Apr), MM (04), M (4), dd (09), d (9), EEEE (Thursday), EEE (Thu). Text in single quotes and other non-letter characters are literal. Month and we... |\n| `socials` | no | `boolean` | Whether to render the primary organization's social profiles from organization.socials, one line per platform in key order. A handle is formatted through the platform's socialPlatforms record (companyUrlPattern, else... |\n\n\n### Slide\n\n- Type: `object`\n- Required fields: none\n- Purpose: A single slide. Content can be authored as a full-slide root payload, or inside promoted named region keys such as 'left', 'center+right', and 'top:left'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for the slide within the document. Use when another system needs to reference a slide across edits, comments, generation state, exports, or narrative tooling. Slide order is defined by the s... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline` | Optional full-slide content kind. When omitted, engines infer the kind from root payload fields. |\n| `beat` | no | `oneOf:string / array<string>` | Optional reference to one or more beats of the deck's narrative (each value is the id of a beat in the narrative record that the root 'narrative' resolves to). A single string declares the slide's primary beat; an arr... |\n| `layout` | no | `ref:CatalogReference` | Optional layout: a layouts reference, a bare id ('pillars') or 'name:id' ('acme:hero'), or 'auto'. When omitted or 'auto', the slide is composed with no layout record: engines arrange title, subtitle, tag and content... |\n| `title` | no | `oneOf:string / array<ref:TextRun>` | Slide-level title content. A v2 layout renders this in its title heading area, or an implicit heading row when that area is absent. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold,... |\n| `subtitle` | no | `oneOf:string / array<ref:TextRun>` | Slide-level subtitle or supporting line. A v2 layout renders this in its subtitle area when present, otherwise in the title heading group. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `tag` | no | `oneOf:string / array<ref:TextRun>` | Small slide-level label or badge. A v2 layout renders this before the title in its title heading group; tag is not a separate layout area. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Full-slide text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Full-slide structured list payload. Presence of this field infers type 'list'. Entries are strings, TextRun[] or objects with `text`, optional `description` (supporting detail under the entry), `level` and `start`. Us... |\n| `bullets` | no | `array<ref:BulletItem>` | Full-slide prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries are strings, TextRun[] or objects with `text`,... |\n| `numbering` | no | `ref:NumberingSpec` | Number the full-slide `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one ent... |\n| `image` | no | `ref:Asset` | Shorthand for one image block: the picture composes as body content, like { type: 'image', image: value }, with the effective design.imageFit. A v2 layout binds it to a compatible body region; a region's bleed setting... |\n| `video` | no | `ref:Asset` | Full-slide video source. Presence of this field infers type 'video'. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the... |\n| `chart` | no | `ref:Chart` | Full-slide chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Full-slide table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Full-slide code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Full-slide metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers... |\n| `quote` | no | `oneOf:string / ref:Quote` | Full-slide quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution and source metadata. Presence of this field infers type 'quote'. |\n| `timeline` | no | `ref:Timeline` | Full-slide timeline payload. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata. Presence of this field infers type 'timeline'. |\n| `caption` | no | `ref:Caption` | Caption for the slide's root image, chart, table or video payload. Valid only when the slide root holds exactly one of those payloads. |\n| `blocks` | no | `array<ref:ContentPayload>` | Layout-agnostic content blocks rendered together as a composed payload when exact placement is unspecified. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as short... |\n| `design` | no | `ref:SlideDesign` | Slide-level design applied on top of the deck-wide design. |\n| `left` | no | `ref:ContentPayload` | |\n| `center` | no | `ref:ContentPayload` | |\n| `right` | no | `ref:ContentPayload` | |\n| `left+center` | no | `ref:ContentPayload` | |\n| `center+right` | no | `ref:ContentPayload` | |\n| `left+center+right` | no | `ref:ContentPayload` | |\n| `top` | no | `ref:ContentPayload` | |\n| `middle` | no | `ref:ContentPayload` | |\n| `bottom` | no | `ref:ContentPayload` | |\n| `top+middle` | no | `ref:ContentPayload` | |\n| `middle+bottom` | no | `ref:ContentPayload` | |\n| `top+middle+bottom` | no | `ref:ContentPayload` | |\n| `top:left` | no | `ref:ContentPayload` | |\n| `top:center` | no | `ref:ContentPayload` | |\n| `top:right` | no | `ref:ContentPayload` | |\n| `top:left+center` | no | `ref:ContentPayload` | |\n| `top:center+right` | no | `ref:ContentPayload` | |\n| `top:left+center+right` | no | `ref:ContentPayload` | |\n| `middle:left` | no | `ref:ContentPayload` | |\n| `middle:center` | no | `ref:ContentPayload` | |\n| `middle:right` | no | `ref:ContentPayload` | |\n| `middle:left+center` | no | `ref:ContentPayload` | |\n| `middle:center+right` | no | `ref:ContentPayload` | |\n| `middle:left+center+right` | no | `ref:ContentPayload` | |\n| `bottom:left` | no | `ref:ContentPayload` | |\n| `bottom:center` | no | `ref:ContentPayload` | |\n| `bottom:right` | no | `ref:ContentPayload` | |\n| `bottom:left+center` | no | `ref:ContentPayload` | |\n| `bottom:center+right` | no | `ref:ContentPayload` | |\n| `bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left` | no | `ref:ContentPayload` | |\n| `top+middle:center` | no | `ref:ContentPayload` | |\n| `top+middle:right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center` | no | `ref:ContentPayload` | |\n| `top+middle:center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left` | no | `ref:ContentPayload` | |\n| `middle+bottom:center` | no | `ref:ContentPayload` | |\n| `middle+bottom:right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `notes` | no | `string` | Speaker notes shown in presenter view. |\n| `section` | no | `string` | PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports. Also the slide-scoped built-in variable '{{sli... |\n| `hidden` | no | `boolean` | Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (o... |\n| `composition` | no | `ref:Composition` | |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at slide scope; ignored by the engine but preserved across read/write round-trips. Use for review state, generation provenance, or authoring conventions such as { \"authoring... |\n\n\n### ContentPayload\n\n- Type: `allOf:schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema`\n- Required fields: none\n- Purpose: A content leaf or recursively composed group. A group contains blocks and optional composition; it cannot mix blocks with leaf payload fields. Groups may nest up to 32 levels.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for this payload, unique among slide and payload ids in the document. Use when another system needs to address the payload across edits patch-style agent edits, comments, review state, or ge... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at payload scope; ignored by the engine but preserved across read/write round-trips. |\n| `region` | no | `string` | Pin a root block (slides.N.blocks.I) to a region of the slide's template layout by name (OPF 0.19). The block goes to that region when the region exists, accepts the block's kind and has room; otherwise the pin is ign... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline \\| group` | Optional content kind. When omitted, engines infer the kind from the fields present. |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Structured list payload (type 'list'). Each item is either a plain string, a TextRun[] rich text sequence, or a ListItem object with optional `description`. List nesting uses item.level rather than nested content payl... |\n| `bullets` | no | `array<ref:BulletItem>` | Prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries have no `description`; use `items` for entries with suppo... |\n| `numbering` | no | `ref:NumberingSpec` | Number the payload's `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one entr... |\n| `columns` | no | `oneOf:const:\"auto\" / integer` | Lay the payload's `items` or `bullets` out in columns (OPF 0.19). 'auto' tries 1, 2 then 3 equal columns at the starting text size and uses the first count at which the list fits, as long as a column stays at least a... |\n| `image` | no | `ref:Asset` | Source for an image item. |\n| `fit` | no | `enum:cover \\| contain \\| stretch` | Image payloads only. How the picture fills its frame: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered; 'stretch' scales it to the frame exactly. Default: the effectiv... |\n| `focus` | no | `ref:ImageFocus` | Image payloads only. Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `aspectRatio` | no | `number` | Image payloads only. Width-to-height ratio of the frame: the frame becomes the largest centered box with this ratio inside the block's region, for example 2.39 for a cinematic letterbox or 0.5 for a phone-shaped frame... |\n| `shape` | no | `enum:rectangle \\| rounded \\| circle \\| hexagon` | Image payloads only. Mask applied to the frame, exported as the picture's native preset geometry (rect, roundRect, ellipse, hexagon). 'circle' makes the frame square. Default 'rectangle'. |\n| `cornerRadius` | no | `number` | Image payloads only. Corner radius for shape 'rounded' as a fraction of the frame's shorter side. Default 0.16667, PowerPoint's roundRect default. |\n| `border` | no | `ref:ImageBorder` | Image payloads only. Line along the frame's shape. |\n| `opacity` | no | `number` | Image payloads only. Picture opacity from 0 to 1, exported as a native alphaModFix. The border and overlay keep their own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Image payloads only. Grayscale or duotone treatment of the picture's pixels. |\n| `overlay` | no | `ref:Overlay` | Image payloads only. Scrim over the picture in the frame's shape, or a band along one edge of a rectangle frame. |\n| `placement` | no | `ref:ImagePlacement` | Image payloads only, and only on a top-level block (slides.N.blocks.I), at most one per edge. Bleeds the image to one slide edge; the headings and the other blocks compose in the rest of the slide. |\n| `video` | no | `ref:Asset` | Source for a video item. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the same placeholder as native shapes linked to... |\n| `chart` | no | `ref:Chart` | Chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers format them... |\n| `quote` | no | `oneOf:string / ref:Quote` | Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution, role, photo and source metadata. |\n| `timeline` | no | `ref:Timeline` | Timeline payload ordered by narrative or chronology. |\n| `caption` | no | `ref:Caption` | Caption for an image, chart, table or video payload, composed inside the block's region (below the media by default). Invalid on other payload kinds and on groups. |\n| `blocks` | no | `array<ref:ContentPayload>` | Ordered children of a group. Each child is a leaf or another group. |\n| `composition` | no | `ref:Composition` | Arrangement within this group. Only minFontSize and overflow inherit from the parent; strict overflow cannot be weakened. |\n\n\n### Quote\n\n- Type: `object`\n- Required fields: `text`\n- Purpose: Quote content with optional attribution metadata. Use 'text' for the quoted text, 'attribution' for the credited person or organization, 'role' for that person's title and organization, 'photo' for their headshot and 'source' for a citation or URL. A string value in a quote field is shorthand for { \"text\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | Quoted text: a string, or TextRun[] for inline rich text (a colored phrase, bold, a link, or a citation or footnote marker). The engine draws the surrounding quotation marks. |\n| `attribution` | no | `string` | Person or organization credited for the quote. Drawn after the quote text, in the muted text color. |\n| `role` | no | `string` | Title and organization of the attributed person. Drawn on its own line under the attribution; without an attribution it stands alone. It does not replace the attribution, so the person's name belongs in 'attribution'. |\n| `photo` | no | `ref:Asset` | Headshot of the attributed person, drawn as a circle beside the attribution and role lines (on the logical start side: left in a left-to-right deck, right in a right-to-left deck). Give it alt text (validate's opf/mis... |\n| `source` | no | `string` | Optional quote source, citation, or URL. Follows the attribution and role after ' - '. |\n\n\n### Code\n\n- Type: `object`\n- Required fields: `source`\n- Purpose: Code content with optional rendering metadata. Use 'source' for the code text, 'language' for syntax highlighting, 'filename' when the rendered block should show a file label, and 'highlight' to emphasize lines. A string value in a code field is shorthand for { \"source\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | yes | `string` | Source code text to display. |\n| `language` | no | `string` | Language identifier used for syntax highlighting. |\n| `filename` | no | `string` | Optional file label shown with the code block. |\n| `highlight` | no | `array<oneOf:integer / array<integer>>` | Source lines to emphasize, 1-based: each entry is a line number or an inclusive [start, end] range, so [3, [5, 7]] marks lines 3 and 5 to 7. Lines are counted by line break in 'source', so a line that wraps stays one... |\n\n\n### Metric\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: Metric content with optional display metadata. Use 'value' for the primary value, 'label' for the metric name, 'description' for supporting context, 'unit' for a suffix/currency marker, 'delta' for change, 'trend' for direction, and 'sentiment' for whether the change is good news. A string or number value in a metric field is shorthand for { \"value\": value }; numeric values remain numbers and are formatted by renderers.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `oneOf:string / number` | Primary metric value. |\n| `label` | no | `string` | Metric label. |\n| `description` | no | `string` | Optional supporting context for the metric. |\n| `unit` | no | `string` | Metric unit, suffix, or currency marker. |\n| `delta` | no | `oneOf:string / number` | Metric change value. |\n| `trend` | no | `enum:up \\| down \\| flat` | Metric trend direction. The preview and the PowerPoint export draw an arrow beside the trend word that always points the way the trend does (up, down or flat) and colour the arrow, the trend word and the delta text by... |\n| `sentiment` | no | `enum:positive \\| negative \\| neutral` | Whether the change is good news. Positive draws the trend arrow and the trend and delta text in green, negative in red and neutral in the neutral text colour, each kept at 4.5:1 contrast or more against the slide back... |\n\n\n### Timeline\n\n- Type: `oneOf:array<ref:TimelineEvent> / object`\n- Required fields: none\n- Purpose: Timeline content. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata.\n\n_No named properties._\n\n\n### TimelineEvent\n\n- Type: `object`\n- Required fields: `what`\n- Purpose: A single event inside a timeline content payload.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `when` | no | `string` | Event time, date, or sequence label. Use ISO-like values when possible, but human labels are allowed for quarters, eras, and relative milestones. |\n| `what` | yes | `string` | Short event label. |\n| `description` | no | `string` | Optional event detail. |\n| `status` | no | `enum:done \\| current \\| planned` | Progress of the event, drawn from the deck's own colors by both the SVG renderer and the PPTX exporter. Absent means no status and the event is drawn as a plain filled marker with normal text, which is also how 'done'... |\n\n\n### ListItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat item inside a list. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds description and nesting depth without creating nested slide content payloads.\n\n_No named properties._\n\n\n### BulletItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat bullet item. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds nesting depth without list-item descriptions.\n\n_No named properties._\n\n\n### NumberingStyle\n\n- Type: `enum:arabic | roman-upper | roman-lower | alpha-upper | alpha-lower`\n- Required fields: none\n- Purpose: A list number style: 1, 2, 3; I, II, III; i, ii, iii; A, B, C; a, b, c. Alphabetic numbering past 26 repeats the letter as PowerPoint does (aa, bb, cc). Roman numerals stop at 3999; larger values are drawn in arabic with a numbering-adapted diagnostic.\n\n_No named properties._\n\n\n### Numbering\n\n- Type: `object`\n- Required fields: none\n- Purpose: Numbering of one list level.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `style` | no | `ref:NumberingStyle` | Number style. Default arabic. |\n| `start` | no | `integer` | First number counted at this level. Default 1. Native PowerPoint accepts 1 to 32767. |\n| `suffix` | no | `enum:period \\| paren \\| paren-both` | Text after the number: period (1.), paren (1)) or paren-both ((1)). Default period. |\n\n\n### NumberingSpec\n\n- Type: `oneOf:ref:NumberingStyle / ref:Numbering / array<oneOf:ref:NumberingStyle / ref:Numbering>`\n- Required fields: none\n- Purpose: The value of a numbering field: a style name or Numbering object for every level, or an array with one entry per level (at most 9, the native depth).\n\n_No named properties._\n\n\n### TextRun\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: A contiguous run of text. Strings cover unformatted spans; object form adds character formatting.\n\n_No named properties._\n\n\n### Caption\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A caption for an image, chart, table or video payload. A string or TextRun[] is the caption text placed below the media; object form adds the position and alignment.\n\n_No named properties._\n\n\n### Reference\n\n- Type: `object`\n- Required fields: `id`, `text`\n- Purpose: A source that runs cite with 'cite'. Cited references are listed in the footnote area of the slides that cite them, numbered per deck in order of first use; referencesSlide() builds an ordinary list slide of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Identifier runs cite. Unique within the references list. |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | The reference as it is listed: a string or TextRun[] for inline rich text. |\n| `url` | no | `string` | Optional link for the reference; a references slide links its entry to it. |\n\n\n### Chart\n\n- Type: `object`\n- Required fields: `type`, `data`\n- Purpose: Chart content. The chart object keeps chart-specific fields together so slides and regions do not expose loose chart fields.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `enum:100pct-stacked-area \\| 100pct-stacked-bar \\| 100pct-stacked-column \\| area \\| bar \\| box-and-whisker \\| column \\| combo \\| doughnut \\| filled-radar \\| funnel \\| histogram \\| line \\| line-with-markers \\| pareto \\| pie \\| radar \\| radar-with-markers \\| scatter \\| stacked-area \\| stacked-bar \\| stacked-column \\| stacked-line \\| stacked-line-with-markers \\| treemap \\| waterfall \\| world` | Chart type, an engine vocabulary validated directly (no catalog lookup): the chart types Aspose.Slides officially supports, plus 'combo' (clustered columns with line series, see 'line' and 'secondaryAxis'). Each engin... |\n| `data` | yes | `oneOf:ref:ChartData / ref:DatasetRef` | Chart data. Inline data uses a tabular columns/rows shape; renderers convert rows to chart series internally. A DatasetRef ({ \"dataset\": \"<id>\" }) plots a top-level dataset instead. Data from a spreadsheet or file is... |\n| `mapping` | no | `ref:ChartMapping` | Optional series mapping by column name: which column is the category, which is the X column of a scatter chart, and which columns are plotted, in order. Absent keeps the positional rule (first column the category, the... |\n| `line` | no | `array<string>` | Combo charts only (type 'combo'): the plotted series, by column name, that are drawn as lines with markers; every other plotted series is drawn as clustered columns. Absent: the last plotted series is the line. A comb... |\n| `secondaryAxis` | no | `array<string>` | Combo charts only: line series, by column name, plotted against a secondary value axis at the right of the plot. That axis has its own scale, and its tick labels use the number format of the first secondary series' co... |\n| `axisTitles` | no | `ref:ChartAxisTitles` | Optional axis titles. Absent keeps today's untitled axes. Supported on the chart types that have a category/value (or X/Y) axis pair (column, bar, line, area, scatter, and the histogram, pareto, waterfall and box-and-... |\n| `legend` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Optional legend position. 'none' hides the legend. Absent keeps today's behaviour exactly (a legend at the right of multi-series charts and of pie and doughnut charts, none for single-series charts). A named position... |\n| `dataLabels` | no | `oneOf:boolean / ref:ChartDataLabels` | Optional data labels. true shows value labels at each type's default position, false (or absent) shows none, which is today's behaviour. Use the object form for the label content, position and separator. |\n| `alt` | no | `string` | Text alternative for the chart: what the data shows (the point and the key numbers), not 'a chart' or 'bar chart'. The preview exposes it as the chart's accessible name (role img with aria-label) and the PowerPoint ex... |\n| `colors` | no | `array<ref:ColorRef>` | Optional series colors, by position: entry i replaces palette color i, the color of series i (the i-th plotted value column), of slice or category i on a pie, doughnut, treemap or funnel chart, and of the i-th color a... |\n| `highlight` | no | `ref:ChartHighlight` | Optional emphasis: the series and/or categories that carry the message. The engines draw the highlighted marks in the deck's primary (accent) color and every other mark in a muted neutral derived from the theme (a lig... |\n\n\n### ChartAxisTitles\n\n- Type: `object`\n- Required fields: none\n- Purpose: Titles for the axes of a chart. 'category' is the axis that carries the row labels (the horizontal axis of a column or line chart, the vertical axis of a bar chart, the X axis of a scatter chart); 'value' is the other axis; 'secondary' is the secondary value axis of a combo chart.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | Title of the category (X) axis. |\n| `value` | no | `string` | Title of the value (Y) axis. On a combo chart, the primary (left) value axis. |\n| `secondary` | no | `string` | Combo charts only: title of the secondary value axis at the right of the plot, drawn rotated like the primary value axis title. Dropped with a 'chart-option-adapted' diagnostic on any other chart type and on a combo c... |\n\n\n### ChartHighlight\n\n- Type: `object`\n- Required fields: none\n- Purpose: Which series and categories a chart emphasizes. Names are matched exactly: series by data column name (after any 'fields' selection; a series is a plotted value column), categories by row label (the first column, or mapping.category), as text. A category label shared by several rows highlights each of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `series` | no | `array<string>` | Series (value columns) to highlight: all marks of each named series. A name that is not a column is a 'chart-highlight-unknown-name' error; a column that is not plotted as a series (the category or X column, or one ma... |\n| `categories` | no | `array<string>` | Categories (row labels) to highlight: the marks of each named category (a column or bar of every series, a line's points, a pie or doughnut slice). A label no row has is a 'chart-highlight-unknown-name' error. |\n\n\n### ChartDataLabels\n\n- Type: `object`\n- Required fields: none\n- Purpose: Data label settings. A label shows the selected content parts in the fixed order category, value, percent, joined by the separator.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `content` | no | `array<enum:value \\| percent \\| category>` | Which parts a label shows. Defaults to ['value']. 'percent' is the share of the total and exists only on pie and doughnut charts; 'category' shows the category name (the X value on a scatter chart). A part a chart typ... |\n| `position` | no | `enum:auto \\| center \\| inside-end \\| inside-base \\| outside-end \\| above \\| below \\| left \\| right` | Where a label sits relative to its mark. 'auto' (the default) is the type's default: outside-end for clustered columns and bars, pie slices and the histogram, pareto and waterfall constructs; center for stacked column... |\n| `separator` | no | `string` | Text between the parts of a label that shows more than one. Defaults to ', '. |\n\n\n### Table\n\n- Type: `object`\n- Required fields: none\n- Purpose: Table content, inline or dataset-backed. An inline table has 'rows' (required) and optional 'columns'. A dataset-backed table has 'dataset' (required) and optional 'fields', and no 'rows' or 'columns': it takes its headers, rows and column formats from the dataset (per-cell styles need an inline table).\n- Conditional requirement: `dataset`, or otherwise `rows`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | no | `array<oneOf:string / array<ref:TextRun> / ref:StyledTableCell / ref:DataColumn / null>` | Optional column labels. Labels may be strings, rich runs, styled cell objects or DataColumn objects ({ \"name\", \"format\" }). A header's 'format' (DataColumn or StyledTableCell) is the column's number format; a body cel... |\n| `rows` | no | `array<array<ref:TableCell>>` | Two-dimensional table row data; each row aligns by index with columns when columns are supplied. |\n| `dataset` | no | `ref:DatasetId` | Id of a top-level dataset that supplies this table's headers, rows and column formats. Excludes 'rows' and 'columns'. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | Dataset tables only: the dataset columns to show, by name and in order. Absent shows every column. An unknown name is a 'dataset-field-unknown' error. |\n| `alt` | no | `string` | Text alternative for the table: what it shows (the point and the key numbers), not 'a table'. The preview exposes it as the table's accessible name (role group with aria-label; the cells stay readable) and the PowerPo... |\n\n\n### ChartData\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: Inline tabular data driving a chart. The first column usually supplies category/x-axis labels; subsequent columns are plotted measures unless 'chart.mapping' or a chart type maps them differently. Value cells are numbers; a string is read only in strict decimal syntax ('12', '-3.5', '1e6'), and anything else ('12%', '$5', '(5)', '1,234') is a gap and a 'chart-value-not-numeric' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered column labels for the chart data table. A label is a string or a DataColumn ({ \"name\", \"format\" }) whose number format the data labels, value axis and exported workbook use. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Tabular chart rows. Each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance: where this inline data came from. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. |\n\n\n### DataColumn\n\n- Type: `object`\n- Required fields: `name`\n- Purpose: A named data column with an optional number format. Anywhere a chart or dataset column is a string it may be a DataColumn; the string form is { \"name\": value } with no format. A table column header may also be a DataColumn.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | yes | `string` | Column label, used as the series name, the table header and the name 'fields' and 'chart.mapping' address. |\n| `format` | no | `ref:NumberFormat` | Number format of the column's number values (data labels, value axis, table cells, exported workbook). Absent: the General form. |\n\n\n### DataSourceRef\n\n- Type: `object`\n- Required fields: `src`\n- Purpose: Provenance of inline data or a dataset: the file, sheet and range it came from and when. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. Re-import the data to update it.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | yes | `string` | Where the data came from: an 'asset:<id>' reference, a URL, or a relative or local path. |\n| `sheet` | no | `string` | Sheet or table name inside a spreadsheet source. |\n| `range` | no | `string` | A1-style range inside a spreadsheet source. |\n| `fields` | no | `array<string>` | The source fields the data was taken from, in order. |\n| `retrieved` | no | `anyOf:schema / schema` | When the data was taken from the source: an ISO 8601 date or date-time. |\n| `description` | no | `string` | Free-form note about the source. |\n\n\n### DatasetId\n\n- Type: `string`\n- Required fields: none\n- Purpose: Id of an entry in the top-level datasets map (the assets id pattern).\n\n_No named properties._\n\n\n### DatasetFields\n\n- Type: `array<string>`\n- Required fields: none\n- Purpose: Dataset column names to use, in order, each at most once. Selects and orders the dataset's columns; an unknown name is a 'dataset-field-unknown' error.\n\n_No named properties._\n\n\n### Datasets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Shared data tables keyed by id (the assets id pattern). Charts reference one with 'chart.data': { \"dataset\": \"<id>\" } and tables with 'table': { \"dataset\": \"<id>\" }.\n\n_No named properties._\n\n\n### Dataset\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: One shared data table: named columns (strings or DataColumn objects with a number format) and rows of scalar cells. Column names are unique ('data-column-duplicate' error). Rich text and styling belong in an inline table.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `title` | no | `string` | Human name of the dataset, shown by editors. |\n| `description` | no | `string` | What the dataset holds. |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered, uniquely named columns. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Rows of scalar cells (string, number, boolean, null); each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance. Engines never read, fetch or refresh it. |\n\n\n### DatasetRef\n\n- Type: `object`\n- Required fields: `dataset`\n- Purpose: Chart data taken from a top-level dataset. Engines inline it before plotting; 'chart.mapping' names columns after the 'fields' selection.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `dataset` | yes | `ref:DatasetId` | Id of the dataset. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | The dataset columns to use, by name and in order. Absent uses every column. |\n\n\n### ChartMapping\n\n- Type: `object`\n- Required fields: none\n- Purpose: Series mapping by column name (after any 'fields' selection). Mapping only selects and orders columns; pie, doughnut and other single-series constructs still plot one series. An unknown name is a 'chart-mapping-unknown-column' error; a series that repeats the category or X column, and an X column on a chart type without an X axis, are dropped with a 'chart-mapping-adapted' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | The label (category) column. Default: the first column. |\n| `x` | no | `string` | The X column of an XY (scatter) chart. Default: the second column (the first, when the category is the second column), and only when there are three or more columns: with two, the second column is the one series, plot... |\n| `series` | no | `array<string>` | The plotted columns, in order. Default: every column that is not the category or the X column. |\n\n\n### ChartDataCell\n\n- Type: `oneOf:string / number / boolean / null`\n- Required fields: none\n- Purpose: A cell in inline chart data.\n\n_No named properties._\n\n\n### TableCell\n\n- Type: `oneOf:ref:TableCellValue / ref:StyledTableCell`\n- Required fields: none\n- Purpose: A scalar, rich-run array, or styled/spanning cell object. Existing scalar and rich forms remain valid.\n\n_No named properties._\n\n\n### TableCellValue\n\n- Type: `oneOf:string / number / boolean / null / array<ref:TextRun>`\n- Required fields: none\n- Purpose: A scalar table value or canonical rich text runs, without cell decoration or geometry.\n\n_No named properties._\n\n\n### StyledTableCell\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: A cell with explicit visual style or merged geometry. Its position remains its array column index; use null placeholders for every covered grid position.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `ref:TableCellValue` | Editable cell content; styling and spans do not change its scalar type or rich runs. |\n| `style` | no | `ref:TableCellStyle` | |\n| `colSpan` | no | `integer` | Number of grid columns covered, starting at this cell. Covered positions must contain null. Default 1. |\n| `rowSpan` | no | `integer` | Number of grid rows covered, starting at this cell. Covered positions must contain null. Header cells cannot span into body rows. Default 1. |\n| `format` | no | `ref:NumberFormat` | Number format. On a body cell it formats that cell's number value (and wins over the column's format); on a header cell (in 'columns') it is the column's format, the same as a DataColumn header. Strings, booleans, nul... |\n\n\n### TableCellStyle\n\n- Type: `object`\n- Required fields: none\n- Purpose: Cell appearance. Sizes use reference pixels at a 720-pixel canvas short edge and scale with the slide.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `fill` | no | `ref:ColorRef` | Cell background: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `color` | no | `ref:ColorRef` | Default text color, overridden by individual rich run colors. Accepts a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `bold` | no | `boolean` | Bold cell text. Runs that set their own bold keep it; a number cell stays bold and still uses the cell's or column's number format. |\n| `align` | no | `enum:left \\| center \\| right` | Horizontal text alignment inside the cell. |\n| `verticalAlign` | no | `enum:top \\| middle \\| bottom` | Vertical alignment inside the padded cell box. |\n| `padding` | no | `ref:TableCellPadding` | |\n| `borders` | no | `object` | Independent cell edges. Omitted edges retain the table theme border; width 0 removes an edge. |\n\n\n### TableCellPadding\n\n- Type: `object`\n- Required fields: none\n- Purpose: Text insets in reference pixels. Defaults: top 8, right 10, bottom 4, left 10.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `top` | no | `number` | |\n| `right` | no | `number` | |\n| `bottom` | no | `number` | |\n| `left` | no | `number` | |\n\n\n### TableCellBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: One explicit cell border.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Border color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `width` | yes | `number` | Border width in reference pixels; 0 removes this edge. |\n| `dash` | no | `enum:solid \\| dash \\| dot` | Default solid. |\n\n\n### Catalogs\n\n- Type: `object`\n- Required fields: none\n- Purpose: The catalog records a document embeds, grouped by the catalog they came from. Each property is a group; inside a group, records are keyed by kind and then by id. - 'default': the catalog bare ids come from. It needs a 'source' when it holds records. Omitted: bare ids fall back to the host's default catalog. false: no catalog fallback, so every bare id must be embedded (under 'custom' or 'default'). - 'custom': the records the document defines itself. It has no source. - any other name ('acme'...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `default` | no | `oneOf:allOf:ref:CatalogGroup + schema / const:false` | The catalog bare ids come from: its source and the records of it this document embeds. false turns off the catalog fallback for bare ids. |\n| `custom` | no | `allOf:ref:CatalogGroup + schema` | Records the document defines itself, referenced by bare id. Checked first for every bare id. Forking a catalog record copies it here under a new id, so the change of ownership is visible. |\n\n\n### CatalogGroup\n\n- Type: `object`\n- Required fields: none\n- Purpose: One catalog group: the catalog's source and the records of it the document embeds, by kind and then by id.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | no | `ref:CatalogSource` | |\n| `layouts` | no | `ref:CatalogRecords` | Layout records keyed by id, each with the fields of https://openpresentation.org/schema/opf-layout/v2. Referenced from Slide.layout. |\n| `themes` | no | `ref:CatalogRecords` | Theme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-theme/v1. Referenced from design.theme. |\n| `colorSchemes` | no | `ref:CatalogRecords` | Colour-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-color-scheme/v1. Referenced from design.colorScheme and a theme's colorScheme. |\n| `fontSchemes` | no | `ref:CatalogRecords` | Font-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-font-scheme/v1. Referenced from design.fontScheme, a theme's fontScheme and a language's font schemes. |\n| `narratives` | no | `ref:CatalogRecords` | Narrative records keyed by id, each with the fields of https://openpresentation.org/schema/opf-narrative/v1. Referenced from narrative. |\n| `audiences` | no | `ref:CatalogRecords` | Audience records keyed by id, each with the fields of https://openpresentation.org/schema/opf-audience/v1. Referenced from audience. |\n| `purposes` | no | `ref:CatalogRecords` | Purpose records keyed by id, each with the fields of https://openpresentation.org/schema/opf-purpose/v1. Referenced from purpose. |\n| `tones` | no | `ref:CatalogRecords` | Tone records keyed by id, each with the fields of https://openpresentation.org/schema/opf-tone/v1. Referenced from tone. |\n\n\n### CatalogSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: Identity of a catalog: an HTTPS URL ('https://www.pptx.gallery') or a package reference ('pkg:@acme/opf-catalog'). It names the catalog; engines never fetch or install it. A host registers the catalog's records under this exact string, and copying slides between documents matches groups by it.\n\n_No named properties._\n\n\n### CatalogRecords\n\n- Type: `object`\n- Required fields: none\n- Purpose: Embedded records of one kind, keyed by id. The key is the record's id; an embedded record carries no '$schema', 'id', origin or digest. Catalog display metadata ('x-*' members) is stripped when a record is embedded.\n\n_No named properties._\n\n\n### CatalogReference\n\n- Type: `string`\n- Required fields: none\n- Purpose: A content reference: a bare id ('two-column') or 'name:id' ('acme:hero'), where the prefix names a group of 'catalogs'. URLs and 'pkg:' strings are not references; a named catalog group replaces them. See Catalogs for the resolution order.\n\n_No named properties._\n\n\n### LanguageTag\n\n- Type: `string`\n- Required fields: none\n- Purpose: A BCP-47 language tag ('en-US', 'ja', 'zh-Hans', 'sr-Latn-ME'), an engine vocabulary. Use 'en-GB' for UK English; 'en-UK' is not a valid region.\n\n_No named properties._\n\n\n### Composition\n\n- Type: `object`\n- Required fields: none\n- Purpose: Automatic slide and content-group arrangement. Nested content groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions. A layout defines its grid as one ordered list of regions; its composition supports gap, padding, minFontSize and overflow rather than mode, columns or weights.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `mode` | no | `enum:auto \\| grid \\| row \\| column` | auto chooses a grid from available space and content; grid uses columns; row and column use one horizontal or vertical track. |\n| `columns` | no | `integer` | Column count for grid. In auto mode this caps the number of columns. |\n| `gap` | no | `number` | Space between cells as a fraction of the container short edge (canvas at slide root). Default 0.03333333333333333. |\n| `padding` | no | `number` | Inset as a fraction of the container short edge. Default 0.08 on a slide, 0 inside a group. |\n| `weights` | no | `array<number>` | Relative track sizes: columns for row/grid/auto, rows for column. Omitted tracks have weight 1; extra weights are ignored. |\n| `minFontSize` | no | `number` | Minimum readable text size in reference pixels at a 720-pixel canvas short edge. Default 16. Overflow is diagnosed when text cannot fit at this size. |\n| `overflow` | no | `enum:warn \\| error` | warn returns diagnostics for content that does not fit; error rejects layout. Content is never silently removed. Default warn. |\n"
|
|
303
|
+
"markdown": "# OPF Presentation Schema Reference\n\nThis reference documents the author-facing shape of a complete `*.opf.json` presentation document. It summarizes the canonical schema in `spec/schemas/opf.schema.json`; the schema remains the source of truth for validators.\n\n## Document Contract\n\n- Schema id: `https://openpresentation.org/schema/opf/v1`\n- Required top-level fields: `slides`\n- Additional top-level fields: not allowed\n\n## Top-Level Fields\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `$schema` | no | `const:\"https://openpresentation.org/schema/opf/v1\"` | Optional OPF schema version. When omitted, validators and engines should assume the latest supported OPF schema. |\n| `name` | no | `string` | Display name of the presentation for GUI/TUI lists, library/search indexing, OS-level metadata, and default export filenames. This is deck identity, not slide content. Use slides[].title and slides[].subtitle for text... |\n| `description` | no | `string` | Free-form prose describing what this presentation is about. Used by agents and humans as a deck-level summary; complements purpose (the goal) and narrative (the structured storyline). Round-trips to OOXML 'docProps/co... |\n| `filename` | no | `string` | Optional base filename for exports (without extension). The opf CLI (convert) and the editor strip a trailing .pptx, .pdf, .png, or .svg (case-insensitive) and append the target format's extension. When omitted, they... |\n| `organization` | no | `oneOf:ref:Organization / array<ref:Organization>` | Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) su... |\n| `speaker` | no | `oneOf:ref:Speaker / array<ref:Speaker>` | Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' read it, 'speakers' lists every name, and 'speaker.<id... |\n| `author` | no | `oneOf:string / array<string>` | Optional credit for the person who authored or contributed to the deck, distinct from speaker. Array form supports multiple contributors. Round-trips to OOXML 'docProps/core.xml' as '<dc:creator>' (semicolon-joined wh... |\n| `audience` | no | `oneOf:string / ref:Audience / array<oneOf:string / ref:Audience>` | Intended audiences for the presentation. Accepts any of: - A single string: an audiences reference ('executive', or 'acme:board' for a record of the catalogs.acme group), or free-form text ('Series B investors'). A st... |\n| `purpose` | no | `oneOf:string / ref:Purpose` | Primary goal of the presentation. Accepts either: - A string: a purposes reference ('decide', or 'acme:fundraise' for a record of the catalogs.acme group), or a free-form goal ('Raise a Series B round of $30M'). A str... |\n| `language` | no | `oneOf:ref:LanguageTag / ref:Language` | Language of the presentation content, an engine vocabulary: a BCP-47 language tag, validated directly with no catalog lookup. Accepts either: - A BCP-47 tag ('en-US', 'en-GB', 'ja', 'zh-Hans', 'ar-SA'). - An inline La... |\n| `tone` | no | `oneOf:ref:CatalogReference / ref:Tone` | Desired tone for the presentation. Accepts either: - A tones reference: a bare id ('formal') or 'name:id' for a record of a named catalogs group. - An inline Tone object for custom tone metadata or overrides on a refe... |\n| `takeaway` | no | `oneOf:string / array<string>` | Audience-facing takeaway the presentation should leave behind. Array form supports multiple takeaways. Deck-level intent used by AI to seed and pressure-test slide content. |\n| `duration` | no | `integer` | Target presentation duration, as an integer number of minutes. The opf-render presenter view counts the elapsed time against it, and core validate warns when it lies outside the resolved narrative's 'duration' range.... |\n| `tags` | no | `array<string>` | Free-form labels used for categorization, search, and filtering. Lowercase kebab-case is recommended for consistency across a deck library. |\n| `design` | no | `ref:Design` | Optional design system covering theme, color scheme, font scheme, dimensions, background, logo, watermark, header, and footer applied to the deck. When omitted, engines use their default design configuration. |\n| `variables` | no | `ref:Variables` | Optional named variables: deck colors referenced as 'var:<id>' (the original use), and typed content variables (text, number, date, image, url, list) referenced inline as '{{<id>}}' or whole as 'var:<id>'. Variables a... |\n| `template` | no | `boolean` | Marks this document as a template: an incomplete OPF file. A template declares variables (top-level 'variables') and references them from content, and may leave required variables unfilled; validation then reports the... |\n| `narrative` | no | `ref:CatalogReference` | The deck's narrative plan, by reference: a bare narratives id ('classic-story') or 'name:id' for a record of a named catalogs group ('acme:founder-pitch'). The deck holds only this pointer; the plan (the arc, its beat... |\n| `slides` | yes | `array<ref:Slide>` | Ordered array of slides that make up the presentation. |\n| `references` | no | `array<ref:Reference>` | Sources that text runs cite with 'cite'. Ids are unique. A cited reference is listed in the footnote area of every slide that cites it, with a marker number assigned per deck in order of first use; a reference no run... |\n| `datasets` | no | `ref:Datasets` | Optional shared data tables, keyed by id. A chart ('chart.data': { \"dataset\": \"<id>\" }) or a table ('table': { \"dataset\": \"<id>\" }) references one instead of holding its own copy; engines inline the reference before c... |\n| `assets` | no | `ref:Assets` | Optional reusable asset registry for images, data files, videos, documents, fonts, and other resources referenced elsewhere in the deck via 'asset:<id>' strings. |\n| `catalogs` | no | `ref:Catalogs` | The catalog records this document embeds, grouped by the catalog they came from: 'default' (the catalog bare ids come from), 'custom' (records the document defines itself) and any number of named catalogs ('acme', ref... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows; ignored by the engine but preserved across read/write round-trips. |\n\n## Object And Type Reference\n\n### Assets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Reusable asset registry for resources used by slides, charts, metadata, and design. Keys are stable asset ids referenced elsewhere as 'asset:<id>'. Each asset can be a source string or an object with src plus optional metadata.\n\n_No named properties._\n\n\n### Asset\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: Reusable or inline resource. A string is shorthand for { \"src\": value }. Source strings accept 'asset:<id>' references, HTTPS URLs, data URIs, relative paths resolved against the OPF file location, or local filesystem paths. Use object form when metadata such as alt text, title or mediaType matters.\n\n_No named properties._\n\n\n### Audience\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline audience metadata for the presentation. Use 'id' to reference an audiences record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline audience.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional audiences reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable audience name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the audience. |\n| `description` | no | `string` | Longer prose describing the audience and how to address them. |\n| `seniority` | no | `enum:ic \\| manager \\| director \\| vp \\| c-suite \\| mixed` | Typical seniority level of the audience. |\n| `technicalFluency` | no | `enum:low \\| medium \\| high \\| mixed` | Typical technical fluency of the audience. |\n| `decisionPower` | no | `enum:informational \\| advisory \\| decision-maker` | Whether the audience is expected to be informed, advise, or decide. |\n| `attentionBudgetMinutes` | no | `number` | Realistic upper bound on focused attention for a single presentation, in minutes. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Purpose\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline purpose metadata for the presentation. Use 'id' to reference a purposes record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline purpose.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional purposes reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable purpose name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the purpose. |\n| `description` | no | `string` | Longer prose describing when to use this purpose and how it should shape a deck. |\n| `outcome` | no | `string` | Desired audience outcome after the presentation. |\n| `successCriteria` | no | `array<string>` | Observable signals that the deck accomplished this purpose. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Language\n\n- Type: `object`\n- Required fields: `bcp47`\n- Purpose: Inline language metadata for the presentation: a BCP-47 'bcp47' tag plus overrides of what engines know about it. Languages are an engine vocabulary, not catalog records, so there is no 'id'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | no | `string` | Human-readable language name. |\n| `bcp47` | yes | `ref:LanguageTag` | BCP-47 language tag used for locale-aware rendering, proofing, and accessibility metadata. Use 'en-GB' for UK English; 'en-UK' is not a valid BCP-47 region form. |\n| `ooxmlLang` | no | `string` | Culture tag for OOXML text-run language attributes (a:rPr/@lang, a:endParaRPr/@lang), in the language-[Script-]REGION form Office recognizes (e.g. 'ja-JP', 'ar-SA', 'ms-MY', 'nb-NO', 'fil-PH', 'zh-CN'). Without it, en... |\n| `code` | no | `string` | ISO 639-3 or 639-2 language code carried for engines that prefer ISO codes. |\n| `direction` | no | `enum:ltr \\| rtl` | Base text direction for the language. When omitted, engines derive it from the script: Arabic (Arab), Hebrew (Hebr), Syriac (Syrc), Thaana (Thaa), N'Ko (Nkoo), Adlam (Adlm), Samaritan (Samr), Mandaic (Mand) and Hanifi... |\n| `script` | no | `string` | ISO 15924 script code of the language's writing system. The script selects the OOXML font slot the language's text uses: East Asian scripts (Hans, Hant, Hani, Jpan, Kore, Hang, Hira, Kana, Bopo, Yiii) use the eastAsia... |\n| `fontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') for this language's script slot when targeting PowerPoint output. Its major/minor families fill the language's script slot (eastAsian or complexScript, chosen by 'script'... |\n| `googleFontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') used in place of 'fontScheme' when resolving script fonts for Google Slides output. |\n| `summary` | no | `string` | One-sentence note about coverage or font defaults. |\n| `description` | no | `string` | Longer prose describing the language record and any font-pairing rationale. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Tone\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline tone metadata for the presentation. Use 'id' to reference a tones record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline tone.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional tones reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable tone name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the tone. |\n| `description` | no | `string` | Longer prose describing the tone and the kinds of decks it suits. |\n| `voiceCues` | no | `array<string>` | Short directives that shape AI generation toward this tone. |\n| `avoid` | no | `array<string>` | Anti-patterns that AI generation should not produce when this tone is active. |\n| `samplePhrases` | no | `array<string>` | Short example phrases that exemplify this tone. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids this tone pairs well with, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Organization\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the cover and section logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'organization.<id>.<field>'), and every logo a slide-scoped image ('var:organization.logo.icon', 'var:organization...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the organization, used to reference it from Speaker.organizationId and to address it in built-in variables as 'organization.<id>.<field>'. Must be unique among organizations (a duplicate is a val... |\n| `name` | yes | `string` | Display name. The built-in variable 'organization.name' (primary organization) or 'organization.<id>.name' carries it into any string, such as a header or footer text of '{{organization.name}}'. |\n| `legalName` | no | `string` | Optional legal entity name when it differs from the display name. Never drawn automatically; available as the built-in variable 'organization.legalName'. |\n| `logo` | no | `oneOf:ref:Asset / ref:OrganizationLogo` | The organization's logo. A path, URL or Asset is one logo for every shape; an object gives up to four shapes (full, stacked, icon, wordmark), each a path or Asset, or { onLight, onDark } for the two backgrounds. A pat... |\n| `domain` | no | `string` | Bare internet domain for the organization. Never drawn automatically and not used to look up assets; available as the built-in variable 'organization.domain'. |\n| `email` | no | `string` | General contact email for the organization. Never drawn automatically; available as the built-in variable 'organization.email'. |\n| `phone` | no | `string` | Main contact phone number for the organization. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'organization.phone'. |\n| `tagline` | no | `string` | Short tagline. Never drawn automatically (cover slides do not show it); available as the built-in variable 'organization.tagline', for example in a footer 'text' of '{{organization.tagline}}'. |\n| `role` | no | `enum:primary \\| partner \\| client \\| sponsor \\| host` | Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (cover and section logo, the 'socials' header/footer field, and the 'organization.<field>' built-... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the organization. The primary organization's socials render in header/footer zones that set socials: true; otherwise they are authoring metadata. |\n\n\n### Speaker\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, a header or footer text included, and 'var:speaker.photo' as a whole image field). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the speaker. Must be unique among speakers (a duplicate is a validation error). Addresses this speaker in built-in variables as 'speaker.<id>.<field>', such as '{{speaker.alice.title}}'. |\n| `name` | yes | `string` | Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; every name is listed by 'speakers'. |\n| `title` | no | `string` | Role or title. Built-in variable 'speaker.title' (or 'speaker.<id>.title'), for example in a footer text of '{{speaker.name}}, {{speaker.title}}'. |\n| `photo` | no | `ref:Asset` | Source for the speaker's headshot image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are JPG or PNG; SVG is not appropr... |\n| `email` | no | `string` | Contact email. Never drawn automatically; available as the built-in variable 'speaker.email'. |\n| `phone` | no | `string` | Contact phone number for the speaker. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'speaker.phone'. |\n| `bio` | no | `string` | Short biographical paragraph. Never drawn automatically; available as the built-in variable 'speaker.bio', for a bio or 'about the speaker' slide you write. |\n| `organizationId` | no | `string` | Reference to an Organization.id in organization; it must name an existing organization (a validation error otherwise). Attribution metadata for hosts: no engine draws it or changes which organization is primary becaus... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the speaker. Authoring metadata only: nothing draws speaker socials and there is no built-in variable for them (the 'socials' header/footer field draws the primary organizatio... |\n\n\n### Socials\n\n- Type: `object`\n- Required fields: none\n- Purpose: Social media handles or URLs, keyed by platform. The keys are an engine vocabulary validated directly (no catalog lookup): engines link a handle with the platform's URL pattern and handle prefix (spec/reference/engine-vocabularies.json). Each value is a string, either a full URL or a platform handle (e.g., '@acme'). Brand colours and icons are catalog display metadata for authoring UIs; engines render the profile URL, not icons or brand colours. Used both for organization and speaker records.\n\n_No named properties._\n\n\n### Design\n\n- Type: `object`\n- Required fields: none\n- Purpose: Visual design system applied to the presentation; individual slides may override fields via Slide.design.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `theme` | no | `ref:CatalogReference` | Theme for the deck: a themes reference, a bare id ('minimal') or 'name:id' ('acme:brand'). The theme supplies the default colour scheme, font scheme, background and dimensions; design.colorScheme, design.fontScheme, d... |\n| `colorScheme` | no | `oneOf:string / ref:ColorScheme` | Color scheme for the presentation. Accepts two forms: - A colorSchemes reference: a bare id ('cool-horizon') or 'name:id' ('acme:ocean'). - Object form: a ColorScheme with an optional 'id' reference as the base plus s... |\n| `fontScheme` | no | `oneOf:string / ref:FontScheme` | Font scheme for heading, body, accent, and code text. Accepts two forms: - A fontSchemes reference: a bare id ('aptos') or 'name:id' ('acme:inter'). - Object form: a FontScheme with an optional 'id' reference as the b... |\n| `dimensions` | no | `oneOf:ref:DimensionPreset / ref:Dimensions` | Slide dimensions and aspect ratio. String shorthand such as 'widescreen' is equivalent to { preset: 'widescreen' }. |\n| `background` | no | `oneOf:ref:BackgroundShortcut / ref:Background` | Default slide background applied across the deck unless overridden on a slide (slides.N.design.background); the resolved theme's background is the fallback. A background is the only canvas fill and never moves content... |\n| `variant` | no | `oneOf:ref:ThemeBackgroundSlot / ref:ColorVariant` | Optional color variant, as a background slot ('light1', 'light2', 'dark1', 'dark2'; the same as { background: slot }) or the object form, which is needed only for an accent order: which slot of the color scheme is the... |\n| `logo` | no | `oneOf:string / const:false` | Which logo covers, section dividers and picture bullets draw. Logos live on the organization (Organization.logo); without this field covers and sections draw the primary organization's full logo and picture bullets it... |\n| `watermark` | no | `oneOf:const:false / ref:Asset / ref:Watermark` | Optional decorative watermark applied across slides: an image or a text stamp, in the fixed frame and at the opacity described on Watermark. Use false to suppress an inherited watermark in slide-level design; a string... |\n| `header` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated header furniture rendered outside the main slide content. Use false to suppress an inherited header. |\n| `footer` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated footer furniture rendered outside the main slide content. Use false to suppress an inherited footer. |\n| `titleAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for the title heading area (the title and the tag) in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.titleAlignment, then the layout... |\n| `contentAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for body/content regions in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.contentAlignment, then the layout record's design.content... |\n| `contentBox` | no | `boolean` | Whether body/content regions are rendered inside a visible card or surface. Effective value: the slide's design, then the deck's design, then the theme's design.contentBox, then the layout record's design.contentBox,... |\n| `contentDirection` | no | `enum:horizontal \\| vertical` | Axis along which parallel body content is arranged: 'vertical' is a column, 'horizontal' a row. Effective value: the slide's design, then the deck's design, then the theme's design.contentDirection, then the layout re... |\n| `chartPrimary` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's design.chartPrimary. It applies to automatic slides only; a template layout says t... |\n| `imageFit` | no | `enum:cover \\| contain \\| stretch` | Default fit of image blocks (and Slide.image) that set no fit of their own: 'cover' fills the frame and crops what overflows around the block's focus, 'contain' shows the whole picture centered in the frame, 'stretch'... |\n| `listBullet` | no | `enum:character \\| image` | Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the theme's design.listBullet, then the layout record's design.listBullet, then 'character'. 'character' (the... |\n| `mirror` | no | `boolean` | Draw the slide's template layout mirrored (OPF 0.19): each row's cells and the column sizes reversed, so a 'beside' layout puts its picture, chart or table at the end. Logical: a right-to-left deck mirrors on top. Bin... |\n\n\n### ColorScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Color palette used by the design system. Every slot and role is a hex color ('#RGB', '#RRGGBB' or '#RRGGBBAA'); a color-scheme name or a 'var:<id>' reference is not valid here, because those resolve through the scheme. The slot fields (accent1-accent6, dark1, dark2, light1, light2, hyperlink, followedHyperlink) mirror color-scheme.schema.json (https://openpresentation.org/schema/opf-color-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Colour-scheme reference (a bare id or 'name:id') resolved as the base; slot and role overrides on the surrounding ColorScheme object take precedence over the resolved scheme. |\n| `accent1` | no | `ref:HexColor` | Accent 1 color (hex). Mirrors the OOXML accent1 slot. |\n| `accent2` | no | `ref:HexColor` | Accent 2 color (hex). Mirrors the OOXML accent2 slot. |\n| `accent3` | no | `ref:HexColor` | Accent 3 color (hex). Mirrors the OOXML accent3 slot. |\n| `accent4` | no | `ref:HexColor` | Accent 4 color (hex). Mirrors the OOXML accent4 slot. |\n| `accent5` | no | `ref:HexColor` | Accent 5 color (hex). Mirrors the OOXML accent5 slot. |\n| `accent6` | no | `ref:HexColor` | Accent 6 color (hex). Mirrors the OOXML accent6 slot. |\n| `dark1` | no | `ref:HexColor` | Dark 1 color (hex). Typically the deepest neutral; OOXML dark1. |\n| `dark2` | no | `ref:HexColor` | Dark 2 color (hex). Secondary dark; OOXML dark2. |\n| `light1` | no | `ref:HexColor` | Light 1 color (hex). Typically the slide canvas; OOXML lt1. |\n| `light2` | no | `ref:HexColor` | Light 2 color (hex). Secondary light surface; OOXML lt2. |\n| `hyperlink` | no | `ref:HexColor` | Hyperlink color (hex). OOXML hlink. Link runs with no color of their own are drawn underlined in it, in the preview and in PowerPoint, unless it has under 4.5:1 contrast against the slide background, then in the slide... |\n| `followedHyperlink` | no | `ref:HexColor` | Followed-hyperlink color (hex). OOXML folHlink. |\n| `primary` | no | `ref:HexColor` | Abstract role: primary brand color (hex). The engine maps this onto an OOXML accent slot when serializing. |\n| `secondary` | no | `ref:HexColor` | Abstract role: secondary brand color (hex). |\n| `accent` | no | `ref:HexColor` | Abstract role: accent color used for highlights and emphasis (hex). |\n| `background` | no | `ref:HexColor` | Abstract role: default slide background color (hex), used when the design names no single-color background (and for gradient and picture backgrounds). Overrides light1 as that default. |\n| `surface` | no | `ref:HexColor` | Abstract role: color for elevated surfaces such as cards and panels (hex). Overrides the default of light2 (dark2 on a dark slide). |\n| `text` | no | `ref:HexColor` | Abstract role: primary body text color (hex). Overrides dark1 on a light slide only; a dark slide always uses light1. |\n| `textSecondary` | no | `ref:HexColor` | Abstract role: secondary or muted text color used for captions and supporting copy (hex). Overrides the default of dark2 (light2 on a dark slide). |\n\n\n### FontScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Typography selections used by the design system. The pair fields (major, minor) and refinement fields (type, app, languageFamily) mirror font-scheme.schema.json (https://openpresentation.org/schema/opf-font-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel models are supported and may be mixed: - OOXML pairs (major, minor) - heading and body family names that round-trip directly to PowerPoint majorFont/minorFont entries. - Abstract roles...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') resolved as the base; field overrides on the surrounding FontScheme object take precedence over the resolved scheme. |\n| `major` | no | `string` | Heading (major) font family mirrors the OOXML majorFont entry. Pairs with 'minor'. |\n| `minor` | no | `string` | Body (minor) font family mirrors the OOXML minorFont entry. Pairs with 'major'. |\n| `eastAsian` | no | `object` | East Asian script fonts. Maps to the OOXML a:ea element of majorFont (major) and minorFont (minor), and to run-level a:ea. When set, they fill the eastAsian slot for every language; when omitted, the slot comes from t... |\n| `complexScript` | no | `object` | Complex-script fonts (for example Arabic, Hebrew, Indic and Thai). Maps to the OOXML a:cs element of majorFont (major) and minorFont (minor), and to run-level a:cs. When set, they fill the complexScript slot for every... |\n| `type` | no | `enum:sans-serif \\| serif \\| monospace` | High-level typographic class of the scheme. |\n| `app` | no | `enum:powerpoint \\| google-slides` | Target application this font pairing is intended for. Metadata for pickers and catalog filters: no engine changes its output by it. |\n| `languageFamily` | no | `enum:latin \\| ea \\| cs \\| eastAsian \\| complexScript` | Font-language family this scheme is intended for: 'latin' for Latin-script content, 'ea' (or 'eastAsian', the same value) for East Asian scripts, 'cs' (or 'complexScript', the same value) for Complex Scripts. The long... |\n| `heading` | no | `string` | Abstract role: font family name used for slide titles and headings. Maps onto the OOXML major slot when serializing. |\n| `body` | no | `string` | Abstract role: font family name used for body copy. Maps onto the OOXML minor slot when serializing. |\n| `accent` | no | `string` | Abstract role: font family name used for accent text. When set, the slide tag (eyebrow) and the quote body use this family instead of the body and heading families; nothing else changes. resolveFontFamilies() returns... |\n| `code` | no | `string` | Abstract role: monospaced font family name used for code blocks and for inline code runs (TextRun.code). No direct OOXML slot. Resolution: this override, then the resolved font-scheme record's 'code' (for example Cons... |\n\n\n### SlideDesign\n\n- Type: `allOf:ref:Design + schema`\n- Required fields: none\n- Purpose: A slide's design: every Design field except dimensions. A PPTX has one slide size, so the size is set once, on the deck's design.dimensions (or its theme), and a slide's design cannot set it. A slide-level theme whose resolved dimensions differ from the deck's is a validate warning (opf/slide-theme-dimensions); exporting such a deck to PPTX fails with mixed-slide-dimensions.\n\n_No named properties._\n\n\n### DimensionPreset\n\n- Type: `enum:16:9 | 4:3 | 16:10 | 1:1 | 4:5 | 9:16 | letter | a4 | widescreen | standard`\n- Required fields: none\n- Purpose: Named preset; sets aspect ratio and physical size. 16:9 ('widescreen'): 13.333 x 7.5 in, PowerPoint's Widescreen. 4:3 ('standard'): 10 x 7.5 in. 16:10: 10 x 6.25 in. letter (11 x 8.5 in) and a4 (11.69 x 8.27 in) are paper sizes, not PowerPoint's Letter Paper or A4 Paper. 1:1, 4:5 and 9:16 are 7.5 in on the short edge.\n\n_No named properties._\n\n\n### Dimensions\n\n- Type: `object`\n- Required fields: none\n- Purpose: Slide dimensions: a preset, or custom inches that override it. PowerPoint's Letter Paper (10 x 7.5 in), A4 Paper (10.8333 x 7.5 in) and On-screen Show (16:9) (10 x 5.625 in) are not OPF presets; use widthInches and heightInches. docs/slide-sizes.md compares every preset.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `preset` | no | `ref:DimensionPreset` | |\n| `widthInches` | no | `number` | Custom slide width in inches; overrides the preset width when provided. |\n| `heightInches` | no | `number` | Custom slide height in inches; overrides the preset height when provided. |\n\n\n### ThemeBackgroundSlot\n\n- Type: `enum:light1 | light2 | dark1 | dark2`\n- Required fields: none\n- Purpose: PowerPoint theme-controlled slide background slot from the active color scheme. These are slots, not assumptions about actual colors: light1 is usually white and dark1 is usually black by convention, but the color scheme controls the real values.\n\n_No named properties._\n\n\n### HexColor\n\n- Type: `string`\n- Required fields: none\n- Purpose: Hex color shorthand accepted by selected string fields.\n\n_No named properties._\n\n\n### ColorRef\n\n- Type: `anyOf:ref:HexColor / enum:accent1 | accent2 | accent3 | accent4 | accent5 | accent6 | dark1 | dark2 | light1 | light2 | hyperlink | followedHyperlink | primary | secondary | accent | background | surface | surfaceAlt | text | textSecondary / string`\n- Required fields: none\n- Purpose: A color value or reference, enforced on styled table cell fill and text colors, cell border colors, solid background colors, gradient stop colors and pattern foreground and background colors. Three forms: - Literal hex: '#RGB', '#RRGGBB', or '#RRGGBBAA'. - Color-scheme name, resolved through the effective color scheme after design resolution: an OOXML slot ('accent1'-'accent6', 'dark1', 'dark2', 'light1', 'light2', 'hyperlink', 'followedHyperlink') or an abstract role ('primary', 'secondary',...\n\n_No named properties._\n\n\n### Variables\n\n- Type: `object`\n- Required fields: none\n- Purpose: Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: ex...\n\n_No named properties._\n\n\n### Variable\n\n- Type: `oneOf:ref:HexColor / ref:ColorVariable / ref:TextVariable / ref:NumberVariable / ref:DateVariable / ref:ImageVariable / ref:UrlVariable / ref:ListVariable`\n- Required fields: none\n- Purpose: A single named variable: a hex string (shorthand for a color variable) or an object whose 'type' is color, text, number, date, image, url or list. 'value' is the current value and is optional: a variable with no value is unfilled, which a template allows and a normal deck does not. 'example' only illustrates the slot (fill forms, template previews) and never reaches output.\n\n_No named properties._\n\n\n### ColorVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A named color. Content color fields reference it as 'var:<id>'. Colors resolve at render time through the ordinary color-reference path, so a color variable keeps working as before.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"color\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:HexColor` | Hex color this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:HexColor` | Illustrative color shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### TextVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: Text content. Plain string or rich TextRun[]. Insert it inline as '{{<id>}}' inside any string (rich text is flattened to plain text there), or reference it whole as 'var:<id>' in a field that accepts string or TextRun[] (the rich runs are kept).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"text\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `oneOf:string / array<ref:TextRun>` | Text this variable resolves to: a plain string, or TextRun[] for rich text. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `oneOf:string / array<ref:TextRun>` | Illustrative text shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### NumberVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A number. '{{<id>}}' inserts it as text using 'format'; 'var:<id>' as a whole field supplies the number itself (chart values, font sizes).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"number\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `number` | Number this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `number` | Illustrative number shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `ref:NumberFormat` | Display pattern used by '{{<id>}}' (the shared NumberFormat syntax). A literal prefix, a numeric part of '#', '0', ',' and '.', and a literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits afte... |\n\n\n### NumberFormat\n\n- Type: `string`\n- Required fields: none\n- Purpose: Number display pattern, shared by NumberVariable.format, data column formats (DataColumn.format) and table cell formats (StyledTableCell.format). An optional literal prefix, a numeric part of '#', '0', ',' and '.', and an optional literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the prefix or suffix multiplies the value by 100. English separators only. A format applies only to number values; st...\n\n_No named properties._\n\n\n### DateVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A calendar date as an ISO YYYY-MM-DD string. No time zone and no clock are involved. '{{<id>}}' inserts it formatted with 'format'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"date\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | ISO calendar date (YYYY-MM-DD) this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative ISO date shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `string` | Date display pattern, the same LDML-style tokens as header/footer dateFormat: yyyy, yy, MMMM, MMM, MM, M, dd, d, EEEE, EEE and quoted literals. English names. Default 'MMMM d, yyyy'. |\n\n\n### ImageVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: An image source: any Asset (an 'asset:<id>' reference, HTTPS URL, data URI, relative or local path, or an object with src, alt and metadata). Reference it whole as 'var:<id>' in an image, asset or src field; '{{<id>}}' inserts the source string.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"image\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:Asset` | Image source this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:Asset` | Illustrative image source shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### UrlVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A link target (http, https, mailto or tel). Use it as '{{<id>}}' inside a link string or reference it whole as 'var:<id>' in a link field.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"url\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | Link target this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative link shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### ListVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A list of strings, for bullets and list items. A whole-string array element 'var:<id>' splices every entry into the array in place; a whole field 'var:<id>' becomes the array; '{{<id>}}' joins the entries with ', ' (or the separator after a pipe: '{{<id>|; }}').\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"list\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `array<string>` | Entries this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `array<string>` | Illustrative entries shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### BackgroundShortcut\n\n- Type: `oneOf:ref:ThemeBackgroundSlot / ref:HexColor / ref:ImageSource`\n- Required fields: none\n- Purpose: String shorthand for a background. Theme slots ('light1', 'light2', 'dark1', 'dark2') are equivalent to { type: 'theme', slot: value }; hex colors are equivalent to { type: 'solid', color: value }; an image source (starting with 'asset:', 'https://', 'data:', './' or '../') is equivalent to { type: 'image', src: value }, a cover image. Any other string is invalid.\n\n_No named properties._\n\n\n### ImageSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: An image source in a string shorthand: an 'asset:<id>' reference, an HTTPS URL, a data URI, or a relative path starting with './' or '../' (resolved against the OPF file location).\n\n_No named properties._\n\n\n### ColorVariant\n\n- Type: `object`\n- Required fields: `background`\n- Purpose: A variant of the deck's single color scheme, as a mapping over its twelve slots (PowerPoint's clrMap): 'background' is the slot the slide background and the role bg1 take, and with it the other roles: light1 maps bg1 = light1, tx1 = dark1, bg2 = light2, tx2 = dark2; dark1 maps bg1 = dark1, tx1 = light1, bg2 = dark2, tx2 = light2; light2 and dark2 make that slot both the background and the alternate surface (light2: bg1 = bg2 = light2, tx1 = dark1, tx2 = dark2; dark2: bg1 = bg2 = dark2, tx1 =...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `background` | yes | `ref:ThemeBackgroundSlot` | The scheme slot that is the background (bg1) of the variant. |\n| `accents` | no | `array<enum:accent1 \\| accent2 \\| accent3 \\| accent4 \\| accent5 \\| accent6>` | Optional accent order: a permutation of accent1 to accent6. Entry i is the scheme slot drawn as accent i + 1, so primary, secondary and accent (the first three) and the chart series colors follow it. Default: the sche... |\n\n\n### Background\n\n- Type: `oneOf:ref:ThemeBackground / ref:SolidBackground / ref:GradientBackground / ref:ImageBackground / ref:PatternBackground`\n- Required fields: none\n- Purpose: Background fill applied to slides. Theme backgrounds preserve PowerPoint's color-scheme background choice; other variants represent fixed background fills.\n\n_No named properties._\n\n\n### ThemeBackground\n\n- Type: `object`\n- Required fields: `type`, `slot`\n- Purpose: Theme-controlled PowerPoint slide background. The slot is resolved through the active color scheme and remains theme-aware.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"theme\"` | Theme-controlled background fill. |\n| `slot` | yes | `ref:ThemeBackgroundSlot` | |\n\n\n### SolidBackground\n\n- Type: `object`\n- Required fields: `type`, `color`\n- Purpose: Fixed solid slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"solid\"` | Fixed solid background fill. |\n| `color` | yes | `ref:ColorRef` | Fixed solid fill color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef, resolved against the effective color scheme and the deck variables). Use { type: 'theme', slot: ...... |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### GradientBackground\n\n- Type: `object`\n- Required fields: `type`, `gradient`\n- Purpose: Fixed gradient slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `tone` | no | `enum:dark \\| light` | Whether the background is dark or light, which picks the color map (dark1 or light1) so text, lines and neutrals read on it. Without a tone a picture or gradient takes the deck's variant, else counts as light. |\n| `type` | yes | `const:\"gradient\"` | Fixed gradient background fill. |\n| `gradient` | yes | `object` | Gradient fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### ImageBackground\n\n- Type: `object`\n- Required fields: `type`, `src`\n- Purpose: Picture background: the image fills the whole canvas behind everything and never moves content (to push content aside, use an image block with placement). The string shorthand 'asset:x' (or any image source) is { type: 'image', src: 'asset:x' }, a cover image. Paint order: the colour scheme's default slide background, the picture, then the overlay, then furniture and content.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `tone` | no | `enum:dark \\| light` | Whether the background is dark or light, which picks the color map (dark1 or light1) so text, lines and neutrals read on it. Without a tone a picture or gradient takes the deck's variant, else counts as light. |\n| `type` | yes | `const:\"image\"` | Picture background. |\n| `src` | yes | `string` | Source for the background image: an 'asset:<id>' reference, HTTPS URL, data URI, or a relative path resolved against the OPF file location. |\n| `alt` | no | `string` | Alternative text. A background with alt is meaningful: PowerPoint export draws it as a full-slide picture at the back that carries the text, so screen readers see it. Without alt it is decorative and exports as the na... |\n| `fit` | no | `enum:cover \\| contain \\| stretch \\| tile` | How the picture fills the canvas: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered over the colour scheme's default background; 'stretch' scales it to the canvas exact... |\n| `focus` | no | `ref:ImageFocus` | Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `opacity` | no | `number` | Picture opacity from 0 (fully transparent) to 1 (fully opaque); the overlay keeps its own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Grayscale or duotone treatment of the picture's pixels, as on an image block: recolor and opacity apply to the pixels, then the overlay is drawn above. |\n| `overlay` | no | `ref:Overlay` | Scrim over the whole picture or a band along one edge (edge, size), beneath furniture and content, for example to keep a title readable over a busy photo. |\n\n\n### ImageFocus\n\n- Type: `object`\n- Required fields: `x`, `y`\n- Purpose: A point of a picture as fractions of its width and height, from the top-left corner. A cover fit centers this point in the frame as far as the picture still covers the frame, so it always stays in view: { x: 0.5, y: 0.5 } (the default) is a center crop, { x: 0, y: 0 } keeps the top-left corner.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `x` | yes | `number` | Horizontal position, 0 (left edge) to 1 (right edge). |\n| `y` | yes | `number` | Vertical position, 0 (top edge) to 1 (bottom edge). |\n\n\n### Overlay\n\n- Type: `object`\n- Required fields: `color`, `opacity`\n- Purpose: Solid scrim over a picture: the whole frame in the frame's shape, or a band along one edge. Shared by image backgrounds and image blocks. Drawn directly above its picture and beneath headings and content; PowerPoint export writes one native shape above the picture.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Overlay fill color: a hex color (eight-digit hex keeps its alpha), a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `opacity` | yes | `number` | Overlay fill opacity. |\n| `edge` | no | `enum:top \\| bottom \\| left \\| right` | Cover only a band along this edge of the frame, for example a caption strip. Backgrounds and rectangle image frames only; on another shape the overlay is not drawn and composition reports unsupported-image-treatment.... |\n| `size` | no | `number` | Band share of the frame for an edge overlay. Default 0.3. Ignored without edge. |\n\n\n### ImagePlacement\n\n- Type: `object`\n- Required fields: `edge`\n- Purpose: Bleeds an image block to one edge of the slide: the image takes a band along that edge, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. Only top-level blocks (slides.N.blocks.I) can be placed, at most one per edge. Template layout regions do not supply image placement; set it on the image block itself. Bands are taken in block order, each spanning the free area left by earlier ones. Placement is the only mechanism that moves content asi...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `edge` | yes | `enum:left \\| right \\| top \\| bottom` | The slide edge the image bleeds to. |\n| `size` | no | `number` | Share of the slide width (left, right) or height (top, bottom) given to the band. Default 0.5. |\n| `inset` | no | `boolean` | Draw the frame inside the slide padding on every side of the band, like a card, instead of edge to edge. Default false. |\n\n\n### ImageBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: Solid line along the image frame's shape, centered on the outline with a miter join, like a native picture line. A thick dark border on a rounded portrait frame gives a device bezel.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Line color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `width` | yes | `number` | Line width in reference pixels at a 720-pixel short edge. 0 removes the line. |\n\n\n### ImageRecolor\n\n- Type: `oneOf:const:\"grayscale\" / object`\n- Required fields: none\n- Purpose: Color treatment for the image pixels. Luminance uses Rec. 601 weights (0.299, 0.587, 0.114) on sRGB values.\n\n_No named properties._\n\n\n### PatternBackground\n\n- Type: `object`\n- Required fields: `type`, `pattern`\n- Purpose: Fixed pattern slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"pattern\"` | Fixed pattern background fill. |\n| `pattern` | yes | `object` | Pattern fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### OrganizationLogo\n\n- Type: `object`\n- Required fields: none\n- Purpose: An organization's logo in up to four shapes. Each shape is a path or Asset, or { onLight, onDark } for the two backgrounds. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon; within a shape a missing onLight or onDark uses the other. Shapes are placed with slide-scoped references such as 'var:organization.logo.icon'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `full` | no | `ref:LogoSource` | The full logo (lockup): mark and name side by side. Drawn on cover and section slides, and placed by 'var:organization.logo'. |\n| `stacked` | no | `ref:LogoSource` | The stacked lockup: mark above the name, for portrait or square slots. Placed by 'var:organization.logo.stacked'. |\n| `icon` | no | `ref:LogoSource` | The mark or symbol without the name, for tight spaces such as header and footer zones and picture bullets. Placed by 'var:organization.logo.icon'. |\n| `wordmark` | no | `ref:LogoSource` | The name set in the brand's lettering, without the mark. Placed by 'var:organization.logo.wordmark'. |\n\n\n### LogoSource\n\n- Type: `oneOf:ref:Asset / object`\n- Required fields: none\n- Purpose: One logo shape: a path or Asset for every background, or { onLight, onDark }. onLight is drawn on light backgrounds (usually dark artwork) and onDark on dark backgrounds (usually light artwork), by each slide's background; a missing one uses the other.\n\n_No named properties._\n\n\n### Watermark\n\n- Type: `oneOf:schema / schema`\n- Required fields: `opacity`\n- Purpose: Decorative watermark: an image ('src') or a text stamp ('text'), exactly one of the two, with its opacity. An image is drawn once per slide, contained and centered in a fixed frame (the middle 40% of the slide width and height, from 30% to 70% on each axis). A text watermark is one line of text in the heading font and the theme text color at the given opacity, centered on the slide and rotated 30 degrees counterclockwise (rising to the right), sized to span at most 70% of the slide width and...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | no | `string` | Source for the watermark image: an 'asset:<id>' reference, HTTPS URL, data URI, relative path or local path, as for Asset.src. Exactly one of src and text is set. |\n| `text` | no | `string` | Text for a text watermark, such as DRAFT or CONFIDENTIAL; line breaks are drawn as spaces. Exactly one of src and text is set. |\n| `opacity` | yes | `number` | Watermark opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### HeaderFooter\n\n- Type: `object`\n- Required fields: none\n- Purpose: Repeated header or footer content split into left, center, and right zones. Header/footer content is slide furniture, separate from the main slide content payloads.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `left` | no | `ref:HeaderFooterItem` | Left-aligned header/footer content. |\n| `center` | no | `ref:HeaderFooterItem` | Centered header/footer content. |\n| `right` | no | `ref:HeaderFooterItem` | Right-aligned header/footer content. |\n\n\n### HeaderFooterItem\n\n- Type: `object`\n- Required fields: none\n- Purpose: One header/footer zone. Every configured field renders, side by side in one row in the order image, text, socials, date (mirrored in a right-to-left deck), aligned to the zone's edge (left, center or right) and vertically centered on each other; a line break inside text or socials ('\\n') stacks lines within that part. When the parts are wider than the zone their text wraps, and a row that cannot fit even so is reported as overflow. Generated values such as the slide number, the slide count, t...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | no | `string` | Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-i... |\n| `image` | no | `ref:Asset` | Image rendered first in this zone's row, such as a logo, partner mark, certification badge, or icon: a path, URL or Asset, or an organization logo reference ('var:organization.logo.icon', 'var:organization.<id>.logo.w... |\n| `date` | no | `oneOf:boolean / string` | true renders the current date: the renderer or exporter must be given an explicit ISO date by its host (core never reads a clock). PPTX export writes a native date field only for a supported dateFormat whose complete... |\n| `dateFormat` | no | `string` | Date pattern for date. Tokens: yyyy (2026), yy (26), MMMM (April), MMM (Apr), MM (04), M (4), dd (09), d (9), EEEE (Thursday), EEE (Thu). Text in single quotes and other non-letter characters are literal. Month and we... |\n| `socials` | no | `boolean` | Whether to render the primary organization's social profiles from organization.socials, one line per platform in key order. A handle is formatted through the platform's socialPlatforms record (companyUrlPattern, else... |\n\n\n### Slide\n\n- Type: `object`\n- Required fields: none\n- Purpose: A single slide. Content can be authored as a full-slide root payload, or inside promoted named region keys such as 'left', 'center+right', and 'top:left'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for the slide within the document. Use when another system needs to reference a slide across edits, comments, generation state, exports, or narrative tooling. Slide order is defined by the s... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline` | Optional full-slide content kind. When omitted, engines infer the kind from root payload fields. |\n| `beat` | no | `oneOf:string / array<string>` | Optional reference to one or more beats of the deck's narrative (each value is the id of a beat in the narrative record that the root 'narrative' resolves to). A single string declares the slide's primary beat; an arr... |\n| `layout` | no | `ref:CatalogReference` | Optional layout: a layouts reference, a bare id ('pillars') or 'name:id' ('acme:hero'), or 'auto'. When omitted or 'auto', the slide is composed with no layout record: engines arrange title, subtitle, tag and content... |\n| `title` | no | `oneOf:string / array<ref:TextRun>` | Slide-level title content. A v2 layout renders this in its title heading area, or an implicit heading row when that area is absent. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold,... |\n| `subtitle` | no | `oneOf:string / array<ref:TextRun>` | Slide-level subtitle or supporting line. A v2 layout renders this in its subtitle area when present, otherwise in the title heading group. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `tag` | no | `oneOf:string / array<ref:TextRun>` | Small slide-level label or badge. A v2 layout renders this before the title in its title heading group; tag is not a separate layout area. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Full-slide text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Full-slide structured list payload. Presence of this field infers type 'list'. Entries are strings, TextRun[] or objects with `text`, optional `description` (supporting detail under the entry), `level` and `start`. Us... |\n| `bullets` | no | `array<ref:BulletItem>` | Full-slide prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries are strings, TextRun[] or objects with `text`,... |\n| `numbering` | no | `ref:NumberingSpec` | Number the full-slide `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one ent... |\n| `image` | no | `ref:Asset` | Shorthand for one image block: the picture composes as body content, like { type: 'image', image: value }, with the effective design.imageFit. A v2 layout binds it to a compatible body region; a region's bleed setting... |\n| `video` | no | `ref:Asset` | Full-slide video source. Presence of this field infers type 'video'. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the... |\n| `chart` | no | `ref:Chart` | Full-slide chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Full-slide table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Full-slide code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Full-slide metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers... |\n| `quote` | no | `oneOf:string / ref:Quote` | Full-slide quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution and source metadata. Presence of this field infers type 'quote'. |\n| `timeline` | no | `ref:Timeline` | Full-slide timeline payload. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata. Presence of this field infers type 'timeline'. |\n| `caption` | no | `ref:Caption` | Caption for the slide's root image, chart, table or video payload. Valid only when the slide root holds exactly one of those payloads. |\n| `blocks` | no | `array<ref:ContentPayload>` | Layout-agnostic content blocks rendered together as a composed payload when exact placement is unspecified. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as short... |\n| `design` | no | `ref:SlideDesign` | Slide-level design applied on top of the deck-wide design. |\n| `left` | no | `ref:ContentPayload` | |\n| `center` | no | `ref:ContentPayload` | |\n| `right` | no | `ref:ContentPayload` | |\n| `left+center` | no | `ref:ContentPayload` | |\n| `center+right` | no | `ref:ContentPayload` | |\n| `left+center+right` | no | `ref:ContentPayload` | |\n| `top` | no | `ref:ContentPayload` | |\n| `middle` | no | `ref:ContentPayload` | |\n| `bottom` | no | `ref:ContentPayload` | |\n| `top+middle` | no | `ref:ContentPayload` | |\n| `middle+bottom` | no | `ref:ContentPayload` | |\n| `top+middle+bottom` | no | `ref:ContentPayload` | |\n| `top:left` | no | `ref:ContentPayload` | |\n| `top:center` | no | `ref:ContentPayload` | |\n| `top:right` | no | `ref:ContentPayload` | |\n| `top:left+center` | no | `ref:ContentPayload` | |\n| `top:center+right` | no | `ref:ContentPayload` | |\n| `top:left+center+right` | no | `ref:ContentPayload` | |\n| `middle:left` | no | `ref:ContentPayload` | |\n| `middle:center` | no | `ref:ContentPayload` | |\n| `middle:right` | no | `ref:ContentPayload` | |\n| `middle:left+center` | no | `ref:ContentPayload` | |\n| `middle:center+right` | no | `ref:ContentPayload` | |\n| `middle:left+center+right` | no | `ref:ContentPayload` | |\n| `bottom:left` | no | `ref:ContentPayload` | |\n| `bottom:center` | no | `ref:ContentPayload` | |\n| `bottom:right` | no | `ref:ContentPayload` | |\n| `bottom:left+center` | no | `ref:ContentPayload` | |\n| `bottom:center+right` | no | `ref:ContentPayload` | |\n| `bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left` | no | `ref:ContentPayload` | |\n| `top+middle:center` | no | `ref:ContentPayload` | |\n| `top+middle:right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center` | no | `ref:ContentPayload` | |\n| `top+middle:center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left` | no | `ref:ContentPayload` | |\n| `middle+bottom:center` | no | `ref:ContentPayload` | |\n| `middle+bottom:right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `notes` | no | `string` | Speaker notes shown in presenter view. |\n| `section` | no | `string` | PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports. Also the slide-scoped built-in variable '{{sli... |\n| `hidden` | no | `boolean` | Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (o... |\n| `composition` | no | `ref:Composition` | |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at slide scope; ignored by the engine but preserved across read/write round-trips. Use for review state, generation provenance, or authoring conventions such as { \"authoring... |\n\n\n### ContentPayload\n\n- Type: `allOf:schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema`\n- Required fields: none\n- Purpose: A content leaf or recursively composed group. A group contains blocks and optional composition; it cannot mix blocks with leaf payload fields. Groups may nest up to 32 levels.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for this payload, unique among slide and payload ids in the document. Use when another system needs to address the payload across edits patch-style agent edits, comments, review state, or ge... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at payload scope; ignored by the engine but preserved across read/write round-trips. |\n| `region` | no | `string` | Pin a root block (slides.N.blocks.I) to a region of the slide's template layout by name (OPF 0.19). The block goes to that region when the region exists, accepts the block's kind and has room; otherwise the pin is ign... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline \\| group` | Optional content kind. When omitted, engines infer the kind from the fields present. |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Structured list payload (type 'list'). Each item is either a plain string, a TextRun[] rich text sequence, or a ListItem object with optional `description`. List nesting uses item.level rather than nested content payl... |\n| `bullets` | no | `array<ref:BulletItem>` | Prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries have no `description`; use `items` for entries with suppo... |\n| `numbering` | no | `ref:NumberingSpec` | Number the payload's `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one entr... |\n| `columns` | no | `oneOf:const:\"auto\" / integer` | Lay the payload's `items` or `bullets` out in columns (OPF 0.19). 'auto' tries 1, 2 then 3 equal columns at the starting text size and uses the first count at which the list fits, as long as a column stays at least a... |\n| `image` | no | `ref:Asset` | Source for an image item. |\n| `fit` | no | `enum:cover \\| contain \\| stretch` | Image payloads only. How the picture fills its frame: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered; 'stretch' scales it to the frame exactly. Default: the effectiv... |\n| `focus` | no | `ref:ImageFocus` | Image payloads only. Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `aspectRatio` | no | `number` | Image payloads only. Width-to-height ratio of the frame: the frame becomes the largest centered box with this ratio inside the block's region, for example 2.39 for a cinematic letterbox or 0.5 for a phone-shaped frame... |\n| `shape` | no | `enum:rectangle \\| rounded \\| circle \\| hexagon` | Image payloads only. Mask applied to the frame, exported as the picture's native preset geometry (rect, roundRect, ellipse, hexagon). 'circle' makes the frame square. Default 'rectangle'. |\n| `cornerRadius` | no | `number` | Image payloads only. Corner radius for shape 'rounded' as a fraction of the frame's shorter side. Default 0.16667, PowerPoint's roundRect default. |\n| `border` | no | `ref:ImageBorder` | Image payloads only. Line along the frame's shape. |\n| `opacity` | no | `number` | Image payloads only. Picture opacity from 0 to 1, exported as a native alphaModFix. The border and overlay keep their own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Image payloads only. Grayscale or duotone treatment of the picture's pixels. |\n| `overlay` | no | `ref:Overlay` | Image payloads only. Scrim over the picture in the frame's shape, or a band along one edge of a rectangle frame. |\n| `placement` | no | `ref:ImagePlacement` | Image payloads only, and only on a top-level block (slides.N.blocks.I), at most one per edge. Bleeds the image to one slide edge; the headings and the other blocks compose in the rest of the slide. |\n| `video` | no | `ref:Asset` | Source for a video item. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the same placeholder as native shapes linked to... |\n| `chart` | no | `ref:Chart` | Chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers format them... |\n| `quote` | no | `oneOf:string / ref:Quote` | Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution, role, photo and source metadata. |\n| `timeline` | no | `ref:Timeline` | Timeline payload ordered by narrative or chronology. |\n| `caption` | no | `ref:Caption` | Caption for an image, chart, table or video payload, composed inside the block's region (below the media by default). Invalid on other payload kinds and on groups. |\n| `blocks` | no | `array<ref:ContentPayload>` | Ordered children of a group. Each child is a leaf or another group. |\n| `composition` | no | `ref:Composition` | Arrangement within this group. Only minFontSize and overflow inherit from the parent; strict overflow cannot be weakened. |\n\n\n### Quote\n\n- Type: `object`\n- Required fields: `text`\n- Purpose: Quote content with optional attribution metadata. Use 'text' for the quoted text, 'attribution' for the credited person or organization, 'role' for that person's title and organization, 'photo' for their headshot and 'source' for a citation or URL. A string value in a quote field is shorthand for { \"text\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | Quoted text: a string, or TextRun[] for inline rich text (a colored phrase, bold, a link, or a citation or footnote marker). The engine draws the surrounding quotation marks. |\n| `attribution` | no | `string` | Person or organization credited for the quote. Drawn after the quote text, in the muted text color. |\n| `role` | no | `string` | Title and organization of the attributed person. Drawn on its own line under the attribution; without an attribution it stands alone. It does not replace the attribution, so the person's name belongs in 'attribution'. |\n| `photo` | no | `ref:Asset` | Headshot of the attributed person, drawn as a circle beside the attribution and role lines (on the logical start side: left in a left-to-right deck, right in a right-to-left deck). Give it alt text (validate's opf/mis... |\n| `source` | no | `string` | Optional quote source, citation, or URL. Follows the attribution and role after ' - '. |\n\n\n### Code\n\n- Type: `object`\n- Required fields: `source`\n- Purpose: Code content with optional rendering metadata. Use 'source' for the code text, 'language' for syntax highlighting, 'filename' when the rendered block should show a file label, and 'highlight' to emphasize lines. A string value in a code field is shorthand for { \"source\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | yes | `string` | Source code text to display. |\n| `language` | no | `string` | Language identifier used for syntax highlighting. |\n| `filename` | no | `string` | Optional file label shown with the code block. |\n| `highlight` | no | `array<oneOf:integer / array<integer>>` | Source lines to emphasize, 1-based: each entry is a line number or an inclusive [start, end] range, so [3, [5, 7]] marks lines 3 and 5 to 7. Lines are counted by line break in 'source', so a line that wraps stays one... |\n\n\n### Metric\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: Metric content with optional display metadata. Use 'value' for the primary value, 'label' for the metric name, 'description' for supporting context, 'unit' for a suffix/currency marker, 'delta' for change, 'trend' for direction, and 'sentiment' for whether the change is good news. A string or number value in a metric field is shorthand for { \"value\": value }; numeric values remain numbers and are formatted by renderers.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `oneOf:string / number` | Primary metric value. |\n| `label` | no | `string` | Metric label. |\n| `description` | no | `string` | Optional supporting context for the metric. |\n| `unit` | no | `string` | Metric unit, suffix, or currency marker. |\n| `delta` | no | `oneOf:string / number` | Metric change value. |\n| `trend` | no | `enum:up \\| down \\| flat` | Metric trend direction. The preview and the PowerPoint export draw an arrow beside the trend word that always points the way the trend does (up, down or flat) and colour the arrow, the trend word and the delta text by... |\n| `sentiment` | no | `enum:positive \\| negative \\| neutral` | Whether the change is good news. Positive draws the trend arrow and the trend and delta text in green, negative in red and neutral in the neutral text colour, each kept at 4.5:1 contrast or more against the slide back... |\n\n\n### Timeline\n\n- Type: `oneOf:array<ref:TimelineEvent> / object`\n- Required fields: none\n- Purpose: Timeline content. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata.\n\n_No named properties._\n\n\n### TimelineEvent\n\n- Type: `object`\n- Required fields: `what`\n- Purpose: A single event inside a timeline content payload.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `when` | no | `string` | Event time, date, or sequence label. Use ISO-like values when possible, but human labels are allowed for quarters, eras, and relative milestones. |\n| `what` | yes | `string` | Short event label. |\n| `description` | no | `string` | Optional event detail. |\n| `status` | no | `enum:done \\| current \\| planned` | Progress of the event, drawn from the deck's own colors by both the SVG renderer and the PPTX exporter. Absent means no status and the event is drawn as a plain filled marker with normal text, which is also how 'done'... |\n\n\n### ListItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat item inside a list. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds description and nesting depth without creating nested slide content payloads.\n\n_No named properties._\n\n\n### BulletItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat bullet item. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds nesting depth without list-item descriptions.\n\n_No named properties._\n\n\n### NumberingStyle\n\n- Type: `enum:arabic | roman-upper | roman-lower | alpha-upper | alpha-lower`\n- Required fields: none\n- Purpose: A list number style: 1, 2, 3; I, II, III; i, ii, iii; A, B, C; a, b, c. Alphabetic numbering past 26 repeats the letter as PowerPoint does (aa, bb, cc). Roman numerals stop at 3999; larger values are drawn in arabic with a numbering-adapted diagnostic.\n\n_No named properties._\n\n\n### Numbering\n\n- Type: `object`\n- Required fields: none\n- Purpose: Numbering of one list level.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `style` | no | `ref:NumberingStyle` | Number style. Default arabic. |\n| `start` | no | `integer` | First number counted at this level. Default 1. Native PowerPoint accepts 1 to 32767. |\n| `suffix` | no | `enum:period \\| paren \\| paren-both` | Text after the number: period (1.), paren (1)) or paren-both ((1)). Default period. |\n\n\n### NumberingSpec\n\n- Type: `oneOf:ref:NumberingStyle / ref:Numbering / array<oneOf:ref:NumberingStyle / ref:Numbering>`\n- Required fields: none\n- Purpose: The value of a numbering field: a style name or Numbering object for every level, or an array with one entry per level (at most 9, the native depth).\n\n_No named properties._\n\n\n### TextRun\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: A contiguous run of text. Strings cover unformatted spans; object form adds character formatting.\n\n_No named properties._\n\n\n### Caption\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A caption for an image, chart, table or video payload. A string or TextRun[] is the caption text placed below the media; object form adds the position and alignment.\n\n_No named properties._\n\n\n### Reference\n\n- Type: `object`\n- Required fields: `id`, `text`\n- Purpose: A source that runs cite with 'cite'. Cited references are listed in the footnote area of the slides that cite them, numbered per deck in order of first use; referencesSlide() builds an ordinary list slide of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Identifier runs cite. Unique within the references list. |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | The reference as it is listed: a string or TextRun[] for inline rich text. |\n| `url` | no | `string` | Optional link for the reference; a references slide links its entry to it. |\n\n\n### Chart\n\n- Type: `object`\n- Required fields: `type`, `data`\n- Purpose: Chart content. The chart object keeps chart-specific fields together so slides and regions do not expose loose chart fields.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `enum:100pct-stacked-area \\| 100pct-stacked-bar \\| 100pct-stacked-column \\| area \\| bar \\| box-and-whisker \\| column \\| combo \\| doughnut \\| filled-radar \\| funnel \\| histogram \\| line \\| line-with-markers \\| pareto \\| pie \\| radar \\| radar-with-markers \\| scatter \\| stacked-area \\| stacked-bar \\| stacked-column \\| stacked-line \\| stacked-line-with-markers \\| treemap \\| waterfall \\| world` | Chart type, an engine vocabulary validated directly (no catalog lookup): the chart types Aspose.Slides officially supports, plus 'combo' (clustered columns with line series, see 'line' and 'secondaryAxis'). Each engin... |\n| `data` | yes | `oneOf:ref:ChartData / ref:DatasetRef` | Chart data. Inline data uses a tabular columns/rows shape; renderers convert rows to chart series internally. A DatasetRef ({ \"dataset\": \"<id>\" }) plots a top-level dataset instead. Data from a spreadsheet or file is... |\n| `mapping` | no | `ref:ChartMapping` | Optional series mapping by column name: which column is the category, which is the X column of a scatter chart, and which columns are plotted, in order. Absent keeps the positional rule (first column the category, the... |\n| `line` | no | `array<string>` | Combo charts only (type 'combo'): the plotted series, by column name, that are drawn as lines with markers; every other plotted series is drawn as clustered columns. Absent: the last plotted series is the line. A comb... |\n| `secondaryAxis` | no | `array<string>` | Combo charts only: line series, by column name, plotted against a secondary value axis at the right of the plot. That axis has its own scale, and its tick labels use the number format of the first secondary series' co... |\n| `axisTitles` | no | `ref:ChartAxisTitles` | Optional axis titles. Absent keeps today's untitled axes. Supported on the chart types that have a category/value (or X/Y) axis pair (column, bar, line, area, scatter, and the histogram, pareto, waterfall and box-and-... |\n| `legend` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Optional legend position. 'none' hides the legend. Absent keeps today's behaviour exactly (a legend at the right of multi-series charts and of pie and doughnut charts, none for single-series charts). A named position... |\n| `dataLabels` | no | `oneOf:boolean / ref:ChartDataLabels` | Optional data labels. true shows value labels at each type's default position, false (or absent) shows none, which is today's behaviour. Use the object form for the label content, position and separator. |\n| `alt` | no | `string` | Text alternative for the chart: what the data shows (the point and the key numbers), not 'a chart' or 'bar chart'. The preview exposes it as the chart's accessible name (role img with aria-label) and the PowerPoint ex... |\n| `colors` | no | `array<ref:ColorRef>` | Optional series colors, by position: entry i replaces palette color i, the color of series i (the i-th plotted value column), of slice or category i on a pie, doughnut, treemap or funnel chart, and of the i-th color a... |\n| `highlight` | no | `ref:ChartHighlight` | Optional emphasis: the series and/or categories that carry the message. The engines draw the highlighted marks in the deck's primary (accent) color and every other mark in a muted neutral derived from the theme (a lig... |\n\n\n### ChartAxisTitles\n\n- Type: `object`\n- Required fields: none\n- Purpose: Titles for the axes of a chart. 'category' is the axis that carries the row labels (the horizontal axis of a column or line chart, the vertical axis of a bar chart, the X axis of a scatter chart); 'value' is the other axis; 'secondary' is the secondary value axis of a combo chart.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | Title of the category (X) axis. |\n| `value` | no | `string` | Title of the value (Y) axis. On a combo chart, the primary (left) value axis. |\n| `secondary` | no | `string` | Combo charts only: title of the secondary value axis at the right of the plot, drawn rotated like the primary value axis title. Dropped with a 'chart-option-adapted' diagnostic on any other chart type and on a combo c... |\n\n\n### ChartHighlight\n\n- Type: `object`\n- Required fields: none\n- Purpose: Which series and categories a chart emphasizes. Names are matched exactly: series by data column name (after any 'fields' selection; a series is a plotted value column), categories by row label (the first column, or mapping.category), as text. A category label shared by several rows highlights each of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `series` | no | `array<string>` | Series (value columns) to highlight: all marks of each named series. A name that is not a column is a 'chart-highlight-unknown-name' error; a column that is not plotted as a series (the category or X column, or one ma... |\n| `categories` | no | `array<string>` | Categories (row labels) to highlight: the marks of each named category (a column or bar of every series, a line's points, a pie or doughnut slice). A label no row has is a 'chart-highlight-unknown-name' error. |\n\n\n### ChartDataLabels\n\n- Type: `object`\n- Required fields: none\n- Purpose: Data label settings. A label shows the selected content parts in the fixed order category, value, percent, joined by the separator.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `content` | no | `array<enum:value \\| percent \\| category>` | Which parts a label shows. Defaults to ['value']. 'percent' is the share of the total and exists only on pie and doughnut charts; 'category' shows the category name (the X value on a scatter chart). A part a chart typ... |\n| `position` | no | `enum:auto \\| center \\| inside-end \\| inside-base \\| outside-end \\| above \\| below \\| left \\| right` | Where a label sits relative to its mark. 'auto' (the default) is the type's default: outside-end for clustered columns and bars, pie slices and the histogram, pareto and waterfall constructs; center for stacked column... |\n| `separator` | no | `string` | Text between the parts of a label that shows more than one. Defaults to ', '. |\n\n\n### Table\n\n- Type: `object`\n- Required fields: none\n- Purpose: Table content, inline or dataset-backed. An inline table has 'rows' (required) and optional 'columns'. A dataset-backed table has 'dataset' (required) and optional 'fields', and no 'rows' or 'columns': it takes its headers, rows and column formats from the dataset (per-cell styles need an inline table).\n- Conditional requirement: `dataset`, or otherwise `rows`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | no | `array<oneOf:string / array<ref:TextRun> / ref:StyledTableCell / ref:DataColumn / null>` | Optional column labels. Labels may be strings, rich runs, styled cell objects or DataColumn objects ({ \"name\", \"format\" }). A header's 'format' (DataColumn or StyledTableCell) is the column's number format; a body cel... |\n| `rows` | no | `array<array<ref:TableCell>>` | Two-dimensional table row data; each row aligns by index with columns when columns are supplied. |\n| `dataset` | no | `ref:DatasetId` | Id of a top-level dataset that supplies this table's headers, rows and column formats. Excludes 'rows' and 'columns'. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | Dataset tables only: the dataset columns to show, by name and in order. Absent shows every column. An unknown name is a 'dataset-field-unknown' error. |\n| `alt` | no | `string` | Text alternative for the table: what it shows (the point and the key numbers), not 'a table'. The preview exposes it as the table's accessible name (role group with aria-label; the cells stay readable) and the PowerPo... |\n\n\n### ChartData\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: Inline tabular data driving a chart. The first column usually supplies category/x-axis labels; subsequent columns are plotted measures unless 'chart.mapping' or a chart type maps them differently. Value cells are numbers; a string is read only in strict decimal syntax ('12', '-3.5', '1e6'), and anything else ('12%', '$5', '(5)', '1,234') is a gap and a 'chart-value-not-numeric' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered column labels for the chart data table. A label is a string or a DataColumn ({ \"name\", \"format\" }) whose number format the data labels, value axis and exported workbook use. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Tabular chart rows. Each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance: where this inline data came from. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. |\n\n\n### DataColumn\n\n- Type: `object`\n- Required fields: `name`\n- Purpose: A named data column with an optional number format. Anywhere a chart or dataset column is a string it may be a DataColumn; the string form is { \"name\": value } with no format. A table column header may also be a DataColumn.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | yes | `string` | Column label, used as the series name, the table header and the name 'fields' and 'chart.mapping' address. |\n| `format` | no | `ref:NumberFormat` | Number format of the column's number values (data labels, value axis, table cells, exported workbook). Absent: the General form. |\n\n\n### DataSourceRef\n\n- Type: `object`\n- Required fields: `src`\n- Purpose: Provenance of inline data or a dataset: the file, sheet and range it came from and when. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. Re-import the data to update it.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | yes | `string` | Where the data came from: an 'asset:<id>' reference, a URL, or a relative or local path. |\n| `sheet` | no | `string` | Sheet or table name inside a spreadsheet source. |\n| `range` | no | `string` | A1-style range inside a spreadsheet source. |\n| `fields` | no | `array<string>` | The source fields the data was taken from, in order. |\n| `retrieved` | no | `anyOf:schema / schema` | When the data was taken from the source: an ISO 8601 date or date-time. |\n| `description` | no | `string` | Free-form note about the source. |\n\n\n### DatasetId\n\n- Type: `string`\n- Required fields: none\n- Purpose: Id of an entry in the top-level datasets map (the assets id pattern).\n\n_No named properties._\n\n\n### DatasetFields\n\n- Type: `array<string>`\n- Required fields: none\n- Purpose: Dataset column names to use, in order, each at most once. Selects and orders the dataset's columns; an unknown name is a 'dataset-field-unknown' error.\n\n_No named properties._\n\n\n### Datasets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Shared data tables keyed by id (the assets id pattern). Charts reference one with 'chart.data': { \"dataset\": \"<id>\" } and tables with 'table': { \"dataset\": \"<id>\" }.\n\n_No named properties._\n\n\n### Dataset\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: One shared data table: named columns (strings or DataColumn objects with a number format) and rows of scalar cells. Column names are unique ('data-column-duplicate' error). Rich text and styling belong in an inline table.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `title` | no | `string` | Human name of the dataset, shown by editors. |\n| `description` | no | `string` | What the dataset holds. |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered, uniquely named columns. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Rows of scalar cells (string, number, boolean, null); each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance. Engines never read, fetch or refresh it. |\n\n\n### DatasetRef\n\n- Type: `object`\n- Required fields: `dataset`\n- Purpose: Chart data taken from a top-level dataset. Engines inline it before plotting; 'chart.mapping' names columns after the 'fields' selection.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `dataset` | yes | `ref:DatasetId` | Id of the dataset. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | The dataset columns to use, by name and in order. Absent uses every column. |\n\n\n### ChartMapping\n\n- Type: `object`\n- Required fields: none\n- Purpose: Series mapping by column name (after any 'fields' selection). Mapping only selects and orders columns; pie, doughnut and other single-series constructs still plot one series. An unknown name is a 'chart-mapping-unknown-column' error; a series that repeats the category or X column, and an X column on a chart type without an X axis, are dropped with a 'chart-mapping-adapted' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | The label (category) column. Default: the first column. |\n| `x` | no | `string` | The X column of an XY (scatter) chart. Default: the second column (the first, when the category is the second column), and only when there are three or more columns: with two, the second column is the one series, plot... |\n| `series` | no | `array<string>` | The plotted columns, in order. Default: every column that is not the category or the X column. |\n\n\n### ChartDataCell\n\n- Type: `oneOf:string / number / boolean / null`\n- Required fields: none\n- Purpose: A cell in inline chart data.\n\n_No named properties._\n\n\n### TableCell\n\n- Type: `oneOf:ref:TableCellValue / ref:StyledTableCell`\n- Required fields: none\n- Purpose: A scalar, rich-run array, or styled/spanning cell object. Existing scalar and rich forms remain valid.\n\n_No named properties._\n\n\n### TableCellValue\n\n- Type: `oneOf:string / number / boolean / null / array<ref:TextRun>`\n- Required fields: none\n- Purpose: A scalar table value or canonical rich text runs, without cell decoration or geometry.\n\n_No named properties._\n\n\n### StyledTableCell\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: A cell with explicit visual style or merged geometry. Its position remains its array column index; use null placeholders for every covered grid position.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `ref:TableCellValue` | Editable cell content; styling and spans do not change its scalar type or rich runs. |\n| `style` | no | `ref:TableCellStyle` | |\n| `colSpan` | no | `integer` | Number of grid columns covered, starting at this cell. Covered positions must contain null. Default 1. |\n| `rowSpan` | no | `integer` | Number of grid rows covered, starting at this cell. Covered positions must contain null. Header cells cannot span into body rows. Default 1. |\n| `format` | no | `ref:NumberFormat` | Number format. On a body cell it formats that cell's number value (and wins over the column's format); on a header cell (in 'columns') it is the column's format, the same as a DataColumn header. Strings, booleans, nul... |\n\n\n### TableCellStyle\n\n- Type: `object`\n- Required fields: none\n- Purpose: Cell appearance. Sizes use reference pixels at a 720-pixel canvas short edge and scale with the slide.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `fill` | no | `ref:ColorRef` | Cell background: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `color` | no | `ref:ColorRef` | Default text color, overridden by individual rich run colors. Accepts a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `bold` | no | `boolean` | Bold cell text. Runs that set their own bold keep it; a number cell stays bold and still uses the cell's or column's number format. |\n| `align` | no | `enum:left \\| center \\| right` | Horizontal text alignment inside the cell. |\n| `verticalAlign` | no | `enum:top \\| middle \\| bottom` | Vertical alignment inside the padded cell box. |\n| `padding` | no | `ref:TableCellPadding` | |\n| `borders` | no | `object` | Independent cell edges. An omitted edge keeps the table's default rule: a line under every row (and over the first row), none between columns. Width 0 removes an edge. |\n\n\n### TableCellPadding\n\n- Type: `object`\n- Required fields: none\n- Purpose: Text insets in reference pixels. Defaults: top 8, right 10, bottom 4, left 10.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `top` | no | `number` | |\n| `right` | no | `number` | |\n| `bottom` | no | `number` | |\n| `left` | no | `number` | |\n\n\n### TableCellBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: One explicit cell border.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Border color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `width` | yes | `number` | Border width in reference pixels; 0 removes this edge. |\n| `dash` | no | `enum:solid \\| dash \\| dot` | Default solid. |\n\n\n### Catalogs\n\n- Type: `object`\n- Required fields: none\n- Purpose: The catalog records a document embeds, grouped by the catalog they came from. Each property is a group; inside a group, records are keyed by kind and then by id. - 'default': the catalog bare ids come from. It needs a 'source' when it holds records. Omitted: bare ids fall back to the host's default catalog. false: no catalog fallback, so every bare id must be embedded (under 'custom' or 'default'). - 'custom': the records the document defines itself. It has no source. - any other name ('acme'...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `default` | no | `oneOf:allOf:ref:CatalogGroup + schema / const:false` | The catalog bare ids come from: its source and the records of it this document embeds. false turns off the catalog fallback for bare ids. |\n| `custom` | no | `allOf:ref:CatalogGroup + schema` | Records the document defines itself, referenced by bare id. Checked first for every bare id. Forking a catalog record copies it here under a new id, so the change of ownership is visible. |\n\n\n### CatalogGroup\n\n- Type: `object`\n- Required fields: none\n- Purpose: One catalog group: the catalog's source and the records of it the document embeds, by kind and then by id.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | no | `ref:CatalogSource` | |\n| `layouts` | no | `ref:CatalogRecords` | Layout records keyed by id, each with the fields of https://openpresentation.org/schema/opf-layout/v2. Referenced from Slide.layout. |\n| `themes` | no | `ref:CatalogRecords` | Theme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-theme/v1. Referenced from design.theme. |\n| `colorSchemes` | no | `ref:CatalogRecords` | Colour-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-color-scheme/v1. Referenced from design.colorScheme and a theme's colorScheme. |\n| `fontSchemes` | no | `ref:CatalogRecords` | Font-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-font-scheme/v1. Referenced from design.fontScheme, a theme's fontScheme and a language's font schemes. |\n| `narratives` | no | `ref:CatalogRecords` | Narrative records keyed by id, each with the fields of https://openpresentation.org/schema/opf-narrative/v1. Referenced from narrative. |\n| `audiences` | no | `ref:CatalogRecords` | Audience records keyed by id, each with the fields of https://openpresentation.org/schema/opf-audience/v1. Referenced from audience. |\n| `purposes` | no | `ref:CatalogRecords` | Purpose records keyed by id, each with the fields of https://openpresentation.org/schema/opf-purpose/v1. Referenced from purpose. |\n| `tones` | no | `ref:CatalogRecords` | Tone records keyed by id, each with the fields of https://openpresentation.org/schema/opf-tone/v1. Referenced from tone. |\n\n\n### CatalogSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: Identity of a catalog: an HTTPS URL ('https://www.pptx.gallery') or a package reference ('pkg:@acme/opf-catalog'). It names the catalog; engines never fetch or install it. A host registers the catalog's records under this exact string, and copying slides between documents matches groups by it.\n\n_No named properties._\n\n\n### CatalogRecords\n\n- Type: `object`\n- Required fields: none\n- Purpose: Embedded records of one kind, keyed by id. The key is the record's id; an embedded record carries no '$schema', 'id', origin or digest. Catalog display metadata ('x-*' members) is stripped when a record is embedded.\n\n_No named properties._\n\n\n### CatalogReference\n\n- Type: `string`\n- Required fields: none\n- Purpose: A content reference: a bare id ('two-column') or 'name:id' ('acme:hero'), where the prefix names a group of 'catalogs'. URLs and 'pkg:' strings are not references; a named catalog group replaces them. See Catalogs for the resolution order.\n\n_No named properties._\n\n\n### LanguageTag\n\n- Type: `string`\n- Required fields: none\n- Purpose: A BCP-47 language tag ('en-US', 'ja', 'zh-Hans', 'sr-Latn-ME'), an engine vocabulary. Use 'en-GB' for UK English; 'en-UK' is not a valid region.\n\n_No named properties._\n\n\n### Composition\n\n- Type: `object`\n- Required fields: none\n- Purpose: Automatic slide and content-group arrangement. Nested content groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions. A layout defines its grid as one ordered list of regions; its composition supports gap, padding, minFontSize and overflow rather than mode, columns or weights.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `mode` | no | `enum:auto \\| grid \\| row \\| column` | auto chooses a grid from available space and content; grid uses columns; row and column use one horizontal or vertical track. |\n| `columns` | no | `integer` | Column count for grid. In auto mode this caps the number of columns. |\n| `gap` | no | `number` | Space between cells as a fraction of the container short edge (canvas at slide root). Default 0.03333333333333333. |\n| `padding` | no | `number` | Inset as a fraction of the container short edge. Default 0.08 on a slide, 0 inside a group. |\n| `weights` | no | `array<number>` | Relative track sizes: columns for row/grid/auto, rows for column. Omitted tracks have weight 1; extra weights are ignored. |\n| `minFontSize` | no | `number` | Minimum readable text size in reference pixels at a 720-pixel canvas short edge. Default 16. Overflow is diagnosed when text cannot fit at this size. |\n| `overflow` | no | `enum:warn \\| error` | warn returns diagnostics for content that does not fit; error rejects layout. Content is never silently removed. Default warn. |\n"
|
|
302
304
|
},
|
|
303
305
|
{
|
|
304
306
|
"slug": "security-2026-09-09",
|