@extend-ai/react-pptx 0.1.1 → 0.1.2
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 +3 -0
- package/dist/index.js +257 -69
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
- Make slide navigation observable before off-window rendering completes, isolate resource
|
|
12
|
+
readiness per slide mount, update zoom and fit geometry without rebuilding mounted slide DOM,
|
|
13
|
+
and reduce thumbnail work outside the visible rail.
|
|
11
14
|
- Expose consumer-owned slide thumbnail rails through `usePptxViewerThumbnails`, including stable
|
|
12
15
|
container refs, resolution bounds, render status, imperative rendering, and cleanup. Add
|
|
13
16
|
DOCX-style visible/prefetch render windows and virtualize the built-in thumbnail scrollport.
|
package/dist/index.js
CHANGED
|
@@ -9249,6 +9249,11 @@ var DEFAULT_TEXT_VERTICAL_INSET_EMU = 45720;
|
|
|
9249
9249
|
var POWERPOINT_SINGLE_LINE_HEIGHT = 1.15;
|
|
9250
9250
|
var LIST_ITEM_GAP = 24;
|
|
9251
9251
|
var FALLBACK_VIEWPORT_RECT = { width: 800, height: 600 };
|
|
9252
|
+
function scaleListAnchorOffset(offset, previousSlideHeight, nextSlideHeight) {
|
|
9253
|
+
if (previousSlideHeight <= 0) return offset;
|
|
9254
|
+
if (offset <= previousSlideHeight) return offset * (nextSlideHeight / previousSlideHeight);
|
|
9255
|
+
return nextSlideHeight + (offset - previousSlideHeight);
|
|
9256
|
+
}
|
|
9252
9257
|
var METAFILE_FONT_MAP = Object.fromEntries(
|
|
9253
9258
|
Object.entries(OFFICE_FONT_FALLBACKS).map(([family, fallbacks]) => [
|
|
9254
9259
|
family,
|
|
@@ -9795,7 +9800,7 @@ var NormalizedPresentationViewer = class {
|
|
|
9795
9800
|
metafileUrls = /* @__PURE__ */ new Map();
|
|
9796
9801
|
listCleanup;
|
|
9797
9802
|
highlight;
|
|
9798
|
-
|
|
9803
|
+
activeMountResources;
|
|
9799
9804
|
mountedHandles = /* @__PURE__ */ new Set();
|
|
9800
9805
|
handlesByTarget = /* @__PURE__ */ new Map();
|
|
9801
9806
|
destroyed = false;
|
|
@@ -9826,6 +9831,10 @@ var NormalizedPresentationViewer = class {
|
|
|
9826
9831
|
const height = this.presentation.size.heightEmu / EMU_PER_CSS_PIXEL;
|
|
9827
9832
|
return Number.isFinite(height) && height > 0 ? height : 1;
|
|
9828
9833
|
}
|
|
9834
|
+
scaleForViewport(viewportWidth) {
|
|
9835
|
+
const fitScale = this.fit === "contain" ? Math.min(1, (viewportWidth || this.naturalSlideWidth) / this.naturalSlideWidth) : 1;
|
|
9836
|
+
return fitScale * (this.zoom / 100);
|
|
9837
|
+
}
|
|
9829
9838
|
isRenderActive(generation) {
|
|
9830
9839
|
return !this.destroyed && generation === this.renderGeneration;
|
|
9831
9840
|
}
|
|
@@ -9878,12 +9887,13 @@ var NormalizedPresentationViewer = class {
|
|
|
9878
9887
|
applyAsset(assetId, apply, nodeId) {
|
|
9879
9888
|
const asset = this.presentation.assets[assetId];
|
|
9880
9889
|
if (!asset) return;
|
|
9890
|
+
const resources = this.activeMountResources;
|
|
9881
9891
|
const pending = this.assetUrl(asset).then((url) => {
|
|
9882
9892
|
if (url && !this.destroyed) apply(url);
|
|
9883
9893
|
}).catch((error) => {
|
|
9884
9894
|
if (!this.destroyed) this.callbacks.onNodeError?.(nodeId, error);
|
|
9885
|
-
})
|
|
9886
|
-
|
|
9895
|
+
});
|
|
9896
|
+
resources?.add(pending);
|
|
9887
9897
|
}
|
|
9888
9898
|
/**
|
|
9889
9899
|
* Applies DrawingML picture recolor effects (`a:biLevel`, `a:grayscl`,
|
|
@@ -10039,11 +10049,7 @@ var NormalizedPresentationViewer = class {
|
|
|
10039
10049
|
image.setAttribute("preserveAspectRatio", "none");
|
|
10040
10050
|
image.setAttribute("clip-path", `url(#${clipId})`);
|
|
10041
10051
|
image.style.opacity = String(Math.max(0, Math.min(1, fillStyle.opacity ?? 1)));
|
|
10042
|
-
this.applyAsset(
|
|
10043
|
-
fillStyle.assetId,
|
|
10044
|
-
(url) => image.setAttribute("href", url),
|
|
10045
|
-
node.id
|
|
10046
|
-
);
|
|
10052
|
+
this.applyAsset(fillStyle.assetId, (url) => image.setAttribute("href", url), node.id);
|
|
10047
10053
|
svg.append(image);
|
|
10048
10054
|
}
|
|
10049
10055
|
if (node.line) {
|
|
@@ -10406,23 +10412,35 @@ var NormalizedPresentationViewer = class {
|
|
|
10406
10412
|
element,
|
|
10407
10413
|
target,
|
|
10408
10414
|
ready: Promise.resolve(),
|
|
10415
|
+
setScale: () => {
|
|
10416
|
+
},
|
|
10409
10417
|
dispose: () => element.remove()
|
|
10410
10418
|
};
|
|
10411
10419
|
}
|
|
10412
10420
|
mount(index, target, scale) {
|
|
10413
10421
|
if (this.destroyed) return this.emptyHandle(target);
|
|
10414
10422
|
this.handlesByTarget.get(target)?.dispose();
|
|
10415
|
-
const
|
|
10416
|
-
const
|
|
10417
|
-
|
|
10423
|
+
const resources = /* @__PURE__ */ new Set();
|
|
10424
|
+
const previousResources = this.activeMountResources;
|
|
10425
|
+
this.activeMountResources = resources;
|
|
10426
|
+
let slide;
|
|
10427
|
+
try {
|
|
10428
|
+
slide = this.createSlide(index);
|
|
10429
|
+
} finally {
|
|
10430
|
+
this.activeMountResources = previousResources;
|
|
10431
|
+
}
|
|
10432
|
+
const effectiveScale = scale ?? this.scaleForViewport(target.clientWidth);
|
|
10418
10433
|
const wrapper = document.createElement("div");
|
|
10419
10434
|
wrapper.dataset.rpvSlideWrapper = String(index);
|
|
10420
10435
|
wrapper.style.position = "relative";
|
|
10421
|
-
wrapper.style.width = `${this.naturalSlideWidth * effectiveScale}px`;
|
|
10422
|
-
wrapper.style.height = `${this.naturalSlideHeight * effectiveScale}px`;
|
|
10423
10436
|
wrapper.style.margin = "0 auto";
|
|
10424
10437
|
slide.style.transformOrigin = "top left";
|
|
10425
|
-
|
|
10438
|
+
const setScale = (nextScale) => {
|
|
10439
|
+
wrapper.style.width = `${this.naturalSlideWidth * nextScale}px`;
|
|
10440
|
+
wrapper.style.height = `${this.naturalSlideHeight * nextScale}px`;
|
|
10441
|
+
slide.style.transform = `scale(${nextScale})`;
|
|
10442
|
+
};
|
|
10443
|
+
setScale(effectiveScale);
|
|
10426
10444
|
wrapper.append(slide);
|
|
10427
10445
|
target.replaceChildren(wrapper);
|
|
10428
10446
|
this.callbacks.onSlideRendered?.(index, slide);
|
|
@@ -10430,7 +10448,8 @@ var NormalizedPresentationViewer = class {
|
|
|
10430
10448
|
const handle = {
|
|
10431
10449
|
element: slide,
|
|
10432
10450
|
target,
|
|
10433
|
-
ready: Promise.all([...
|
|
10451
|
+
ready: Promise.all([...resources]).then(() => void 0),
|
|
10452
|
+
setScale,
|
|
10434
10453
|
dispose: () => {
|
|
10435
10454
|
if (disposed) return;
|
|
10436
10455
|
disposed = true;
|
|
@@ -10458,6 +10477,32 @@ var NormalizedPresentationViewer = class {
|
|
|
10458
10477
|
await Promise.resolve();
|
|
10459
10478
|
}
|
|
10460
10479
|
}
|
|
10480
|
+
async yieldNavigationPaint() {
|
|
10481
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
10482
|
+
await new Promise((resolve) => {
|
|
10483
|
+
let settled = false;
|
|
10484
|
+
let firstFrame;
|
|
10485
|
+
let secondFrame;
|
|
10486
|
+
const complete = () => {
|
|
10487
|
+
if (settled) return;
|
|
10488
|
+
settled = true;
|
|
10489
|
+
clearTimeout(timeout2);
|
|
10490
|
+
if (typeof cancelAnimationFrame !== "undefined") {
|
|
10491
|
+
if (firstFrame !== void 0) cancelAnimationFrame(firstFrame);
|
|
10492
|
+
if (secondFrame !== void 0) cancelAnimationFrame(secondFrame);
|
|
10493
|
+
}
|
|
10494
|
+
resolve();
|
|
10495
|
+
};
|
|
10496
|
+
const timeout2 = setTimeout(complete, 100);
|
|
10497
|
+
firstFrame = requestAnimationFrame(() => {
|
|
10498
|
+
if (settled) return;
|
|
10499
|
+
secondFrame = requestAnimationFrame(complete);
|
|
10500
|
+
});
|
|
10501
|
+
});
|
|
10502
|
+
} else {
|
|
10503
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
10504
|
+
}
|
|
10505
|
+
}
|
|
10461
10506
|
async mountInBatches(placeholders, indices, batchSize, generation) {
|
|
10462
10507
|
const uniqueIndices = [...new Set(indices)].filter(
|
|
10463
10508
|
(index) => index >= 0 && index < placeholders.length
|
|
@@ -10490,12 +10535,13 @@ var NormalizedPresentationViewer = class {
|
|
|
10490
10535
|
createListPlaceholder(index, item, generation, scale) {
|
|
10491
10536
|
let handle;
|
|
10492
10537
|
let mountPromise;
|
|
10538
|
+
let effectiveScale = scale;
|
|
10493
10539
|
const isActive = () => this.isRenderActive(generation) && this.listState?.generation === generation;
|
|
10494
10540
|
const mount = () => {
|
|
10495
10541
|
if (!isActive()) return Promise.resolve();
|
|
10496
10542
|
if (handle) return handle.ready;
|
|
10497
10543
|
if (mountPromise) return mountPromise;
|
|
10498
|
-
const nextHandle = this.mount(index, item,
|
|
10544
|
+
const nextHandle = this.mount(index, item, effectiveScale);
|
|
10499
10545
|
handle = nextHandle;
|
|
10500
10546
|
const pending = nextHandle.ready.then(() => {
|
|
10501
10547
|
if (!isActive() || handle !== nextHandle) {
|
|
@@ -10511,11 +10557,16 @@ var NormalizedPresentationViewer = class {
|
|
|
10511
10557
|
const unmount = () => {
|
|
10512
10558
|
const mounted = handle;
|
|
10513
10559
|
handle = void 0;
|
|
10560
|
+
mountPromise = void 0;
|
|
10514
10561
|
if (mounted) {
|
|
10515
10562
|
mounted.dispose();
|
|
10516
10563
|
}
|
|
10517
10564
|
};
|
|
10518
|
-
|
|
10565
|
+
const setScale = (nextScale) => {
|
|
10566
|
+
effectiveScale = nextScale;
|
|
10567
|
+
handle?.setScale(nextScale);
|
|
10568
|
+
};
|
|
10569
|
+
return { item, mount, unmount, isMounted: () => Boolean(handle), setScale };
|
|
10519
10570
|
}
|
|
10520
10571
|
async renderList(options = {}) {
|
|
10521
10572
|
const windowingEnabled = options.enabled !== false;
|
|
@@ -10539,11 +10590,9 @@ var NormalizedPresentationViewer = class {
|
|
|
10539
10590
|
Math.min(this.slideCount - 1, options.initialSlideIndex ?? this.current)
|
|
10540
10591
|
);
|
|
10541
10592
|
const scroller = options.scrollElement ?? this.container;
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
const slideHeight = this.naturalSlideHeight * effectiveScale;
|
|
10546
|
-
const itemStride = slideHeight + LIST_ITEM_GAP;
|
|
10593
|
+
let effectiveScale = this.scaleForViewport(scroller.clientWidth);
|
|
10594
|
+
let slideHeight = this.naturalSlideHeight * effectiveScale;
|
|
10595
|
+
let itemStride = slideHeight + LIST_ITEM_GAP;
|
|
10547
10596
|
if (!windowingEnabled) {
|
|
10548
10597
|
const placeholders2 = this.presentation.slides.map((_, index) => {
|
|
10549
10598
|
const item = document.createElement("div");
|
|
@@ -10551,9 +10600,35 @@ var NormalizedPresentationViewer = class {
|
|
|
10551
10600
|
item.style.minHeight = `${slideHeight}px`;
|
|
10552
10601
|
item.style.margin = `0 auto ${LIST_ITEM_GAP}px`;
|
|
10553
10602
|
this.container.append(item);
|
|
10554
|
-
return this.createListPlaceholder(index, item, generation);
|
|
10603
|
+
return this.createListPlaceholder(index, item, generation, effectiveScale);
|
|
10555
10604
|
});
|
|
10556
10605
|
const state2 = { generation, options: normalizedOptions, placeholders: placeholders2 };
|
|
10606
|
+
state2.updateScale = () => {
|
|
10607
|
+
if (!this.isRenderActive(generation) || this.listState !== state2) return;
|
|
10608
|
+
const previousStride = itemStride;
|
|
10609
|
+
const previousSlideHeight = slideHeight;
|
|
10610
|
+
const listStart = scroller === this.container ? 0 : this.container.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;
|
|
10611
|
+
const listOffset = scroller.scrollTop - listStart;
|
|
10612
|
+
const anchorIndex = Math.max(
|
|
10613
|
+
0,
|
|
10614
|
+
Math.min(this.slideCount - 1, Math.floor(listOffset / previousStride))
|
|
10615
|
+
);
|
|
10616
|
+
const anchorOffset = listOffset - anchorIndex * previousStride;
|
|
10617
|
+
effectiveScale = this.scaleForViewport(scroller.clientWidth);
|
|
10618
|
+
slideHeight = this.naturalSlideHeight * effectiveScale;
|
|
10619
|
+
itemStride = slideHeight + LIST_ITEM_GAP;
|
|
10620
|
+
placeholders2.forEach((placeholder) => {
|
|
10621
|
+
placeholder.item.style.minHeight = `${slideHeight}px`;
|
|
10622
|
+
placeholder.setScale(effectiveScale);
|
|
10623
|
+
});
|
|
10624
|
+
if (listOffset < 0) return;
|
|
10625
|
+
const nextAnchorOffset = scaleListAnchorOffset(
|
|
10626
|
+
anchorOffset,
|
|
10627
|
+
previousSlideHeight,
|
|
10628
|
+
slideHeight
|
|
10629
|
+
);
|
|
10630
|
+
scroller.scrollTop = Math.max(0, listStart + anchorIndex * itemStride + nextAnchorOffset);
|
|
10631
|
+
};
|
|
10557
10632
|
this.listState = state2;
|
|
10558
10633
|
await this.mountInBatches(
|
|
10559
10634
|
placeholders2,
|
|
@@ -10595,6 +10670,20 @@ var NormalizedPresentationViewer = class {
|
|
|
10595
10670
|
const state = { generation, options: normalizedOptions, placeholders };
|
|
10596
10671
|
this.listState = state;
|
|
10597
10672
|
const isActive = () => this.isRenderActive(generation) && this.listState === state;
|
|
10673
|
+
const deferredMounts = /* @__PURE__ */ new Map();
|
|
10674
|
+
let virtualWindow = /* @__PURE__ */ new Set();
|
|
10675
|
+
state.deferMount = (index) => {
|
|
10676
|
+
deferredMounts.set(index, (deferredMounts.get(index) ?? 0) + 1);
|
|
10677
|
+
};
|
|
10678
|
+
state.resumeMount = (index) => {
|
|
10679
|
+
const remaining = (deferredMounts.get(index) ?? 0) - 1;
|
|
10680
|
+
if (remaining > 0) {
|
|
10681
|
+
deferredMounts.set(index, remaining);
|
|
10682
|
+
return;
|
|
10683
|
+
}
|
|
10684
|
+
deferredMounts.delete(index);
|
|
10685
|
+
if (virtualWindow.has(index)) void placeholders[index]?.mount();
|
|
10686
|
+
};
|
|
10598
10687
|
const measureViewportRect = () => {
|
|
10599
10688
|
const rect = scroller.getBoundingClientRect();
|
|
10600
10689
|
if (rect.width > 0 && rect.height > 0) return { width: rect.width, height: rect.height };
|
|
@@ -10606,16 +10695,25 @@ var NormalizedPresentationViewer = class {
|
|
|
10606
10695
|
const initialRect = measureViewportRect();
|
|
10607
10696
|
const requestedOverscan = options.overscanViewport ?? 1.5;
|
|
10608
10697
|
const overscanViewports = Number.isFinite(requestedOverscan) && requestedOverscan >= 0 ? requestedOverscan : 1.5;
|
|
10609
|
-
|
|
10698
|
+
let overscan = Math.max(1, Math.ceil(overscanViewports * initialRect.height / itemStride));
|
|
10610
10699
|
const measureScrollMargin = () => sizer.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;
|
|
10611
10700
|
let scrollMargin = measureScrollMargin();
|
|
10612
10701
|
let needsSettleAlignment = initialIndex > 0;
|
|
10613
10702
|
let lastProgrammaticTop;
|
|
10703
|
+
const writeScrollTop = (top, behavior) => {
|
|
10704
|
+
lastProgrammaticTop = top;
|
|
10705
|
+
if (typeof scroller.scrollTo === "function") {
|
|
10706
|
+
scroller.scrollTo(behavior ? { top, behavior } : { top });
|
|
10707
|
+
} else {
|
|
10708
|
+
scroller.scrollTop = top;
|
|
10709
|
+
}
|
|
10710
|
+
};
|
|
10614
10711
|
const reconcile = (instance) => {
|
|
10615
10712
|
if (!isActive()) return;
|
|
10616
10713
|
const windowed = new Set(instance.getVirtualItems().map((item) => item.index));
|
|
10714
|
+
virtualWindow = windowed;
|
|
10617
10715
|
placeholders.forEach((placeholder, index) => {
|
|
10618
|
-
if (windowed.has(index)) void placeholder.mount();
|
|
10716
|
+
if (windowed.has(index) && !deferredMounts.has(index)) void placeholder.mount();
|
|
10619
10717
|
else placeholder.unmount();
|
|
10620
10718
|
});
|
|
10621
10719
|
const offset = (instance.scrollOffset ?? 0) - scrollMargin;
|
|
@@ -10678,12 +10776,7 @@ var NormalizedPresentationViewer = class {
|
|
|
10678
10776
|
},
|
|
10679
10777
|
scrollToFn: (offset, { adjustments, behavior }) => {
|
|
10680
10778
|
const top = offset + (adjustments ?? 0);
|
|
10681
|
-
|
|
10682
|
-
if (typeof scroller.scrollTo === "function") {
|
|
10683
|
-
scroller.scrollTo(behavior ? { top, behavior } : { top });
|
|
10684
|
-
} else {
|
|
10685
|
-
scroller.scrollTop = top;
|
|
10686
|
-
}
|
|
10779
|
+
writeScrollTop(top, behavior);
|
|
10687
10780
|
},
|
|
10688
10781
|
onChange: (instance) => reconcile(instance)
|
|
10689
10782
|
});
|
|
@@ -10694,6 +10787,39 @@ var NormalizedPresentationViewer = class {
|
|
|
10694
10787
|
behavior: scrollOptions?.behavior === "smooth" ? "smooth" : "auto"
|
|
10695
10788
|
});
|
|
10696
10789
|
};
|
|
10790
|
+
state.updateScale = () => {
|
|
10791
|
+
if (!isActive()) return;
|
|
10792
|
+
const previousStride = itemStride;
|
|
10793
|
+
const previousSlideHeight = slideHeight;
|
|
10794
|
+
const anchorIndex = this.current;
|
|
10795
|
+
const observedScrollTop = virtualizer.scrollOffset ?? scroller.scrollTop;
|
|
10796
|
+
const observedIndex = Math.floor((observedScrollTop - scrollMargin) / previousStride);
|
|
10797
|
+
const intendedIndex = lastProgrammaticTop === void 0 ? void 0 : Math.floor((lastProgrammaticTop - scrollMargin) / previousStride);
|
|
10798
|
+
const previousScrollTop = Math.abs(observedIndex - anchorIndex) > 1 && intendedIndex !== void 0 && Math.abs(intendedIndex - anchorIndex) <= 1 ? lastProgrammaticTop : observedScrollTop;
|
|
10799
|
+
const previousOffset = previousScrollTop - scrollMargin;
|
|
10800
|
+
const anchorOffset = previousOffset - anchorIndex * previousStride;
|
|
10801
|
+
effectiveScale = this.scaleForViewport(scroller.clientWidth);
|
|
10802
|
+
slideHeight = this.naturalSlideHeight * effectiveScale;
|
|
10803
|
+
itemStride = slideHeight + LIST_ITEM_GAP;
|
|
10804
|
+
sizer.style.height = `${this.slideCount * itemStride - LIST_ITEM_GAP}px`;
|
|
10805
|
+
placeholders.forEach((placeholder, index) => {
|
|
10806
|
+
placeholder.item.style.height = `${slideHeight}px`;
|
|
10807
|
+
placeholder.item.style.transform = `translateY(${index * itemStride}px)`;
|
|
10808
|
+
placeholder.setScale(effectiveScale);
|
|
10809
|
+
});
|
|
10810
|
+
const nextAnchorOffset = scaleListAnchorOffset(
|
|
10811
|
+
anchorOffset,
|
|
10812
|
+
previousSlideHeight,
|
|
10813
|
+
slideHeight
|
|
10814
|
+
);
|
|
10815
|
+
const nextScrollTop = previousOffset < 0 ? previousScrollTop : Math.max(0, scrollMargin + anchorIndex * itemStride + nextAnchorOffset);
|
|
10816
|
+
const viewportRect = measureViewportRect();
|
|
10817
|
+
overscan = Math.max(1, Math.ceil(overscanViewports * viewportRect.height / itemStride));
|
|
10818
|
+
virtualizer.scrollOffset = nextScrollTop;
|
|
10819
|
+
writeScrollTop(nextScrollTop);
|
|
10820
|
+
virtualizer.setOptions({ ...virtualizer.options, overscan });
|
|
10821
|
+
virtualizer.measure();
|
|
10822
|
+
};
|
|
10697
10823
|
const teardown = virtualizer._didMount();
|
|
10698
10824
|
this.listCleanup = teardown;
|
|
10699
10825
|
virtualizer._willUpdate();
|
|
@@ -10721,16 +10847,27 @@ var NormalizedPresentationViewer = class {
|
|
|
10721
10847
|
if (state && this.isRenderActive(state.generation)) {
|
|
10722
10848
|
const navigation = ++this.navigationGeneration;
|
|
10723
10849
|
const placeholder = state.placeholders[next];
|
|
10724
|
-
|
|
10725
|
-
if (
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10850
|
+
let mountDeferred = Boolean(state.deferMount && placeholder && !placeholder.isMounted());
|
|
10851
|
+
if (mountDeferred) state.deferMount?.(next);
|
|
10852
|
+
try {
|
|
10853
|
+
if (changed) {
|
|
10854
|
+
if (state.scrollToIndex) state.scrollToIndex(next, scrollOptions);
|
|
10855
|
+
else placeholder?.item.scrollIntoView?.(scrollOptions);
|
|
10856
|
+
this.current = next;
|
|
10857
|
+
this.notifySlideChange(next);
|
|
10858
|
+
}
|
|
10859
|
+
if (mountDeferred) await this.yieldNavigationPaint();
|
|
10860
|
+
if (this.destroyed || navigation !== this.navigationGeneration || !this.isRenderActive(state.generation) || this.listState !== state) {
|
|
10861
|
+
return;
|
|
10862
|
+
}
|
|
10863
|
+
if (mountDeferred) {
|
|
10864
|
+
state.resumeMount?.(next);
|
|
10865
|
+
mountDeferred = false;
|
|
10866
|
+
}
|
|
10867
|
+
await placeholder?.mount();
|
|
10868
|
+
} finally {
|
|
10869
|
+
if (mountDeferred) state.resumeMount?.(next);
|
|
10731
10870
|
}
|
|
10732
|
-
this.current = next;
|
|
10733
|
-
if (changed) this.notifySlideChange(next);
|
|
10734
10871
|
return;
|
|
10735
10872
|
}
|
|
10736
10873
|
if (!changed && this.container.querySelector(`[data-rpv-slide-index="${next}"]`)) return;
|
|
@@ -10741,15 +10878,33 @@ var NormalizedPresentationViewer = class {
|
|
|
10741
10878
|
if (this.zoom === next) return;
|
|
10742
10879
|
this.zoom = next;
|
|
10743
10880
|
if (this.renderState?.mode === "continuous") {
|
|
10744
|
-
|
|
10745
|
-
|
|
10881
|
+
const state = this.listState;
|
|
10882
|
+
if (state && this.isRenderActive(state.generation) && state.updateScale) {
|
|
10883
|
+
state.updateScale();
|
|
10884
|
+
} else {
|
|
10885
|
+
await this.renderList({ ...this.renderState.options, initialSlideIndex: this.current });
|
|
10886
|
+
}
|
|
10887
|
+
} else if (this.renderState) {
|
|
10888
|
+
const handle = this.handlesByTarget.get(this.container);
|
|
10889
|
+
if (handle) handle.setScale(this.scaleForViewport(this.container.clientWidth));
|
|
10890
|
+
else await this.renderSlide(this.current);
|
|
10891
|
+
}
|
|
10746
10892
|
}
|
|
10747
10893
|
async setFitMode(mode) {
|
|
10748
10894
|
if (this.fit === mode) return;
|
|
10749
10895
|
this.fit = mode;
|
|
10750
10896
|
if (this.renderState?.mode === "continuous") {
|
|
10751
|
-
|
|
10752
|
-
|
|
10897
|
+
const state = this.listState;
|
|
10898
|
+
if (state && this.isRenderActive(state.generation) && state.updateScale) {
|
|
10899
|
+
state.updateScale();
|
|
10900
|
+
} else {
|
|
10901
|
+
await this.renderList({ ...this.renderState.options, initialSlideIndex: this.current });
|
|
10902
|
+
}
|
|
10903
|
+
} else if (this.renderState) {
|
|
10904
|
+
const handle = this.handlesByTarget.get(this.container);
|
|
10905
|
+
if (handle) handle.setScale(this.scaleForViewport(this.container.clientWidth));
|
|
10906
|
+
else await this.renderSlide(this.current);
|
|
10907
|
+
}
|
|
10753
10908
|
}
|
|
10754
10909
|
searchText(query, options) {
|
|
10755
10910
|
return searchDocument(this.presentation, query, options);
|
|
@@ -10831,7 +10986,7 @@ var NormalizedPresentationViewer = class {
|
|
|
10831
10986
|
this.objectUrls.clear();
|
|
10832
10987
|
this.assetUrls.clear();
|
|
10833
10988
|
this.metafileUrls.clear();
|
|
10834
|
-
this.
|
|
10989
|
+
this.activeMountResources = void 0;
|
|
10835
10990
|
for (const handle of [...this.mountedHandles]) handle.dispose();
|
|
10836
10991
|
this.handlesByTarget.clear();
|
|
10837
10992
|
this.renderState = void 0;
|
|
@@ -11099,7 +11254,7 @@ var presentationParsingDefaults = Object.freeze({
|
|
|
11099
11254
|
maxInputBytes: DEFAULT_MAX_INPUT_BYTES
|
|
11100
11255
|
});
|
|
11101
11256
|
var THUMBNAIL_ROW_ESTIMATE = 123;
|
|
11102
|
-
var THUMBNAIL_OVERSCAN =
|
|
11257
|
+
var THUMBNAIL_OVERSCAN = 1;
|
|
11103
11258
|
var THUMBNAIL_INITIAL_RECT = { height: 780, width: 184 };
|
|
11104
11259
|
var observeThumbnailFilmstripRect = (instance, callback) => {
|
|
11105
11260
|
const element = instance.scrollElement;
|
|
@@ -11820,9 +11975,12 @@ function usePptxViewer() {
|
|
|
11820
11975
|
}
|
|
11821
11976
|
var EMU_PER_CSS_PIXEL2 = 9525;
|
|
11822
11977
|
var DEFAULT_MAX_DIMENSION2 = 160;
|
|
11823
|
-
function normalizeSlideIndexes(indexes, slideCount) {
|
|
11978
|
+
function normalizeSlideIndexes(indexes, slideCount, sort = true) {
|
|
11824
11979
|
if (!indexes?.length || slideCount <= 0) return [];
|
|
11825
|
-
|
|
11980
|
+
const normalized = [...new Set(indexes)].filter(
|
|
11981
|
+
(index) => Number.isInteger(index) && index >= 0 && index < slideCount
|
|
11982
|
+
);
|
|
11983
|
+
return sort ? normalized.sort((left, right) => left - right) : normalized;
|
|
11826
11984
|
}
|
|
11827
11985
|
function finitePositive2(value) {
|
|
11828
11986
|
return value !== void 0 && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
@@ -11860,6 +12018,7 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
11860
12018
|
const attachedElementsRef = useRef(/* @__PURE__ */ new Map());
|
|
11861
12019
|
const cleanupsRef = useRef(/* @__PURE__ */ new Map());
|
|
11862
12020
|
const prefetchedRef = useRef(/* @__PURE__ */ new Map());
|
|
12021
|
+
const prefetchInFlightRef = useRef(/* @__PURE__ */ new Map());
|
|
11863
12022
|
const renderGenerationRef = useRef(/* @__PURE__ */ new Map());
|
|
11864
12023
|
const refCallbacksRef = useRef(/* @__PURE__ */ new Map());
|
|
11865
12024
|
const attachRef = useRef(() => {
|
|
@@ -11872,7 +12031,7 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
11872
12031
|
[options.renderWindow?.visibleSlideIndexes, slideCount]
|
|
11873
12032
|
);
|
|
11874
12033
|
const prefetchSlideIndexes = useMemo(
|
|
11875
|
-
() => normalizeSlideIndexes(options.renderWindow?.prefetchSlideIndexes, slideCount),
|
|
12034
|
+
() => normalizeSlideIndexes(options.renderWindow?.prefetchSlideIndexes, slideCount, false),
|
|
11876
12035
|
[options.renderWindow?.prefetchSlideIndexes, slideCount]
|
|
11877
12036
|
);
|
|
11878
12037
|
const visibleSlideIndexesKey = visibleSlideIndexes.join(",");
|
|
@@ -11987,28 +12146,46 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
11987
12146
|
}, [renderThumbnail]);
|
|
11988
12147
|
const prefetchThumbnail = useCallback(
|
|
11989
12148
|
async (slideIndex) => {
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
const element = globalThis.document.createElement("div");
|
|
11996
|
-
setThumbnailState(slideIndex, { status: "rendering" });
|
|
11997
|
-
try {
|
|
11998
|
-
const cleanup = await controller.renderThumbnail(slideIndex, element, {
|
|
11999
|
-
width: size.width
|
|
12000
|
-
});
|
|
12001
|
-
if (!mountedRef.current || renderGenerationRef.current.get(slideIndex) !== generation || attachedElementsRef.current.has(slideIndex) || !requestedPrefetchSlideIndexSetRef.current.has(slideIndex)) {
|
|
12002
|
-
cleanup();
|
|
12003
|
-
return;
|
|
12149
|
+
while (true) {
|
|
12150
|
+
const existing = prefetchInFlightRef.current.get(slideIndex);
|
|
12151
|
+
if (existing) {
|
|
12152
|
+
await existing;
|
|
12153
|
+
continue;
|
|
12004
12154
|
}
|
|
12005
|
-
|
|
12006
|
-
setThumbnailState(slideIndex, { status: "ready" });
|
|
12007
|
-
} catch (reason) {
|
|
12008
|
-
if (!mountedRef.current || renderGenerationRef.current.get(slideIndex) !== generation) {
|
|
12155
|
+
if (disabled || !controller?.isReady() || attachedElementsRef.current.has(slideIndex) || prefetchedRef.current.has(slideIndex) || !requestedPrefetchSlideIndexSetRef.current.has(slideIndex)) {
|
|
12009
12156
|
return;
|
|
12010
12157
|
}
|
|
12011
|
-
|
|
12158
|
+
const generation = (renderGenerationRef.current.get(slideIndex) ?? 0) + 1;
|
|
12159
|
+
renderGenerationRef.current.set(slideIndex, generation);
|
|
12160
|
+
const element = globalThis.document.createElement("div");
|
|
12161
|
+
setThumbnailState(slideIndex, { status: "rendering" });
|
|
12162
|
+
const pending = (async () => {
|
|
12163
|
+
try {
|
|
12164
|
+
const cleanup = await controller.renderThumbnail(slideIndex, element, {
|
|
12165
|
+
width: size.width
|
|
12166
|
+
});
|
|
12167
|
+
if (!mountedRef.current || renderGenerationRef.current.get(slideIndex) !== generation || attachedElementsRef.current.has(slideIndex) || !requestedPrefetchSlideIndexSetRef.current.has(slideIndex)) {
|
|
12168
|
+
cleanup();
|
|
12169
|
+
return;
|
|
12170
|
+
}
|
|
12171
|
+
prefetchedRef.current.set(slideIndex, { cleanup, element, width: size.width });
|
|
12172
|
+
setThumbnailState(slideIndex, { status: "ready" });
|
|
12173
|
+
} catch (reason) {
|
|
12174
|
+
if (!mountedRef.current || renderGenerationRef.current.get(slideIndex) !== generation) {
|
|
12175
|
+
return;
|
|
12176
|
+
}
|
|
12177
|
+
setThumbnailState(slideIndex, { error: asError(reason), status: "error" });
|
|
12178
|
+
}
|
|
12179
|
+
})();
|
|
12180
|
+
prefetchInFlightRef.current.set(slideIndex, pending);
|
|
12181
|
+
try {
|
|
12182
|
+
await pending;
|
|
12183
|
+
} finally {
|
|
12184
|
+
if (prefetchInFlightRef.current.get(slideIndex) === pending) {
|
|
12185
|
+
prefetchInFlightRef.current.delete(slideIndex);
|
|
12186
|
+
}
|
|
12187
|
+
}
|
|
12188
|
+
return;
|
|
12012
12189
|
}
|
|
12013
12190
|
},
|
|
12014
12191
|
[controller, disabled, setThumbnailState, size.width]
|
|
@@ -12024,14 +12201,17 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
12024
12201
|
cleanupsRef.current.clear();
|
|
12025
12202
|
for (const prefetched of prefetchedRef.current.values()) prefetched.cleanup();
|
|
12026
12203
|
prefetchedRef.current.clear();
|
|
12204
|
+
prefetchInFlightRef.current.clear();
|
|
12027
12205
|
};
|
|
12028
12206
|
}, []);
|
|
12029
12207
|
useEffect(
|
|
12030
12208
|
() => () => {
|
|
12209
|
+
for (const index of prefetchInFlightRef.current.keys()) invalidateThumbnail(index);
|
|
12210
|
+
prefetchInFlightRef.current.clear();
|
|
12031
12211
|
for (const prefetched of prefetchedRef.current.values()) prefetched.cleanup();
|
|
12032
12212
|
prefetchedRef.current.clear();
|
|
12033
12213
|
},
|
|
12034
|
-
[controller, size.width]
|
|
12214
|
+
[controller, invalidateThumbnail, size.width]
|
|
12035
12215
|
);
|
|
12036
12216
|
useEffect(() => {
|
|
12037
12217
|
void rerenderAttachedThumbnails();
|
|
@@ -12043,6 +12223,13 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
12043
12223
|
if (!attachedElementsRef.current.has(index)) setThumbnailState(index, { status: "idle" });
|
|
12044
12224
|
}
|
|
12045
12225
|
}
|
|
12226
|
+
for (const index of prefetchInFlightRef.current.keys()) {
|
|
12227
|
+
if (disabled || attachedElementsRef.current.has(index) || !requestedPrefetchSlideIndexSetRef.current.has(index)) {
|
|
12228
|
+
invalidateThumbnail(index);
|
|
12229
|
+
prefetchInFlightRef.current.delete(index);
|
|
12230
|
+
if (!attachedElementsRef.current.has(index)) setThumbnailState(index, { status: "idle" });
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
12046
12233
|
if (disabled) return;
|
|
12047
12234
|
let cancelled = false;
|
|
12048
12235
|
void (async () => {
|
|
@@ -12057,6 +12244,7 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
12057
12244
|
}, [
|
|
12058
12245
|
disabled,
|
|
12059
12246
|
disposePrefetchedThumbnail,
|
|
12247
|
+
invalidateThumbnail,
|
|
12060
12248
|
prefetchSlideIndexesKey,
|
|
12061
12249
|
prefetchThumbnail,
|
|
12062
12250
|
setThumbnailState
|