@silurus/ooxml 0.69.0 → 0.70.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.
@@ -68,6 +68,25 @@ export declare interface BlipBullet {
68
68
  sizePct: number | null;
69
69
  }
70
70
 
71
+ /**
72
+ * Build the transparent text-selection overlay for a rendered pptx slide. Unlike
73
+ * docx (flat spans), pptx groups runs into one positioned `<div>` per shape frame
74
+ * (keyed by the shape's geometry + total rotation) and applies a CSS `rotate()` to
75
+ * the group when the shape is rotated, so the browser selection tracks the drawn,
76
+ * rotated text as a unit. Each run's `<span>` is absolutely positioned INSIDE its
77
+ * shape div (`inShapeX`/`inShapeY`). Extracted verbatim from
78
+ * `PptxViewer._buildTextLayer` so the pager (PptxViewer) and the continuous-scroll
79
+ * viewer (PptxScrollViewer, WS4) share one implementation; public API for
80
+ * integrators (design §10). MAIN render mode only — `onTextRun` cannot cross the
81
+ * worker boundary.
82
+ *
83
+ * @param layer the overlay div.
84
+ * @param runs per-run + per-shape geometry from `renderSlide({ onTextRun })`.
85
+ * @param cssWidth the rendered canvas's CSS width (px, number).
86
+ * @param cssHeight the rendered canvas's CSS height (px, number).
87
+ */
88
+ export declare function buildPptxTextLayer(layer: HTMLDivElement, runs: PptxTextRunInfo[], cssWidth: number, cssHeight: number): void;
89
+
71
90
  /**
72
91
  * PPTX bullet marker. The shared core {@link CoreBullet} union
73
92
  * (none/inherit/char/autoNum) plus the PPTX-only picture bullet
@@ -136,117 +155,19 @@ declare interface ChartDataPointOverride {
136
155
  */
137
156
  export declare interface ChartElement {
138
157
  type: 'chart';
158
+ /** Frame geometry on the slide, in EMU. */
139
159
  x: number;
140
160
  y: number;
141
161
  width: number;
142
162
  height: number;
143
- chartType: string;
144
- title: string | null;
145
- categories: string[];
146
- series: ChartSeries[];
147
- valMax: number | null;
148
- valMin: number | null;
149
- subtotalIndices: number[];
150
- showDataLabels: boolean;
151
- catAxisHidden: boolean;
152
- valAxisHidden: boolean;
153
- /** `<c:catAx><c:spPr><a:ln><a:noFill>` — line-only hide; labels stay. */
154
- catAxisLineHidden?: boolean;
155
- /** `<c:valAx><c:spPr><a:ln><a:noFill>` — line-only hide; labels stay. */
156
- valAxisLineHidden?: boolean;
157
- plotAreaBg: string | null;
158
- /** Outer chartSpace background (hex without '#'). null when noFill/absent. */
159
- chartBg: string | null;
160
- /** True when <c:legend> is declared; false suppresses the legend entirely. */
161
- showLegend: boolean;
162
- /** catAx crossBetween: "between" (default, 0.5-step padding) or "midCat". */
163
- catAxisCrossBetween: 'between' | 'midCat' | string;
164
- /** `<c:valAx><c:majorTickMark>`. "cross" (default) | "out" | "in" | "none". */
165
- valAxisMajorTickMark: 'cross' | 'out' | 'in' | 'none' | string;
166
- /** `<c:catAx><c:majorTickMark>`. */
167
- catAxisMajorTickMark: 'cross' | 'out' | 'in' | 'none' | string;
168
- /** Title font size in OOXML hundredths of a point (1600 = 16pt). null = default. */
169
- titleFontSizeHpt: number | null;
170
- /** Title font color as a hex string without '#'. null = default/theme. */
171
- titleFontColor?: string | null;
172
- /** Title font family (`<a:latin typeface>`). null = default/theme. */
173
- titleFontFace?: string | null;
174
- /** `<c:catAx><c:txPr>` font size (hpt). null = proportional default. */
175
- catAxisFontSizeHpt: number | null;
176
- /** `<c:valAx><c:txPr>` font size (hpt). null = proportional default. */
177
- valAxisFontSizeHpt: number | null;
178
- /** `<c:catAx><c:txPr>…<a:solidFill>` tick-label color (hex without '#'). */
179
- catAxisFontColor?: string | null;
180
- /** `<c:valAx><c:txPr>…<a:solidFill>` tick-label color (hex without '#'). */
181
- valAxisFontColor?: string | null;
182
- /** `<c:catAx><c:spPr><a:ln><a:solidFill>` axis-line color (hex without '#'). */
183
- catAxisLineColor?: string | null;
184
- /** `<c:catAx><c:spPr><a:ln w>` axis-line width in EMU. */
185
- catAxisLineWidthEmu?: number | null;
186
- /** `<c:valAx><c:spPr><a:ln><a:solidFill>` axis-line color (hex without '#'). */
187
- valAxisLineColor?: string | null;
188
- /** `<c:valAx><c:spPr><a:ln w>` axis-line width in EMU. */
189
- valAxisLineWidthEmu?: number | null;
190
- /** `<c:dLbls><c:txPr>` font size (hpt) for data-point value labels. */
191
- dataLabelFontSizeHpt: number | null;
192
- /** `<c:legend><c:legendPos val>` — "r" (default) | "l" | "t" | "b" | "tr". */
193
- legendPos?: 'r' | 'l' | 't' | 'b' | 'tr' | null;
194
- /** `<c:barChart><c:gapWidth val>` — % of bar width between category groups (default 150). */
195
- barGapWidth?: number | null;
196
- /** `<c:barChart><c:overlap val>` — signed % of bar width for cluster overlap. */
197
- barOverlap?: number | null;
198
- /** `<c:dLbls><c:dLblPos val>` — data label placement ("ctr" | "inEnd" | "outEnd" | …). */
199
- dataLabelPosition?: string | null;
200
- /** `<c:dLbls><c:txPr>…<a:solidFill>` resolved to hex (no '#'). null = renderer default. */
201
- dataLabelFontColor?: string | null;
202
- /** `<c:dLbls><c:numFmt formatCode>` — data label number format. */
203
- dataLabelFormatCode?: string | null;
204
- /** `<c:valAx><c:numFmt formatCode>` — value-axis tick label number format. */
205
- valAxisFormatCode?: string | null;
206
- /** `<c:plotArea><c:layout><c:manualLayout>` (ECMA-376 §21.2.2.32) — explicit
207
- * plot-area placement so bars don't extend past the chart-frame's intended
208
- * inner region (sample-2 slide-16 horizontal bar chart). */
209
- plotAreaManualLayout?: ChartManualLayout | null;
210
- /** `<c:scatterChart><c:scatterStyle val>` (ECMA-376 §21.2.2.42) — drives
211
- * whether scatter charts connect points with straight or smooth lines. */
212
- scatterStyle?: string | null;
213
- /** `<c:radarChart><c:radarStyle val>` (ECMA-376 §21.2.3.10). */
214
- radarStyle?: string | null;
215
- /** `<c:catAx><c:title>` plain text (ECMA-376 §21.2.2.6). For scatter the
216
- * bottom `<c:valAx>` (axPos b/t) feeds this. null = no title. */
217
- catAxisTitle?: string | null;
218
- /** `<c:valAx><c:title>` plain text. For scatter the left `<c:valAx>`
219
- * (axPos l/r) feeds this. null = no title. */
220
- valAxisTitle?: string | null;
221
- /** `<c:catAx><c:title>` run-prop font size (hpt). Distinct from
222
- * `catAxisFontSizeHpt` (tick labels). null = renderer default. */
223
- catAxisTitleSize?: number | null;
224
- /** `<c:catAx><c:title>` run-prop bold flag. null = not bold. */
225
- catAxisTitleBold?: boolean | null;
226
- /** `<c:catAx><c:title>` run-prop color (hex without '#'). null = default. */
227
- catAxisTitleColor?: string | null;
228
- /** `<c:valAx><c:title>` run-prop font size (hpt). null = renderer default. */
229
- valAxisTitleSize?: number | null;
230
- /** `<c:valAx><c:title>` run-prop bold flag. null = not bold. */
231
- valAxisTitleBold?: boolean | null;
232
- /** `<c:valAx><c:title>` run-prop color (hex without '#'). null = default. */
233
- valAxisTitleColor?: string | null;
234
- /** `<c:title>...defRPr@b` chart title bold flag. null = not bold. */
235
- titleFontBold?: boolean | null;
236
- /** `<c:catAx><c:txPr>...defRPr@b` X-axis tick-label bold flag. null = not bold. */
237
- catAxisFontBold?: boolean | null;
238
- /** `<c:valAx><c:txPr>...defRPr@b` Y-axis tick-label bold flag. null = not bold. */
239
- valAxisFontBold?: boolean | null;
240
- /** Explicit chart border color (hex without '#') from
241
- * `<c:chartSpace><c:spPr><a:ln><a:solidFill><a:srgbClr>`. Only set when the
242
- * XML explicitly declares a paintable line; null otherwise (no default border). */
243
- chartBorderColor?: string | null;
244
- /** `<c:chartSpace><c:spPr><a:ln@w>` border width in EMU. null = 1px hairline
245
- * when a color is present. */
246
- chartBorderWidthEmu?: number | null;
247
- /** Secondary value axis for combo charts (bar + line with a right-hand
248
- * axis). null/absent for the common single value-axis case. */
249
- secondaryValAxis?: SecondaryValueAxis | null;
163
+ /**
164
+ * The chart payload, already in the canonical {@link ChartModel} shape emitted
165
+ * by the Rust parser (`ooxml_common::chart::ChartModel`). Passed straight to
166
+ * `@silurus/ooxml-core`'s `renderChart` — no per-field adapter. The former
167
+ * 60-field flat copy on this interface is gone; all chart properties now live
168
+ * on `chart`.
169
+ */
170
+ chart: ChartModel;
250
171
  }
251
172
 
252
173
  declare interface ChartErrBars {
@@ -600,6 +521,20 @@ declare interface ChartSeriesDataLabels {
600
521
  */
601
522
  declare type ChartType = 'line' | 'stackedLine' | 'stackedLinePct' | 'clusteredBar' | 'clusteredBarH' | 'stackedBar' | 'stackedBarH' | 'stackedBarPct' | 'stackedBarHPct' | 'area' | 'stackedArea' | 'stackedAreaPct' | 'pie' | 'doughnut' | 'scatter' | 'bubble' | 'radar' | 'waterfall' | string;
602
523
 
524
+ /**
525
+ * Translucent overlay drawn over a finished slide so it reads faintly
526
+ * (PowerPoint's hidden-slide thumbnail look). A pure render mechanism: the
527
+ * renderer never decides *when* to dim — the caller ({@link PptxViewer}'s
528
+ * `'dim'` mode) does. Both fields are required at the engine boundary; the
529
+ * viewer-facing override (`PptxViewerOptions.hiddenSlideDim`) is partial.
530
+ */
531
+ export declare interface DimOptions {
532
+ /** CSS color of the overlay (e.g. `'#ffffff'`). */
533
+ color: string;
534
+ /** Overlay opacity 0..1 (e.g. `0.6` ⇒ underlying content shows at 40%). */
535
+ opacity: number;
536
+ }
537
+
603
538
  /**
604
539
  * An OMML equation embedded in a paragraph (ECMA-376 §22.1). Parsed into the
605
540
  * shared math AST and rendered by `@silurus/ooxml-core`'s math engine.
@@ -654,6 +589,9 @@ export declare interface GradientStop {
654
589
  color: string;
655
590
  }
656
591
 
592
+ /** How {@link PptxViewer} presents hidden slides (`<p:sld show="0">`). */
593
+ export declare type HiddenSlideMode = 'show' | 'skip' | 'dim';
594
+
657
595
  /**
658
596
  * Image fill — ECMA-376 §20.1.8.14 (CT_BlipFillProperties). The embedded blip
659
597
  * is carried as a zip path + MIME; the renderer fetches the bytes on demand via
@@ -746,6 +684,21 @@ declare interface LoadOptions_2 {
746
684
  * via `@font-face` in your application CSS.
747
685
  */
748
686
  useGoogleFonts?: boolean;
687
+ /**
688
+ * Override the URL the parser worker fetches the WebAssembly module from.
689
+ *
690
+ * By default each format resolves the `.wasm` asset that ships next to its
691
+ * bundle (relative to the module URL), so no configuration is needed. Set
692
+ * this to serve the parser WASM from a CDN or a self-hosted path instead — a
693
+ * relative value is resolved against the current document URL. The same
694
+ * dependency-injection contract across docx / pptx / xlsx.
695
+ *
696
+ * The referenced file must be the matching format's `*_parser_bg.wasm`
697
+ * artifact (the one wasm-bindgen emitted for that parser); pointing it at a
698
+ * mismatched or missing file makes `load()` reject when the worker
699
+ * instantiates it.
700
+ */
701
+ wasmUrl?: string | URL;
749
702
  /**
750
703
  * Override the per-entry ZIP decompression cap (bytes) used by the zip-bomb
751
704
  * guard in the Rust parser. Defaults to 512 MiB. Raise it to load documents
@@ -753,6 +706,16 @@ declare interface LoadOptions_2 {
753
706
  * untrusted input. Zero / negative values fall back to the default.
754
707
  */
755
708
  maxZipEntryBytes?: number;
709
+ /**
710
+ * Reject the parse request if the parser worker does not answer within this
711
+ * many milliseconds. Opt-in safety net for a wedged or crashed worker that
712
+ * would otherwise leave `load()` pending forever. **Default: unlimited** —
713
+ * parsing a large document with heavy embedded media can legitimately take
714
+ * tens of seconds, so no timeout is imposed unless you set one. A worker that
715
+ * throws or fails to load already rejects immediately regardless of this
716
+ * value; this bound only covers the "silent, never-responds" case.
717
+ */
718
+ workerTimeoutMs?: number;
756
719
  /**
757
720
  * Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Inject it
758
721
  * **once** here and every render of this document / presentation / workbook
@@ -1177,6 +1140,11 @@ export declare class PptxPresentation {
1177
1140
  get slideWidth(): number;
1178
1141
  /** Slide height in EMU. */
1179
1142
  get slideHeight(): number;
1143
+ /** The render mode this engine was loaded with ('main' | 'worker'). A fact for
1144
+ * integrators and the scroll viewer: an injected engine's mode decides whether
1145
+ * slides render via renderSlide (main) or renderSlideToBitmap (worker) — no
1146
+ * probing (design §11: no silent mis-pathing). */
1147
+ get mode(): 'main' | 'worker';
1180
1148
  /**
1181
1149
  * Speaker-notes text for a slide (`ppt/notesSlides/notesSlideN.xml`,
1182
1150
  * ECMA-376 §13.3.5 — Notes Slide). Returns the notes-body text as a single
@@ -1197,6 +1165,14 @@ export declare class PptxPresentation {
1197
1165
  * }
1198
1166
  */
1199
1167
  getNotes(slideIndex: number): string | null;
1168
+ /**
1169
+ * Whether the slide at `slideIndex` (0-based, absolute) is marked hidden
1170
+ * (`<p:sld show="0">`, ECMA-376 §19.3.1.38). Like {@link getNotes} the index
1171
+ * is NOT clamped — out-of-range / non-integer ⇒ `false`. This is a *fact*
1172
+ * about the model; deciding what to do with a hidden slide (skip / dim) is the
1173
+ * caller's policy (see {@link PptxViewer}'s `hiddenSlideMode` modes).
1174
+ */
1175
+ isHidden(slideIndex: number): boolean;
1200
1176
  /** Render a slide onto the given canvas. */
1201
1177
  renderSlide(canvas: HTMLCanvasElement | OffscreenCanvas, slideIndex: number, opts?: RenderSlideOptions): Promise<void>;
1202
1178
  /**
@@ -1234,6 +1210,475 @@ export declare class PptxPresentation {
1234
1210
  destroy(): void;
1235
1211
  }
1236
1212
 
1213
+ export declare class PptxScrollViewer {
1214
+ private _pres;
1215
+ private readonly _injected;
1216
+ private readonly _opts;
1217
+ private readonly _container;
1218
+ private readonly _wrapper;
1219
+ private readonly _scrollHost;
1220
+ private readonly _spacer;
1221
+ /** Resolved render mode. When an engine is injected the engine's own `mode`
1222
+ * is authoritative (design §11 — no silent mis-pathing / no probing); an
1223
+ * explicitly conflicting `opts.mode` is rejected at construction. When self-
1224
+ * loading, `opts.mode` decides and `load()` passes it to `PptxPresentation.load`. */
1225
+ private _mode;
1226
+ /** Dimensionless zoom multiplier over the 96-dpi natural slide size (mirrors
1227
+ * `DocxScrollViewer`, whose `_scale` multiplies `widthPt × PT_TO_PX`). The
1228
+ * natural (1×) slide width in CSS px is `slideEmu / EMU_PER_PX`; the base fit
1229
+ * sets `_scale` so that natural width maps to the container width, and zoom
1230
+ * multiplies it further (design §7). */
1231
+ private _scale;
1232
+ /** Whether the base fit scale has been established. Set true the first time
1233
+ * `relayout()` resolves a positive base scale. We use an explicit flag rather
1234
+ * than a `_scale === 1` sentinel because a fit scale of exactly 1 is a valid
1235
+ * established state (a 1× fit would otherwise be re-fit forever). */
1236
+ private _scaleEstablished;
1237
+ /** Live slots keyed by slide index. */
1238
+ private readonly _slots;
1239
+ /** Recyclable detached slots (canvas + textLayer reused across slides). */
1240
+ private readonly _free;
1241
+ /** Cached per-slide heights in px at the current scale (index-aligned). All
1242
+ * slides are the same size, so every entry equals the uniform slide height. */
1243
+ private _heights;
1244
+ private _lastRange;
1245
+ private _lastTopIndex;
1246
+ private _scrollListener;
1247
+ /** Set by `destroy()`. Async render callbacks (main + worker) check it before
1248
+ * reporting an error so a rejection that lands after teardown is swallowed
1249
+ * rather than surfaced to a `onError` on a dead viewer. */
1250
+ private _destroyed;
1251
+ /** Worker mode: slide indices whose bitmap render is currently dispatched to the
1252
+ * engine. Coalesces a scroll storm — we never dispatch a second render for a
1253
+ * slide whose first is still in flight — and lets us drop slides that scrolled
1254
+ * out of the window before dispatch (design §11 worker coalescing).
1255
+ *
1256
+ * T4 ZOOM HAZARD (RESOLVED by the render epoch below): coalescing keys on slide
1257
+ * INDEX only, with no notion of the scale a dispatch was made at. Once
1258
+ * `setScale` can change the zoom mid-flight, an in-flight bitmap dispatched at
1259
+ * the OLD scale can still pass the on-resolution identity check if the SAME
1260
+ * slot object is re-mounted for slide `i` (the pool reuses slot objects, so
1261
+ * `_slots.get(i) === slot && slot.renderedSlide === i` can hold for an old
1262
+ * dispatch), and get painted at the WRONG resolution. We fix this with a render
1263
+ * epoch (`_renderEpoch`): each dispatch captures the epoch, and on resolution a
1264
+ * moved epoch ⇒ STALE (close + re-dispatch the live slot). See
1265
+ * `_renderSlotBitmap`. */
1266
+ private readonly _slideInFlight;
1267
+ /** Render generation, bumped on every effective `setScale` (and the resize
1268
+ * re-fit in `_onResize`, which routes through `setScale`). Stamped into each async render
1269
+ * dispatch; a resolution whose captured epoch ≠ this value is STALE — its
1270
+ * pixels/geometry are at a superseded scale. Worker path: close the orphan
1271
+ * bitmap + re-dispatch the live slot. Main path: skip the (stale) text-layer
1272
+ * build; the engine's per-canvas token already discards the stale pixels. */
1273
+ private _renderEpoch;
1274
+ /** Pending settle-render timer handle (design §7 mechanism 2). Set by
1275
+ * `_scheduleSettle` after each `setScale`, reset on the next one so a burst
1276
+ * dispatches ONE settle at the end, and cleared in `destroy()`. `ReturnType`
1277
+ * of `setTimeout` (a number in the DOM, a Timeout object in node) so the type
1278
+ * is host-agnostic. */
1279
+ private _settleTimer;
1280
+ private _wheelListener;
1281
+ /** One-shot latch for the worker-mode text-selection warning. The overlay is a
1282
+ * main-mode-only feature: in worker mode the per-run `onTextRun` geometry
1283
+ * cannot cross the worker boundary, so an `enableTextSelection` overlay stays
1284
+ * empty. We warn once (parity with `PptxViewer`) rather than per slot. */
1285
+ private _warnedNoTextSelection;
1286
+ /** Observes the container so a width change re-fits the base scale. Disconnected
1287
+ * in `destroy()`. */
1288
+ private _resizeObserver;
1289
+ /** The base fit scale at the last established/re-fit layout. `_onResize` divides
1290
+ * `_scale` by this to recover the current zoom multiplier so a width change
1291
+ * re-fits the base while preserving the user's zoom (design §11). */
1292
+ private _prevBase;
1293
+ /** The fit width (px) the base scale was last established at. Lets `_onResize`
1294
+ * skip the re-fit when only the height changed (a ResizeObserver fires on ANY
1295
+ * box change, but only a WIDTH change alters the fit-to-width base scale). */
1296
+ private _lastFitWidth;
1297
+ /** Resolved slide-canvas `box-shadow` (design: the recipe drop shadow by
1298
+ * default). Resolved ONCE with `??` — NOT `||` — so `pageShadow: false`
1299
+ * survives as the "no shadow" sentinel (a `||` would treat `false` as absent
1300
+ * and wrongly re-apply the default). Applied by `_applyPageShadow` at EVERY
1301
+ * canvas-creation site (`_acquireSlot` and the double-buffer spare in
1302
+ * `_settleSlot`) so a recycled/re-mounted slot and a settle-swapped spare all
1303
+ * carry it. */
1304
+ private readonly _pageShadow;
1305
+ constructor(container: HTMLElement, opts?: PptxScrollViewerOptions);
1306
+ /**
1307
+ * Load a PPTX from URL or ArrayBuffer and render the first window.
1308
+ * UNSUPPORTED when an engine was injected via `opts.presentation` (throws) — the
1309
+ * caller already owns the parsed engine.
1310
+ */
1311
+ load(source: string | ArrayBuffer): Promise<void>;
1312
+ get slideCount(): number;
1313
+ /** Uniform slide width in CSS px at the current scale. `_scale` is a
1314
+ * dimensionless multiplier over the natural 96-dpi width (`slideEmu /
1315
+ * EMU_PER_PX`), mirroring docx's `widthPt × PT_TO_PX × _scale`. */
1316
+ private _slideWidthPx;
1317
+ /** Uniform slide height in CSS px at the current scale. */
1318
+ private _slideHeightPx;
1319
+ /** The fit width (px), deferring when the container is unlaid-out. An EXPLICIT
1320
+ * `opts.width` is the slide's CSS-width contract and is returned UNCHANGED (the
1321
+ * gutters still apply around placement, not to the width). The container-derived
1322
+ * default instead targets `containerWidth − padL − padR` so a slide sits INSIDE
1323
+ * the horizontal gutters at 100%. A non-positive result (gutters wider than the
1324
+ * container) is treated as unlaid-out — the same deferral as a zero-width box. */
1325
+ private _fitWidthPx;
1326
+ /** Base scale: the DIMENSIONLESS multiplier that fits the (uniform) slide
1327
+ * width to the fit-width. `natural = slideWidthEmu / EMU_PER_PX` is the 96-dpi
1328
+ * CSS-px width; `base = fitWidth / natural` (mirrors docx's `w / (widthPt ×
1329
+ * PT_TO_PX)`). Returns 0 when the container has no width yet (deferral). */
1330
+ private _baseScale;
1331
+ /**
1332
+ * Recompute per-slide heights + the spacer and re-mount the visible window.
1333
+ *
1334
+ * The viewer already calls this automatically after `load()`, an injected
1335
+ * engine, a container resize, and a zoom, so most integrations never need it.
1336
+ * It is public as a deliberate escape hatch: if the host mutates the layout in
1337
+ * a way the `ResizeObserver` cannot observe (e.g. a CSS change on an ancestor
1338
+ * that resizes the container without a box-size event, or a font that finishes
1339
+ * loading after first paint), call `relayout()` to force a re-fit. Idempotent —
1340
+ * safe to call repeatedly, and a no-op while the container has zero width (the
1341
+ * fit is deferred until width appears, design §11).
1342
+ */
1343
+ relayout(): void;
1344
+ /** All slides are the same size, so heights = n × uniform. We still feed this
1345
+ * full array to computeVisibleRange (never special-case uniform) so offsets /
1346
+ * topIndex live in one tested place (design §5.1). */
1347
+ private _recomputeHeights;
1348
+ private _gap;
1349
+ private _overscan;
1350
+ /** Desk padding fed to `computeVisibleRange`: `paddingTop`/`paddingBottom`,
1351
+ * each defaulting to `gap` (uniform rhythm). Resolved here (not stored) to
1352
+ * mirror `_gap()`/`_overscan()`, and consumed at EVERY `computeVisibleRange`
1353
+ * call site so the padded offsets are the single source of geometry. */
1354
+ private _pad;
1355
+ /** Horizontal desk gutters: `paddingLeft`/`paddingRight`, each defaulting to
1356
+ * `gap` (uniform rhythm — the horizontal gutters match the vertical padding).
1357
+ * Consumed by `_fitWidthPx` (to shrink the container-derived fit), by
1358
+ * `_positionSlot` (the flush-left floor), and by `_syncSpacerWidth` (the spacer
1359
+ * width). Resolved here (not stored) to mirror `_gap()`/`_pad()`. */
1360
+ private _padH;
1361
+ private _range;
1362
+ private _syncSpacer;
1363
+ /** Horizontal scroll extent: the (uniform deck-wide) slide width plus both
1364
+ * gutters. A spacer NARROWER than the container never creates a scrollbar
1365
+ * (scrollWidth = max(clientWidth, content)), so it is always safe to set — it
1366
+ * only matters when a zoomed-in slide grows past the viewport, where it gives
1367
+ * the gutters something to scroll to on either side. Called from `_syncSpacer`
1368
+ * and after every scale change (zoom / resize re-fit) so the extent tracks the
1369
+ * current slide px width. */
1370
+ private _syncSpacerWidth;
1371
+ private _onScroll;
1372
+ /** Mount/recycle slots for the current visible window. */
1373
+ private _mountVisible;
1374
+ /** Apply the resolved slide-canvas shadow (design: recipe drop shadow by
1375
+ * default, `false` ⇒ none). Single source so `_acquireSlot` and the
1376
+ * double-buffer spare in `_settleSlot` stay in lock-step — a spare that missed
1377
+ * this would lose the shadow on the settle swap. `box-shadow` never affects
1378
+ * layout, so this is safe to (re)set on a live/pooled canvas without shifting
1379
+ * any offset. */
1380
+ private _applyPageShadow;
1381
+ private _acquireSlot;
1382
+ private _recycleSlot;
1383
+ private _positionSlot;
1384
+ /** Device-pixel ratio for a render (opts override → window → 1). */
1385
+ private _dpr;
1386
+ /**
1387
+ * Render slide `i` into `slot`. Routes strictly on the constructor-resolved
1388
+ * `_mode` (design §11 — no probing, no silent mis-pathing): `main` ⇒ paint the
1389
+ * slot's canvas directly via `renderSlide`; `worker` ⇒ transfer an ImageBitmap
1390
+ * from `renderSlideToBitmap`.
1391
+ *
1392
+ * Slot-identity guard: a slot recycled to a DIFFERENT slide while a previous
1393
+ * render is in flight must not repaint the stale slide. `slot.renderedSlide`
1394
+ * tracks the slide this slot is committed to; we stamp it up-front and bail on
1395
+ * resolution if it changed (the engine's own token guard is per-canvas; this is
1396
+ * the viewer's per-slot slide-identity check).
1397
+ *
1398
+ * Render epoch (main path): pixel staleness after a mid-flight `setScale` is
1399
+ * already handled by the engine's per-canvas token (the newer renderSlide on the
1400
+ * same canvas wins) — `setScale` recycles + re-mounts, and the re-mount always
1401
+ * re-dispatches `renderSlide` (renderedSlide reset to -1), so a fresh render is
1402
+ * always issued. But the viewer-side side effects of a STALE resolution — the
1403
+ * text-layer build (its run geometry is at the OLD scale) and the renderedSlide
1404
+ * bookkeeping — must NOT run, or a superseded render would rebuild the overlay
1405
+ * with stale x/y/w/h (the pool reuses slot objects, so the identity check alone
1406
+ * can pass for an old-epoch resolution). We gate them on the captured epoch.
1407
+ */
1408
+ private _renderSlot;
1409
+ /** Warn once when an `enableTextSelection` overlay was requested but the render
1410
+ * mode is `worker` (so the overlay stays empty). Same wording as
1411
+ * `PptxViewer` — one warning per viewer, not per slot. */
1412
+ private _maybeWarnNoTextSelection;
1413
+ /** Route an async render failure to `onError`, or `console.error` when none is
1414
+ * set (so failures are never fully silent), and never after teardown. */
1415
+ private _reportRenderError;
1416
+ /**
1417
+ * Worker-mode slot render: dispatch `renderSlideToBitmap`, transfer the result
1418
+ * via a per-slot `bitmaprenderer` context, and manage the ImageBitmap lifecycle.
1419
+ *
1420
+ * Coalescing / drop-stale (design §11):
1421
+ * - Skip if slide `i` is already in flight (a scroll storm won't double-dispatch).
1422
+ * - Skip if slide `i` already left the mounted window before dispatch.
1423
+ * - On resolution, if `slot` is no longer THIS slide's live slot (it recycled to
1424
+ * another slide, or slide `i` re-mounted onto a DIFFERENT slot while this render
1425
+ * was in flight), close the orphan bitmap and skip the paint. In that
1426
+ * re-mount case a live slot for `i` still awaits a render, so once we clear
1427
+ * the in-flight guard we re-dispatch it — a slide that recycled and re-mounted
1428
+ * mid-flight must never stay blank.
1429
+ * - RENDER EPOCH: the dispatch captures `this._renderEpoch`. `setScale` bumps
1430
+ * the epoch, so a resolution whose captured epoch ≠ the live epoch is STALE
1431
+ * even when the SAME slot object is still mounted for slide `i` (the pool
1432
+ * reuses slot objects, so the identity check alone can't catch a zoom that
1433
+ * happened mid-flight). A moved epoch ⇒ close the orphan + re-dispatch the
1434
+ * live slot at the new scale, never paint the old-scale bitmap.
1435
+ *
1436
+ * Do NOT pass `dim` or `skipMediaControls` to `renderSlideToBitmap`. The scroll
1437
+ * viewer never dims slides (design §8.2 / Delta 6); passing neither means the
1438
+ * static play-badge renders on media slides (matching `PptxViewer`'s
1439
+ * non-media-playback path) — acceptable for v1.
1440
+ */
1441
+ private _renderSlotBitmap;
1442
+ /**
1443
+ * Set the absolute (dimensionless) zoom scale — a multiplier over the 96-dpi
1444
+ * natural slide size, matching `DocxScrollViewer` — clamped inline to
1445
+ * `[zoomMin ?? 0.1, zoomMax ?? 4]` (absolute bounds, XlsxViewer convention — NOT
1446
+ * multiples of the base fit; design §3 keeps the clamp in the viewer, not core),
1447
+ * then re-anchor VERTICALLY so the slide currently under the viewport top stays
1448
+ * fixed. A no-op when nothing is loaded or when the clamped scale is unchanged.
1449
+ *
1450
+ * FLICKER-FREE (design §7): this does NOT re-render the visible slides inline.
1451
+ * It shows an immediate CSS preview (stretch the existing bitmaps, scale the
1452
+ * overlays) and DEBOUNCES a full-resolution settle re-render for ZOOM_SETTLE_MS,
1453
+ * so a wheel/pinch burst never blanks a slide and coalesces into one crisp render.
1454
+ *
1455
+ * Re-anchor (written from scratch — XlsxViewer only re-anchors horizontally):
1456
+ * capture `top = topIndex` and the intra-slide fraction `intraFrac` from the
1457
+ * CURRENT range BEFORE rescale; after recomputing heights at the new scale,
1458
+ * `newScrollTop = offsets'[top] + intraFrac × heights'[top]`, clamped to
1459
+ * `[0, totalHeight' − viewportHeight]`. Because a slide's height scales linearly
1460
+ * with `_scale`, the same fractional position maps exactly to the new geometry.
1461
+ *
1462
+ * CAVEAT — base fit below the floor: `relayout()` sets `_scale = base` WITHOUT
1463
+ * clamping to `[zoomMin, zoomMax]`. If the base fit is below `zoomMin` (a wide
1464
+ * slide in a narrow container), the initial scale sits under the floor, but once
1465
+ * the user zooms via `setScale` the clamp pins the minimum to `zoomMin`, so they
1466
+ * can no longer return below the floor to the original base fit through this API.
1467
+ */
1468
+ setScale(scale: number): void;
1469
+ /**
1470
+ * CSS preview of the visible window at the current `_scale` (design §7
1471
+ * mechanism 1), WITHOUT re-rendering. Slots leaving the window recycle normally;
1472
+ * slots ENTERING the window mount fresh (rendered at the current scale directly,
1473
+ * so they never need a preview); slots that STAY are repositioned and their
1474
+ * canvas + text overlay are CSS-transformed to the new size (the device buffer
1475
+ * is untouched — that is the whole point: no synchronous clear, no blank frame).
1476
+ */
1477
+ private _previewVisible;
1478
+ /**
1479
+ * CSS-preview a single already-mounted slot at the new geometry (design §7): the
1480
+ * wrapper is repositioned + sized (via `_positionSlot`), the canvas bitmap is
1481
+ * STRETCHED to the new CSS size (no `canvas.width` — the device buffer, and thus
1482
+ * the drawn pixels, are left intact, just scaled by the browser), and the text
1483
+ * overlay is scaled by `newScale / renderedScale` so it tracks the stretched
1484
+ * slide. `renderedScale <= 0` means the slot's first render hasn't resolved yet
1485
+ * (nothing to stretch); the pending render captured the current scale, so it
1486
+ * lands correct and no preview is needed.
1487
+ */
1488
+ private _previewSlot;
1489
+ /** (Re)schedule the debounced settle re-render (design §7 mechanism 2). Resets
1490
+ * the timer on every call so a burst of `setScale` dispatches ONE settle
1491
+ * ZOOM_SETTLE_MS after the LAST call. Cleared in `destroy()`. */
1492
+ private _scheduleSettle;
1493
+ /** Full-resolution settle re-render of the visible window (design §7 mechanisms
1494
+ * 2+3). Re-renders each mounted slot at the current scale via the double-buffer
1495
+ * swap (main) / same-canvas transfer (worker). Main mode also rebuilds the text
1496
+ * overlay and clears its preview transform; in worker mode the overlay is
1497
+ * permanently empty (text selection is main-mode-only), so the transform is
1498
+ * inert there and is reset on recycle. Dispatched at the CURRENT epoch; the
1499
+ * existing epoch gate discards it if a later `setScale` supersedes it
1500
+ * mid-render. */
1501
+ private _settleRender;
1502
+ /**
1503
+ * Settle-render one slot at the current scale (design §7 mechanism 3).
1504
+ *
1505
+ * WORKER: re-dispatch the bitmap render into the SAME canvas. The worker path
1506
+ * sizes the device buffer and `transferFromImageBitmap`s it in ONE synchronous
1507
+ * step (no await between `canvas.width = …` and the transfer), so the browser
1508
+ * never composites an intermediate blank frame — no spare canvas is needed. The
1509
+ * `renderedScale === _scale` gate in `_settleRender` plus the epoch gate inside
1510
+ * `_renderSlotBitmap` keep this correct and idempotent.
1511
+ *
1512
+ * MAIN: `renderSlide` synchronously sets `canvas.width = …` (which CLEARS the
1513
+ * backing store to blank) BEFORE its first await and paints AFTER — so rendering
1514
+ * into the on-screen canvas would flash it white. Render into a SPARE off-DOM
1515
+ * canvas instead; only once it resolves at the current epoch do we swap it into
1516
+ * the wrapper (replacing the old canvas). The old canvas keeps showing the
1517
+ * stretched preview until the instant of the swap — blank-free.
1518
+ */
1519
+ private _settleSlot;
1520
+ /**
1521
+ * Scroll so slide `index`'s top edge sits at the viewport top. Clamps `index` to
1522
+ * `[0, slideCount-1]` (the pager convention) and the resulting scrollTop to
1523
+ * `[0, totalHeight − viewportHeight]` so the last slides don't scroll past the
1524
+ * end. A no-op when nothing is loaded or the deck is empty.
1525
+ *
1526
+ * `opts.behavior` ('auto' | 'smooth', default 'auto') is honoured via
1527
+ * `scrollHost.scrollTo({ top, behavior })` when the host supports it (a real
1528
+ * browser); the stub-DOM has no `scrollTo`, so the fallback sets `scrollTop`
1529
+ * directly (which is what the tests assert). We then call `_mountVisible` once.
1530
+ *
1531
+ * MOUNTING CAVEAT: synchronous mounting of the target slide is guaranteed only on
1532
+ * the DEFAULT/'auto' path — there `scrollTop` has already jumped to `top`, so the
1533
+ * `_mountVisible` call reads the final scroll position and the target slide's slots
1534
+ * exist immediately. With `behavior: 'smooth'` the scroll animates ASYNCHRONOUSLY:
1535
+ * `scrollTop` is still near the old position when `_mountVisible` runs, so the
1536
+ * target slide mounts lazily via the animation's subsequent `scroll` events, not
1537
+ * from this call.
1538
+ */
1539
+ scrollToSlide(index: number, opts?: {
1540
+ behavior?: 'auto' | 'smooth';
1541
+ }): void;
1542
+ /**
1543
+ * Re-fit the base scale on a container resize while PRESERVING the current zoom
1544
+ * multiplier (design §11), then re-anchor + re-render. A `ResizeObserver` fires
1545
+ * on any box change, but only a WIDTH change alters the fit-to-width base scale;
1546
+ * a height-only change skips the re-fit yet STILL re-mounts the visible window
1547
+ * (via `_mountVisible`), because a taller viewport reveals rows that were below
1548
+ * the fold and would otherwise stay blank until the next scroll. Empty/unloaded
1549
+ * ⇒ no-op; a still-zero width ⇒ defer.
1550
+ *
1551
+ * Zero-width recovery: a container that was 0-wide at construction never
1552
+ * established a scale (`_scaleEstablished` is false), so the first non-zero
1553
+ * resize establishes it here via `relayout()` — completing the T2 deferral.
1554
+ *
1555
+ * Re-fit math (zoom multiplier preserved):
1556
+ * mult = _scale / _prevBase (the user's zoom over the old base)
1557
+ * newScale = newBase × mult
1558
+ * Routing through `setScale(newScale)` bumps `_renderEpoch` (resize IS an epoch
1559
+ * event — T4 banner) and re-anchors + CSS-previews + debounces a settle re-render
1560
+ * of every slot at the new geometry, exactly like a zoom (design §7 flicker-free
1561
+ * path — a rapid ResizeObserver burst therefore also coalesces into one settle).
1562
+ * `setScale`'s clamp/no-op guards apply: an unchanged newScale (identical width)
1563
+ * is a no-op there — so we short-circuit BEFORE it when the fit-width is
1564
+ * unchanged (mounting the revealed window without a needless re-render), and
1565
+ * after it we call `_mountVisible` again to cover the case where the clamp made
1566
+ * `setScale` no-op yet the viewport still grew.
1567
+ */
1568
+ private _onResize;
1569
+ get topVisibleSlide(): number;
1570
+ /* Excluded from this release type: mountedSlideIndicesForTest */
1571
+ /* Excluded from this release type: scaleForTest */
1572
+ /* Excluded from this release type: baseScaleForTest */
1573
+ /* Excluded from this release type: renderEpochForTest */
1574
+ /* Excluded from this release type: resizeForTest */
1575
+ /**
1576
+ * Tear down the viewer: remove the DOM subtree and (only for a self-loaded
1577
+ * engine) destroy the engine. An injected engine is left intact — the caller
1578
+ * owns its lifecycle. Per-slot worker ImageBitmaps are closed on recycle.
1579
+ */
1580
+ destroy(): void;
1581
+ }
1582
+
1583
+ /**
1584
+ * Options for {@link PptxScrollViewer}. Extends `RenderSlideOptions` (per-slide
1585
+ * render knobs, minus `onTextRun`) and `LoadOptions` (parse/worker knobs). See
1586
+ * design §8.2.
1587
+ *
1588
+ * `onTextRun` is omitted deliberately: the viewer drives it internally per
1589
+ * mounted slot to build the optional per-slide selection overlay (gated by
1590
+ * `enableTextSelection`), so exposing it here would let a caller's callback be
1591
+ * silently overridden.
1592
+ *
1593
+ * NOTE: `RenderSlideOptions` also carries `dim` and `skipMediaControls`. The v1
1594
+ * scroll viewer never sets `dim` or `skipMediaControls` (hidden-slide dimming is
1595
+ * a PAGER policy, not a scroll-viewer feature — design §8.2 / Delta 6). These
1596
+ * inherited fields are accepted for type-compatibility but are not part of the
1597
+ * scroll-viewer's supported API.
1598
+ */
1599
+ export declare interface PptxScrollViewerOptions extends Omit<RenderSlideOptions, 'onTextRun'>, LoadOptions {
1600
+ /** Base fit width in CSS px → base zoom scale. Default: the container's width
1601
+ * at first non-zero layout (design §7/§11 zero-width deferral). */
1602
+ width?: number;
1603
+ /** Vertical gap (px) between consecutive slides. Default 16. */
1604
+ gap?: number;
1605
+ /** Desk padding (px) ABOVE the FIRST slide — the margin a presentation viewer
1606
+ * leaves between the top of the scroll surface and the first slide. Default:
1607
+ * `gap` (uniform desk rhythm — the first slide sits the same distance from the
1608
+ * top as slides sit from each other). Pass `0` for a flush-top layout. */
1609
+ paddingTop?: number;
1610
+ /** Desk padding (px) BELOW the LAST slide — the margin below the final slide.
1611
+ * Default: `gap`. Pass `0` for a flush-bottom layout. */
1612
+ paddingBottom?: number;
1613
+ /** Desk gutter (px) to the LEFT of the slides — the horizontal margin between
1614
+ * the left edge of the scroll surface and a slide sitting flush-left (i.e. once
1615
+ * zoomed wide enough that centering no longer applies). Default: `gap` (uniform
1616
+ * desk rhythm — the horizontal gutters match the vertical ones). It also shrinks
1617
+ * the container-derived FIT width so a slide sits inside the gutters at 100%
1618
+ * (an EXPLICIT `opts.width` is the slide's CSS-width contract and is NOT reduced;
1619
+ * the gutters still apply around placement). Pass `0` for a flush-left layout. */
1620
+ paddingLeft?: number;
1621
+ /** Desk gutter (px) to the RIGHT of the slides. Default: `gap`. Shrinks the
1622
+ * container-derived fit width symmetrically with `paddingLeft`. Pass `0` for a
1623
+ * flush-right layout. */
1624
+ paddingRight?: number;
1625
+ /** Slides kept mounted beyond the viewport on each side. Default 1. */
1626
+ overscan?: number;
1627
+ /** Per-slide transparent text-selection overlay. MAIN render mode only:
1628
+ * in worker mode `onTextRun` cannot cross the worker boundary, so the overlay
1629
+ * stays empty and the viewer logs one warning (design §11). */
1630
+ enableTextSelection?: boolean;
1631
+ /** Minimum zoom scale — a DIMENSIONLESS multiplier over the 96-dpi natural
1632
+ * slide size (10% = 0.1), matching `DocxScrollViewer`. Default 0.1. */
1633
+ zoomMin?: number;
1634
+ /** Maximum zoom scale (dimensionless multiplier, 400% = 4). Default 4. */
1635
+ zoomMax?: number;
1636
+ /** Enable `Ctrl`/`Cmd`+wheel zoom. Default true. */
1637
+ enableZoom?: boolean;
1638
+ /**
1639
+ * CSS `background` shorthand for the scroll surface (the "desk") visible
1640
+ * behind and between slides — the gray a presentation viewer paints around the
1641
+ * slide. Applied to the viewer-owned scroll host. The slides themselves are
1642
+ * always drawn on their own white canvas and are unaffected. Default
1643
+ * `undefined`: the scroll surface stays transparent so the host container's
1644
+ * background shows through (non-breaking).
1645
+ */
1646
+ background?: string;
1647
+ /**
1648
+ * CSS `box-shadow` painted on every slide CANVAS (not the wrapper — the
1649
+ * text-selection overlay must not cast its own shadow). The soft drop shadow a
1650
+ * presentation viewer leaves under each slide.
1651
+ *
1652
+ * - Default (`undefined`): `'0 1px 3px rgba(0,0,0,0.2)'` — the recipe look, so
1653
+ * the scroll viewer reproduces the Examples appearance with zero config.
1654
+ * - `false`: NO shadow (flat slides).
1655
+ * - A custom string is applied verbatim. A spread-only ring such as
1656
+ * `'0 0 0 1px #c8ccd0'` gives a crisp 1px BORDER look — and because
1657
+ * `box-shadow` never affects layout (unlike `border`, which would grow the
1658
+ * box and shift every offset), a border and a drop shadow are the SAME knob
1659
+ * here rather than two competing options.
1660
+ */
1661
+ pageShadow?: string | false;
1662
+ /**
1663
+ * Inject an already-loaded engine to share one parse across panes (design §14).
1664
+ * When set: `load()` is unsupported (throws), the engine's own `mode` wins (an
1665
+ * explicitly conflicting `opts.mode` throws at construction, design §11), and
1666
+ * `destroy()` does NOT destroy this engine (the caller owns its lifecycle).
1667
+ */
1668
+ presentation?: PptxPresentation;
1669
+ /** Fires when the top-most visible slide changes. `topIndex` from
1670
+ * `computeVisibleRange` (the first slide intersecting the viewport top,
1671
+ * EXCLUDING overscan). */
1672
+ onVisibleSlideChange?: (topIndex: number, total: number) => void;
1673
+ /** Error callback. When set, `load()` invokes it and resolves (otherwise the
1674
+ * error is rethrown — shared viewer error contract). It ALSO fires for async
1675
+ * per-slot render failures (both main `renderSlide` and worker
1676
+ * `renderSlideToBitmap` rejections); a failed slide is left blank rather than
1677
+ * crashing the loop. Without an `onError`, render failures are logged via
1678
+ * `console.error` so they are never fully silent. */
1679
+ onError?: (err: Error) => void;
1680
+ }
1681
+
1237
1682
  /** Information about a rendered text segment for building a transparent selection overlay. */
1238
1683
  export declare interface PptxTextRunInfo {
1239
1684
  text: string;
@@ -1279,10 +1724,20 @@ export declare interface PptxTextRunInfo {
1279
1724
  export declare class PptxViewer {
1280
1725
  private readonly canvas;
1281
1726
  private readonly wrapper;
1727
+ /** The canvas's DOM position BEFORE the constructor reparented it into
1728
+ * {@link wrapper}, captured so {@link destroy} can return the caller-owned
1729
+ * canvas to exactly where it was. `null` parent = canvas was passed
1730
+ * detached. */
1731
+ private readonly _originalParent;
1732
+ private readonly _originalNextSibling;
1733
+ /** The canvas's inline `display` before the constructor forced `block`
1734
+ * (empty string if it was unset), restored on {@link destroy}. */
1735
+ private readonly _originalDisplay;
1282
1736
  private textLayer;
1283
1737
  private engine;
1284
1738
  private readonly opts;
1285
1739
  private currentSlide;
1740
+ private _hiddenMode;
1286
1741
  private handle;
1287
1742
  private readonly _mode;
1288
1743
  /** The canvas's bitmaprenderer context, used only by the static worker-mode
@@ -1303,6 +1758,21 @@ export declare class PptxViewer {
1303
1758
  goToSlide(index: number): Promise<void>;
1304
1759
  nextSlide(): Promise<void>;
1305
1760
  prevSlide(): Promise<void>;
1761
+ /** Next index for sequential nav: skip mode jumps over hidden slides. */
1762
+ private _step;
1763
+ /** Initial slide for load() / mode switch: skip mode lands on a visible one. */
1764
+ private _initialSlide;
1765
+ /** Resolved `'dim'` overlay (defaults merged with the `hiddenSlideDim` option). */
1766
+ private _dim;
1767
+ /**
1768
+ * Switch the hidden-slide mode at runtime and re-render. Entering `'skip'`
1769
+ * while on a hidden slide advances to the nearest visible slide.
1770
+ */
1771
+ setHiddenSlideMode(mode: HiddenSlideMode): Promise<void>;
1772
+ /** The current hidden-slide mode. */
1773
+ get hiddenSlideMode(): HiddenSlideMode;
1774
+ /** Number of non-hidden slides (absolute `slideCount` is unchanged). */
1775
+ get visibleSlideCount(): number;
1306
1776
  get slideIndex(): number;
1307
1777
  get slideCount(): number;
1308
1778
  /**
@@ -1316,7 +1786,15 @@ export declare class PptxViewer {
1316
1786
  get canvasElement(): HTMLCanvasElement;
1317
1787
  private renderCurrentSlide;
1318
1788
  private _buildTextLayer;
1319
- /** Clean up the viewer and terminate the background worker. */
1789
+ /**
1790
+ * Clean up the viewer and terminate the background worker.
1791
+ *
1792
+ * The caller-owned `<canvas>` is returned to the DOM position it held before
1793
+ * the constructor was called (same parent, same next-sibling) and its inline
1794
+ * `display` is restored, so the canvas can be reused — e.g. to construct a new
1795
+ * viewer on the same element. If the canvas was passed detached (no parent) it
1796
+ * is simply removed from the internal wrapper. Safe to call more than once.
1797
+ */
1320
1798
  destroy(): void;
1321
1799
  }
1322
1800
 
@@ -1338,6 +1816,24 @@ export declare interface PptxViewerOptions extends RenderOptions, LoadOptions {
1338
1816
  * browser's native text selection works on slide content.
1339
1817
  */
1340
1818
  enableTextSelection?: boolean;
1819
+ /**
1820
+ * How hidden slides (`<p:sld show="0">`, §19.3.1.38) are presented:
1821
+ * - `'show'` (default): drawn like any other slide.
1822
+ * - `'skip'`: sequential navigation (`nextSlide`/`prevSlide`, initial load)
1823
+ * jumps over them; absolute indices are unchanged, and an explicit
1824
+ * `goToSlide(i)` to a hidden slide is still honored.
1825
+ * - `'dim'`: drawn under a translucent overlay (PowerPoint thumbnail look).
1826
+ *
1827
+ * Named to match the {@link PptxViewer.hiddenSlideMode} getter and
1828
+ * {@link PptxViewer.setHiddenSlideMode} setter.
1829
+ */
1830
+ hiddenSlideMode?: HiddenSlideMode;
1831
+ /**
1832
+ * Overrides for the `'dim'` overlay. Merged over the default
1833
+ * `{ color: '#ffffff', opacity: 0.6 }`. A `Partial<DimOptions>` so it stays
1834
+ * in sync if {@link DimOptions} gains a field.
1835
+ */
1836
+ hiddenSlideDim?: Partial<DimOptions>;
1341
1837
  }
1342
1838
 
1343
1839
  export declare interface Presentation {
@@ -1440,6 +1936,8 @@ export declare interface RenderSlideOptions {
1440
1936
  * its own play/pause chrome without duplication.
1441
1937
  */
1442
1938
  skipMediaControls?: boolean;
1939
+ /** Translucent overlay drawn over the finished slide (hidden-slide dimming). */
1940
+ dim?: DimOptions;
1443
1941
  }
1444
1942
 
1445
1943
  /** Options for {@link PptxPresentation.renderSlideToBitmap}. */
@@ -1449,6 +1947,8 @@ export declare interface RenderSlideToBitmapOptions {
1449
1947
  /** Device pixel ratio. Defaults to window.devicePixelRatio (workers have none). */
1450
1948
  dpr?: number;
1451
1949
  /* Excluded from this release type: skipMediaControls */
1950
+ /** Translucent overlay drawn over the finished slide (hidden-slide dimming). */
1951
+ dim?: DimOptions;
1452
1952
  }
1453
1953
 
1454
1954
  /**
@@ -1599,6 +2099,13 @@ export declare interface Slide {
1599
2099
  * when the slide has no comments.
1600
2100
  */
1601
2101
  comments?: PptxComment[];
2102
+ /**
2103
+ * `<p:sld show="0">` — the slide is marked hidden in the slide show
2104
+ * (ECMA-376 §19.3.1.38). Absent (`undefined`) ⇒ shown. The renderer ignores
2105
+ * this; it is a fact surfaced for tools and for {@link PptxViewer}'s hidden-
2106
+ * slide modes (read it via `PptxPresentation.isHidden`).
2107
+ */
2108
+ hidden?: boolean;
1602
2109
  }
1603
2110
 
1604
2111
  export declare type SlideElement = ShapeElement | PictureElement | TableElement | ChartElement | MediaElement;
@@ -1611,6 +2118,7 @@ export declare type SlideElement = ShapeElement | PictureElement | TableElement
1611
2118
  */
1612
2119
  declare type SlideRenderOptions = RenderOptions & {
1613
2120
  math?: MathRenderer;
2121
+ dim?: DimOptions;
1614
2122
  };
1615
2123
 
1616
2124
  /** ECMA-376 §20.1.8.31 (CT_SoftEdgesEffect) — feather radius in EMU. */