@extend-ai/react-pptx 0.1.2 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @extend-ai/react-pptx
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Add first-class controlled and uncontrolled responsive zoom modes, resize-aware resolved zoom,
8
+ and controller APIs for consumer-owned zoom controls while preserving `fitMode` compatibility.
9
+
3
10
  ## 0.1.1
4
11
 
5
12
  ### Patch Changes
@@ -8,6 +15,9 @@
8
15
 
9
16
  ## Unreleased
10
17
 
18
+ - Add a cold-start `createPptxThumbnailRenderer` API with package-owned PNG, canvas, ImageBitmap,
19
+ and SVG outputs, explicit thumbnail font policy, cancellation, bounded concurrency, shared
20
+ presentation assets, and phase-level performance measurements.
11
21
  - Make slide navigation observable before off-window rendering completes, isolate resource
12
22
  readiness per slide mount, update zoom and fit geometry without rebuilding mounted slide DOM,
13
23
  and reduce thumbnail work outside the visible rail.
package/README.md CHANGED
@@ -25,6 +25,36 @@ during SSR is safe.
25
25
  Unknown or degraded content is surfaced through `onWarning` and `showDiagnostics` rather
26
26
  than silently removed.
27
27
 
28
+ ## Responsive zoom
29
+
30
+ The viewer accepts the same responsive zoom shape as `@extend-ai/react-docx`. Numeric values are
31
+ percentages, while `automatic`, `fit-page`, and `fit-width` remain active as the viewport changes.
32
+ For example, the shared API can be expressed as:
33
+
34
+ ```tsx
35
+ <Viewer defaultZoom="fit-width" />
36
+ ```
37
+
38
+ With this package's component and required source, use:
39
+
40
+ ```tsx
41
+ <ReactPptxViewer
42
+ source={file}
43
+ defaultZoom="fit-width"
44
+ onZoomChange={({ level, resolvedZoom }) => {
45
+ console.log(level, resolvedZoom);
46
+ }}
47
+ />
48
+ ```
49
+
50
+ `automatic` shrinks slides that are wider than the viewport without enlarging them. `fit-width`
51
+ may enlarge or shrink to fill the available width, and `fit-page` uses both viewport dimensions.
52
+ The controller exposes `setZoom(level)`, `getZoom()`, and `getResolvedZoom()`, so a custom toolbar
53
+ can retain the semantic mode while displaying its current percentage. Zooming in or out from a
54
+ responsive mode should pass `getResolvedZoom() + delta` to `setZoom()`, which switches to a numeric
55
+ level. The older `fitMode` prop remains supported for compatibility; `contain` maps to `automatic`
56
+ when the new zoom props are absent.
57
+
28
58
  ## Supported presentation formats
29
59
 
30
60
  `.pptx` and legacy PowerPoint 97–2003 `.ppt` files are both accepted by the same `source`
@@ -150,6 +180,49 @@ return (
150
180
  `usePptxViewer` exposes the reactive navigation/search/zoom controller. `onViewportReady`
151
181
  provides the internal slide surface for advanced integrations and test harnesses.
152
182
 
183
+ ## Cold-start thumbnails
184
+
185
+ `createPptxThumbnailRenderer` renders directly from a `ParsedPresentation`; it does not mount or
186
+ initialize `ReactPptxViewer`. One renderer reuses its font manager, DOM/SVG rendering context, and
187
+ presentation asset cache across slide requests. The parsed presentation can later be passed to
188
+ `ReactPptxViewer` without fetching or parsing it again.
189
+
190
+ ```ts
191
+ import { createPptxThumbnailRenderer, parsePresentation } from '@extend-ai/react-pptx';
192
+
193
+ const presentation = await parsePresentation(source, {
194
+ onPerformanceMeasurement: recordMeasurement,
195
+ });
196
+ const renderer = createPptxThumbnailRenderer(presentation, {
197
+ fonts: {
198
+ loadEmbeddedFonts: false,
199
+ waitForFonts: false,
200
+ },
201
+ concurrency: 2,
202
+ onPerformanceMeasurement: recordMeasurement,
203
+ });
204
+
205
+ const thumbnail = await renderer.renderSlide(0, {
206
+ maxWidth: 240,
207
+ maxHeight: 135,
208
+ output: 'blob',
209
+ signal: abortController.signal,
210
+ });
211
+
212
+ image.src = URL.createObjectURL(thumbnail.data);
213
+ renderer.destroy();
214
+ ```
215
+
216
+ The output can be `blob` (PNG, the default), `canvas`, `imageBitmap`, or `svg`. `renderSlides()`
217
+ accepts an explicit index list, preserves its order, and never initializes other slides. Both
218
+ single and batch requests use the renderer's bounded queue. By default, the thumbnail-specific
219
+ font policy skips embedded fonts, global font readiness, and missing-font probing; callers can opt
220
+ back into any of those behaviors through `fonts`.
221
+
222
+ Performance observers receive `download`, `wasm-parsing`, `embedded-font-preparation`,
223
+ `slide-dom-svg-rendering`, `image-decoding`, and `raster-encoding` measurements as applicable. Each
224
+ render result also contains its slide-specific measurements.
225
+
153
226
  ## Thumbnail hook
154
227
 
155
228
  `usePptxViewerThumbnails` exposes detached slide previews for a consumer-owned filmstrip. PPTX
package/dist/index.css CHANGED
@@ -135,9 +135,6 @@
135
135
  min-height: var(--rpv-height);
136
136
  overflow: visible;
137
137
  }
138
- .rpv-viewport > * {
139
- max-width: 100%;
140
- }
141
138
  .rpv-status {
142
139
  position: absolute;
143
140
  z-index: 5;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.css"],"sourcesContent":[".rpv-root {\n --rpv-ink: #17211d;\n --rpv-paper: #f8f5ee;\n --rpv-panel: #ebe5d9;\n --rpv-rule: #c7bfb1;\n --rpv-accent: #d8663a;\n color: var(--rpv-ink);\n background: var(--rpv-paper);\n border: 1px solid var(--rpv-rule);\n font:\n 500 14px/1.4 ui-sans-serif,\n system-ui,\n sans-serif;\n min-width: 0;\n}\n.rpv-root button {\n color: inherit;\n font: inherit;\n}\n.rpv-toolbar {\n min-height: 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 0 14px;\n border-bottom: 1px solid var(--rpv-rule);\n background: color-mix(in srgb, var(--rpv-paper) 90%, white);\n}\n.rpv-toolbar__cluster {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.rpv-toolbar button {\n width: 30px;\n height: 30px;\n border: 0;\n background: transparent;\n cursor: pointer;\n}\n.rpv-toolbar button:hover:not(:disabled) {\n background: var(--rpv-panel);\n}\n.rpv-toolbar button:focus-visible,\n.rpv-thumbnail:focus-visible {\n outline: 3px solid color-mix(in srgb, var(--rpv-accent) 55%, transparent);\n outline-offset: 2px;\n}\n.rpv-toolbar button:disabled {\n opacity: 0.35;\n}\n.rpv-toolbar__count {\n min-width: 66px;\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\n.rpv-workspace {\n display: flex;\n min-height: var(--rpv-height);\n max-height: var(--rpv-height);\n}\n.rpv-filmstrip {\n width: 184px;\n flex: 0 0 184px;\n overflow: auto;\n padding: 18px 14px 32px;\n border-right: 1px solid var(--rpv-rule);\n background: var(--rpv-panel);\n}\n.rpv-filmstrip__sizer {\n position: relative;\n width: 100%;\n}\n.rpv-filmstrip__item {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n}\n.rpv-thumbnail {\n position: relative;\n display: block;\n width: 100%;\n padding: 0 0 15px 24px;\n border: 0;\n background: transparent;\n cursor: pointer;\n text-align: left;\n}\n.rpv-thumbnail__number {\n position: absolute;\n top: 4px;\n left: 0;\n color: #756d63;\n font-size: 10px;\n font-variant-numeric: tabular-nums;\n}\n.rpv-thumbnail__canvas {\n display: block;\n min-height: 76px;\n overflow: hidden;\n background: white;\n border: 1px solid var(--rpv-rule);\n transition:\n transform 160ms cubic-bezier(0.22, 1, 0.36, 1),\n border-color 160ms;\n}\n.rpv-thumbnail:hover .rpv-thumbnail__canvas {\n transform: translateY(-2px);\n}\n.rpv-thumbnail[data-active] .rpv-thumbnail__canvas {\n border: 2px solid var(--rpv-accent);\n}\n.rpv-stage {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n background: #d8d2c8;\n}\n.rpv-viewport {\n width: 100%;\n height: 100%;\n overflow: auto;\n overflow-anchor: none;\n padding: 34px;\n box-sizing: border-box;\n}\n.rpv-root[data-rpv-scroll-owner='external'] .rpv-workspace {\n min-height: 0;\n max-height: none;\n}\n.rpv-viewport[data-rpv-scroll-owner='external'] {\n height: auto;\n min-height: var(--rpv-height);\n overflow: visible;\n}\n.rpv-viewport > * {\n max-width: 100%;\n}\n.rpv-status {\n position: absolute;\n z-index: 5;\n inset: 0;\n display: grid;\n place-content: center;\n justify-items: center;\n gap: 10px;\n padding: 40px;\n text-align: center;\n background: #d8d2c8;\n}\n.rpv-status--error strong {\n font-size: 18px;\n}\n.rpv-status--error span {\n max-width: 52ch;\n color: #645e56;\n}\n.rpv-spinner {\n width: 25px;\n height: 25px;\n border: 2px solid #aaa197;\n border-top-color: var(--rpv-accent);\n border-radius: 50%;\n animation: rpv-spin 0.8s linear infinite;\n}\n.rpv-slide-label {\n position: absolute;\n right: 14px;\n bottom: 10px;\n padding: 3px 7px;\n background: color-mix(in srgb, var(--rpv-ink) 82%, transparent);\n color: var(--rpv-paper);\n font-size: 11px;\n}\n.rpv-diagnostics {\n width: 270px;\n flex: 0 0 270px;\n overflow: auto;\n border-left: 1px solid var(--rpv-rule);\n padding: 20px;\n background: var(--rpv-paper);\n}\n.rpv-diagnostics h2,\n.rpv-notes h2 {\n margin: 0 0 16px;\n font-size: 12px;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.rpv-diagnostics dl {\n margin: 0 0 24px;\n}\n.rpv-diagnostics dl div {\n display: flex;\n justify-content: space-between;\n padding: 7px 0;\n border-bottom: 1px solid var(--rpv-rule);\n}\n.rpv-diagnostics dd {\n margin: 0;\n font-variant-numeric: tabular-nums;\n}\n.rpv-diagnostics p {\n padding: 10px 0;\n margin: 0;\n border-bottom: 1px solid var(--rpv-rule);\n font-size: 12px;\n}\n.rpv-diagnostics p strong {\n display: block;\n color: var(--rpv-accent);\n}\n.rpv-notes {\n padding: 20px 24px;\n border-top: 1px solid var(--rpv-rule);\n}\n.rpv-notes p {\n max-width: 72ch;\n}\n@keyframes rpv-spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (max-width: 760px) {\n .rpv-filmstrip {\n width: 108px;\n flex-basis: 108px;\n padding-inline: 8px;\n }\n .rpv-thumbnail {\n padding-left: 0;\n }\n .rpv-thumbnail__number {\n display: none;\n }\n .rpv-diagnostics {\n position: absolute;\n right: 0;\n z-index: 4;\n height: 100%;\n box-sizing: border-box;\n }\n .rpv-viewport {\n padding: 18px;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .rpv-spinner {\n animation: none;\n }\n .rpv-thumbnail__canvas {\n transition: none;\n }\n}\n"],"mappings":";AAAA,CAAC;AACC,aAAW;AACX,eAAa;AACb,eAAa;AACb,cAAY;AACZ,gBAAc;AACd,SAAO,IAAI;AACX,cAAY,IAAI;AAChB,UAAQ,IAAI,MAAM,IAAI;AACtB;AAAA,IACE,IAAI,IAAI,CAAC,IAAI,aAAa;AAAA,IAC1B,SAAS;AAAA,IACT;AACF,aAAW;AACb;AACA,CAfC,SAeS;AACR,SAAO;AACP,QAAM;AACR;AACA,CAAC;AACC,cAAY;AACZ,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,OAAK;AACL,WAAS,EAAE;AACX,iBAAe,IAAI,MAAM,IAAI;AAC7B,cAAY,UAAU,GAAG,IAAI,EAAE,IAAI,aAAa,GAAG,EAAE;AACvD;AACA,CAAC;AACC,WAAS;AACT,eAAa;AACb,OAAK;AACP;AACA,CAfC,YAeY;AACX,SAAO;AACP,UAAQ;AACR,UAAQ;AACR,cAAY;AACZ,UAAQ;AACV;AACA,CAtBC,YAsBY,MAAM,MAAM,KAAK;AAC5B,cAAY,IAAI;AAClB;AACA,CAzBC,YAyBY,MAAM;AACnB,CAAC,aAAa;AACZ,WAAS,IAAI,MAAM,UAAU,GAAG,IAAI,EAAE,IAAI,cAAc,GAAG,EAAE;AAC7D,kBAAgB;AAClB;AACA,CA9BC,YA8BY,MAAM;AACjB,WAAS;AACX;AACA,CAAC;AACC,aAAW;AACX,cAAY;AACZ,wBAAsB;AACxB;AACA,CAAC;AACC,WAAS;AACT,cAAY,IAAI;AAChB,cAAY,IAAI;AAClB;AACA,CAAC;AACC,SAAO;AACP,QAAM,EAAE,EAAE;AACV,YAAU;AACV,WAAS,KAAK,KAAK;AACnB,gBAAc,IAAI,MAAM,IAAI;AAC5B,cAAY,IAAI;AAClB;AACA,CAAC;AACC,YAAU;AACV,SAAO;AACT;AACA,CAAC;AACC,YAAU;AACV,OAAK;AACL,QAAM;AACN,SAAO;AACT;AACA,CAnCC;AAoCC,YAAU;AACV,WAAS;AACT,SAAO;AACP,WAAS,EAAE,EAAE,KAAK;AAClB,UAAQ;AACR,cAAY;AACZ,UAAQ;AACR,cAAY;AACd;AACA,CAAC;AACC,YAAU;AACV,OAAK;AACL,QAAM;AACN,SAAO;AACP,aAAW;AACX,wBAAsB;AACxB;AACA,CAAC;AACC,WAAS;AACT,cAAY;AACZ,YAAU;AACV,cAAY;AACZ,UAAQ,IAAI,MAAM,IAAI;AACtB,cACE,UAAU,MAAM,aAAa,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAC9C,aAAa;AACjB;AACA,CA/DC,aA+Da,OAAO,CAVpB;AAWC,aAAW,WAAW;AACxB;AACA,CAlEC,aAkEa,CAAC,aAAa,CAb3B;AAcC,UAAQ,IAAI,MAAM,IAAI;AACxB;AACA,CAAC;AACC,YAAU;AACV,QAAM,EAAE,EAAE;AACV,aAAW;AACX,YAAU;AACV,cAAY;AACd;AACA,CAAC;AACC,SAAO;AACP,UAAQ;AACR,YAAU;AACV,mBAAiB;AACjB,WAAS;AACT,cAAY;AACd;AACA,CAjIC,QAiIQ,CAAC,gCAAkC,CAxE3C;AAyEC,cAAY;AACZ,cAAY;AACd;AACA,CAZC,YAYY,CAAC;AACZ,UAAQ;AACR,cAAY,IAAI;AAChB,YAAU;AACZ;AACA,CAjBC,aAiBa,EAAE;AACd,aAAW;AACb;AACA,CAAC;AACC,YAAU;AACV,WAAS;AACT,SAAO;AACP,WAAS;AACT,iBAAe;AACf,iBAAe;AACf,OAAK;AACL,WAAS;AACT,cAAY;AACZ,cAAY;AACd;AACA,CAAC,kBAAkB;AACjB,aAAW;AACb;AACA,CAHC,kBAGkB;AACjB,aAAW;AACX,SAAO;AACT;AACA,CAAC;AACC,SAAO;AACP,UAAQ;AACR,UAAQ,IAAI,MAAM;AAClB,oBAAkB,IAAI;AACtB,iBAAe;AACf,aAAW,SAAS,KAAK,OAAO;AAClC;AACA,CAAC;AACC,YAAU;AACV,SAAO;AACP,UAAQ;AACR,WAAS,IAAI;AACb,cAAY,UAAU,GAAG,IAAI,EAAE,IAAI,WAAW,GAAG,EAAE;AACnD,SAAO,IAAI;AACX,aAAW;AACb;AACA,CAAC;AACC,SAAO;AACP,QAAM,EAAE,EAAE;AACV,YAAU;AACV,eAAa,IAAI,MAAM,IAAI;AAC3B,WAAS;AACT,cAAY,IAAI;AAClB;AACA,CARC,gBAQgB;AACjB,CAAC,UAAU;AACT,UAAQ,EAAE,EAAE;AACZ,aAAW;AACX,kBAAgB;AAChB,kBAAgB;AAClB;AACA,CAfC,gBAegB;AACf,UAAQ,EAAE,EAAE;AACd;AACA,CAlBC,gBAkBgB,GAAG;AAClB,WAAS;AACT,mBAAiB;AACjB,WAAS,IAAI;AACb,iBAAe,IAAI,MAAM,IAAI;AAC/B;AACA,CAxBC,gBAwBgB;AACf,UAAQ;AACR,wBAAsB;AACxB;AACA,CA5BC,gBA4BgB;AACf,WAAS,KAAK;AACd,UAAQ;AACR,iBAAe,IAAI,MAAM,IAAI;AAC7B,aAAW;AACb;AACA,CAlCC,gBAkCgB,EAAE;AACjB,WAAS;AACT,SAAO,IAAI;AACb;AACA,CA7BC;AA8BC,WAAS,KAAK;AACd,cAAY,IAAI,MAAM,IAAI;AAC5B;AACA,CAjCC,UAiCU;AACT,aAAW;AACb;AACA,WAxDa;AAyDX;AACE,eAAW,OAAO;AACpB;AACF;AACA,QAAO,WAAY;AACjB,GAtKD;AAuKG,WAAO;AACP,gBAAY;AACZ,oBAAgB;AAClB;AACA,GA5LD;AA6LG,kBAAc;AAChB;AACA,GAlJD;AAmJG,aAAS;AACX;AACA,GA9DD;AA+DG,cAAU;AACV,WAAO;AACP,aAAS;AACT,YAAQ;AACR,gBAAY;AACd;AACA,GA7HD;AA8HG,aAAS;AACX;AACF;AACA,QAAO,wBAAyB;AAC9B,GA3FD;AA4FG,eAAW;AACb;AACA,GA5JD;AA6JG,gBAAY;AACd;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/styles.css"],"sourcesContent":[".rpv-root {\n --rpv-ink: #17211d;\n --rpv-paper: #f8f5ee;\n --rpv-panel: #ebe5d9;\n --rpv-rule: #c7bfb1;\n --rpv-accent: #d8663a;\n color: var(--rpv-ink);\n background: var(--rpv-paper);\n border: 1px solid var(--rpv-rule);\n font:\n 500 14px/1.4 ui-sans-serif,\n system-ui,\n sans-serif;\n min-width: 0;\n}\n.rpv-root button {\n color: inherit;\n font: inherit;\n}\n.rpv-toolbar {\n min-height: 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 0 14px;\n border-bottom: 1px solid var(--rpv-rule);\n background: color-mix(in srgb, var(--rpv-paper) 90%, white);\n}\n.rpv-toolbar__cluster {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.rpv-toolbar button {\n width: 30px;\n height: 30px;\n border: 0;\n background: transparent;\n cursor: pointer;\n}\n.rpv-toolbar button:hover:not(:disabled) {\n background: var(--rpv-panel);\n}\n.rpv-toolbar button:focus-visible,\n.rpv-thumbnail:focus-visible {\n outline: 3px solid color-mix(in srgb, var(--rpv-accent) 55%, transparent);\n outline-offset: 2px;\n}\n.rpv-toolbar button:disabled {\n opacity: 0.35;\n}\n.rpv-toolbar__count {\n min-width: 66px;\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\n.rpv-workspace {\n display: flex;\n min-height: var(--rpv-height);\n max-height: var(--rpv-height);\n}\n.rpv-filmstrip {\n width: 184px;\n flex: 0 0 184px;\n overflow: auto;\n padding: 18px 14px 32px;\n border-right: 1px solid var(--rpv-rule);\n background: var(--rpv-panel);\n}\n.rpv-filmstrip__sizer {\n position: relative;\n width: 100%;\n}\n.rpv-filmstrip__item {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n}\n.rpv-thumbnail {\n position: relative;\n display: block;\n width: 100%;\n padding: 0 0 15px 24px;\n border: 0;\n background: transparent;\n cursor: pointer;\n text-align: left;\n}\n.rpv-thumbnail__number {\n position: absolute;\n top: 4px;\n left: 0;\n color: #756d63;\n font-size: 10px;\n font-variant-numeric: tabular-nums;\n}\n.rpv-thumbnail__canvas {\n display: block;\n min-height: 76px;\n overflow: hidden;\n background: white;\n border: 1px solid var(--rpv-rule);\n transition:\n transform 160ms cubic-bezier(0.22, 1, 0.36, 1),\n border-color 160ms;\n}\n.rpv-thumbnail:hover .rpv-thumbnail__canvas {\n transform: translateY(-2px);\n}\n.rpv-thumbnail[data-active] .rpv-thumbnail__canvas {\n border: 2px solid var(--rpv-accent);\n}\n.rpv-stage {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n background: #d8d2c8;\n}\n.rpv-viewport {\n width: 100%;\n height: 100%;\n overflow: auto;\n overflow-anchor: none;\n padding: 34px;\n box-sizing: border-box;\n}\n.rpv-root[data-rpv-scroll-owner='external'] .rpv-workspace {\n min-height: 0;\n max-height: none;\n}\n.rpv-viewport[data-rpv-scroll-owner='external'] {\n height: auto;\n min-height: var(--rpv-height);\n overflow: visible;\n}\n.rpv-status {\n position: absolute;\n z-index: 5;\n inset: 0;\n display: grid;\n place-content: center;\n justify-items: center;\n gap: 10px;\n padding: 40px;\n text-align: center;\n background: #d8d2c8;\n}\n.rpv-status--error strong {\n font-size: 18px;\n}\n.rpv-status--error span {\n max-width: 52ch;\n color: #645e56;\n}\n.rpv-spinner {\n width: 25px;\n height: 25px;\n border: 2px solid #aaa197;\n border-top-color: var(--rpv-accent);\n border-radius: 50%;\n animation: rpv-spin 0.8s linear infinite;\n}\n.rpv-slide-label {\n position: absolute;\n right: 14px;\n bottom: 10px;\n padding: 3px 7px;\n background: color-mix(in srgb, var(--rpv-ink) 82%, transparent);\n color: var(--rpv-paper);\n font-size: 11px;\n}\n.rpv-diagnostics {\n width: 270px;\n flex: 0 0 270px;\n overflow: auto;\n border-left: 1px solid var(--rpv-rule);\n padding: 20px;\n background: var(--rpv-paper);\n}\n.rpv-diagnostics h2,\n.rpv-notes h2 {\n margin: 0 0 16px;\n font-size: 12px;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.rpv-diagnostics dl {\n margin: 0 0 24px;\n}\n.rpv-diagnostics dl div {\n display: flex;\n justify-content: space-between;\n padding: 7px 0;\n border-bottom: 1px solid var(--rpv-rule);\n}\n.rpv-diagnostics dd {\n margin: 0;\n font-variant-numeric: tabular-nums;\n}\n.rpv-diagnostics p {\n padding: 10px 0;\n margin: 0;\n border-bottom: 1px solid var(--rpv-rule);\n font-size: 12px;\n}\n.rpv-diagnostics p strong {\n display: block;\n color: var(--rpv-accent);\n}\n.rpv-notes {\n padding: 20px 24px;\n border-top: 1px solid var(--rpv-rule);\n}\n.rpv-notes p {\n max-width: 72ch;\n}\n@keyframes rpv-spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (max-width: 760px) {\n .rpv-filmstrip {\n width: 108px;\n flex-basis: 108px;\n padding-inline: 8px;\n }\n .rpv-thumbnail {\n padding-left: 0;\n }\n .rpv-thumbnail__number {\n display: none;\n }\n .rpv-diagnostics {\n position: absolute;\n right: 0;\n z-index: 4;\n height: 100%;\n box-sizing: border-box;\n }\n .rpv-viewport {\n padding: 18px;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .rpv-spinner {\n animation: none;\n }\n .rpv-thumbnail__canvas {\n transition: none;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -373,14 +373,32 @@ interface ParsedPresentation {
373
373
  readonly document: PresentationDocument;
374
374
  readonly warnings: readonly PresentationWarning[];
375
375
  }
376
+ type PptxPerformancePhase = 'download' | 'wasm-parsing' | 'embedded-font-preparation' | 'slide-dom-svg-rendering' | 'image-decoding' | 'raster-encoding';
377
+ interface PptxPerformanceMeasurement {
378
+ /** Wall-clock duration for this phase. */
379
+ durationMs: number;
380
+ phase: PptxPerformancePhase;
381
+ /** Present for work attributable to one slide. */
382
+ slideIndex?: number;
383
+ }
376
384
  type PresentationSource = BinaryPresentationSource | PresentationDocument | ParsedPresentation;
377
385
  type ViewerMode = 'slide' | 'continuous';
386
+ /** @deprecated Superseded by `ViewerZoomLevel` and the `zoom` prop. */
378
387
  type FitMode = 'contain' | 'none';
388
+ type ViewerZoomMode = 'automatic' | 'fit-page' | 'fit-width';
389
+ type ViewerZoomLevel = number | ViewerZoomMode;
390
+ type ViewerZoomState = {
391
+ level: ViewerZoomLevel;
392
+ /** Resolved percentage; `100` means actual size. */
393
+ resolvedZoom: number;
394
+ };
379
395
  interface ParsePresentationOptions {
380
396
  signal?: AbortSignal;
381
397
  formatHint?: PresentationFormat;
382
398
  maxInputBytes?: number;
383
399
  fetchInit?: Omit<RequestInit, 'signal'>;
400
+ /** Receives cold-start download and native parsing timings. */
401
+ onPerformanceMeasurement?: (measurement: PptxPerformanceMeasurement) => void;
384
402
  }
385
403
  interface VirtualizationOptions {
386
404
  /**
@@ -466,6 +484,55 @@ interface PptxSlideThumbnailRenderOptions {
466
484
  /** Rendered thumbnail width in CSS pixels. */
467
485
  width?: number;
468
486
  }
487
+ type PptxThumbnailOutput = 'blob' | 'canvas' | 'imageBitmap' | 'svg';
488
+ interface PptxThumbnailRendererOptions {
489
+ /**
490
+ * Thumbnail-specific font policy. Defaults to skipping embedded fonts and
491
+ * not waiting for the global font set.
492
+ */
493
+ fonts?: PptxFontOptions;
494
+ /** Maximum number of slide renders executing at once. Default `2`. */
495
+ concurrency?: number;
496
+ onPerformanceMeasurement?: (measurement: PptxPerformanceMeasurement) => void;
497
+ onWarning?: (warning: PresentationWarning) => void;
498
+ }
499
+ interface PptxThumbnailRenderOptions {
500
+ maxHeight?: number;
501
+ maxWidth?: number;
502
+ /** Output pixel density. Default `1`. */
503
+ pixelRatio?: number;
504
+ output?: PptxThumbnailOutput;
505
+ /** PNG encoding quality where supported. */
506
+ quality?: number;
507
+ signal?: AbortSignal;
508
+ }
509
+ interface PptxThumbnailRenderResult<T> {
510
+ data: T;
511
+ height: number;
512
+ measurements: readonly PptxPerformanceMeasurement[];
513
+ output: PptxThumbnailOutput;
514
+ slideIndex: number;
515
+ width: number;
516
+ }
517
+ interface PptxThumbnailRenderer {
518
+ readonly presentation: ParsedPresentation;
519
+ readonly ready: Promise<void>;
520
+ renderSlide(slideIndex: number, options: PptxThumbnailRenderOptions & {
521
+ output: 'canvas';
522
+ }): Promise<PptxThumbnailRenderResult<HTMLCanvasElement>>;
523
+ renderSlide(slideIndex: number, options: PptxThumbnailRenderOptions & {
524
+ output: 'imageBitmap';
525
+ }): Promise<PptxThumbnailRenderResult<ImageBitmap>>;
526
+ renderSlide(slideIndex: number, options: PptxThumbnailRenderOptions & {
527
+ output: 'svg';
528
+ }): Promise<PptxThumbnailRenderResult<string>>;
529
+ renderSlide(slideIndex: number, options?: PptxThumbnailRenderOptions & {
530
+ output?: 'blob';
531
+ }): Promise<PptxThumbnailRenderResult<Blob>>;
532
+ /** Renders exactly the supplied indexes, preserving their order. */
533
+ renderSlides(slideIndexes: readonly number[], options?: PptxThumbnailRenderOptions): Promise<PptxThumbnailRenderResult<Blob | HTMLCanvasElement | ImageBitmap | string>[]>;
534
+ destroy(): void;
535
+ }
469
536
  interface PptxSlideThumbnailRenderWindow {
470
537
  /**
471
538
  * Slide indexes whose attached thumbnail containers should render first.
@@ -526,7 +593,8 @@ interface PptxViewerController {
526
593
  goToSlide(index: number, scrollOptions?: ScrollIntoViewOptions): Promise<void>;
527
594
  next(): Promise<void>;
528
595
  previous(): Promise<void>;
529
- setZoom(percent: number): Promise<void>;
596
+ setZoom(level: ViewerZoomLevel): Promise<void>;
597
+ /** @deprecated Use `setZoom()` with a responsive zoom mode. */
530
598
  setFitMode(mode: FitMode): Promise<void>;
531
599
  search(query: string | RegExp, options?: ViewerSearchOptions): PresentationSearchResult[];
532
600
  highlightSearchResult(result: PresentationSearchResult, options?: SearchHighlightOptions): Promise<void>;
@@ -537,7 +605,10 @@ interface PptxViewerController {
537
605
  renderThumbnail(index: number, target: HTMLElement, options?: PptxSlideThumbnailRenderOptions): Promise<() => void>;
538
606
  getDocument(): PresentationDocument | null;
539
607
  getSlideIndex(): number;
540
- getZoom(): number;
608
+ /** Current numeric percentage or active responsive mode. */
609
+ getZoom(): ViewerZoomLevel;
610
+ /** Current clamped percentage after resolving the viewport-dependent mode. */
611
+ getResolvedZoom(): number;
541
612
  }
542
613
  interface ReactPptxViewerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onError' | 'onLoad'> {
543
614
  source: PresentationSource;
@@ -546,9 +617,11 @@ interface ReactPptxViewerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chi
546
617
  slideIndex?: number;
547
618
  /** Initial zero-based slide index for uncontrolled usage. */
548
619
  initialSlide?: number;
549
- /** Controlled zoom percentage. */
550
- zoom?: number;
551
- defaultZoom?: number;
620
+ /** Controlled zoom percentage or responsive zoom mode. */
621
+ zoom?: ViewerZoomLevel;
622
+ /** Initial zoom percentage or responsive zoom mode for uncontrolled usage. */
623
+ defaultZoom?: ViewerZoomLevel;
624
+ /** @deprecated Superseded by `zoom`. `contain` maps to `automatic`. */
552
625
  fitMode?: FitMode;
553
626
  width?: number;
554
627
  height?: number | string;
@@ -578,10 +651,14 @@ interface ReactPptxViewerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chi
578
651
  onError?: (error: PptxViewerError) => void;
579
652
  onWarning?: (warning: PresentationWarning) => void;
580
653
  onSlideChange?: (index: number) => void;
654
+ /** Reports semantic zoom changes and responsive percentage recalculation. */
655
+ onZoomChange?: (state: ViewerZoomState) => void;
581
656
  onSlideRendered?: (index: number, element: HTMLElement) => void;
582
657
  onSlideUnmounted?: (index: number) => void;
583
658
  onSearchResults?: (results: readonly PresentationSearchResult[]) => void;
584
659
  onThumbnailRendered?: (index: number, element: HTMLElement) => void;
660
+ /** Receives parsing, font preparation, and rendering timings. */
661
+ onPerformanceMeasurement?: (measurement: PptxPerformanceMeasurement) => void;
585
662
  /** Supplies the internal slide surface to host integrations and test harnesses. */
586
663
  onViewportReady?: (element: HTMLDivElement) => void;
587
664
  ref?: Ref<PptxViewerController>;
@@ -628,8 +705,10 @@ declare function usePptxViewer(): UsePptxViewerResult;
628
705
  */
629
706
  declare function usePptxViewerThumbnails(controller: PptxViewerController | null | undefined, options?: UsePptxViewerThumbnailsOptions): PptxViewerThumbnails;
630
707
 
708
+ declare function createPptxThumbnailRenderer(presentation: ParsedPresentation, options?: PptxThumbnailRendererOptions): PptxThumbnailRenderer;
709
+
631
710
  /** Cross-platform, metrically similar substitutions for common Office typefaces. */
632
711
  declare const OFFICE_FONT_FALLBACKS: Readonly<Record<string, readonly string[]>>;
633
712
  declare function resolvePptxFontFamily(fontFamily: string, options?: Pick<PptxFontOptions, 'fallbacks' | 'fallbackFamilies' | 'useOfficeFallbacks'>): string;
634
713
 
635
- export { type BinaryPresentationSource, type FitMode, OFFICE_FONT_FALLBACKS, type ParsePresentationOptions, type ParsedPresentation, type PptxFontOptions, type PptxFontSource, type PptxSlideThumbnailItem, type PptxSlideThumbnailRenderOptions, type PptxSlideThumbnailRenderWindow, type PptxSlideThumbnailResolution, type PptxSlideThumbnailStatus, type PptxViewerController, PptxViewerError, type PptxViewerThumbnails, type PresentationAsset, type PresentationDocument, type PresentationEmbeddedFont, type PresentationFormat, type PresentationMetadata, type PresentationSearchResult, type PresentationSlide, type PresentationSource, type PresentationTheme, type PresentationWarning, ReactPptxViewer, type ReactPptxViewerProps, type SearchHighlightOptions, type Slide, type SlideComment, type SlideLayout, type SlideMaster, type SlideNode, type SlideNote, type ThumbnailRenderContext, type UsePptxPresentationState, type UsePptxViewerResult, type UsePptxViewerThumbnailsOptions, type ViewerMode, type ViewerSearchOptions, type VirtualizationOptions, type WasmSource, type WorkerWasmSource, initWasm, parsePresentation, presentationParsingDefaults, resolvePptxFontFamily, setWasmSource, usePptxModel, usePptxPresentation, usePptxViewer, usePptxViewerThumbnails };
714
+ export { type BinaryPresentationSource, type FitMode, OFFICE_FONT_FALLBACKS, type ParsePresentationOptions, type ParsedPresentation, type PptxFontOptions, type PptxFontSource, type PptxPerformanceMeasurement, type PptxPerformancePhase, type PptxSlideThumbnailItem, type PptxSlideThumbnailRenderOptions, type PptxSlideThumbnailRenderWindow, type PptxSlideThumbnailResolution, type PptxSlideThumbnailStatus, type PptxThumbnailOutput, type PptxThumbnailRenderOptions, type PptxThumbnailRenderResult, type PptxThumbnailRenderer, type PptxThumbnailRendererOptions, type PptxViewerController, PptxViewerError, type PptxViewerThumbnails, type PresentationAsset, type PresentationDocument, type PresentationEmbeddedFont, type PresentationFormat, type PresentationMetadata, type PresentationSearchResult, type PresentationSlide, type PresentationSource, type PresentationTheme, type PresentationWarning, ReactPptxViewer, type ReactPptxViewerProps, type SearchHighlightOptions, type Slide, type SlideComment, type SlideLayout, type SlideMaster, type SlideNode, type SlideNote, type ThumbnailRenderContext, type UsePptxPresentationState, type UsePptxViewerResult, type UsePptxViewerThumbnailsOptions, type ViewerMode, type ViewerSearchOptions, type ViewerZoomLevel, type ViewerZoomMode, type ViewerZoomState, type VirtualizationOptions, type WasmSource, type WorkerWasmSource, createPptxThumbnailRenderer, initWasm, parsePresentation, presentationParsingDefaults, resolvePptxFontFamily, setWasmSource, usePptxModel, usePptxPresentation, usePptxViewer, usePptxViewerThumbnails };