@openpresentation/opf 0.17.0 → 0.18.1

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.
Files changed (66) hide show
  1. package/README.md +4 -4
  2. package/dist/browser.js +38 -0
  3. package/dist/{catalog-refs-C4K4y-vh.d.ts → catalog-refs-BSfGELpt.d.ts} +1 -1
  4. package/dist/{catalog-schemas-g7RFQBdc.d.ts → catalog-schemas-CinPh5WA.d.ts} +1 -1
  5. package/dist/catalog.d.ts +2 -2
  6. package/dist/{chart-data-DTxdRRT1.d.ts → chart-data-BnNqgnC-.d.ts} +1 -1
  7. package/dist/{chunk-O2CX5NEH.js → chunk-64YSJZDH.js} +6 -5
  8. package/dist/{chunk-RA64UQKI.js → chunk-6WW2EDUW.js} +95 -71
  9. package/dist/{chunk-IS5H5JKH.js → chunk-CJPDTLGD.js} +1 -1
  10. package/dist/{chunk-BIC3SWPK.js → chunk-DBYZI75U.js} +30 -5
  11. package/dist/{chunk-NLNYNBTS.js → chunk-JT3QXA22.js} +25 -24
  12. package/dist/{chunk-YT7FMQER.js → chunk-JXVS7BJK.js} +1 -1
  13. package/dist/{chunk-IPW5BS3X.js → chunk-KXBZ6ZSB.js} +190 -172
  14. package/dist/{chunk-CW7GEAV3.js → chunk-MBU42S5Y.js} +291 -133
  15. package/dist/{chunk-TCD2FB2K.js → chunk-NS3VYB57.js} +5 -5
  16. package/dist/{chunk-WWYWFVOI.js → chunk-QLFWWHRF.js} +151 -25
  17. package/dist/{chunk-PPROJP2E.js → chunk-RAKEVM52.js} +1 -1
  18. package/dist/{chunk-ESRYBYLY.js → chunk-VCGRMNY2.js} +1 -1
  19. package/dist/{chunk-M4UR3NPN.js → chunk-YWXB4XAT.js} +2 -2
  20. package/dist/{chunk-O3CVNJTL.js → chunk-YZXTXX2D.js} +1 -1
  21. package/dist/{chunk-O263APNI.js → chunk-ZCFSDSUX.js} +3 -3
  22. package/dist/{chunk-N4EVOFEA.js → chunk-ZOHFLP3O.js} +4 -4
  23. package/dist/{composition-BbZ_9C-c.d.ts → composition-C-OoqvAr.d.ts} +80 -40
  24. package/dist/composition.d.ts +3 -3
  25. package/dist/composition.js +1 -1
  26. package/dist/{conversion-SBpTOwdg.d.ts → conversion-C5foedh6.d.ts} +96 -38
  27. package/dist/conversion-KGDVJM75.js +21 -0
  28. package/dist/convert.d.ts +4 -4
  29. package/dist/convert.js +7 -7
  30. package/dist/data.d.ts +7 -7
  31. package/dist/data.js +2 -2
  32. package/dist/{deck-report-Bx3naFlF.d.ts → deck-report-wszX7uo8.d.ts} +5 -5
  33. package/dist/deck.d.ts +8 -8
  34. package/dist/deck.js +10 -10
  35. package/dist/diff.d.ts +1 -1
  36. package/dist/diff.js +5 -5
  37. package/dist/docs.js +28 -28
  38. package/dist/examples.d.ts +1 -1
  39. package/dist/examples.js +181 -214
  40. package/dist/{finding-CDQ_wH8j.d.ts → finding-CT_f_vbD.d.ts} +1 -1
  41. package/dist/format.js +2 -2
  42. package/dist/index.d.ts +153 -20
  43. package/dist/index.js +62 -15
  44. package/dist/markdown.d.ts +12 -8
  45. package/dist/markdown.js +7 -7
  46. package/dist/node-engine.d.ts +37 -17
  47. package/dist/node-engine.js +39 -16
  48. package/dist/{pagination-BhX4M48Y.d.ts → pagination-BaUjDrTz.d.ts} +7 -4
  49. package/dist/pagination.d.ts +4 -4
  50. package/dist/pagination.js +4 -4
  51. package/dist/patch.d.ts +1 -1
  52. package/dist/patch.js +4 -4
  53. package/dist/{presentation-fb8CZmRq.d.ts → presentation-HondgJ3l.d.ts} +269 -382
  54. package/dist/repo-readme.js +1 -1
  55. package/dist/schemas.d.ts +4 -2
  56. package/dist/schemas.js +1 -1
  57. package/dist/spec/schemas/opf.schema.json +65 -70
  58. package/dist/types.d.ts +3 -3
  59. package/dist/{validation-types-iZbXfES6.d.ts → validation-types-PbGDrNUr.d.ts} +12 -7
  60. package/dist/validator.d.ts +8 -8
  61. package/dist/validator.js +3 -3
  62. package/dist/yaml.d.ts +6 -6
  63. package/dist/yaml.js +7 -7
  64. package/package.json +12 -10
  65. package/dist/node.d.ts +0 -60
  66. package/dist/node.js +0 -54
@@ -1,5 +1,5 @@
1
1
  // src/generated/repo-readme.ts
2
- var repoReadmeRaw = '# Open Presentation Format (OPF)\n\n[![npm version](https://img.shields.io/npm/v/@openpresentation/opf?label=npm)](https://www.npmjs.com/package/@openpresentation/opf)\n[![npm downloads](https://img.shields.io/npm/dw/@openpresentation/opf)](https://www.npmjs.com/package/@openpresentation/opf)\n[![license](https://img.shields.io/npm/l/@openpresentation/opf)](./LICENSE)\n\nPublic npm package: [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf) (`npm install @openpresentation/opf`).\n\nOpen Presentation Format is the portable, human-readable JSON document format for slide decks.\n\nThis repository is the canonical home for the OPF **spec**, **JSON Schemas**, **catalog presets**, examples, generated developer types, local validation tooling, and integration docs for the render/edit/convert toolkit. OpenPresentation publishes open-source code and documentation only; it does not provide hosted APIs, hosted rendering functions, queues, storage, authentication, jobs, previews, SLAs, telemetry, or managed infrastructure.\n\nFor AI agents, use the [OPF skill set](docs/agent-skills.md) for authoring, layout, presets, editing, export, and schema inspection.\n\nPublished CLI 0.16.0 installs all six skills into your project with `npx @openpresentation/cli@0.16.0 skills install`. It uses local copies, preserves existing instructions and refuses to overwrite customized skills. See the [installation and update guide](docs/agent-skills.md) for personal or agent-specific targets and source development.\n\nFor a fresh Node 24 project that installs **published** packages (not this\ncheckout), follow the [developer quickstart](docs/quickstart.md) and the\n[compatibility matrix](docs/compatibility-matrix.md).\n\nFor LLM authoring, start with [the authoring guide](docs/llm-authoring.md), [dynamic composition](docs/dynamic-composition.md), and [local ecosystem verification](docs/ecosystem-development.md).\n\n## File naming\n\nOpen Presentation Format documents are JSON files. Use `*.opf.json` for complete OPF presentation documents, for example `board-review.opf.json` or `deck.opf.json`.\n\nAvoid using bare `*.opf` for OPF JSON. The `.opf` extension is already used by other document and project formats, while `.opf.json` keeps the OPF identity and still makes the underlying JSON format clear to editors, validators, agents, and version-control tooling.\n\n## Naming and reuse\n\nThe format name is **Open Presentation Format**. The schemas, catalogs, packages, and local tooling in this repository are free and open source under the MIT license, so third-party tools may read, write, validate, render, convert, and describe support for Open Presentation Format without adopting any product-specific branding.\n\n## Why OPF\n\n`.pptx` is a zipped bundle of XML. Humans can\'t diff it, LLMs can\'t read or write it reliably, and git can\'t track it meaningfully. Every change looks like a binary blob.\n\nOPF is plain JSON. A human can open it in an editor. A model can read and write it without guessing at schema-by-example. Decks live in git like the rest of your work.\n\nThat\'s the shift that lets LLMs actually *author* decks. When the format stops fighting them, models can do the work that matters \u2014 narrative structure, persuasive framing, data analysis, chart recommendations, ruthless revision passes \u2014 instead of wrestling with `<p:sp>` tags.\n\nAnd they don\'t start from a blank canvas. [pptx.gallery](https://pptx.gallery) is the human-browsable reference for OPF catalog presets: layouts, themes, color schemes, font schemes, chart types, narratives, audiences, purposes, tones, languages, and social platforms.\n\n## Start in three steps\n\n1. **Install the coordinated published packages** on Node 24. See [the developer quickstart](docs/quickstart.md) for the current pin set: core 0.16.0, renderer 0.16.0, editor 0.16.0, PPTX 0.16.1 and CLI 0.16.0.\n2. **Author, validate, paginate, preview and export.** Copy [`docs/quickstart/developer-quickstart.opf.json`](./docs/quickstart/developer-quickstart.opf.json) and run the commands in that guide. From code, `convert("deck.opf.md", "deck.pdf")` of `@openpresentation/opf/node` is the simple path to a PDF, PNG, SVG or PPTX file. `validate` / `opf validate` is one local checker for format, references, accessibility, layout and content ([guide](docs/validate.md)), not visual verification.\n3. **Know the limits.** The [compatibility matrix](docs/compatibility-matrix.md) lists shipped APIs versus renderer issue 24, native PowerPoint issue 87, and other deferred work. Browse presets at [pptx.gallery](https://pptx.gallery).\n\nYour deck can live in git from the first commit. After installing dependencies and supplying referenced assets, these commands run locally without a model provider, account or hosted OPF API.\n\n## JavaScript and TypeScript\n\nThe canonical JavaScript/TypeScript package is published at [`packages/javascript`](./packages/javascript) as [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf). The schema is pre-stable (0.x \u2014 expect breaking changes between minor versions until 1.0). Its responsibility is local and format-level only:\n\n- export the canonical schemas from [`spec/`](./spec)\n- export the pinned default catalog (the pptx.gallery snapshot in [`spec/catalogs/`](./spec/catalogs)) as the opt-in `@openpresentation/opf/catalog`, for hosts to register\n- export a typed raw spec file manifest for package-addressable `spec/` content\n- generate TypeScript types, with `Presentation` as the top-level type\n- validate OPF JSON and catalog records locally\n\nIts Node-only `@openpresentation/opf/node` entry reads and writes files (`convert`, `open`, `save`) and draws them through the optional engines below; the root and every other entry run in a browser. It does not generate content with AI, fetch remote catalogs, call hosted APIs, or provide managed services. Render/edit/convert packages live in separate MIT repos that depend on `@openpresentation/opf`. The format package also exposes pure composition geometry so those packages share layout behavior.\n\n## Toolkit libraries\n\nOPF is five packages. **Core** (`@openpresentation/opf`) is the format and its API: it reads and writes the text formats (JSON, YAML, Markdown) and validates, and `@openpresentation/opf/node` reads and writes files. **The CLI** (`@openpresentation/cli`) is the `opf` command. **Render** (`opf-render`), **PPTX** (`opf-pptx`) and **the editor** (`opf-editor`) are the engines: drawing, PowerPoint and editing, installed as needed. The engines and the editor live in sibling repositories. See [ecosystem development](docs/ecosystem-development.md) for coordinated builds and verification, and [dynamic composition](docs/dynamic-composition.md) for portable layout rules.\n\n| Repo | Role | Boundary |\n|---|---|---|\n| `cli` (`@openpresentation/cli`, this repository) | `opf` command | Runs core\'s `/node` engine: reads and writes files and calls the engines below as optional peers |\n| `opf-render` | OPF to SVG/PNG/PDF | Local and embeddable rendering library |\n| `opf-editor` | WYSIWYG bindings/components | Headless editor primitives plus optional UI components |\n| `opf-pptx` | OPF to PPTX and PPTX to OPF | Pure local import/export library for browser and server use where supported |\n\nThese repos provide OSS primitives only. Downstream applications own hosting, auth, storage, collaboration, queues, previews, analytics, support, and workflow UX.\n\n## Usage\n\nInstall from npm:\n\n```sh\npnpm add @openpresentation/opf\n# or: npm install @openpresentation/opf\n```\n\nTo work on the package itself, clone this repo and build the workspace:\n\n```sh\npnpm install\npnpm build\n```\n\nUse the format package from JavaScript or TypeScript:\n\n```ts\nimport { presentation, validate } from "@openpresentation/opf";\nimport { defaultCatalog } from "@openpresentation/opf/catalog";\n\nimport type { Presentation } from "@openpresentation/opf";\n\nconst deck: Presentation = {\n name: "Quarterly Review",\n slides: [{ title: "Quarterly Review", items: ["Revenue", "Product", "Hiring"] }],\n};\n\nconst report = validate(deck);\nconsole.log(report.valid); // no finding has severity "error"\nconsole.log(report.findings); // every finding: rule id, severity, category, JSON Pointer path, message, fixes\n// Core ships no catalog records in its main entry; a host registers the catalogs it trusts.\nconsole.log(validate({ ...deck, design: { theme: "classic" } }, { catalogs: [defaultCatalog] }).valid);\nconsole.log(Object.keys(defaultCatalog.audiences).length);\n```\n\nConvert, open and save files in Node with `@openpresentation/opf/node` (the engines `@openpresentation/opf-render` and `@openpresentation/opf-pptx` are optional peers, installed as needed; see [OPF files in Node](docs/node.md)):\n\n```ts\nimport * as opf from "@openpresentation/opf/node";\n\nawait opf.convert("deck.opf.md", "deck.pdf");\nconst deck = await opf.open("deck.opf.md");\ndeck.slides.push({ title: "Q4" });\nawait opf.save(deck, "deck.opf.md");\nconst { files } = await opf.convert(deck, { format: "pptx" }); // bytes, nothing written\n```\n\nUse focused imports when you only need one surface:\n\n```ts\nimport { presentation } from "@openpresentation/opf/schemas";\nimport { defaultCatalog } from "@openpresentation/opf/catalog";\nimport { specFileEntries } from "@openpresentation/opf/spec-files";\nimport { validate } from "@openpresentation/opf/validator";\nimport type { Presentation } from "@openpresentation/opf/types";\n```\n\nUse raw JSON when an engine or resolver needs package-addressable files:\n\n```ts\nimport presentationSchema from "@openpresentation/opf/spec/schemas/opf.schema.json" with {\n type: "json",\n};\n```\n\nUse the [installable OPF CLI](./packages/cli/README.md) to create, validate, and edit files locally:\n\n```sh\npnpm --filter @openpresentation/cli build\nnode packages/cli/dist/index.js create deck.opf.json --title "Decision brief"\nnode packages/cli/dist/index.js validate deck.opf.json\nnode packages/cli/dist/index.js edit deck.opf.json --patch changes.json --in-place\n```\n\nSee [CSV and JSON data import](./docs/data-import.md) for editable tables and charts in the editor, CLI, and package API, and [content conversions](./docs/conversions.md) for the pure converters (`@openpresentation/opf/convert`) that change a block\'s kind, nest list items, restructure a slide and split or merge slides, and [Markdown and outlines](./docs/markdown.md) for the deterministic Markdown dialect (`@openpresentation/opf/markdown`, `opf from-md`, `opf to-md`) that reads and writes a whole deck as text.\n\n[OPF as YAML](./docs/yaml.md) describes the strict YAML form of a deck (`@openpresentation/opf/yaml`, `opf from-yaml`, `opf to-yaml`, `.opf.yaml` files in every command, editor autocomplete); JSON stays the canonical form. A deck can equally be a Markdown file, `deck.opf.md`: every command reads and writes it (see [Markdown decks in every command](./docs/markdown.md#markdown-decks-in-every-command)), and `parse` / `stringify` read and write a deck in JSON, YAML or Markdown with one call.\n\nSee [Templates and variables](./docs/templates-and-variables.md) for fillable OPF templates: typed variables, `{{id}}` tokens, `opf fill` and `resolveVariables`.\n\n## Layout\n\n| Path | Contents |\n|---|---|\n| [`spec/schemas/opf.schema.json`](./spec/schemas/opf.schema.json) | Canonical JSON Schema for top-level OPF `Presentation` documents. |\n| [`docs/how-opf-works.md`](./docs/how-opf-works.md) | Conceptual introduction: the document model, content shapes, catalog resolution, and the validation philosophy. Start here. |\n| [`docs/design-resolution.md`](./docs/design-resolution.md) | The design precedence algorithm (slide design \u2192 deck design \u2192 resolved theme \u2192 engine defaults) with worked examples. |\n| [`docs/schema-reference.md`](./docs/schema-reference.md) | Author-facing reference for top-level OPF fields and every presentation schema `$defs` object/type. |\n| [`docs/catalog-schema-reference.md`](./docs/catalog-schema-reference.md) | Author-facing reference for every companion catalog schema. |\n| [`docs/content-payloads.md`](./docs/content-payloads.md) | Author-facing notes for slide and region content payloads, including chart and table object shapes. |\n| [`docs/footnotes-citations-captions.md`](./docs/footnotes-citations-captions.md) | Citations (`cite`), inline footnotes, the deck `references` list, captions on images, charts, tables and videos, and how every engine draws them. |\n| [`docs/examples.md`](./docs/examples.md) | Guide to the expanded scenario-oriented examples under `examples/gallery/`. |\n| [`docs/live-editor.md`](./docs/live-editor.md) | Browser canvas, live OPF editing, font loading, published packages, and current fidelity limits. |\n| [`docs/release-process.md`](./docs/release-process.md) | Maintainer runbook for tagging, trusted npm publishing, verification, and GitHub release notes. |\n| [`spec/schemas/*.schema.json`](./spec/schemas) | Companion schemas for catalog records and sub-objects. |\n| [`spec/catalogs/<catalog-kind>/`](./spec/catalogs) | The pinned snapshot of the default catalog pptx.gallery publishes (shipped as `@openpresentation/opf/catalog`). |\n| [`spec/openapi.yaml`](./spec/openapi.yaml) | Optional reference OpenAPI contract for downstream services that choose to expose OPF over HTTP. OpenPresentation does not host this API. |\n| [`examples/technical/`](./examples/technical) | Focused OPF fixtures for validator, renderer, catalog-resolution, design, content-payload, and region behavior. |\n| [`examples/gallery/`](./examples/gallery) | Broader OPF example decks organized by industry, function, education, government, presentation type, international, and design/media scenarios. |\n| [`packages/javascript/`](./packages/javascript) | Public pre-stable source for `@openpresentation/opf`. |\n| [`packages/cli/`](./packages/cli) | Installable local CLI for creating, validating, editing, and inspecting OPF. |\n| [`legacy/`](./legacy) | Tombstone for service-specific clients, CLIs, tool integrations, and workflows removed from the OpenPresentation OSS repo. |\n\n## OpenPresentation Boundary\n\nOpenPresentation defines the format, bundled presets, local validation, examples, docs, and planned local render/edit/convert libraries. It does not provide hosted functions or managed product surfaces.\n\nFuture non-JavaScript OPF packages should follow the same local-first boundary: Python and Go packages should expose schemas, types/models, catalogs, validation, and package-addressable assets. Future toolkit packages should expose embeddable library APIs with no required network calls, hosted callbacks, hidden telemetry, or managed infrastructure assumptions.\n\nThe published JavaScript package copies package-addressable OPF schemas, catalogs, reference assets, and the optional reference `spec/openapi.yaml` from `spec/`. It intentionally remains `@openpresentation/opf` instead of introducing a separate `@openpresentation/opf-spec` package so downstream imports can advance by semver-pinning one canonical package.\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n';
2
+ var repoReadmeRaw = '# Open Presentation Format (OPF)\n\n[![npm version](https://img.shields.io/npm/v/@openpresentation/opf?label=npm)](https://www.npmjs.com/package/@openpresentation/opf)\n[![npm downloads](https://img.shields.io/npm/dw/@openpresentation/opf)](https://www.npmjs.com/package/@openpresentation/opf)\n[![license](https://img.shields.io/npm/l/@openpresentation/opf)](./LICENSE)\n\nPublic npm package: [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf) (`npm install @openpresentation/opf`).\n\nOpen Presentation Format is the portable, human-readable JSON document format for slide decks.\n\nThis repository is the canonical home for the OPF **spec**, **JSON Schemas**, **catalog presets**, examples, generated developer types, local validation tooling, and integration docs for the render/edit/convert toolkit. OpenPresentation publishes open-source code and documentation only; it does not provide hosted APIs, hosted rendering functions, queues, storage, authentication, jobs, previews, SLAs, telemetry, or managed infrastructure.\n\nFor AI agents, use the [OPF skill set](docs/agent-skills.md) for authoring, layout, presets, editing, export, and schema inspection.\n\nPublished CLI 0.17.1 installs all six skills into your project with `npx @openpresentation/cli@0.17.1 skills install`. It uses local copies, preserves existing instructions and refuses to overwrite customized skills. See the [installation and update guide](docs/agent-skills.md) for personal or agent-specific targets and source development.\n\nFor a fresh Node 24 project that installs **published** packages (not this\ncheckout), follow the [developer quickstart](docs/quickstart.md) and the\n[compatibility matrix](docs/compatibility-matrix.md).\n\nFor LLM authoring, start with [the authoring guide](docs/llm-authoring.md), [dynamic composition](docs/dynamic-composition.md), and [local ecosystem verification](docs/ecosystem-development.md).\n\n## File naming\n\nOpen Presentation Format documents are JSON files. Use `*.opf.json` for complete OPF presentation documents, for example `board-review.opf.json` or `deck.opf.json`.\n\nAvoid using bare `*.opf` for OPF JSON. The `.opf` extension is already used by other document and project formats, while `.opf.json` keeps the OPF identity and still makes the underlying JSON format clear to editors, validators, agents, and version-control tooling.\n\n## Naming and reuse\n\nThe format name is **Open Presentation Format**. The schemas, catalogs, packages, and local tooling in this repository are free and open source under the MIT license, so third-party tools may read, write, validate, render, convert, and describe support for Open Presentation Format without adopting any product-specific branding.\n\n## Why OPF\n\n`.pptx` is a zipped bundle of XML. Humans can\'t diff it, LLMs can\'t read or write it reliably, and git can\'t track it meaningfully. Every change looks like a binary blob.\n\nOPF is plain JSON. A human can open it in an editor. A model can read and write it without guessing at schema-by-example. Decks live in git like the rest of your work.\n\nThat\'s the shift that lets LLMs actually *author* decks. When the format stops fighting them, models can do the work that matters \u2014 narrative structure, persuasive framing, data analysis, chart recommendations, ruthless revision passes \u2014 instead of wrestling with `<p:sp>` tags.\n\nAnd they don\'t start from a blank canvas. [pptx.gallery](https://pptx.gallery) is the human-browsable reference for OPF catalog presets: layouts, themes, color schemes, font schemes, chart types, narratives, audiences, purposes, tones, languages, and social platforms.\n\n## Start in three steps\n\n1. **Install the coordinated published packages** on Node 24. See [the developer quickstart](docs/quickstart.md) for the current pin set: core 0.17.0, renderer 0.17.1, editor 0.17.0, PPTX 0.17.0 and CLI 0.17.1.\n2. **Author, validate, paginate, preview and export.** Copy [`docs/quickstart/developer-quickstart.opf.json`](./docs/quickstart/developer-quickstart.opf.json) and run the commands in that guide. From code, `convert("deck.opf.md", "deck.pdf")` of `@openpresentation/opf` is the simple path to a PDF, PNG, SVG or PPTX file. `validate` / `opf validate` is one local checker for format, references, accessibility, layout and content ([guide](docs/validate.md)), not visual verification.\n3. **Know the limits.** The [compatibility matrix](docs/compatibility-matrix.md) lists shipped APIs versus renderer issue 24, native PowerPoint issue 87, and other deferred work. Browse presets at [pptx.gallery](https://pptx.gallery).\n\nYour deck can live in git from the first commit. After installing dependencies and supplying referenced assets, these commands run locally without a model provider, account or hosted OPF API.\n\n## JavaScript and TypeScript\n\nThe canonical JavaScript/TypeScript package is published at [`packages/javascript`](./packages/javascript) as [`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf). The schema is pre-stable (0.x \u2014 expect breaking changes between minor versions until 1.0). Its responsibility is local and format-level only:\n\n- export the canonical schemas from [`spec/`](./spec)\n- export the pinned default catalog (the pptx.gallery snapshot in [`spec/catalogs/`](./spec/catalogs)) as the opt-in `@openpresentation/opf/catalog`, for hosts to register\n- export a typed raw spec file manifest for package-addressable `spec/` content\n- generate TypeScript types, with `Presentation` as the top-level type\n- validate OPF JSON and catalog records locally\n\nIn Node (and Bun and Deno) its root also reads and writes files (`convert`, `open`, `save`) and draws them through the optional engines below; in a browser or a worker the same import is a browser-safe build with the same names, where those three reject with `node-only`. Every other entry runs in a browser. It does not generate content with AI, fetch remote catalogs, call hosted APIs, or provide managed services. Render/edit/convert packages live in separate MIT repos that depend on `@openpresentation/opf`. The format package also exposes pure composition geometry so those packages share layout behavior.\n\n## Toolkit libraries\n\nOPF is five packages. **Core** (`@openpresentation/opf`) is the format and its API: it reads and writes the text formats (JSON, YAML, Markdown) and validates, and in Node reads and writes files, through the same import. **The CLI** (`@openpresentation/cli`) is the `opf` command. **Render** (`opf-render`), **PPTX** (`opf-pptx`) and **the editor** (`opf-editor`) are the engines: drawing, PowerPoint and editing, installed as needed. The engines and the editor live in sibling repositories. See [ecosystem development](docs/ecosystem-development.md) for coordinated builds and verification, and [dynamic composition](docs/dynamic-composition.md) for portable layout rules.\n\n| Repo | Role | Boundary |\n|---|---|---|\n| `cli` (`@openpresentation/cli`, this repository) | `opf` command | Runs core\'s Node engine: reads and writes files and calls the engines below as optional peers |\n| `opf-render` | OPF to SVG/PNG/PDF | Local and embeddable rendering library |\n| `opf-editor` | WYSIWYG bindings/components | Headless editor primitives plus optional UI components |\n| `opf-pptx` | OPF to PPTX and PPTX to OPF | Pure local import/export library for browser and server use where supported |\n\nThese repos provide OSS primitives only. Downstream applications own hosting, auth, storage, collaboration, queues, previews, analytics, support, and workflow UX.\n\n## Usage\n\nInstall from npm:\n\n```sh\npnpm add @openpresentation/opf\n# or: npm install @openpresentation/opf\n```\n\nTo work on the package itself, clone this repo and build the workspace:\n\n```sh\npnpm install\npnpm build\n```\n\nUse the format package from JavaScript or TypeScript:\n\n```ts\nimport { presentation, validate } from "@openpresentation/opf";\nimport { defaultCatalog } from "@openpresentation/opf/catalog";\n\nimport type { Presentation } from "@openpresentation/opf";\n\nconst deck: Presentation = {\n name: "Quarterly Review",\n slides: [{ title: "Quarterly Review", items: ["Revenue", "Product", "Hiring"] }],\n};\n\nconst report = validate(deck);\nconsole.log(report.valid); // no finding has severity "error"\nconsole.log(report.findings); // every finding: rule id, severity, category, JSON Pointer path, message, fixes\n// Core ships no catalog records in its main entry; a host registers the catalogs it trusts.\nconsole.log(validate({ ...deck, design: { theme: "classic" } }, { catalogs: [defaultCatalog] }).valid);\nconsole.log(Object.keys(defaultCatalog.audiences).length);\n```\n\nConvert, open and save files in Node with `@openpresentation/opf`, the same import a browser uses (the engines `@openpresentation/opf-render` and `@openpresentation/opf-pptx` are optional peers, installed as needed; see [OPF files in Node](docs/node.md)):\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf");\nconst deck = await opf.open("deck.opf.md");\ndeck.slides.push({ title: "Q4" });\nawait opf.save(deck, "deck.opf.md");\nconst { files } = await opf.convert(deck, { format: "pptx" }); // bytes, nothing written\n```\n\nUse focused imports when you only need one surface:\n\n```ts\nimport { presentation } from "@openpresentation/opf/schemas";\nimport { defaultCatalog } from "@openpresentation/opf/catalog";\nimport { specFileEntries } from "@openpresentation/opf/spec-files";\nimport { validate } from "@openpresentation/opf/validator";\nimport type { Presentation } from "@openpresentation/opf/types";\n```\n\nUse raw JSON when an engine or resolver needs package-addressable files:\n\n```ts\nimport presentationSchema from "@openpresentation/opf/spec/schemas/opf.schema.json" with {\n type: "json",\n};\n```\n\nUse the [installable OPF CLI](./packages/cli/README.md) to create, validate, and edit files locally:\n\n```sh\npnpm --filter @openpresentation/cli build\nnode packages/cli/dist/index.js create deck.opf.json --title "Decision brief"\nnode packages/cli/dist/index.js validate deck.opf.json\nnode packages/cli/dist/index.js edit deck.opf.json --patch changes.json --in-place\n```\n\nSee [CSV and JSON data import](./docs/data-import.md) for editable tables and charts in the editor, CLI, and package API, and [content conversions](./docs/conversions.md) for the pure converters (`@openpresentation/opf/convert`) that change a block\'s kind, nest list items, restructure a slide and split or merge slides, and [Markdown and outlines](./docs/markdown.md) for the deterministic Markdown dialect (`@openpresentation/opf/markdown`, `opf convert deck.md deck.opf.json`) that reads and writes a whole deck as text.\n\n[OPF as YAML](./docs/yaml.md) describes the strict YAML form of a deck (`@openpresentation/opf/yaml`, `opf convert deck.opf.json deck.opf.yaml`, `.opf.yaml` files in every command, editor autocomplete); JSON stays the canonical form. A deck can equally be a Markdown file, `deck.opf.md`: every command reads and writes it (see [Markdown decks in every command](./docs/markdown.md#markdown-decks-in-every-command)), and `parse` / `stringify` read and write a deck in JSON, YAML or Markdown with one call.\n\nSee [Templates and variables](./docs/templates-and-variables.md) for fillable OPF templates: typed variables, `{{id}}` tokens, `opf fill` and `resolveVariables`.\n\n## Layout\n\n| Path | Contents |\n|---|---|\n| [`spec/schemas/opf.schema.json`](./spec/schemas/opf.schema.json) | Canonical JSON Schema for top-level OPF `Presentation` documents. |\n| [`docs/how-opf-works.md`](./docs/how-opf-works.md) | Conceptual introduction: the document model, content shapes, catalog resolution, and the validation philosophy. Start here. |\n| [`docs/design-resolution.md`](./docs/design-resolution.md) | The design precedence algorithm (slide design \u2192 deck design \u2192 resolved theme \u2192 engine defaults) with worked examples. |\n| [`docs/schema-reference.md`](./docs/schema-reference.md) | Author-facing reference for top-level OPF fields and every presentation schema `$defs` object/type. |\n| [`docs/catalog-schema-reference.md`](./docs/catalog-schema-reference.md) | Author-facing reference for every companion catalog schema. |\n| [`docs/content-payloads.md`](./docs/content-payloads.md) | Author-facing notes for slide and region content payloads, including chart and table object shapes. |\n| [`docs/footnotes-citations-captions.md`](./docs/footnotes-citations-captions.md) | Citations (`cite`), inline footnotes, the deck `references` list, captions on images, charts, tables and videos, and how every engine draws them. |\n| [`docs/examples.md`](./docs/examples.md) | Guide to the expanded scenario-oriented examples under `examples/gallery/`. |\n| [`docs/live-editor.md`](./docs/live-editor.md) | Browser canvas, live OPF editing, font loading, published packages, and current fidelity limits. |\n| [`docs/release-process.md`](./docs/release-process.md) | Maintainer runbook for tagging, trusted npm publishing, verification, and GitHub release notes. |\n| [`spec/schemas/*.schema.json`](./spec/schemas) | Companion schemas for catalog records and sub-objects. |\n| [`spec/catalogs/<catalog-kind>/`](./spec/catalogs) | The pinned snapshot of the default catalog pptx.gallery publishes (shipped as `@openpresentation/opf/catalog`). |\n| [`spec/openapi.yaml`](./spec/openapi.yaml) | Optional reference OpenAPI contract for downstream services that choose to expose OPF over HTTP. OpenPresentation does not host this API. |\n| [`examples/technical/`](./examples/technical) | Focused OPF fixtures for validator, renderer, catalog-resolution, design, content-payload, and region behavior. |\n| [`examples/gallery/`](./examples/gallery) | Broader OPF example decks organized by industry, function, education, government, presentation type, international, and design/media scenarios. |\n| [`packages/javascript/`](./packages/javascript) | Public pre-stable source for `@openpresentation/opf`. |\n| [`packages/cli/`](./packages/cli) | Installable local CLI for creating, validating, editing, and inspecting OPF. |\n| [`legacy/`](./legacy) | Tombstone for service-specific clients, CLIs, tool integrations, and workflows removed from the OpenPresentation OSS repo. |\n\n## OpenPresentation Boundary\n\nOpenPresentation defines the format, bundled presets, local validation, examples, docs, and planned local render/edit/convert libraries. It does not provide hosted functions or managed product surfaces.\n\nFuture non-JavaScript OPF packages should follow the same local-first boundary: Python and Go packages should expose schemas, types/models, catalogs, validation, and package-addressable assets. Future toolkit packages should expose embeddable library APIs with no required network calls, hosted callbacks, hidden telemetry, or managed infrastructure assumptions.\n\nThe published JavaScript package copies package-addressable OPF schemas, catalogs, reference assets, and the optional reference `spec/openapi.yaml` from `spec/`. It intentionally remains `@openpresentation/opf` instead of introducing a separate `@openpresentation/opf-spec` package so downstream imports can advance by semver-pinning one canonical package.\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n';
3
3
 
4
4
  // src/repo-readme.ts
5
5
  var repoReadme = repoReadmeRaw;
package/dist/schemas.d.ts CHANGED
@@ -43,7 +43,8 @@ declare const presentation: JsonSchemaDocument & {
43
43
  readonly "ImageBorder": JsonObject;
44
44
  readonly "ImageRecolor": JsonObject;
45
45
  readonly "PatternBackground": JsonObject;
46
- readonly "LogoSet": JsonObject;
46
+ readonly "OrganizationLogo": JsonObject;
47
+ readonly "LogoSource": JsonObject;
47
48
  readonly "Watermark": JsonObject;
48
49
  readonly "HeaderFooter": JsonObject;
49
50
  readonly "HeaderFooterItem": JsonObject;
@@ -179,7 +180,8 @@ declare const schemas: {
179
180
  readonly "ImageBorder": JsonObject;
180
181
  readonly "ImageRecolor": JsonObject;
181
182
  readonly "PatternBackground": JsonObject;
182
- readonly "LogoSet": JsonObject;
183
+ readonly "OrganizationLogo": JsonObject;
184
+ readonly "LogoSource": JsonObject;
183
185
  readonly "Watermark": JsonObject;
184
186
  readonly "HeaderFooter": JsonObject;
185
187
  readonly "HeaderFooterItem": JsonObject;
package/dist/schemas.js CHANGED
@@ -1 +1 @@
1
- export { audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './chunk-RA64UQKI.js';
1
+ export { audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './chunk-6WW2EDUW.js';
@@ -36,7 +36,7 @@
36
36
  "minItems": 1
37
37
  }
38
38
  ],
39
- "description": "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) supplies the default deck logo, the 'socials' header/footer field and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
39
+ "description": "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) supplies the cover and section logo, the 'socials' header/footer field and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
40
40
  },
41
41
  "speaker": {
42
42
  "oneOf": [
@@ -494,7 +494,7 @@
494
494
  },
495
495
  "Organization": {
496
496
  "type": "object",
497
- "description": "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 default deck logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'var:organization.logo', 'organization.<id>.<field>'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
497
+ "description": "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.<id>.logo'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
498
498
  "required": ["id", "name"],
499
499
  "properties": {
500
500
  "id": {
@@ -514,12 +514,16 @@
514
514
  "examples": ["Acme Corporation, Inc.", "Beta Industries, LLC"]
515
515
  },
516
516
  "logo": {
517
- "$ref": "#/$defs/Asset",
518
- "description": "Source for the organization's logo image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are SVG (preferred for vector logos), PNG (with transparency), or JPG. The primary organization's logo (role 'primary', else the first organization) is the fallback source wherever the deck logo is drawn: a slide's design.logo, then design.logo, then this value. It is therefore drawn on cover and section slides and by header/footer zones with logo: true, exactly as design.logo would be (see design.logo and docs/design-resolution.md). It is also the built-in image variable 'organization.logo' (or 'organization.<id>.logo'), placed with 'var:organization.logo' in an image field.",
517
+ "oneOf": [
518
+ { "$ref": "#/$defs/Asset" },
519
+ { "$ref": "#/$defs/OrganizationLogo" }
520
+ ],
521
+ "description": "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 path is resolved against the OPF file location; an HTTPS URL, data URI or 'asset:<id>' reference works too. SVG (preferred for vector logos) or PNG with transparency are typical. The primary organization's full logo (role 'primary', else the first organization) is drawn on cover and section slides unless design.logo names another or is false. Every logo is also a slide-scoped built-in image, placed with a whole-field reference in any image field: 'var:organization.logo' (the full logo), 'var:organization.logo.icon' (likewise stacked, wordmark, full), and 'var:organization.<id>.logo' or 'var:organization.<id>.logo.<shape>' for another organization. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon. The onLight or onDark value is chosen for each slide's background; a missing one uses the other. See docs/templates-and-variables.md and docs/design-resolution.md.",
519
522
  "examples": [
523
+ "./assets/acme-logo.svg",
524
+ { "full": "./assets/acme-logo.svg", "icon": "./assets/acme-mark.svg" },
525
+ { "full": { "onLight": "./assets/acme-logo.svg", "onDark": "./assets/acme-logo-white.svg" }, "icon": "./assets/acme-mark.svg" },
520
526
  "https://cdn.acme.com/logo.svg",
521
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9V3iWggAAAAASUVORK5CYII=",
522
- "./assets/logo.jpg",
523
527
  "asset:acme-logo"
524
528
  ]
525
529
  },
@@ -547,7 +551,7 @@
547
551
  "role": {
548
552
  "type": "string",
549
553
  "enum": ["primary", "partner", "client", "sponsor", "host"],
550
- "description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (deck logo, the 'socials' header/footer field, and the 'organization.<field>' built-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
554
+ "description": "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-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
551
555
  },
552
556
  "socials": {
553
557
  "$ref": "#/$defs/Socials",
@@ -691,14 +695,14 @@
691
695
  },
692
696
  "logo": {
693
697
  "oneOf": [
694
- { "$ref": "#/$defs/Asset" },
695
- { "$ref": "#/$defs/LogoSet" }
698
+ {
699
+ "type": "string",
700
+ "pattern": "^var:organization(\\.[A-Za-z0-9_-]+)?\\.logo(\\.(full|stacked|icon|wordmark))?$"
701
+ },
702
+ { "type": "boolean", "const": false }
696
703
  ],
697
- "description": "Deck logo assets used by covers, section dividers, headers, footers and picture bullets. A string or Asset object is the default logo source; the LogoSet object form provides light/dark, stacked, icon, and wordmark variants (see LogoSet for the selection order). Source precedence: a slide's design.logo, then this value, then the primary organization's logo; absence inherits. Placement (reference engines, vetoable): on a cover or section slide (no body payload on a heading-only layout such as title, title-subtitle or section-divider, or no layout) the lockup variant is drawn at the top-left of the free area, inside the slide padding and below any header furniture, in a box 56 reference pixels tall and at most four times as wide; the tag/title/subtitle group then centers in the remaining span. Content slides never get an automatic logo. Header and footer zones draw the icon variant when they set logo: true, and design.listBullet 'image' draws it as the picture bullet. composeSlide returns the cover box as geometry.logo.",
698
- "examples": [
699
- "asset:acme-logo",
700
- { "default": "asset:acme-logo", "light": "asset:acme-logo-white", "icon": "asset:acme-icon" }
701
- ]
704
+ "description": "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 its icon. A logo reference overrides that: 'var:organization.beta.logo' draws organization beta's logo, and a reference that names a shape ('var:organization.logo.wordmark') draws that shape everywhere this field applies. false draws no logo. A slide's design.logo overrides the deck's; absence inherits. An override naming an organization without a logo draws nothing (validation warns). Placement (reference engines, vetoable): on a cover or section slide (no body payload on a heading-only layout such as title, title-subtitle or section-divider, or no layout) the logo is drawn at the top-left of the free area, inside the slide padding and below any header furniture, in a box 56 reference pixels tall and at most four times as wide; the tag/title/subtitle group then centers in the remaining span. Content slides never get an automatic logo; a header or footer zone places one with image: 'var:organization.logo.icon'. composeSlide returns the cover box as geometry.logo.",
705
+ "examples": ["var:organization.logo.wordmark", "var:organization.beta.logo", false]
702
706
  },
703
707
  "watermark": {
704
708
  "oneOf": [
@@ -715,7 +719,7 @@
715
719
  { "$ref": "#/$defs/HeaderFooter" }
716
720
  ],
717
721
  "description": "Repeated header furniture rendered outside the main slide content. Use false to suppress an inherited header.",
718
- "examples": [false, { "left": { "text": "{{slide.section}}" }, "right": { "image": "asset:acme-logo" } }]
722
+ "examples": [false, { "left": { "text": "{{slide.section}}" }, "right": { "image": "var:organization.logo.icon" } }]
719
723
  },
720
724
  "footer": {
721
725
  "oneOf": [
@@ -725,7 +729,7 @@
725
729
  "description": "Repeated footer furniture rendered outside the main slide content. Use false to suppress an inherited footer.",
726
730
  "examples": [
727
731
  false,
728
- { "left": { "image": "asset:acme-logo" }, "center": { "text": "Confidential" }, "right": { "text": "{{slide.number}}" } }
732
+ { "left": { "image": "var:organization.logo.icon" }, "center": { "text": "Confidential" }, "right": { "text": "{{slide.number}}" } }
729
733
  ]
730
734
  },
731
735
  "titleAlignment": {
@@ -760,7 +764,7 @@
760
764
  "listBullet": {
761
765
  "type": "string",
762
766
  "enum": ["character", "image"],
763
- "description": "Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the layout record's design.listBullet, then 'character'. 'character' (the default) draws the glyph marker. 'image' draws the deck's icon logo (a slide's design.logo, then design.logo, then the primary organization's logo; light variants on dark backgrounds) as a picture bullet: a square of the marker's font size whose bottom sits on the marker baseline, so list geometry does not change. Without a logo the glyph is drawn and the engine reports unresolved-content at this field."
767
+ "description": "Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the layout record's design.listBullet, then 'character'. 'character' (the default) draws the glyph marker. 'image' draws the primary organization's icon logo (or the organization and shape design.logo names; the onDark value on dark backgrounds) as a picture bullet: a square of the marker's font size whose bottom sits on the marker baseline, so list geometry does not change. Without a logo the glyph is drawn and the engine reports unresolved-content at this field."
764
768
  }
765
769
  }
766
770
  },
@@ -1027,7 +1031,7 @@
1027
1031
  "additionalProperties": {
1028
1032
  "$ref": "#/$defs/Variable"
1029
1033
  },
1030
- "description": "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: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone|logo' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. See docs/templates-and-variables.md.",
1034
+ "description": "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: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. Organization logos are slide-scoped whole-field images: 'var:organization.logo', 'var:organization.logo.<shape>' (full, stacked, icon, wordmark), 'var:organization.<id>.logo' and 'var:organization.<id>.logo.<shape>' resolve per slide to the onLight or onDark asset for its background. See docs/templates-and-variables.md.",
1031
1035
  "examples": [
1032
1036
  {
1033
1037
  "risk": "#B42318",
@@ -1732,60 +1736,55 @@
1732
1736
  }
1733
1737
  }
1734
1738
  },
1735
- "LogoSet": {
1739
+ "OrganizationLogo": {
1736
1740
  "type": "object",
1737
- "description": "Deck logo variants surfaced by covers, section dividers, headers, footers and picture bullets. Organization identity lives in organization; this object only controls visual rendering assets. Engines select one variant per slot and background tone (resolveLogo in @openpresentation/opf): same-tone variants first, neutral ones next, the opposite tone last. Lockup on a dark background: light, default, stackedLight, stacked, wordmarkLight, wordmark, iconLight, icon, then dark, stackedDark, wordmarkDark, iconDark; on a light background: dark, default, stackedDark, stacked, wordmarkDark, wordmark, iconDark, icon, then light, stackedLight, wordmarkLight, iconLight. The icon slot (headers, footers, picture bullets) tries iconLight or iconDark for the tone, then icon, then the lockup chain; the stacked slot tries stackedLight or stackedDark, then stacked, then the lockup chain.",
1741
+ "description": "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'.",
1738
1742
  "additionalProperties": false,
1743
+ "minProperties": 1,
1739
1744
  "properties": {
1740
- "default": {
1741
- "$ref": "#/$defs/Asset",
1742
- "description": "Default full-lockup logo. Used as fallback when no more specific variant is set."
1743
- },
1744
- "light": {
1745
- "$ref": "#/$defs/Asset",
1746
- "description": "Light-colored full-lockup logo intended for rendering on dark backgrounds."
1747
- },
1748
- "dark": {
1749
- "$ref": "#/$defs/Asset",
1750
- "description": "Dark-colored full-lockup logo intended for rendering on light backgrounds."
1745
+ "full": {
1746
+ "$ref": "#/$defs/LogoSource",
1747
+ "description": "The full logo (lockup): mark and name side by side. Drawn on cover and section slides, and placed by 'var:organization.logo'."
1751
1748
  },
1752
1749
  "stacked": {
1753
- "$ref": "#/$defs/Asset",
1754
- "description": "Stacked vertical logo lockup, suited to portrait or square brand-mark slots."
1755
- },
1756
- "stackedLight": {
1757
- "$ref": "#/$defs/Asset",
1758
- "description": "Light-colored stacked logo variant intended for rendering on dark backgrounds."
1759
- },
1760
- "stackedDark": {
1761
- "$ref": "#/$defs/Asset",
1762
- "description": "Dark-colored stacked logo variant intended for rendering on light backgrounds."
1750
+ "$ref": "#/$defs/LogoSource",
1751
+ "description": "The stacked lockup: mark above the name, for portrait or square slots. Placed by 'var:organization.logo.stacked'."
1763
1752
  },
1764
1753
  "icon": {
1765
- "$ref": "#/$defs/Asset",
1766
- "description": "Default icon, mark, or symbol without wordmark. Useful for tight spaces such as footers, badges, and slide-corner marks."
1767
- },
1768
- "iconLight": {
1769
- "$ref": "#/$defs/Asset",
1770
- "description": "Light-colored icon variant intended for rendering on dark backgrounds."
1771
- },
1772
- "iconDark": {
1773
- "$ref": "#/$defs/Asset",
1774
- "description": "Dark-colored icon variant intended for rendering on light backgrounds."
1754
+ "$ref": "#/$defs/LogoSource",
1755
+ "description": "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'."
1775
1756
  },
1776
1757
  "wordmark": {
1777
- "$ref": "#/$defs/Asset",
1778
- "description": "Default wordmark: the organization name set in branded typography, without icon."
1779
- },
1780
- "wordmarkLight": {
1781
- "$ref": "#/$defs/Asset",
1782
- "description": "Light-colored wordmark variant intended for rendering on dark backgrounds."
1783
- },
1784
- "wordmarkDark": {
1785
- "$ref": "#/$defs/Asset",
1786
- "description": "Dark-colored wordmark variant intended for rendering on light backgrounds."
1758
+ "$ref": "#/$defs/LogoSource",
1759
+ "description": "The name set in the brand's lettering, without the mark. Placed by 'var:organization.logo.wordmark'."
1787
1760
  }
1788
- }
1761
+ },
1762
+ "examples": [
1763
+ { "full": "./assets/acme-logo.svg", "icon": "./assets/acme-mark.svg" },
1764
+ { "full": { "onLight": "./assets/acme-logo.svg", "onDark": "./assets/acme-logo-white.svg" } }
1765
+ ]
1766
+ },
1767
+ "LogoSource": {
1768
+ "description": "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.",
1769
+ "oneOf": [
1770
+ { "$ref": "#/$defs/Asset" },
1771
+ {
1772
+ "type": "object",
1773
+ "additionalProperties": false,
1774
+ "minProperties": 1,
1775
+ "properties": {
1776
+ "onLight": {
1777
+ "$ref": "#/$defs/Asset",
1778
+ "description": "The logo for light backgrounds, usually dark artwork."
1779
+ },
1780
+ "onDark": {
1781
+ "$ref": "#/$defs/Asset",
1782
+ "description": "The logo for dark backgrounds, usually light or white artwork."
1783
+ }
1784
+ }
1785
+ }
1786
+ ],
1787
+ "examples": ["./assets/acme-mark.svg", { "onLight": "./assets/acme-logo.svg", "onDark": "./assets/acme-logo-white.svg" }]
1789
1788
  },
1790
1789
  "Watermark": {
1791
1790
  "type": "object",
@@ -1835,13 +1834,9 @@
1835
1834
  },
1836
1835
  "HeaderFooterItem": {
1837
1836
  "type": "object",
1838
- "description": "One header/footer zone. Every configured field renders; fields in one zone stack top to bottom in the order logo, image, text, socials, date. Generated values such as the slide number, the slide count, the section, the organization or the speaker are variables inside text ('{{slide.number}} / {{deck.slideCount}}', '{{organization.name}}'), so one text writes several values in the author's order; a line break is '\\n'. Put a date and a slide number in different zones to keep each on the zone's single line. See docs/templates-and-variables.md.",
1837
+ "description": "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, the section, the organization or the speaker are variables inside text ('{{slide.number}} / {{deck.slideCount}}', '{{organization.name}}'), so one text writes several values in the author's order. An organization logo is image: 'var:organization.logo.icon'. See docs/templates-and-variables.md.",
1839
1838
  "additionalProperties": false,
1840
1839
  "properties": {
1841
- "logo": {
1842
- "type": "boolean",
1843
- "description": "Whether to render the deck's icon logo in this zone: a slide's design.logo, then design.logo, then the primary organization's logo (LogoSet icon variants first, light ones on dark backgrounds). It is a generated image part in the same box as image; without a logo the engine reports unresolved-content at this field. Use image for a literal picture that is not the deck logo."
1844
- },
1845
1840
  "text": {
1846
1841
  "type": "string",
1847
1842
  "description": "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-ins '{{slide.number}}', '{{slide.section}}' and '{{deck.slideCount}}' for each slide as it is laid out. Each '{{slide.number}}' is a live slide-number field: PPTX export writes it as a native field when its value fits within one accepted text line, and as static text with a diagnostic otherwise. '{{deck.slideCount}}' is fixed text, because PowerPoint has no slide-count field. Write '\\{{' for literal braces.",
@@ -1849,8 +1844,8 @@
1849
1844
  },
1850
1845
  "image": {
1851
1846
  "$ref": "#/$defs/Asset",
1852
- "description": "Generic image rendered in this zone, such as a logo, partner mark, certification badge, or icon.",
1853
- "examples": ["asset:acme-logo", "./assets/footer-badge.svg"]
1847
+ "description": "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.wordmark'), which resolves for each slide's background (onLight or onDark).",
1848
+ "examples": ["var:organization.logo.icon", "./assets/footer-badge.svg", "var:organization.beta.logo"]
1854
1849
  },
1855
1850
  "date": {
1856
1851
  "oneOf": [
@@ -2171,7 +2166,7 @@
2171
2166
  },
2172
2167
  "hidden": {
2173
2168
  "type": "boolean",
2174
- "description": "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 (opf CLI: --include-hidden; the deck-level renderSvg: the opt-in skipHidden). A slide named explicitly (opf render --slides, renderSvg slideIndex) is still rendered."
2169
+ "description": "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 (opf CLI: --include-hidden; the deck-level toSvg: the opt-in skipHidden). A slide named explicitly (opf convert --slides, toSvg with a slide number) is still rendered."
2175
2170
  },
2176
2171
  "composition": {
2177
2172
  "$ref": "#/$defs/Composition"
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { P as Presentation } from './presentation-fb8CZmRq.js';
2
- export { c as CatalogKind } from './catalog-refs-C4K4y-vh.js';
1
+ export { P as Presentation } from './presentation-HondgJ3l.js';
2
+ export { C as CatalogKind } from './catalog-refs-BSfGELpt.js';
3
3
  export { SchemaName } from './schemas.js';
4
- export { F as Finding, a as FindingCategory, b as FindingFix, c as FindingFocus, d as FindingLocation, e as FindingReport, f as FindingSeverity, g as FindingSuggestion, J as JsonPatchOperation, V as ValidationIssue } from './finding-CDQ_wH8j.js';
4
+ export { a as Finding, d as FindingCategory, e as FindingFix, f as FindingFocus, b as FindingLocation, F as FindingReport, c as FindingSeverity, g as FindingSuggestion, J as JsonPatchOperation, V as ValidationIssue } from './finding-CT_f_vbD.js';
5
5
  import './json-BFSdESOE.js';
6
6
 
7
7
  /**
@@ -1,6 +1,6 @@
1
- import { b as Catalog } from './catalog-refs-C4K4y-vh.js';
2
- import { T as TextMeasurement } from './composition-BbZ_9C-c.js';
3
- import { f as FindingSeverity, e as FindingReport } from './finding-CDQ_wH8j.js';
1
+ import { a as Catalog } from './catalog-refs-BSfGELpt.js';
2
+ import { T as TextMeasurement } from './composition-C-OoqvAr.js';
3
+ import { c as FindingSeverity, F as FindingReport } from './finding-CT_f_vbD.js';
4
4
  import { J as JsonPrimitive } from './json-BFSdESOE.js';
5
5
 
6
6
  /**
@@ -136,13 +136,16 @@ interface BuiltinVariableInfo {
136
136
  label: string;
137
137
  /**
138
138
  * `deck` for a value read once from the document; `slide` for `slide.number`, `slide.section` and
139
- * `deck.slideCount`, which vary per slide and resolve as each slide is composed (inline tokens only).
139
+ * `deck.slideCount`, which vary per slide and resolve as each slide is composed (inline tokens only), and for the
140
+ * organization logos (`organization.logo`, `organization.logo.icon`, `organization.<id>.logo.wordmark`), whose
141
+ * onLight or onDark asset follows each slide's background (whole-field `var:` references only).
140
142
  */
141
143
  scope: "deck" | "slide";
142
144
  /** The value the document gives it, when it has one (a string, an Asset or the list of names). Never set for a slide-scoped built-in. */
143
145
  value?: unknown;
144
146
  /**
145
- * True when the document has a source value for it. Unavailable built-ins resolve to nothing. A slide-scoped
147
+ * True when the document has a source value for it. Unavailable built-ins resolve to nothing. A logo is available
148
+ * when its organization has a logo (in any shape: missing shapes fall back). A slide-scoped
146
149
  * built-in is available when every slide gives it a value: always for `slide.number` and `deck.slideCount`, and
147
150
  * for `slide.section` when at least one slide has a section.
148
151
  */
@@ -154,7 +157,9 @@ interface BuiltinVariableInfo {
154
157
  * The built-in variables of a deck, for pickers and agents: the generic names (`deck.*`, `speaker.*`, `speakers`,
155
158
  * `organization.*`) first, then the id-addressed ones (`speaker.<id>.*`, `organization.<id>.*`) of each speaker and
156
159
  * organization that has an id, then the slide-scoped `slide.number`, `slide.section` and `deck.slideCount` (scope
157
- * `slide`, no value). Each carries its kind, current source value and where the document uses it.
160
+ * `slide`, no value), then the slide-scoped organization logos (`organization.logo` and its `stacked`, `icon` and
161
+ * `wordmark` shapes, then the same per organization id; kind `image`, scope `slide`, no value). Each carries its kind,
162
+ * current source value and where the document uses it.
158
163
  */
159
164
  declare function listBuiltinVariables(presentation: unknown): BuiltinVariableInfo[];
160
165
 
@@ -284,4 +289,4 @@ interface ValidationReport extends FindingReport {
284
289
  unfilledVariables?: string[];
285
290
  }
286
291
 
287
- export { variableDeclarations as A, type BuiltinVariableInfo as B, type Contract as C, DEFAULT_VARIABLE_DATE_FORMAT as D, OPFVariableError as O, type ResolveVariablesOptions as R, VARIABLE_KINDS as V, type ResolveVariablesResult as a, type ValidateFonts as b, type ValidateOptions as c, type ValidationCategory as d, type ValidationChecks as e, type ValidationReport as f, type ValidationRuleCost as g, type ValidationRuleInfo as h, type ValidationThresholds as i, type VariableDeclaration as j, type VariableDiagnostic as k, type VariableDiagnosticCode as l, type VariableDiagnosticSeverity as m, type VariableInfo as n, type VariableKind as o, type VariableUse as p, type VariableUseForm as q, type VariableValues as r, coerceVariableValue as s, formatVariableNumber as t, hasContentVariables as u, isTemplate as v, listBuiltinVariables as w, listVariables as x, resolveVariables as y, validationCategories as z };
292
+ export { variableDeclarations as A, type BuiltinVariableInfo as B, type Contract as C, DEFAULT_VARIABLE_DATE_FORMAT as D, OPFVariableError as O, type ResolveVariablesOptions as R, type ValidationReport as V, type ValidateOptions as a, type ValidationThresholds as b, type ValidationRuleInfo as c, type ValidateFonts as d, type ValidationCategory as e, type ValidationChecks as f, type ValidationRuleCost as g, type VariableDiagnostic as h, type ResolveVariablesResult as i, VARIABLE_KINDS as j, type VariableDeclaration as k, type VariableDiagnosticCode as l, type VariableDiagnosticSeverity as m, type VariableInfo as n, type VariableKind as o, type VariableUse as p, type VariableUseForm as q, type VariableValues as r, coerceVariableValue as s, formatVariableNumber as t, hasContentVariables as u, validationCategories as v, isTemplate as w, listBuiltinVariables as x, listVariables as y, resolveVariables as z };
@@ -1,13 +1,13 @@
1
- import { a as CatalogRecordKind } from './catalog-schemas-g7RFQBdc.js';
2
- import { F as Finding } from './finding-CDQ_wH8j.js';
3
- import { P as Presentation } from './presentation-fb8CZmRq.js';
4
- import { i as ValidationThresholds, h as ValidationRuleInfo, f as ValidationReport, c as ValidateOptions } from './validation-types-iZbXfES6.js';
5
- export { C as Contract, b as ValidateFonts, d as ValidationCategory, e as ValidationChecks, g as ValidationRuleCost, z as validationCategories } from './validation-types-iZbXfES6.js';
6
- import './catalog-refs-C4K4y-vh.js';
1
+ import { a as CatalogRecordKind } from './catalog-schemas-CinPh5WA.js';
2
+ import { a as Finding } from './finding-CT_f_vbD.js';
3
+ import { P as Presentation } from './presentation-HondgJ3l.js';
4
+ import { b as ValidationThresholds, c as ValidationRuleInfo, V as ValidationReport, a as ValidateOptions } from './validation-types-PbGDrNUr.js';
5
+ export { C as Contract, d as ValidateFonts, e as ValidationCategory, f as ValidationChecks, g as ValidationRuleCost, v as validationCategories } from './validation-types-PbGDrNUr.js';
6
+ import './catalog-refs-BSfGELpt.js';
7
7
  import './schemas.js';
8
8
  import './json-BFSdESOE.js';
9
- import './composition-BbZ_9C-c.js';
10
- import './chart-data-DTxdRRT1.js';
9
+ import './composition-C-OoqvAr.js';
10
+ import './chart-data-BnNqgnC-.js';
11
11
 
12
12
  /**
13
13
  * The series palette opf-render and opf-pptx draw charts with (`CHART_COLORS`), in series order. A host with
package/dist/validator.js CHANGED
@@ -1,5 +1,5 @@
1
- export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, validate, validateCatalogRecord, validationCategories, validationRules } from './chunk-NLNYNBTS.js';
2
- export { promotedRegionKeys } from './chunk-CW7GEAV3.js';
1
+ export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, validate, validateCatalogRecord, validationCategories, validationRules } from './chunk-JT3QXA22.js';
2
+ export { promotedRegionKeys } from './chunk-MBU42S5Y.js';
3
3
  import './chunk-TRDXRRQK.js';
4
4
  import './chunk-SPFV27O5.js';
5
- import './chunk-RA64UQKI.js';
5
+ import './chunk-6WW2EDUW.js';
package/dist/yaml.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { b as Catalog } from './catalog-refs-C4K4y-vh.js';
2
- import { P as Presentation } from './presentation-fb8CZmRq.js';
3
- import { c as ValidateOptions, f as ValidationReport } from './validation-types-iZbXfES6.js';
4
- import { F as Finding, d as FindingLocation } from './finding-CDQ_wH8j.js';
5
- import './composition-BbZ_9C-c.js';
6
- import './chart-data-DTxdRRT1.js';
1
+ import { a as Catalog } from './catalog-refs-BSfGELpt.js';
2
+ import { P as Presentation } from './presentation-HondgJ3l.js';
3
+ import { a as ValidateOptions, V as ValidationReport } from './validation-types-PbGDrNUr.js';
4
+ import { a as Finding, b as FindingLocation } from './finding-CT_f_vbD.js';
5
+ import './composition-C-OoqvAr.js';
6
+ import './chart-data-BnNqgnC-.js';
7
7
  import './json-BFSdESOE.js';
8
8
 
9
9
  /** A finding located in the YAML source: `yaml/<rule>` syntax and dialect errors (category `format`) and the OPF findings of the parsed deck. */
package/dist/yaml.js CHANGED
@@ -1,9 +1,9 @@
1
- export { OPFYamlError, YAML_SCHEMA_URL, fromYaml, parseYamlData, scanYamlComments, toYaml } from './chunk-TCD2FB2K.js';
2
- import './chunk-IS5H5JKH.js';
3
- import './chunk-YT7FMQER.js';
4
- import './chunk-PPROJP2E.js';
5
- import './chunk-NLNYNBTS.js';
6
- import './chunk-CW7GEAV3.js';
1
+ export { OPFYamlError, YAML_SCHEMA_URL, fromYaml, parseYamlData, scanYamlComments, toYaml } from './chunk-NS3VYB57.js';
2
+ import './chunk-CJPDTLGD.js';
3
+ import './chunk-JXVS7BJK.js';
4
+ import './chunk-RAKEVM52.js';
5
+ import './chunk-JT3QXA22.js';
6
+ import './chunk-MBU42S5Y.js';
7
7
  import './chunk-TRDXRRQK.js';
8
8
  import './chunk-SPFV27O5.js';
9
- import './chunk-RA64UQKI.js';
9
+ import './chunk-6WW2EDUW.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpresentation/opf",
3
- "version": "0.17.0",
3
+ "version": "0.18.1",
4
4
  "description": "Canonical Open Presentation Format schemas, TypeScript types, local validation and composition; the pinned default catalog ships as the opt-in @openpresentation/opf/catalog.",
5
5
  "license": "MIT",
6
6
  "author": "Open Presentation",
@@ -32,7 +32,13 @@
32
32
  "exports": {
33
33
  ".": {
34
34
  "types": "./dist/index.d.ts",
35
- "import": "./dist/index.js"
35
+ "bun": "./dist/index.js",
36
+ "deno": "./dist/index.js",
37
+ "workerd": "./dist/browser.js",
38
+ "worker": "./dist/browser.js",
39
+ "browser": "./dist/browser.js",
40
+ "node": "./dist/index.js",
41
+ "default": "./dist/browser.js"
36
42
  },
37
43
  "./schemas": {
38
44
  "types": "./dist/schemas.d.ts",
@@ -116,13 +122,9 @@
116
122
  "types": "./dist/convert.d.ts",
117
123
  "import": "./dist/convert.js"
118
124
  },
119
- "./node": {
120
- "types": "./dist/node.d.ts",
121
- "import": "./dist/node.js"
122
- },
123
- "./node/engine": {
125
+ "./internal/engine": {
124
126
  "types": "./dist/node-engine.d.ts",
125
- "import": "./dist/node-engine.js"
127
+ "node": "./dist/node-engine.js"
126
128
  }
127
129
  },
128
130
  "files": [
@@ -169,8 +171,8 @@
169
171
  "typescript": "~5.9.3"
170
172
  },
171
173
  "peerDependencies": {
172
- "@openpresentation/opf-pptx": "^0.17.0",
173
- "@openpresentation/opf-render": "^0.17.0"
174
+ "@openpresentation/opf-pptx": "^0.18.0",
175
+ "@openpresentation/opf-render": "^0.18.0"
174
176
  },
175
177
  "peerDependenciesMeta": {
176
178
  "@openpresentation/opf-pptx": {