@extend-ai/react-pptx 0.1.0 → 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 +9 -0
- package/dist/index.js +279 -72
- package/dist/index.js.map +1 -1
- package/dist/pptx_wasm_bg.wasm +0 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
# @extend-ai/react-pptx
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Improve PowerPoint import fidelity for word spacing, hyperlink styling, diagonal stripe shapes, and straight connectors, including zero-width vertical timeline stems.
|
|
8
|
+
|
|
3
9
|
## Unreleased
|
|
4
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.
|
|
5
14
|
- Expose consumer-owned slide thumbnail rails through `usePptxViewerThumbnails`, including stable
|
|
6
15
|
container refs, resolution bounds, render status, imperative rendering, and cleanup. Add
|
|
7
16
|
DOCX-style visible/prefetch render windows and virtualize the built-in thumbnail scrollport.
|
package/dist/index.js
CHANGED
|
@@ -117,8 +117,6 @@ var UNIVERSAL_SANS_FALLBACKS = [
|
|
|
117
117
|
"Noto Sans Arabic",
|
|
118
118
|
"Noto Sans Hebrew",
|
|
119
119
|
"Segoe UI Symbol",
|
|
120
|
-
"Apple Color Emoji",
|
|
121
|
-
"Segoe UI Emoji",
|
|
122
120
|
"sans-serif"
|
|
123
121
|
];
|
|
124
122
|
function normalizeFamily(value) {
|
|
@@ -9251,6 +9249,11 @@ var DEFAULT_TEXT_VERTICAL_INSET_EMU = 45720;
|
|
|
9251
9249
|
var POWERPOINT_SINGLE_LINE_HEIGHT = 1.15;
|
|
9252
9250
|
var LIST_ITEM_GAP = 24;
|
|
9253
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
|
+
}
|
|
9254
9257
|
var METAFILE_FONT_MAP = Object.fromEntries(
|
|
9255
9258
|
Object.entries(OFFICE_FONT_FALLBACKS).map(([family, fallbacks]) => [
|
|
9256
9259
|
family,
|
|
@@ -9407,6 +9410,8 @@ function createTextRunElement(run, paragraph, fontScale) {
|
|
|
9407
9410
|
span.href = href;
|
|
9408
9411
|
span.target = "_blank";
|
|
9409
9412
|
span.rel = "noreferrer noopener";
|
|
9413
|
+
if (!run.color) span.style.color = "inherit";
|
|
9414
|
+
if (!decorations.length) span.style.textDecoration = "none";
|
|
9410
9415
|
}
|
|
9411
9416
|
return span;
|
|
9412
9417
|
}
|
|
@@ -9553,6 +9558,8 @@ function applyLine(element, line) {
|
|
|
9553
9558
|
}
|
|
9554
9559
|
function normalizedPresetGeometryPath(preset) {
|
|
9555
9560
|
switch (preset) {
|
|
9561
|
+
case "straightConnector1":
|
|
9562
|
+
return "M 0 0 L 1 1";
|
|
9556
9563
|
case "heart":
|
|
9557
9564
|
return "M 0.5 0.25 C 0.7083333333 -0.3333333333 1.5208333333 0.25 0.5 1 C -0.5208333333 0.25 0.2916666667 -0.3333333333 0.5 0.25 Z";
|
|
9558
9565
|
default:
|
|
@@ -9579,6 +9586,9 @@ function applyGeometry(element, preset) {
|
|
|
9579
9586
|
case "parallelogram":
|
|
9580
9587
|
element.style.clipPath = "polygon(20% 0, 100% 0, 80% 100%, 0 100%)";
|
|
9581
9588
|
break;
|
|
9589
|
+
case "diagStripe":
|
|
9590
|
+
element.style.clipPath = "polygon(0 50%, 50% 0, 100% 0, 0 100%)";
|
|
9591
|
+
break;
|
|
9582
9592
|
case "hexagon":
|
|
9583
9593
|
element.style.clipPath = "polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%)";
|
|
9584
9594
|
break;
|
|
@@ -9790,7 +9800,7 @@ var NormalizedPresentationViewer = class {
|
|
|
9790
9800
|
metafileUrls = /* @__PURE__ */ new Map();
|
|
9791
9801
|
listCleanup;
|
|
9792
9802
|
highlight;
|
|
9793
|
-
|
|
9803
|
+
activeMountResources;
|
|
9794
9804
|
mountedHandles = /* @__PURE__ */ new Set();
|
|
9795
9805
|
handlesByTarget = /* @__PURE__ */ new Map();
|
|
9796
9806
|
destroyed = false;
|
|
@@ -9821,6 +9831,10 @@ var NormalizedPresentationViewer = class {
|
|
|
9821
9831
|
const height = this.presentation.size.heightEmu / EMU_PER_CSS_PIXEL;
|
|
9822
9832
|
return Number.isFinite(height) && height > 0 ? height : 1;
|
|
9823
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
|
+
}
|
|
9824
9838
|
isRenderActive(generation) {
|
|
9825
9839
|
return !this.destroyed && generation === this.renderGeneration;
|
|
9826
9840
|
}
|
|
@@ -9873,12 +9887,13 @@ var NormalizedPresentationViewer = class {
|
|
|
9873
9887
|
applyAsset(assetId, apply, nodeId) {
|
|
9874
9888
|
const asset = this.presentation.assets[assetId];
|
|
9875
9889
|
if (!asset) return;
|
|
9890
|
+
const resources = this.activeMountResources;
|
|
9876
9891
|
const pending = this.assetUrl(asset).then((url) => {
|
|
9877
9892
|
if (url && !this.destroyed) apply(url);
|
|
9878
9893
|
}).catch((error) => {
|
|
9879
9894
|
if (!this.destroyed) this.callbacks.onNodeError?.(nodeId, error);
|
|
9880
|
-
})
|
|
9881
|
-
|
|
9895
|
+
});
|
|
9896
|
+
resources?.add(pending);
|
|
9882
9897
|
}
|
|
9883
9898
|
/**
|
|
9884
9899
|
* Applies DrawingML picture recolor effects (`a:biLevel`, `a:grayscl`,
|
|
@@ -9964,8 +9979,20 @@ var NormalizedPresentationViewer = class {
|
|
|
9964
9979
|
svg.style.height = "100%";
|
|
9965
9980
|
svg.style.overflow = "visible";
|
|
9966
9981
|
svg.style.pointerEvents = "none";
|
|
9982
|
+
let renderedPathData = pathData;
|
|
9983
|
+
if (node.geometry.preset === "straightConnector1") {
|
|
9984
|
+
if (node.transform.width === 0 && node.transform.height !== 0) {
|
|
9985
|
+
svg.style.left = "-0.5px";
|
|
9986
|
+
svg.style.width = "1px";
|
|
9987
|
+
renderedPathData = "M 0.5 0 L 0.5 1";
|
|
9988
|
+
} else if (node.transform.height === 0 && node.transform.width !== 0) {
|
|
9989
|
+
svg.style.top = "-0.5px";
|
|
9990
|
+
svg.style.height = "1px";
|
|
9991
|
+
renderedPathData = "M 0 0.5 L 1 0.5";
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9967
9994
|
const path = document.createElementNS(namespace, "path");
|
|
9968
|
-
path.setAttribute("d",
|
|
9995
|
+
path.setAttribute("d", renderedPathData);
|
|
9969
9996
|
path.setAttribute("vector-effect", "non-scaling-stroke");
|
|
9970
9997
|
path.style.fill = "none";
|
|
9971
9998
|
const fillStyle = node.fill;
|
|
@@ -10022,11 +10049,7 @@ var NormalizedPresentationViewer = class {
|
|
|
10022
10049
|
image.setAttribute("preserveAspectRatio", "none");
|
|
10023
10050
|
image.setAttribute("clip-path", `url(#${clipId})`);
|
|
10024
10051
|
image.style.opacity = String(Math.max(0, Math.min(1, fillStyle.opacity ?? 1)));
|
|
10025
|
-
this.applyAsset(
|
|
10026
|
-
fillStyle.assetId,
|
|
10027
|
-
(url) => image.setAttribute("href", url),
|
|
10028
|
-
node.id
|
|
10029
|
-
);
|
|
10052
|
+
this.applyAsset(fillStyle.assetId, (url) => image.setAttribute("href", url), node.id);
|
|
10030
10053
|
svg.append(image);
|
|
10031
10054
|
}
|
|
10032
10055
|
if (node.line) {
|
|
@@ -10172,6 +10195,8 @@ var NormalizedPresentationViewer = class {
|
|
|
10172
10195
|
element.href = href;
|
|
10173
10196
|
element.target = "_blank";
|
|
10174
10197
|
element.rel = "noreferrer noopener";
|
|
10198
|
+
element.style.color = "inherit";
|
|
10199
|
+
element.style.textDecoration = "none";
|
|
10175
10200
|
}
|
|
10176
10201
|
if (node.type === "shape") {
|
|
10177
10202
|
const presetPath = normalizedPresetGeometryPath(node.geometry.preset);
|
|
@@ -10387,23 +10412,35 @@ var NormalizedPresentationViewer = class {
|
|
|
10387
10412
|
element,
|
|
10388
10413
|
target,
|
|
10389
10414
|
ready: Promise.resolve(),
|
|
10415
|
+
setScale: () => {
|
|
10416
|
+
},
|
|
10390
10417
|
dispose: () => element.remove()
|
|
10391
10418
|
};
|
|
10392
10419
|
}
|
|
10393
10420
|
mount(index, target, scale) {
|
|
10394
10421
|
if (this.destroyed) return this.emptyHandle(target);
|
|
10395
10422
|
this.handlesByTarget.get(target)?.dispose();
|
|
10396
|
-
const
|
|
10397
|
-
const
|
|
10398
|
-
|
|
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);
|
|
10399
10433
|
const wrapper = document.createElement("div");
|
|
10400
10434
|
wrapper.dataset.rpvSlideWrapper = String(index);
|
|
10401
10435
|
wrapper.style.position = "relative";
|
|
10402
|
-
wrapper.style.width = `${this.naturalSlideWidth * effectiveScale}px`;
|
|
10403
|
-
wrapper.style.height = `${this.naturalSlideHeight * effectiveScale}px`;
|
|
10404
10436
|
wrapper.style.margin = "0 auto";
|
|
10405
10437
|
slide.style.transformOrigin = "top left";
|
|
10406
|
-
|
|
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);
|
|
10407
10444
|
wrapper.append(slide);
|
|
10408
10445
|
target.replaceChildren(wrapper);
|
|
10409
10446
|
this.callbacks.onSlideRendered?.(index, slide);
|
|
@@ -10411,7 +10448,8 @@ var NormalizedPresentationViewer = class {
|
|
|
10411
10448
|
const handle = {
|
|
10412
10449
|
element: slide,
|
|
10413
10450
|
target,
|
|
10414
|
-
ready: Promise.all([...
|
|
10451
|
+
ready: Promise.all([...resources]).then(() => void 0),
|
|
10452
|
+
setScale,
|
|
10415
10453
|
dispose: () => {
|
|
10416
10454
|
if (disposed) return;
|
|
10417
10455
|
disposed = true;
|
|
@@ -10439,6 +10477,32 @@ var NormalizedPresentationViewer = class {
|
|
|
10439
10477
|
await Promise.resolve();
|
|
10440
10478
|
}
|
|
10441
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
|
+
}
|
|
10442
10506
|
async mountInBatches(placeholders, indices, batchSize, generation) {
|
|
10443
10507
|
const uniqueIndices = [...new Set(indices)].filter(
|
|
10444
10508
|
(index) => index >= 0 && index < placeholders.length
|
|
@@ -10471,12 +10535,13 @@ var NormalizedPresentationViewer = class {
|
|
|
10471
10535
|
createListPlaceholder(index, item, generation, scale) {
|
|
10472
10536
|
let handle;
|
|
10473
10537
|
let mountPromise;
|
|
10538
|
+
let effectiveScale = scale;
|
|
10474
10539
|
const isActive = () => this.isRenderActive(generation) && this.listState?.generation === generation;
|
|
10475
10540
|
const mount = () => {
|
|
10476
10541
|
if (!isActive()) return Promise.resolve();
|
|
10477
10542
|
if (handle) return handle.ready;
|
|
10478
10543
|
if (mountPromise) return mountPromise;
|
|
10479
|
-
const nextHandle = this.mount(index, item,
|
|
10544
|
+
const nextHandle = this.mount(index, item, effectiveScale);
|
|
10480
10545
|
handle = nextHandle;
|
|
10481
10546
|
const pending = nextHandle.ready.then(() => {
|
|
10482
10547
|
if (!isActive() || handle !== nextHandle) {
|
|
@@ -10492,11 +10557,16 @@ var NormalizedPresentationViewer = class {
|
|
|
10492
10557
|
const unmount = () => {
|
|
10493
10558
|
const mounted = handle;
|
|
10494
10559
|
handle = void 0;
|
|
10560
|
+
mountPromise = void 0;
|
|
10495
10561
|
if (mounted) {
|
|
10496
10562
|
mounted.dispose();
|
|
10497
10563
|
}
|
|
10498
10564
|
};
|
|
10499
|
-
|
|
10565
|
+
const setScale = (nextScale) => {
|
|
10566
|
+
effectiveScale = nextScale;
|
|
10567
|
+
handle?.setScale(nextScale);
|
|
10568
|
+
};
|
|
10569
|
+
return { item, mount, unmount, isMounted: () => Boolean(handle), setScale };
|
|
10500
10570
|
}
|
|
10501
10571
|
async renderList(options = {}) {
|
|
10502
10572
|
const windowingEnabled = options.enabled !== false;
|
|
@@ -10520,11 +10590,9 @@ var NormalizedPresentationViewer = class {
|
|
|
10520
10590
|
Math.min(this.slideCount - 1, options.initialSlideIndex ?? this.current)
|
|
10521
10591
|
);
|
|
10522
10592
|
const scroller = options.scrollElement ?? this.container;
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
const slideHeight = this.naturalSlideHeight * effectiveScale;
|
|
10527
|
-
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;
|
|
10528
10596
|
if (!windowingEnabled) {
|
|
10529
10597
|
const placeholders2 = this.presentation.slides.map((_, index) => {
|
|
10530
10598
|
const item = document.createElement("div");
|
|
@@ -10532,9 +10600,35 @@ var NormalizedPresentationViewer = class {
|
|
|
10532
10600
|
item.style.minHeight = `${slideHeight}px`;
|
|
10533
10601
|
item.style.margin = `0 auto ${LIST_ITEM_GAP}px`;
|
|
10534
10602
|
this.container.append(item);
|
|
10535
|
-
return this.createListPlaceholder(index, item, generation);
|
|
10603
|
+
return this.createListPlaceholder(index, item, generation, effectiveScale);
|
|
10536
10604
|
});
|
|
10537
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
|
+
};
|
|
10538
10632
|
this.listState = state2;
|
|
10539
10633
|
await this.mountInBatches(
|
|
10540
10634
|
placeholders2,
|
|
@@ -10576,6 +10670,20 @@ var NormalizedPresentationViewer = class {
|
|
|
10576
10670
|
const state = { generation, options: normalizedOptions, placeholders };
|
|
10577
10671
|
this.listState = state;
|
|
10578
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
|
+
};
|
|
10579
10687
|
const measureViewportRect = () => {
|
|
10580
10688
|
const rect = scroller.getBoundingClientRect();
|
|
10581
10689
|
if (rect.width > 0 && rect.height > 0) return { width: rect.width, height: rect.height };
|
|
@@ -10587,16 +10695,25 @@ var NormalizedPresentationViewer = class {
|
|
|
10587
10695
|
const initialRect = measureViewportRect();
|
|
10588
10696
|
const requestedOverscan = options.overscanViewport ?? 1.5;
|
|
10589
10697
|
const overscanViewports = Number.isFinite(requestedOverscan) && requestedOverscan >= 0 ? requestedOverscan : 1.5;
|
|
10590
|
-
|
|
10698
|
+
let overscan = Math.max(1, Math.ceil(overscanViewports * initialRect.height / itemStride));
|
|
10591
10699
|
const measureScrollMargin = () => sizer.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;
|
|
10592
10700
|
let scrollMargin = measureScrollMargin();
|
|
10593
10701
|
let needsSettleAlignment = initialIndex > 0;
|
|
10594
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
|
+
};
|
|
10595
10711
|
const reconcile = (instance) => {
|
|
10596
10712
|
if (!isActive()) return;
|
|
10597
10713
|
const windowed = new Set(instance.getVirtualItems().map((item) => item.index));
|
|
10714
|
+
virtualWindow = windowed;
|
|
10598
10715
|
placeholders.forEach((placeholder, index) => {
|
|
10599
|
-
if (windowed.has(index)) void placeholder.mount();
|
|
10716
|
+
if (windowed.has(index) && !deferredMounts.has(index)) void placeholder.mount();
|
|
10600
10717
|
else placeholder.unmount();
|
|
10601
10718
|
});
|
|
10602
10719
|
const offset = (instance.scrollOffset ?? 0) - scrollMargin;
|
|
@@ -10659,12 +10776,7 @@ var NormalizedPresentationViewer = class {
|
|
|
10659
10776
|
},
|
|
10660
10777
|
scrollToFn: (offset, { adjustments, behavior }) => {
|
|
10661
10778
|
const top = offset + (adjustments ?? 0);
|
|
10662
|
-
|
|
10663
|
-
if (typeof scroller.scrollTo === "function") {
|
|
10664
|
-
scroller.scrollTo(behavior ? { top, behavior } : { top });
|
|
10665
|
-
} else {
|
|
10666
|
-
scroller.scrollTop = top;
|
|
10667
|
-
}
|
|
10779
|
+
writeScrollTop(top, behavior);
|
|
10668
10780
|
},
|
|
10669
10781
|
onChange: (instance) => reconcile(instance)
|
|
10670
10782
|
});
|
|
@@ -10675,6 +10787,39 @@ var NormalizedPresentationViewer = class {
|
|
|
10675
10787
|
behavior: scrollOptions?.behavior === "smooth" ? "smooth" : "auto"
|
|
10676
10788
|
});
|
|
10677
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
|
+
};
|
|
10678
10823
|
const teardown = virtualizer._didMount();
|
|
10679
10824
|
this.listCleanup = teardown;
|
|
10680
10825
|
virtualizer._willUpdate();
|
|
@@ -10702,16 +10847,27 @@ var NormalizedPresentationViewer = class {
|
|
|
10702
10847
|
if (state && this.isRenderActive(state.generation)) {
|
|
10703
10848
|
const navigation = ++this.navigationGeneration;
|
|
10704
10849
|
const placeholder = state.placeholders[next];
|
|
10705
|
-
|
|
10706
|
-
if (
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
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);
|
|
10712
10870
|
}
|
|
10713
|
-
this.current = next;
|
|
10714
|
-
if (changed) this.notifySlideChange(next);
|
|
10715
10871
|
return;
|
|
10716
10872
|
}
|
|
10717
10873
|
if (!changed && this.container.querySelector(`[data-rpv-slide-index="${next}"]`)) return;
|
|
@@ -10722,15 +10878,33 @@ var NormalizedPresentationViewer = class {
|
|
|
10722
10878
|
if (this.zoom === next) return;
|
|
10723
10879
|
this.zoom = next;
|
|
10724
10880
|
if (this.renderState?.mode === "continuous") {
|
|
10725
|
-
|
|
10726
|
-
|
|
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
|
+
}
|
|
10727
10892
|
}
|
|
10728
10893
|
async setFitMode(mode) {
|
|
10729
10894
|
if (this.fit === mode) return;
|
|
10730
10895
|
this.fit = mode;
|
|
10731
10896
|
if (this.renderState?.mode === "continuous") {
|
|
10732
|
-
|
|
10733
|
-
|
|
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
|
+
}
|
|
10734
10908
|
}
|
|
10735
10909
|
searchText(query, options) {
|
|
10736
10910
|
return searchDocument(this.presentation, query, options);
|
|
@@ -10812,7 +10986,7 @@ var NormalizedPresentationViewer = class {
|
|
|
10812
10986
|
this.objectUrls.clear();
|
|
10813
10987
|
this.assetUrls.clear();
|
|
10814
10988
|
this.metafileUrls.clear();
|
|
10815
|
-
this.
|
|
10989
|
+
this.activeMountResources = void 0;
|
|
10816
10990
|
for (const handle of [...this.mountedHandles]) handle.dispose();
|
|
10817
10991
|
this.handlesByTarget.clear();
|
|
10818
10992
|
this.renderState = void 0;
|
|
@@ -11080,7 +11254,7 @@ var presentationParsingDefaults = Object.freeze({
|
|
|
11080
11254
|
maxInputBytes: DEFAULT_MAX_INPUT_BYTES
|
|
11081
11255
|
});
|
|
11082
11256
|
var THUMBNAIL_ROW_ESTIMATE = 123;
|
|
11083
|
-
var THUMBNAIL_OVERSCAN =
|
|
11257
|
+
var THUMBNAIL_OVERSCAN = 1;
|
|
11084
11258
|
var THUMBNAIL_INITIAL_RECT = { height: 780, width: 184 };
|
|
11085
11259
|
var observeThumbnailFilmstripRect = (instance, callback) => {
|
|
11086
11260
|
const element = instance.scrollElement;
|
|
@@ -11801,9 +11975,12 @@ function usePptxViewer() {
|
|
|
11801
11975
|
}
|
|
11802
11976
|
var EMU_PER_CSS_PIXEL2 = 9525;
|
|
11803
11977
|
var DEFAULT_MAX_DIMENSION2 = 160;
|
|
11804
|
-
function normalizeSlideIndexes(indexes, slideCount) {
|
|
11978
|
+
function normalizeSlideIndexes(indexes, slideCount, sort = true) {
|
|
11805
11979
|
if (!indexes?.length || slideCount <= 0) return [];
|
|
11806
|
-
|
|
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;
|
|
11807
11984
|
}
|
|
11808
11985
|
function finitePositive2(value) {
|
|
11809
11986
|
return value !== void 0 && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
@@ -11841,6 +12018,7 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
11841
12018
|
const attachedElementsRef = useRef(/* @__PURE__ */ new Map());
|
|
11842
12019
|
const cleanupsRef = useRef(/* @__PURE__ */ new Map());
|
|
11843
12020
|
const prefetchedRef = useRef(/* @__PURE__ */ new Map());
|
|
12021
|
+
const prefetchInFlightRef = useRef(/* @__PURE__ */ new Map());
|
|
11844
12022
|
const renderGenerationRef = useRef(/* @__PURE__ */ new Map());
|
|
11845
12023
|
const refCallbacksRef = useRef(/* @__PURE__ */ new Map());
|
|
11846
12024
|
const attachRef = useRef(() => {
|
|
@@ -11853,7 +12031,7 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
11853
12031
|
[options.renderWindow?.visibleSlideIndexes, slideCount]
|
|
11854
12032
|
);
|
|
11855
12033
|
const prefetchSlideIndexes = useMemo(
|
|
11856
|
-
() => normalizeSlideIndexes(options.renderWindow?.prefetchSlideIndexes, slideCount),
|
|
12034
|
+
() => normalizeSlideIndexes(options.renderWindow?.prefetchSlideIndexes, slideCount, false),
|
|
11857
12035
|
[options.renderWindow?.prefetchSlideIndexes, slideCount]
|
|
11858
12036
|
);
|
|
11859
12037
|
const visibleSlideIndexesKey = visibleSlideIndexes.join(",");
|
|
@@ -11968,28 +12146,46 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
11968
12146
|
}, [renderThumbnail]);
|
|
11969
12147
|
const prefetchThumbnail = useCallback(
|
|
11970
12148
|
async (slideIndex) => {
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
const element = globalThis.document.createElement("div");
|
|
11977
|
-
setThumbnailState(slideIndex, { status: "rendering" });
|
|
11978
|
-
try {
|
|
11979
|
-
const cleanup = await controller.renderThumbnail(slideIndex, element, {
|
|
11980
|
-
width: size.width
|
|
11981
|
-
});
|
|
11982
|
-
if (!mountedRef.current || renderGenerationRef.current.get(slideIndex) !== generation || attachedElementsRef.current.has(slideIndex) || !requestedPrefetchSlideIndexSetRef.current.has(slideIndex)) {
|
|
11983
|
-
cleanup();
|
|
11984
|
-
return;
|
|
12149
|
+
while (true) {
|
|
12150
|
+
const existing = prefetchInFlightRef.current.get(slideIndex);
|
|
12151
|
+
if (existing) {
|
|
12152
|
+
await existing;
|
|
12153
|
+
continue;
|
|
11985
12154
|
}
|
|
11986
|
-
|
|
11987
|
-
setThumbnailState(slideIndex, { status: "ready" });
|
|
11988
|
-
} catch (reason) {
|
|
11989
|
-
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)) {
|
|
11990
12156
|
return;
|
|
11991
12157
|
}
|
|
11992
|
-
|
|
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;
|
|
11993
12189
|
}
|
|
11994
12190
|
},
|
|
11995
12191
|
[controller, disabled, setThumbnailState, size.width]
|
|
@@ -12005,14 +12201,17 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
12005
12201
|
cleanupsRef.current.clear();
|
|
12006
12202
|
for (const prefetched of prefetchedRef.current.values()) prefetched.cleanup();
|
|
12007
12203
|
prefetchedRef.current.clear();
|
|
12204
|
+
prefetchInFlightRef.current.clear();
|
|
12008
12205
|
};
|
|
12009
12206
|
}, []);
|
|
12010
12207
|
useEffect(
|
|
12011
12208
|
() => () => {
|
|
12209
|
+
for (const index of prefetchInFlightRef.current.keys()) invalidateThumbnail(index);
|
|
12210
|
+
prefetchInFlightRef.current.clear();
|
|
12012
12211
|
for (const prefetched of prefetchedRef.current.values()) prefetched.cleanup();
|
|
12013
12212
|
prefetchedRef.current.clear();
|
|
12014
12213
|
},
|
|
12015
|
-
[controller, size.width]
|
|
12214
|
+
[controller, invalidateThumbnail, size.width]
|
|
12016
12215
|
);
|
|
12017
12216
|
useEffect(() => {
|
|
12018
12217
|
void rerenderAttachedThumbnails();
|
|
@@ -12024,6 +12223,13 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
12024
12223
|
if (!attachedElementsRef.current.has(index)) setThumbnailState(index, { status: "idle" });
|
|
12025
12224
|
}
|
|
12026
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
|
+
}
|
|
12027
12233
|
if (disabled) return;
|
|
12028
12234
|
let cancelled = false;
|
|
12029
12235
|
void (async () => {
|
|
@@ -12038,6 +12244,7 @@ function usePptxViewerThumbnails(controller, options = {}) {
|
|
|
12038
12244
|
}, [
|
|
12039
12245
|
disabled,
|
|
12040
12246
|
disposePrefetchedThumbnail,
|
|
12247
|
+
invalidateThumbnail,
|
|
12041
12248
|
prefetchSlideIndexesKey,
|
|
12042
12249
|
prefetchThumbnail,
|
|
12043
12250
|
setThumbnailState
|