@openpresentation/opf-pptx 0.24.1 → 0.26.0-rc.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.
@@ -26,9 +26,9 @@ The candidate's complete npm audit reports no known vulnerabilities in the insta
26
26
 
27
27
  ## Repeatable patch comparison
28
28
 
29
- After `npm ci`, run `npm test` and `npm run test:packed`. The packed check installs the actual tarball into a fresh temporary consumer, audits its complete graph, checks shipped upstream hashes/license and exercises editable table export/reimport. It does not use consumer overrides.
29
+ After `pnpm install` at the repository root, run `pnpm test` and `pnpm run test:packed` in this package. The packed check packs this package and its workspace siblings with `pnpm pack`, installs the actual tarballs into a fresh temporary consumer, audits its complete graph, checks shipped upstream hashes/license and exercises editable table export/reimport. The only consumer overrides point the lockstep sibling ranges (`^<version>`, not on the registry while the version is tested) at those tarballs.
30
30
 
31
- Run `npm run test:browser` for real browser WebP pixel preservation and rich, conditional and merged/styled table import/preview/export/reimport checks. Local Windows runs use installed Microsoft Edge; other environments first run `npx playwright install --with-deps chromium`. CI and release jobs run the Chromium checks on their built candidate. These assertions cover the named fixtures, not arbitrary Office files.
31
+ Run `pnpm run test:browser` for real browser WebP pixel preservation and rich, conditional and merged/styled table import/preview/export/reimport checks. Local Windows runs use installed Microsoft Edge; other environments first run `npx playwright install --with-deps chromium`. CI and release jobs run the Chromium checks on their built candidate. These assertions cover the named fixtures, not arbitrary Office files.
32
32
 
33
33
  For a comparison against the preceding release, create a separate empty directory, run `npm init -y` there, then `npm install --ignore-scripts --no-audit --no-fund @openpresentation/opf-pptx@0.5.0`. From this repository run `npm run test:compare-published -- <absolute-consumer-directory> <report.json>`. The script verifies the reference version and registry lockfile integrity, then compares every installed core example's PPTX bytes. The older reference deliberately contains the dependency being removed; it is only a comparison input. Explicit replacement images and fallback fonts make the comparison reproducible. Matching bytes do not establish original-asset fidelity or native viewer equivalence.
34
34
 
package/README.md CHANGED
@@ -48,7 +48,7 @@ Version 0.8.0 also measures `design.contentBox` cards through core's padded inte
48
48
  Version 0.8.0 requires core 0.10.0 and uses renderer 0.8.0 for coordinated preview/font measurement. Quotes and code export their accepted internal lines and styles without another fitting pass. [Controlled Windows PowerPoint quote evidence](docs/evidence/shared-quote-integration/comparison.json) records glyph containment, separation, save/reopen and text reimport against its exact source/font hashes. Native quote import restores the quote payload from an unchanged export (see [Quote provenance](#quote-provenance-ff-57)); it does not restore the original typography or readability policy, and an edited or damaged quote imports as editable text blocks. Native chart geometry and general scalar-text wrapping also remain different from preview; editability and valid reimport do not establish raster equivalence.
49
49
 
50
50
  - Package: `@openpresentation/opf-pptx`
51
- - Repository: `OpenPresentation/opf-pptx`
51
+ - Repository: `OpenPresentation/opf` (`packages/pptx`)
52
52
  - License: MIT
53
53
  - Compatibility target: `@openpresentation/opf`
54
54
  - Renderer relationship: may use `@openpresentation/opf-render` for chart rasterization, the PNG fallback of SVG pictures and visual verification
@@ -308,22 +308,18 @@ The package runtime must stay local and deterministic:
308
308
  ## Development
309
309
 
310
310
  ```sh
311
- npm ci
312
- npm run build
313
- npm run typecheck
314
- npm test
315
- npm run validate
311
+ pnpm install # once, at the repository root
312
+ pnpm --filter @openpresentation/opf-pptx build
313
+ pnpm --filter @openpresentation/opf-pptx typecheck
314
+ pnpm --filter @openpresentation/opf-pptx test
315
+ pnpm --filter @openpresentation/opf-pptx validate
316
316
  ```
317
317
 
318
318
  LibreOffice is not a runtime dependency. When it is installed in CI or a local verification environment, generated `.pptx` files can be smoke-opened there as an optional export check.
319
319
 
320
320
  ## Release Lane
321
321
 
322
- Public npm package publication is handled by `.github/workflows/release.yml` through npm Trusted Publishing (GitHub Actions OIDC) with npm provenance; no npm token is stored. The owner authorized agents to prepare and publish npm releases whenever a release is required (2026-09-29). This authorization does not waive any gate.
323
-
324
- 1. Open a release-prep PR containing only the version bump, `CHANGELOG.md` (assembled from `changes/` with `node scripts/changelog-fragments.mjs assemble --version X.Y.Z`), dependency ranges, lockfile and current-instruction docs. Publish in dependency order (core, then renderer, then PPTX, then editor): refresh this repo's lockfile only after the required `@openpresentation/opf` and `@openpresentation/opf-render` versions are on the registry (`npm install --package-lock-only`), then run `npm run test:packed` against them.
325
- 2. Merge after CI is green, then publish by pushing the git tag `opf-pptx-v<version>` (or `@openpresentation/opf-pptx@v<version>`) at the merge commit. The workflow verifies that the tag matches `package.json` and reruns audit, typecheck, validate, tests, packed and browser checks before `npm publish --access public --provenance`. A manual `workflow_dispatch` runs the same job without the tag check and is a fallback only.
326
- 3. Verify with `npm view @openpresentation/opf-pptx@<version> version gitHead dist.attestations` and, from the core repo, `node scripts/test-pptx-publication.mjs <version> <release-commit> <this-checkout>`. Never republish an existing version.
322
+ Packages publish from the OpenPresentation/opf monorepo through npm Trusted Publishing (GitHub Actions OIDC) with npm provenance; no npm token is stored. All six packages share one version and publish together, in dependency order, through the root release workflow described in [`docs/release-process.md`](../../docs/release-process.md) (the unified workflow lands with MR-40; until then the root process applies). Never republish an existing version. Verify a release with `npm view @openpresentation/opf-pptx@<version> version gitHead dist.attestations`.
327
323
 
328
324
  ## Shared dynamic composition
329
325
 
@@ -395,7 +391,7 @@ Set `imageFormat: "preserve"` to embed WebP unchanged when the receiving applica
395
391
 
396
392
  Node conversion lazily loads the pinned open-source Sharp dependency and requires Node 22 or later. Normal package installation must include platform optional dependencies for its native binaries. Browser bundles select a separate browser decoder using local Blob/image/canvas APIs; Sharp and Node code are excluded. Neither path uploads images or fetches asset URLs. Source-preserving export and ordinary PNG/JPEG/GIF operations do not load Sharp.
397
393
 
398
- `npm test` includes the Node pixel-reference cases and verifies browser bundling. To run the browser pixel checks, run `npm run build:browser-check`, serve this repository locally, and open `/artifacts/webp-fallback/browser/index.html`. The page reports 13 checks covering embedded PNG pixels, alpha, EXIF, the first animation frame, fit/crop, resolver calls and DOM canvas fallback. These are browser export checks, separate from the recorded Keynote viewing evidence.
394
+ `pnpm test` includes the Node pixel-reference cases and verifies browser bundling. To run the browser pixel checks, run `pnpm run build:browser-check`, serve this repository locally, and open `/artifacts/webp-fallback/browser/index.html`. The page reports 13 checks covering embedded PNG pixels, alpha, EXIF, the first animation frame, fit/crop, resolver calls and DOM canvas fallback. These are browser export checks, separate from the recorded Keynote viewing evidence.
399
395
 
400
396
 
401
397
  ## Native background fills
@@ -495,7 +491,7 @@ Conditional borders keep the whole-table outer frame separate from interior hori
495
491
 
496
492
  Export consumes the same core geometry as SVG. It writes native merged cells and corrects PptxGenJS's padding-unit heuristic and missing dotted/transparent-border options in generated XML. Rich runs retain their own resolved opacity, including opaque overrides within a translucent cell. Tests inspect actual native XML and repeated conversions; native PowerPoint raster parity is not established.
497
493
 
498
- Version 0.5.0 requires core 0.7.0 and renderer 0.5.0 for coordinated previews. `npm run test:styled-table` runs export/import regressions. For local browser verification, set `OPF_CORE_ROOT` to the core repository and `OPF_RENDER_ROOT` to the coordinated renderer worktree when running `npm run build:browser-check`. Serve the repository and open `/artifacts/native-styled-table-import/browser/index.html`. Omit those variables to exercise installed package dependencies.
494
+ Version 0.5.0 requires core 0.7.0 and renderer 0.5.0 for coordinated previews. `pnpm run test:styled-table` runs export/import regressions. For local browser verification, set `OPF_CORE_ROOT` to the core repository and `OPF_RENDER_ROOT` to the coordinated renderer worktree when running `npm run build:browser-check`. Serve the repository and open `/artifacts/native-styled-table-import/browser/index.html`. Omit those variables to exercise installed package dependencies.
499
495
 
500
496
  Version 0.8.0 exports editable native lines with explicit four-space tab stops. Boundary tags contain no original source words, so native edits and deletions remain authoritative. XML-unrepresentable scalar controls reject with an actionable path rather than silently losing characters. Twenty wide/portrait measured/estimated export cases, current-text mutation controls and damaged-tag fallbacks pass; native Office acceptance remains separate. The coordinated core browser workflow also verifies editing, undo and exact reimport.
501
497
 
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ import type { Catalog, Finding, Fonts, LayoutDiagnostic, UnresolvedReferenceDiag
2
2
  export declare const packageName = "@openpresentation/opf-pptx";
3
3
 
4
4
  export declare const releaseLane: Readonly<{
5
- githubRepository: "OpenPresentation/opf-pptx";
5
+ githubRepository: "OpenPresentation/opf";
6
+ directory: "packages/pptx";
6
7
  npmPackage: "@openpresentation/opf-pptx";
7
8
  compatibilityPackage: "@openpresentation/opf";
8
9
  rendererPackage: "@openpresentation/opf-render";
package/dist/index.js CHANGED
@@ -80,7 +80,8 @@ export {DEFAULT_SIGNAL_LIMITS, SIGNALS_VERSION, isMonospaceFamily} from './impor
80
80
  export const packageName = "@openpresentation/opf-pptx";
81
81
 
82
82
  export const releaseLane = Object.freeze({
83
- githubRepository: "OpenPresentation/opf-pptx",
83
+ githubRepository: "OpenPresentation/opf",
84
+ directory: "packages/pptx",
84
85
  npmPackage: "@openpresentation/opf-pptx",
85
86
  compatibilityPackage: "@openpresentation/opf",
86
87
  rendererPackage: "@openpresentation/opf-render"
@@ -25,7 +25,7 @@
25
25
  // `layout-changed`). A block read from a placeholder of a region gets a `region` pin when plain binding would put it
26
26
  // elsewhere; content that document provenance restored is never pinned.
27
27
 
28
- import {composeLayoutAreas, composeSlide, layoutTemplate, bindRegions, regionAccepts} from '@openpresentation/opf/composition';
28
+ import {composeLayoutAreas, composeSlide, isLayoutTemplate, layoutTemplate, bindRegions, regionAccepts} from '@openpresentation/opf/composition';
29
29
  import {validateCatalogRecord, catalogRecords, resolveReference} from '@openpresentation/opf';
30
30
  import {gallery} from '@openpresentation/gallery';
31
31
  import {decodeTextTag, encodeTextTag} from './code-provenance.js';
@@ -38,7 +38,7 @@ const LAYOUT_TYPE = 'application/vnd.openxmlformats-officedocument.presentationm
38
38
  const TAGS_TYPE = 'application/vnd.openxmlformats-officedocument.presentationml.tags+xml';
39
39
  const EMU = 9525;
40
40
  // Region placeholder indexes (coordinator decisions on RR-81): a base per placeholder kind plus the region's position among the
41
- // layout's regions of that kind, in the record's `regions` order: `obj` 100, `body` 300, `pic` 500, `chart` 700, `tbl` 900,
41
+ // layout's regions of that kind, in the record's `regions` order (core's reading order of the region list): `obj` 100, `body` 300, `pic` 500, `chart` 700, `tbl` 900,
42
42
  // `media` 1100 (so the first chart region of every layout is 700, its second 701). PowerPoint's Change Layout pairs a slide's
43
43
  // placeholders with the new layout's by `idx`, so only regions of one kind pair (Pillars to Text keeps `obj` with `obj`; a chart
44
44
  // bound to Chart beside's `chart` region never lands in Table beside's `tbl` region, as it did with one sequence for every kind).
@@ -85,7 +85,7 @@ export const bindsObject = (type, object) => (BINDS[type] ?? []).includes(object
85
85
  export function regionIndexes(record, nativeBodyRegion) {
86
86
  const regions = new Map(layoutTemplate(record).regions.map(region => [region.name, region]));
87
87
  const counts = {};
88
- const indexes = Object.fromEntries(Object.keys(record?.regions ?? {}).filter(name => regions.has(name)).map(name => {
88
+ const indexes = Object.fromEntries([...regions.keys()].map(name => {
89
89
  const type = placeholderType(regions.get(name));
90
90
  counts[type] = (counts[type] ?? -1) + 1;
91
91
  return [name, REGION_IDX_BASE[type] + counts[type]];
@@ -119,7 +119,7 @@ export function recordHash(record) {
119
119
  */
120
120
  export function slideTemplate(core) {
121
121
  const record = core?.resolved?.layout, provenance = core?.resolved?.provenance?.layout;
122
- if (!record || !provenance || !Object.hasOwn(record, 'areas')) return undefined;
122
+ if (!record || !provenance || !isLayoutTemplate(record)) return undefined;
123
123
  try { layoutTemplate(record); } catch { return undefined; }
124
124
  return {record, id: provenance.id, group: provenance.group, ...(provenance.source !== undefined ? {source: provenance.source} : {}), reference: provenance.reference, origin: provenance.origin,
125
125
  key: templateKey(provenance)};
@@ -127,7 +127,7 @@ export function slideTemplate(core) {
127
127
 
128
128
  const templateKey = ({group, id, source}) => `${group}\u0000${id}\u0000${source ?? ''}`;
129
129
  function isTemplate(record) {
130
- if (record === null || typeof record !== 'object' || !Object.hasOwn(record, 'areas')) return false;
130
+ if (record === null || typeof record !== 'object' || !isLayoutTemplate(record)) return false;
131
131
  try { layoutTemplate(record); return true; } catch { return false; }
132
132
  }
133
133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpresentation/opf-pptx",
3
- "version": "0.24.1",
3
+ "version": "0.26.0-rc.0",
4
4
  "description": "Pure local OPF to PPTX export and PPTX to OPF import tooling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,12 +10,13 @@
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "git+https://github.com/OpenPresentation/opf-pptx.git"
13
+ "url": "git+https://github.com/OpenPresentation/opf.git",
14
+ "directory": "packages/pptx"
14
15
  },
15
16
  "bugs": {
16
- "url": "https://github.com/OpenPresentation/opf-pptx/issues"
17
+ "url": "https://github.com/OpenPresentation/opf/issues"
17
18
  },
18
- "homepage": "https://github.com/OpenPresentation/opf-pptx#readme",
19
+ "homepage": "https://github.com/OpenPresentation/opf/tree/main/packages/pptx#readme",
19
20
  "publishConfig": {
20
21
  "access": "public"
21
22
  },
@@ -35,46 +36,18 @@
35
36
  },
36
37
  "types": "./dist/index.d.ts",
37
38
  "sideEffects": false,
38
- "scripts": {
39
- "build": "node scripts/build.mjs",
40
- "typecheck": "node scripts/check-syntax.mjs src test scripts",
41
- "test": "npm run build && node scripts/run-tests.mjs && npm run build:browser-check && npm run check:runners && npm run check:changes",
42
- "test:contract": "npm run build && node scripts/run-tests.mjs --suite contract && npm run build:browser-check",
43
- "check:runners": "node --test scripts/run-tests.test.mjs",
44
- "check:changes": "node --test scripts/changelog-fragments.test.mjs && node scripts/changelog-fragments.mjs check",
45
- "validate": "node scripts/validate-package.mjs",
46
- "test:packed": "node test/packed-install.mjs && node test/provenance-interop.mjs",
47
- "test:compare-published": "node test/compare-published.mjs",
48
- "test:published-import": "node test/provenance-interop.mjs",
49
- "test:browser": "npm run build:browser-check && node test/browser-check.mjs && node test/packed-bundle-browser.mjs",
50
- "prepack": "npm run build",
51
- "build:browser-check": "node scripts/build-browser-check.mjs",
52
- "test:styled-table": "node test/styled-table.mjs && node test/styled-table-import.mjs",
53
- "test:code": "node test/shared-code.mjs && node test/code-provenance.mjs",
54
- "test:metric": "node test/shared-metric.mjs && node test/metric-provenance.mjs",
55
- "test:font-variants": "node test/font-variants.mjs",
56
- "test:timeline": "node test/shared-timeline.mjs",
57
- "test:furniture": "node test/shared-furniture.mjs && node test/furniture-provenance.mjs && node test/slide-variables.mjs && node test/furniture-fields.mjs && node test/furniture-wrapped-date.mjs && node test/socials-furniture.mjs && node test/native-furniture.mjs",
58
- "test:native-picture-edit-controls": "node test/compare-native-picture-edit-controls.mjs",
59
- "test:native-font-embed-controls": "node test/native-font-embed-controls.mjs",
60
- "test:native-mixed-edit-controls": "node test/native-mixed-edit-controls.mjs",
61
- "test:native-font-inventory-controls": "node test/native-font-inventory-controls.mjs",
62
- "test:zip-date": "node test/zip-date.mjs",
63
- "test:native-text": "node test/native-text-whitespace.mjs",
64
- "test:determinism": "node test/determinism.mjs"
65
- },
66
39
  "dependencies": {
67
40
  "@node-projects/jszip": "4.3.0",
68
- "@openpresentation/gallery": "^4.0.0",
69
- "@openpresentation/opf": "^0.24.0",
70
41
  "@rgrove/parse-xml": "4.2.3",
71
42
  "fast-xml-parser": "^5.8.0",
72
43
  "fflate": "^0.8.3",
73
44
  "pako": "3.0.2",
74
- "sharp": "0.35.5"
45
+ "sharp": "0.35.5",
46
+ "@openpresentation/opf": "^0.26.0-rc.0",
47
+ "@openpresentation/gallery": "^0.26.0-rc.0"
75
48
  },
76
49
  "peerDependencies": {
77
- "@openpresentation/opf-render": "^0.24.1"
50
+ "@openpresentation/opf-render": "^0.26.0-rc.0"
78
51
  },
79
52
  "peerDependenciesMeta": {
80
53
  "@openpresentation/opf-render": {
@@ -98,15 +71,43 @@
98
71
  "@expo-google-fonts/roboto": "0.4.3",
99
72
  "@expo-google-fonts/roboto-mono": "0.4.2",
100
73
  "@expo-google-fonts/tinos": "0.4.2",
101
- "@openpresentation/opf-render": "0.24.1",
102
74
  "@resvg/resvg-js": "2.6.2",
103
75
  "esbuild": "0.28.2",
104
- "playwright": "1.64.0"
76
+ "playwright": "1.64.0",
77
+ "@openpresentation/opf-render": "0.26.0-rc.0"
105
78
  },
106
79
  "imports": {
107
80
  "#image-fallback": {
108
81
  "browser": "./dist/image-fallback-browser.js",
109
82
  "default": "./dist/image-fallback-node.js"
110
83
  }
111
- }
84
+ },
85
+ "scripts": {
86
+ "build:deps": "pnpm --filter \"@openpresentation/opf-pptx^...\" --fail-if-no-match run build",
87
+ "build": "node scripts/build.mjs",
88
+ "typecheck": "node scripts/check-syntax.mjs src test scripts",
89
+ "test": "pnpm run build:deps && pnpm run build && node ../../scripts/run-tests.mjs && pnpm run build:browser-check && pnpm run check:scripts",
90
+ "test:contract": "pnpm run build:deps && pnpm run build && node ../../scripts/run-tests.mjs --suite contract && pnpm run build:browser-check",
91
+ "check:scripts": "node --test scripts/check-syntax.test.mjs scripts/browser-suites.test.mjs scripts/pnpm-lock.test.mjs",
92
+ "validate": "node scripts/validate-package.mjs",
93
+ "test:packed": "pnpm run build:deps && node test/packed-install.mjs && node test/provenance-interop.mjs",
94
+ "test:compare-published": "node test/compare-published.mjs",
95
+ "test:published-import": "node test/provenance-interop.mjs",
96
+ "test:browser": "pnpm run build:deps && pnpm run build:browser-check && node test/browser-check.mjs && node test/packed-bundle-browser.mjs",
97
+ "build:browser-check": "node scripts/build-browser-check.mjs",
98
+ "test:styled-table": "node test/styled-table.mjs && node test/styled-table-import.mjs",
99
+ "test:code": "node test/shared-code.mjs && node test/code-provenance.mjs",
100
+ "test:metric": "node test/shared-metric.mjs && node test/metric-provenance.mjs",
101
+ "test:font-variants": "node test/font-variants.mjs",
102
+ "test:timeline": "node test/shared-timeline.mjs",
103
+ "test:furniture": "node test/shared-furniture.mjs && node test/furniture-provenance.mjs && node test/slide-variables.mjs && node test/furniture-fields.mjs && node test/furniture-wrapped-date.mjs && node test/socials-furniture.mjs && node test/native-furniture.mjs",
104
+ "test:native-picture-edit-controls": "node test/compare-native-picture-edit-controls.mjs",
105
+ "test:native-font-embed-controls": "node test/native-font-embed-controls.mjs",
106
+ "test:native-mixed-edit-controls": "node test/native-mixed-edit-controls.mjs",
107
+ "test:native-font-inventory-controls": "node test/native-font-inventory-controls.mjs",
108
+ "test:zip-date": "node test/zip-date.mjs",
109
+ "test:native-text": "node test/native-text-whitespace.mjs",
110
+ "test:determinism": "node test/determinism.mjs"
111
+ },
112
+ "gitHead": "fb80b1e8a7d6dd4a77946509fab9af7713657ce6"
112
113
  }