pptx-react-viewer 1.1.4 → 1.1.5
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/dist/index.js +512 -96
- package/dist/index.mjs +512 -97
- package/dist/viewer/index.js +530 -102
- package/dist/viewer/index.mjs +530 -103
- package/node_modules/emf-converter/dist/index.d.mts +2 -2
- package/node_modules/emf-converter/dist/index.d.ts +2 -2
- package/node_modules/emf-converter/dist/index.js +91 -33
- package/node_modules/emf-converter/dist/index.mjs +91 -33
- package/node_modules/emf-converter/package.json +1 -1
- package/node_modules/mtx-decompressor/dist/index.js +39 -9
- package/node_modules/mtx-decompressor/dist/index.mjs +39 -9
- package/node_modules/mtx-decompressor/package.json +1 -1
- package/node_modules/pptx-viewer-core/dist/cli/index.js +0 -0
- package/node_modules/pptx-viewer-core/dist/cli/index.mjs +0 -0
- package/node_modules/pptx-viewer-core/dist/converter/index.js +0 -0
- package/node_modules/pptx-viewer-core/dist/converter/index.mjs +0 -0
- package/node_modules/pptx-viewer-core/dist/index.d.mts +95 -11
- package/node_modules/pptx-viewer-core/dist/index.d.ts +95 -11
- package/node_modules/pptx-viewer-core/dist/index.js +795 -257
- package/node_modules/pptx-viewer-core/dist/index.mjs +791 -258
- package/node_modules/pptx-viewer-core/dist/{signature-inspection-status-BcJSdOvb.d.mts → signature-inspection-status-BCUpfCQh.d.mts} +13 -2
- package/node_modules/pptx-viewer-core/dist/{signature-inspection-status-BcJSdOvb.d.ts → signature-inspection-status-BCUpfCQh.d.ts} +13 -2
- package/node_modules/pptx-viewer-core/dist/signature-node/index.d.mts +2 -2
- package/node_modules/pptx-viewer-core/dist/signature-node/index.d.ts +2 -2
- package/node_modules/pptx-viewer-core/dist/signature-node/index.js +17 -3
- package/node_modules/pptx-viewer-core/dist/signature-node/index.mjs +16 -4
- package/node_modules/pptx-viewer-core/package.json +1 -1
- package/package.json +6 -4
package/dist/viewer/index.js
CHANGED
|
@@ -7,6 +7,7 @@ var clsx = require('clsx');
|
|
|
7
7
|
var tailwindMerge = require('tailwind-merge');
|
|
8
8
|
var lu = require('react-icons/lu');
|
|
9
9
|
var pptxViewerCore = require('pptx-viewer-core');
|
|
10
|
+
var DOMPurify = require('dompurify');
|
|
10
11
|
var reactI18next = require('react-i18next');
|
|
11
12
|
var html2canvasPro = require('html2canvas-pro');
|
|
12
13
|
var JSZip = require('jszip');
|
|
@@ -33,6 +34,7 @@ function _interopNamespace(e) {
|
|
|
33
34
|
|
|
34
35
|
var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
|
|
35
36
|
var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
|
|
37
|
+
var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
|
|
36
38
|
var html2canvasPro__default = /*#__PURE__*/_interopDefault(html2canvasPro);
|
|
37
39
|
var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
|
|
38
40
|
|
|
@@ -43596,7 +43598,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
|
|
|
43596
43598
|
return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
|
|
43597
43599
|
}
|
|
43598
43600
|
function useSyncExternalStore$2(subscribe3, getSnapshot2) {
|
|
43599
|
-
didWarnOld18Alpha || void 0 ===
|
|
43601
|
+
didWarnOld18Alpha || void 0 === React100.startTransition || (didWarnOld18Alpha = true, console.error(
|
|
43600
43602
|
"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
|
|
43601
43603
|
));
|
|
43602
43604
|
var value = getSnapshot2();
|
|
@@ -43606,7 +43608,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
|
|
|
43606
43608
|
"The result of getSnapshot should be cached to avoid an infinite loop"
|
|
43607
43609
|
), didWarnUncachedGetSnapshot = true);
|
|
43608
43610
|
}
|
|
43609
|
-
cachedValue =
|
|
43611
|
+
cachedValue = useState86({
|
|
43610
43612
|
inst: { value, getSnapshot: getSnapshot2 }
|
|
43611
43613
|
});
|
|
43612
43614
|
var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
|
|
@@ -43644,8 +43646,8 @@ var require_use_sync_external_store_shim_development = __commonJS({
|
|
|
43644
43646
|
return getSnapshot2();
|
|
43645
43647
|
}
|
|
43646
43648
|
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
43647
|
-
var
|
|
43648
|
-
exports$1.useSyncExternalStore = void 0 !==
|
|
43649
|
+
var React100 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is2, useState86 = React100.useState, useEffect72 = React100.useEffect, useLayoutEffect7 = React100.useLayoutEffect, useDebugValue = React100.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
|
|
43650
|
+
exports$1.useSyncExternalStore = void 0 !== React100.useSyncExternalStore ? React100.useSyncExternalStore : shim;
|
|
43649
43651
|
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
43650
43652
|
})();
|
|
43651
43653
|
}
|
|
@@ -43668,7 +43670,7 @@ var require_with_selector_development = __commonJS({
|
|
|
43668
43670
|
return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
|
|
43669
43671
|
}
|
|
43670
43672
|
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
43671
|
-
var
|
|
43673
|
+
var React100 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef73 = React100.useRef, useEffect72 = React100.useEffect, useMemo42 = React100.useMemo, useDebugValue = React100.useDebugValue;
|
|
43672
43674
|
exports$1.useSyncExternalStoreWithSelector = function(subscribe3, getSnapshot2, getServerSnapshot2, selector, isEqual) {
|
|
43673
43675
|
var instRef = useRef73(null);
|
|
43674
43676
|
if (null === instRef.current) {
|
|
@@ -74089,6 +74091,13 @@ function hasDistinctScriptFonts(fonts) {
|
|
|
74089
74091
|
}
|
|
74090
74092
|
return Boolean(fonts.eastAsia) && fonts.eastAsia !== base || Boolean(fonts.complexScript) && fonts.complexScript !== base || Boolean(fonts.symbol) && fonts.symbol !== base;
|
|
74091
74093
|
}
|
|
74094
|
+
function sanitizeMathMl(markup) {
|
|
74095
|
+
const purify = DOMPurify__default.default;
|
|
74096
|
+
if (typeof purify.sanitize !== "function") {
|
|
74097
|
+
return markup;
|
|
74098
|
+
}
|
|
74099
|
+
return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
|
|
74100
|
+
}
|
|
74092
74101
|
function renderScriptAwareText(text2, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix) {
|
|
74093
74102
|
if (!needsScriptFonts || !text2) {
|
|
74094
74103
|
return text2;
|
|
@@ -74179,14 +74188,15 @@ function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsSc
|
|
|
74179
74188
|
}
|
|
74180
74189
|
function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber) {
|
|
74181
74190
|
const mathml = convertOmmlToMathMl(equationXml);
|
|
74182
|
-
const
|
|
74191
|
+
const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
|
|
74192
|
+
const equationContent = safeMathml ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
74183
74193
|
"span",
|
|
74184
74194
|
{
|
|
74185
74195
|
className: "inline-block align-middle",
|
|
74186
74196
|
style: {
|
|
74187
74197
|
fontFamily: '"Cambria Math", "STIX Two Math", serif'
|
|
74188
74198
|
},
|
|
74189
|
-
dangerouslySetInnerHTML: { __html:
|
|
74199
|
+
dangerouslySetInnerHTML: { __html: safeMathml }
|
|
74190
74200
|
}
|
|
74191
74201
|
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: "Equation" });
|
|
74192
74202
|
if (equationNumber) {
|
|
@@ -86543,7 +86553,7 @@ function ResizeHandle({
|
|
|
86543
86553
|
}
|
|
86544
86554
|
);
|
|
86545
86555
|
}
|
|
86546
|
-
function
|
|
86556
|
+
function SlideThumbnailImpl({
|
|
86547
86557
|
slide,
|
|
86548
86558
|
templateElements,
|
|
86549
86559
|
canvasSize
|
|
@@ -86781,6 +86791,37 @@ function ThumbnailTable({
|
|
|
86781
86791
|
}
|
|
86782
86792
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] text-muted-foreground pointer-events-none", children: "Table" });
|
|
86783
86793
|
}
|
|
86794
|
+
function arePropsEqual(prev, next) {
|
|
86795
|
+
if (prev.slide.id !== next.slide.id) {
|
|
86796
|
+
return false;
|
|
86797
|
+
}
|
|
86798
|
+
if (prev.slide.isDirty !== next.slide.isDirty) {
|
|
86799
|
+
return false;
|
|
86800
|
+
}
|
|
86801
|
+
if (prev.slide.hidden !== next.slide.hidden) {
|
|
86802
|
+
return false;
|
|
86803
|
+
}
|
|
86804
|
+
if (prev.slide.elements !== next.slide.elements) {
|
|
86805
|
+
return false;
|
|
86806
|
+
}
|
|
86807
|
+
if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
|
|
86808
|
+
return false;
|
|
86809
|
+
}
|
|
86810
|
+
if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
|
|
86811
|
+
return false;
|
|
86812
|
+
}
|
|
86813
|
+
if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
|
|
86814
|
+
return false;
|
|
86815
|
+
}
|
|
86816
|
+
if (prev.templateElements !== next.templateElements) {
|
|
86817
|
+
return false;
|
|
86818
|
+
}
|
|
86819
|
+
if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
|
|
86820
|
+
return false;
|
|
86821
|
+
}
|
|
86822
|
+
return true;
|
|
86823
|
+
}
|
|
86824
|
+
var SlideThumbnail = React10__namespace.default.memo(SlideThumbnailImpl, arePropsEqual);
|
|
86784
86825
|
function ContextMenu({
|
|
86785
86826
|
contextMenuState,
|
|
86786
86827
|
mode,
|
|
@@ -90514,7 +90555,7 @@ function BendingProcessRenderer({
|
|
|
90514
90555
|
}
|
|
90515
90556
|
);
|
|
90516
90557
|
}
|
|
90517
|
-
function
|
|
90558
|
+
function SmartArtRendererImpl({
|
|
90518
90559
|
element: element2,
|
|
90519
90560
|
className = ""
|
|
90520
90561
|
}) {
|
|
@@ -90625,6 +90666,30 @@ function renderLayout(layoutType, element2, nodes, palette, style) {
|
|
|
90625
90666
|
}
|
|
90626
90667
|
return /* @__PURE__ */ jsxRuntime.jsx(ListRenderer, { element: element2, nodes, palette, style });
|
|
90627
90668
|
}
|
|
90669
|
+
function arePropsEqual2(prev, next) {
|
|
90670
|
+
if (prev.className !== next.className) {
|
|
90671
|
+
return false;
|
|
90672
|
+
}
|
|
90673
|
+
if (prev.element.id !== next.element.id) {
|
|
90674
|
+
return false;
|
|
90675
|
+
}
|
|
90676
|
+
if (prev.element.type !== next.element.type) {
|
|
90677
|
+
return false;
|
|
90678
|
+
}
|
|
90679
|
+
if (prev.element.width !== next.element.width || prev.element.height !== next.element.height) {
|
|
90680
|
+
return false;
|
|
90681
|
+
}
|
|
90682
|
+
if (prev.element.x !== next.element.x || prev.element.y !== next.element.y) {
|
|
90683
|
+
return false;
|
|
90684
|
+
}
|
|
90685
|
+
const prevData = prev.element.type === "smartArt" ? prev.element.smartArtData : void 0;
|
|
90686
|
+
const nextData = next.element.type === "smartArt" ? next.element.smartArtData : void 0;
|
|
90687
|
+
if (prevData !== nextData) {
|
|
90688
|
+
return false;
|
|
90689
|
+
}
|
|
90690
|
+
return true;
|
|
90691
|
+
}
|
|
90692
|
+
var SmartArtRenderer = React10__namespace.default.memo(SmartArtRendererImpl, arePropsEqual2);
|
|
90628
90693
|
function ZoomElementRenderer({
|
|
90629
90694
|
element: element2,
|
|
90630
90695
|
slides,
|
|
@@ -93663,7 +93728,7 @@ function SectionBlock({
|
|
|
93663
93728
|
)
|
|
93664
93729
|
] });
|
|
93665
93730
|
}
|
|
93666
|
-
function
|
|
93731
|
+
function SlideCardImpl({
|
|
93667
93732
|
slide,
|
|
93668
93733
|
index,
|
|
93669
93734
|
isActive,
|
|
@@ -93717,6 +93782,49 @@ function SlideCard({
|
|
|
93717
93782
|
}
|
|
93718
93783
|
);
|
|
93719
93784
|
}
|
|
93785
|
+
function arePropsEqual3(prev, next) {
|
|
93786
|
+
if (prev.slide.id !== next.slide.id) {
|
|
93787
|
+
return false;
|
|
93788
|
+
}
|
|
93789
|
+
if (prev.slide.isDirty !== next.slide.isDirty) {
|
|
93790
|
+
return false;
|
|
93791
|
+
}
|
|
93792
|
+
if (prev.slide.hidden !== next.slide.hidden) {
|
|
93793
|
+
return false;
|
|
93794
|
+
}
|
|
93795
|
+
if (prev.slide.elements !== next.slide.elements) {
|
|
93796
|
+
return false;
|
|
93797
|
+
}
|
|
93798
|
+
if (prev.index !== next.index) {
|
|
93799
|
+
return false;
|
|
93800
|
+
}
|
|
93801
|
+
if (prev.isActive !== next.isActive) {
|
|
93802
|
+
return false;
|
|
93803
|
+
}
|
|
93804
|
+
if (prev.isDragTarget !== next.isDragTarget) {
|
|
93805
|
+
return false;
|
|
93806
|
+
}
|
|
93807
|
+
if (prev.isSelected !== next.isSelected) {
|
|
93808
|
+
return false;
|
|
93809
|
+
}
|
|
93810
|
+
if (prev.selectedCount !== next.selectedCount) {
|
|
93811
|
+
return false;
|
|
93812
|
+
}
|
|
93813
|
+
if (prev.selectionOrder !== next.selectionOrder) {
|
|
93814
|
+
return false;
|
|
93815
|
+
}
|
|
93816
|
+
if (prev.canEdit !== next.canEdit) {
|
|
93817
|
+
return false;
|
|
93818
|
+
}
|
|
93819
|
+
if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
|
|
93820
|
+
return false;
|
|
93821
|
+
}
|
|
93822
|
+
if (prev.onSlideClick !== next.onSlideClick || prev.onDoubleClick !== next.onDoubleClick || prev.onContextMenu !== next.onContextMenu || prev.onDragStart !== next.onDragStart || prev.onDragOver !== next.onDragOver || prev.onDragLeave !== next.onDragLeave || prev.onDrop !== next.onDrop) {
|
|
93823
|
+
return false;
|
|
93824
|
+
}
|
|
93825
|
+
return true;
|
|
93826
|
+
}
|
|
93827
|
+
var SlideCard = React10__namespace.default.memo(SlideCardImpl, arePropsEqual3);
|
|
93720
93828
|
function SorterContextMenu({
|
|
93721
93829
|
x: x2,
|
|
93722
93830
|
y,
|
|
@@ -94510,6 +94618,14 @@ function getCurrentParagraphIndex(editorEl, segments) {
|
|
|
94510
94618
|
}
|
|
94511
94619
|
return paraIdx;
|
|
94512
94620
|
}
|
|
94621
|
+
var CSS_VALUE_SAFE = /^[a-zA-Z0-9 _,.\-+#'%/]{1,100}$/;
|
|
94622
|
+
function isCssValueSafe(value) {
|
|
94623
|
+
if (value === void 0 || value === null) {
|
|
94624
|
+
return false;
|
|
94625
|
+
}
|
|
94626
|
+
const str = String(value);
|
|
94627
|
+
return str.length > 0 && CSS_VALUE_SAFE.test(str);
|
|
94628
|
+
}
|
|
94513
94629
|
function deriveStyleFromElement(element2, inheritedStyle) {
|
|
94514
94630
|
const style = { ...inheritedStyle };
|
|
94515
94631
|
const tagName = element2.tagName.toLowerCase();
|
|
@@ -94642,17 +94758,17 @@ function segmentsToEditorHtml(segments) {
|
|
|
94642
94758
|
if (segment.style.strikethrough) {
|
|
94643
94759
|
inlineStyles.push("text-decoration:line-through");
|
|
94644
94760
|
}
|
|
94645
|
-
if (segment.style.color) {
|
|
94761
|
+
if (segment.style.color && isCssValueSafe(segment.style.color)) {
|
|
94646
94762
|
inlineStyles.push(`color:${segment.style.color}`);
|
|
94647
94763
|
}
|
|
94648
|
-
if (segment.style.fontSize) {
|
|
94649
|
-
inlineStyles.push(`font-size:${segment.style.fontSize}pt`);
|
|
94764
|
+
if (segment.style.fontSize && Number.isFinite(Number(segment.style.fontSize))) {
|
|
94765
|
+
inlineStyles.push(`font-size:${Number(segment.style.fontSize)}pt`);
|
|
94650
94766
|
}
|
|
94651
|
-
if (segment.style.fontFamily) {
|
|
94767
|
+
if (segment.style.fontFamily && isCssValueSafe(segment.style.fontFamily)) {
|
|
94652
94768
|
inlineStyles.push(`font-family:${segment.style.fontFamily}`);
|
|
94653
94769
|
}
|
|
94654
94770
|
const text2 = escapeHtml(segment.text);
|
|
94655
|
-
if (segment.style.hyperlink) {
|
|
94771
|
+
if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
|
|
94656
94772
|
const href = escapeHtml(segment.style.hyperlink);
|
|
94657
94773
|
return `<a href="${href}" style="color:#4a9eff;text-decoration:underline;cursor:pointer" data-hyperlink="${href}">${text2}</a>`;
|
|
94658
94774
|
}
|
|
@@ -94735,7 +94851,8 @@ function renderRichNotesSegments(segments) {
|
|
|
94735
94851
|
if (segment.style.fontFamily) {
|
|
94736
94852
|
style.fontFamily = segment.style.fontFamily;
|
|
94737
94853
|
}
|
|
94738
|
-
if (segment.style.hyperlink) {
|
|
94854
|
+
if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
|
|
94855
|
+
const safeHref = segment.style.hyperlink;
|
|
94739
94856
|
style.color = "#4a9eff";
|
|
94740
94857
|
style.textDecoration = "underline";
|
|
94741
94858
|
style.cursor = "pointer";
|
|
@@ -94743,11 +94860,11 @@ function renderRichNotesSegments(segments) {
|
|
|
94743
94860
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
94744
94861
|
"a",
|
|
94745
94862
|
{
|
|
94746
|
-
href:
|
|
94863
|
+
href: safeHref,
|
|
94747
94864
|
style,
|
|
94748
94865
|
onClick: (e2) => {
|
|
94749
94866
|
e2.preventDefault();
|
|
94750
|
-
|
|
94867
|
+
safeOpenUrl(safeHref);
|
|
94751
94868
|
},
|
|
94752
94869
|
children: segment.text
|
|
94753
94870
|
},
|
|
@@ -95299,7 +95416,7 @@ function useSlideNotes({
|
|
|
95299
95416
|
const href = target.getAttribute("data-hyperlink") || target.getAttribute("href");
|
|
95300
95417
|
if (href && (e2.ctrlKey || e2.metaKey)) {
|
|
95301
95418
|
e2.preventDefault();
|
|
95302
|
-
|
|
95419
|
+
safeOpenUrl(href);
|
|
95303
95420
|
}
|
|
95304
95421
|
}, []);
|
|
95305
95422
|
return {
|
|
@@ -96972,7 +97089,7 @@ function renderNotesSegments(segments) {
|
|
|
96972
97089
|
return React10__namespace.default.createElement("span", { key: `seg-${index}`, style }, segment.text);
|
|
96973
97090
|
});
|
|
96974
97091
|
}
|
|
96975
|
-
function
|
|
97092
|
+
function ScaledSlidePreviewImpl({
|
|
96976
97093
|
slide,
|
|
96977
97094
|
templateElements,
|
|
96978
97095
|
canvasSize,
|
|
@@ -97119,6 +97236,40 @@ function ScaledSlidePreview({
|
|
|
97119
97236
|
}
|
|
97120
97237
|
);
|
|
97121
97238
|
}
|
|
97239
|
+
function arePropsEqual4(prev, next) {
|
|
97240
|
+
if (prev.slide.id !== next.slide.id) {
|
|
97241
|
+
return false;
|
|
97242
|
+
}
|
|
97243
|
+
if (prev.slide.isDirty !== next.slide.isDirty) {
|
|
97244
|
+
return false;
|
|
97245
|
+
}
|
|
97246
|
+
if (prev.slide.hidden !== next.slide.hidden) {
|
|
97247
|
+
return false;
|
|
97248
|
+
}
|
|
97249
|
+
if (prev.slide.elements !== next.slide.elements) {
|
|
97250
|
+
return false;
|
|
97251
|
+
}
|
|
97252
|
+
if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
|
|
97253
|
+
return false;
|
|
97254
|
+
}
|
|
97255
|
+
if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
|
|
97256
|
+
return false;
|
|
97257
|
+
}
|
|
97258
|
+
if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
|
|
97259
|
+
return false;
|
|
97260
|
+
}
|
|
97261
|
+
if (prev.templateElements !== next.templateElements) {
|
|
97262
|
+
return false;
|
|
97263
|
+
}
|
|
97264
|
+
if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
|
|
97265
|
+
return false;
|
|
97266
|
+
}
|
|
97267
|
+
if (prev.className !== next.className) {
|
|
97268
|
+
return false;
|
|
97269
|
+
}
|
|
97270
|
+
return true;
|
|
97271
|
+
}
|
|
97272
|
+
var ScaledSlidePreview = React10__namespace.default.memo(ScaledSlidePreviewImpl, arePropsEqual4);
|
|
97122
97273
|
function PresenterView({
|
|
97123
97274
|
slides,
|
|
97124
97275
|
currentSlideIndex,
|
|
@@ -111422,6 +111573,13 @@ function convertOmmlToLatex(omml) {
|
|
|
111422
111573
|
}
|
|
111423
111574
|
return ommlChildrenToLatex(oMath);
|
|
111424
111575
|
}
|
|
111576
|
+
function sanitizeMathMl2(markup) {
|
|
111577
|
+
const purify = DOMPurify__default.default;
|
|
111578
|
+
if (typeof purify.sanitize !== "function") {
|
|
111579
|
+
return markup;
|
|
111580
|
+
}
|
|
111581
|
+
return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
|
|
111582
|
+
}
|
|
111425
111583
|
var TEMPLATES = [
|
|
111426
111584
|
{
|
|
111427
111585
|
label: "Fraction",
|
|
@@ -111487,7 +111645,8 @@ var TEMPLATES = [
|
|
|
111487
111645
|
var TEMPLATE_MATHML = TEMPLATES.map((tmpl) => {
|
|
111488
111646
|
try {
|
|
111489
111647
|
const tmplOmml = convertLatexToOmml(tmpl.latex);
|
|
111490
|
-
|
|
111648
|
+
const raw = convertOmmlToMathMl(tmplOmml);
|
|
111649
|
+
return raw ? sanitizeMathMl2(raw) : "";
|
|
111491
111650
|
} catch {
|
|
111492
111651
|
return "";
|
|
111493
111652
|
}
|
|
@@ -111499,7 +111658,7 @@ function MathMlPreview({ mathml }) {
|
|
|
111499
111658
|
return;
|
|
111500
111659
|
}
|
|
111501
111660
|
if (mathml) {
|
|
111502
|
-
containerRef.current.innerHTML = mathml;
|
|
111661
|
+
containerRef.current.innerHTML = sanitizeMathMl2(mathml);
|
|
111503
111662
|
} else {
|
|
111504
111663
|
containerRef.current.innerHTML = "";
|
|
111505
111664
|
}
|
|
@@ -111527,6 +111686,7 @@ function EquationEditorDialog({
|
|
|
111527
111686
|
return convertOmmlToLatex(existingOmml);
|
|
111528
111687
|
}, [existingOmml]);
|
|
111529
111688
|
const [latex, setLatex] = React10.useState(initialLatex);
|
|
111689
|
+
const deferredLatex = React10.useDeferredValue(latex);
|
|
111530
111690
|
const textareaRef = React10.useRef(null);
|
|
111531
111691
|
React10.useEffect(() => {
|
|
111532
111692
|
if (isOpen) {
|
|
@@ -111535,17 +111695,17 @@ function EquationEditorDialog({
|
|
|
111535
111695
|
}
|
|
111536
111696
|
}, [isOpen, initialLatex]);
|
|
111537
111697
|
const { mathml, omml } = React10.useMemo(() => {
|
|
111538
|
-
if (!
|
|
111698
|
+
if (!deferredLatex.trim()) {
|
|
111539
111699
|
return { mathml: "", omml: {} };
|
|
111540
111700
|
}
|
|
111541
111701
|
try {
|
|
111542
|
-
const ommlObj = convertLatexToOmml(
|
|
111702
|
+
const ommlObj = convertLatexToOmml(deferredLatex);
|
|
111543
111703
|
const mathmlStr = convertOmmlToMathMl(ommlObj);
|
|
111544
111704
|
return { mathml: mathmlStr, omml: ommlObj };
|
|
111545
111705
|
} catch {
|
|
111546
111706
|
return { mathml: "", omml: {} };
|
|
111547
111707
|
}
|
|
111548
|
-
}, [
|
|
111708
|
+
}, [deferredLatex]);
|
|
111549
111709
|
const handleInsert = React10.useCallback(() => {
|
|
111550
111710
|
if (!latex.trim()) {
|
|
111551
111711
|
return;
|
|
@@ -114309,6 +114469,7 @@ function useEditorHistory(input) {
|
|
|
114309
114469
|
const historyFutureRef = React10.useRef([]);
|
|
114310
114470
|
const lastHistorySnapshotRef = React10.useRef(null);
|
|
114311
114471
|
const lastHistorySerializedRef = React10.useRef("");
|
|
114472
|
+
const lastCheapHashRef = React10.useRef("");
|
|
114312
114473
|
const isApplyingHistoryRef = React10.useRef(false);
|
|
114313
114474
|
const unlockHistoryTimerRef = React10.useRef(null);
|
|
114314
114475
|
const [canUndo, setCanUndo] = React10.useState(false);
|
|
@@ -114450,15 +114611,21 @@ function useEditorHistory(input) {
|
|
|
114450
114611
|
if (hasActivePointerInteraction()) {
|
|
114451
114612
|
return;
|
|
114452
114613
|
}
|
|
114614
|
+
const cheapHash = `${slides.length}|${activeSlideIndex}|${canvasSize.width}x${canvasSize.height}|${slides.map((s) => `${s.id}:${s.elements.length}`).join("/")}`;
|
|
114615
|
+
if (cheapHash === lastCheapHashRef.current) {
|
|
114616
|
+
return;
|
|
114617
|
+
}
|
|
114453
114618
|
const snapshot2 = buildHistorySnapshot();
|
|
114454
114619
|
const serialized = JSON.stringify(snapshot2);
|
|
114455
114620
|
if (serialized === lastHistorySerializedRef.current) {
|
|
114621
|
+
lastCheapHashRef.current = cheapHash;
|
|
114456
114622
|
return;
|
|
114457
114623
|
}
|
|
114458
114624
|
const previousSnapshot = lastHistorySnapshotRef.current;
|
|
114459
114625
|
if (!previousSnapshot) {
|
|
114460
114626
|
lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
|
|
114461
114627
|
lastHistorySerializedRef.current = serialized;
|
|
114628
|
+
lastCheapHashRef.current = cheapHash;
|
|
114462
114629
|
updateHistoryAvailability();
|
|
114463
114630
|
return;
|
|
114464
114631
|
}
|
|
@@ -114469,13 +114636,18 @@ function useEditorHistory(input) {
|
|
|
114469
114636
|
historyFutureRef.current = [];
|
|
114470
114637
|
lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
|
|
114471
114638
|
lastHistorySerializedRef.current = serialized;
|
|
114639
|
+
lastCheapHashRef.current = cheapHash;
|
|
114472
114640
|
updateHistoryAvailability();
|
|
114473
114641
|
}, [
|
|
114642
|
+
activeSlideIndex,
|
|
114474
114643
|
buildHistorySnapshot,
|
|
114644
|
+
canvasSize.height,
|
|
114645
|
+
canvasSize.width,
|
|
114475
114646
|
error2,
|
|
114476
114647
|
hasActivePointerInteraction,
|
|
114477
114648
|
loading2,
|
|
114478
114649
|
pointerCommitNonce,
|
|
114650
|
+
slides,
|
|
114479
114651
|
updateHistoryAvailability
|
|
114480
114652
|
]);
|
|
114481
114653
|
return {
|
|
@@ -118168,6 +118340,7 @@ var DB_NAME2 = "pptx-viewer-audience";
|
|
|
118168
118340
|
var DB_VERSION2 = 1;
|
|
118169
118341
|
var STORE_NAME2 = "content";
|
|
118170
118342
|
var CONTENT_KEY = "pptx-bytes";
|
|
118343
|
+
var MAX_CONTENT_AGE_MS = 5 * 60 * 1e3;
|
|
118171
118344
|
function openDb() {
|
|
118172
118345
|
return new Promise((resolve2, reject) => {
|
|
118173
118346
|
const request = indexedDB.open(DB_NAME2, DB_VERSION2);
|
|
@@ -118187,7 +118360,8 @@ async function storeAudienceContent(content) {
|
|
|
118187
118360
|
const tx = db.transaction(STORE_NAME2, "readwrite");
|
|
118188
118361
|
const store = tx.objectStore(STORE_NAME2);
|
|
118189
118362
|
const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
|
|
118190
|
-
|
|
118363
|
+
const record = { bytes, createdAt: Date.now() };
|
|
118364
|
+
store.put(record, CONTENT_KEY);
|
|
118191
118365
|
tx.oncomplete = () => {
|
|
118192
118366
|
db.close();
|
|
118193
118367
|
resolve2();
|
|
@@ -118208,13 +118382,24 @@ async function loadAudienceContent() {
|
|
|
118208
118382
|
request.onsuccess = () => {
|
|
118209
118383
|
db.close();
|
|
118210
118384
|
const result = request.result;
|
|
118211
|
-
if (result
|
|
118212
|
-
|
|
118213
|
-
|
|
118214
|
-
|
|
118215
|
-
|
|
118216
|
-
|
|
118385
|
+
if (result && typeof result === "object" && "bytes" in result && "createdAt" in result) {
|
|
118386
|
+
const record = result;
|
|
118387
|
+
const age = Date.now() - record.createdAt;
|
|
118388
|
+
if (age > MAX_CONTENT_AGE_MS) {
|
|
118389
|
+
resolve2(null);
|
|
118390
|
+
return;
|
|
118391
|
+
}
|
|
118392
|
+
const raw = record.bytes;
|
|
118393
|
+
if (raw instanceof Uint8Array) {
|
|
118394
|
+
resolve2(raw);
|
|
118395
|
+
} else if (raw instanceof ArrayBuffer) {
|
|
118396
|
+
resolve2(new Uint8Array(raw));
|
|
118397
|
+
} else {
|
|
118398
|
+
resolve2(null);
|
|
118399
|
+
}
|
|
118400
|
+
return;
|
|
118217
118401
|
}
|
|
118402
|
+
resolve2(null);
|
|
118218
118403
|
};
|
|
118219
118404
|
request.onerror = () => {
|
|
118220
118405
|
db.close();
|
|
@@ -118247,14 +118432,34 @@ async function clearAudienceContent() {
|
|
|
118247
118432
|
var PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
|
|
118248
118433
|
var AUDIENCE_HASH = "#pptx-audience";
|
|
118249
118434
|
var PRESENTER_MSG_ORIGIN = "pptx-viewer-presenter";
|
|
118435
|
+
var AUDIENCE_NONCE_KEY = "nonce";
|
|
118436
|
+
function generateSessionId() {
|
|
118437
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
118438
|
+
return crypto.randomUUID();
|
|
118439
|
+
}
|
|
118440
|
+
return `s${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
118441
|
+
}
|
|
118442
|
+
function parseAudienceNonce() {
|
|
118443
|
+
const hash = window.location.hash;
|
|
118444
|
+
if (!hash.startsWith(AUDIENCE_HASH)) {
|
|
118445
|
+
return null;
|
|
118446
|
+
}
|
|
118447
|
+
const trailing = hash.slice(AUDIENCE_HASH.length);
|
|
118448
|
+
if (!trailing) {
|
|
118449
|
+
return null;
|
|
118450
|
+
}
|
|
118451
|
+
const params2 = new URLSearchParams(trailing.replace(/^[&;?]/, ""));
|
|
118452
|
+
return params2.get(AUDIENCE_NONCE_KEY);
|
|
118453
|
+
}
|
|
118250
118454
|
function isAudienceTab() {
|
|
118251
|
-
return window.location.hash
|
|
118455
|
+
return window.location.hash.startsWith(AUDIENCE_HASH);
|
|
118252
118456
|
}
|
|
118253
118457
|
function usePresenterWindow(input) {
|
|
118254
118458
|
const { currentSlideIndex, isPresenterMode, content } = input;
|
|
118255
118459
|
const audienceWindowRef = React10.useRef(null);
|
|
118256
118460
|
const channelRef = React10.useRef(null);
|
|
118257
118461
|
const pollTimerRef = React10.useRef(null);
|
|
118462
|
+
const sessionIdRef = React10.useRef("");
|
|
118258
118463
|
const getChannel2 = React10.useCallback(() => {
|
|
118259
118464
|
if (!channelRef.current) {
|
|
118260
118465
|
channelRef.current = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
|
|
@@ -118266,10 +118471,14 @@ function usePresenterWindow(input) {
|
|
|
118266
118471
|
}, []);
|
|
118267
118472
|
const syncSlideToAudience = React10.useCallback(
|
|
118268
118473
|
(slideIndex) => {
|
|
118474
|
+
if (!sessionIdRef.current) {
|
|
118475
|
+
return;
|
|
118476
|
+
}
|
|
118269
118477
|
const msg = {
|
|
118270
118478
|
origin: PRESENTER_MSG_ORIGIN,
|
|
118271
118479
|
type: "presenter-slide-change",
|
|
118272
|
-
slideIndex
|
|
118480
|
+
slideIndex,
|
|
118481
|
+
sessionId: sessionIdRef.current
|
|
118273
118482
|
};
|
|
118274
118483
|
try {
|
|
118275
118484
|
getChannel2().postMessage(msg);
|
|
@@ -118279,13 +118488,16 @@ function usePresenterWindow(input) {
|
|
|
118279
118488
|
[getChannel2]
|
|
118280
118489
|
);
|
|
118281
118490
|
const closeAudienceWindow = React10.useCallback(() => {
|
|
118282
|
-
|
|
118283
|
-
|
|
118284
|
-
|
|
118285
|
-
|
|
118286
|
-
|
|
118287
|
-
|
|
118288
|
-
|
|
118491
|
+
if (sessionIdRef.current) {
|
|
118492
|
+
try {
|
|
118493
|
+
const exitMsg = {
|
|
118494
|
+
origin: PRESENTER_MSG_ORIGIN,
|
|
118495
|
+
type: "presenter-exit",
|
|
118496
|
+
sessionId: sessionIdRef.current
|
|
118497
|
+
};
|
|
118498
|
+
getChannel2().postMessage(exitMsg);
|
|
118499
|
+
} catch {
|
|
118500
|
+
}
|
|
118289
118501
|
}
|
|
118290
118502
|
const win = audienceWindowRef.current;
|
|
118291
118503
|
if (win && !win.closed) {
|
|
@@ -118295,6 +118507,7 @@ function usePresenterWindow(input) {
|
|
|
118295
118507
|
}
|
|
118296
118508
|
}
|
|
118297
118509
|
audienceWindowRef.current = null;
|
|
118510
|
+
sessionIdRef.current = "";
|
|
118298
118511
|
if (pollTimerRef.current !== null) {
|
|
118299
118512
|
clearInterval(pollTimerRef.current);
|
|
118300
118513
|
pollTimerRef.current = null;
|
|
@@ -118305,20 +118518,53 @@ function usePresenterWindow(input) {
|
|
|
118305
118518
|
if (isAudienceWindowOpen()) {
|
|
118306
118519
|
closeAudienceWindow();
|
|
118307
118520
|
}
|
|
118308
|
-
|
|
118309
|
-
|
|
118310
|
-
}
|
|
118311
|
-
const url = new URL(window.location.href);
|
|
118312
|
-
url.hash = AUDIENCE_HASH;
|
|
118313
|
-
const win = window.open(url.toString(), "_blank");
|
|
118314
|
-
if (!win) {
|
|
118521
|
+
const blankWin = window.open("about:blank", "_blank");
|
|
118522
|
+
if (!blankWin) {
|
|
118315
118523
|
return false;
|
|
118316
118524
|
}
|
|
118317
|
-
audienceWindowRef.current =
|
|
118525
|
+
audienceWindowRef.current = blankWin;
|
|
118526
|
+
const sessionId = generateSessionId();
|
|
118527
|
+
sessionIdRef.current = sessionId;
|
|
118528
|
+
const audienceUrl = new URL(window.location.href);
|
|
118529
|
+
const params2 = new URLSearchParams();
|
|
118530
|
+
params2.set(AUDIENCE_NONCE_KEY, sessionId);
|
|
118531
|
+
audienceUrl.hash = `${AUDIENCE_HASH}&${params2.toString()}`;
|
|
118532
|
+
const navigateOrClose = (ok) => {
|
|
118533
|
+
const win = audienceWindowRef.current;
|
|
118534
|
+
if (!win || win.closed) {
|
|
118535
|
+
return;
|
|
118536
|
+
}
|
|
118537
|
+
if (!ok) {
|
|
118538
|
+
try {
|
|
118539
|
+
win.close();
|
|
118540
|
+
} catch {
|
|
118541
|
+
}
|
|
118542
|
+
audienceWindowRef.current = null;
|
|
118543
|
+
sessionIdRef.current = "";
|
|
118544
|
+
return;
|
|
118545
|
+
}
|
|
118546
|
+
try {
|
|
118547
|
+
win.location.replace(audienceUrl.toString());
|
|
118548
|
+
} catch {
|
|
118549
|
+
try {
|
|
118550
|
+
win.close();
|
|
118551
|
+
} catch {
|
|
118552
|
+
}
|
|
118553
|
+
audienceWindowRef.current = null;
|
|
118554
|
+
sessionIdRef.current = "";
|
|
118555
|
+
}
|
|
118556
|
+
};
|
|
118557
|
+
if (content) {
|
|
118558
|
+
void storeAudienceContent(content).then(() => navigateOrClose(true)).catch(() => navigateOrClose(false));
|
|
118559
|
+
} else {
|
|
118560
|
+
navigateOrClose(true);
|
|
118561
|
+
}
|
|
118318
118562
|
window.setTimeout(() => syncSlideToAudience(currentSlideIndex), 1500);
|
|
118319
118563
|
pollTimerRef.current = setInterval(() => {
|
|
118320
|
-
|
|
118564
|
+
const win = audienceWindowRef.current;
|
|
118565
|
+
if (!win || win.closed) {
|
|
118321
118566
|
audienceWindowRef.current = null;
|
|
118567
|
+
sessionIdRef.current = "";
|
|
118322
118568
|
if (pollTimerRef.current !== null) {
|
|
118323
118569
|
clearInterval(pollTimerRef.current);
|
|
118324
118570
|
pollTimerRef.current = null;
|
|
@@ -118346,6 +118592,13 @@ function usePresenterWindow(input) {
|
|
|
118346
118592
|
closeAudienceWindow();
|
|
118347
118593
|
}
|
|
118348
118594
|
}, [isPresenterMode, closeAudienceWindow]);
|
|
118595
|
+
React10.useEffect(() => {
|
|
118596
|
+
const handleBeforeUnload = () => {
|
|
118597
|
+
void clearAudienceContent();
|
|
118598
|
+
};
|
|
118599
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
118600
|
+
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
118601
|
+
}, []);
|
|
118349
118602
|
return {
|
|
118350
118603
|
openAudienceWindow,
|
|
118351
118604
|
closeAudienceWindow,
|
|
@@ -118383,6 +118636,7 @@ function useAudienceMode(input) {
|
|
|
118383
118636
|
if (!isAudienceTab()) {
|
|
118384
118637
|
return;
|
|
118385
118638
|
}
|
|
118639
|
+
const expectedSessionId = parseAudienceNonce();
|
|
118386
118640
|
let channel;
|
|
118387
118641
|
try {
|
|
118388
118642
|
channel = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
|
|
@@ -118394,6 +118648,9 @@ function useAudienceMode(input) {
|
|
|
118394
118648
|
if (!data || data.origin !== PRESENTER_MSG_ORIGIN) {
|
|
118395
118649
|
return;
|
|
118396
118650
|
}
|
|
118651
|
+
if (expectedSessionId && data.sessionId !== expectedSessionId) {
|
|
118652
|
+
return;
|
|
118653
|
+
}
|
|
118397
118654
|
if (data.type === "presenter-slide-change") {
|
|
118398
118655
|
onSetActiveSlideIndex(data.slideIndex);
|
|
118399
118656
|
}
|
|
@@ -119327,6 +119584,12 @@ function useTouchGestures(input) {
|
|
|
119327
119584
|
callbacksRef.current = callbacks;
|
|
119328
119585
|
const scaleRef = React10.useRef(currentScale);
|
|
119329
119586
|
scaleRef.current = currentScale;
|
|
119587
|
+
const [targetVersion, setTargetVersion] = React10.useState(0);
|
|
119588
|
+
const lastTargetRef = React10.useRef(null);
|
|
119589
|
+
if (targetRef.current !== lastTargetRef.current) {
|
|
119590
|
+
lastTargetRef.current = targetRef.current;
|
|
119591
|
+
queueMicrotask(() => setTargetVersion((v) => v + 1));
|
|
119592
|
+
}
|
|
119330
119593
|
React10.useEffect(() => {
|
|
119331
119594
|
const el = targetRef.current;
|
|
119332
119595
|
if (!el || !enabled) {
|
|
@@ -119415,7 +119678,7 @@ function useTouchGestures(input) {
|
|
|
119415
119678
|
el.removeEventListener("touchcancel", handleTouchCancel);
|
|
119416
119679
|
cancelLongPress();
|
|
119417
119680
|
};
|
|
119418
|
-
}, [targetRef, enabled]);
|
|
119681
|
+
}, [targetRef, enabled, targetVersion]);
|
|
119419
119682
|
}
|
|
119420
119683
|
|
|
119421
119684
|
// src/viewer/utils/dom-helpers.ts
|
|
@@ -119436,11 +119699,31 @@ function safeConfirm(message) {
|
|
|
119436
119699
|
return false;
|
|
119437
119700
|
}
|
|
119438
119701
|
}
|
|
119702
|
+
function sanitizeDownloadFilename(input) {
|
|
119703
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
119704
|
+
return "presentation.pptx";
|
|
119705
|
+
}
|
|
119706
|
+
let cleaned = input.replace(/[\x00-\x1f\x7f"\\/:*?<>|]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, "").trim();
|
|
119707
|
+
if (cleaned.length === 0) {
|
|
119708
|
+
return "presentation.pptx";
|
|
119709
|
+
}
|
|
119710
|
+
if (cleaned.length > 200) {
|
|
119711
|
+
const dot = cleaned.lastIndexOf(".");
|
|
119712
|
+
if (dot > 0 && cleaned.length - dot <= 16) {
|
|
119713
|
+
const ext = cleaned.slice(dot);
|
|
119714
|
+
cleaned = cleaned.slice(0, 200 - ext.length) + ext;
|
|
119715
|
+
} else {
|
|
119716
|
+
cleaned = cleaned.slice(0, 200);
|
|
119717
|
+
}
|
|
119718
|
+
}
|
|
119719
|
+
return cleaned;
|
|
119720
|
+
}
|
|
119439
119721
|
function downloadBlob(blob, filename) {
|
|
119722
|
+
const safeName = sanitizeDownloadFilename(filename);
|
|
119440
119723
|
const url = URL.createObjectURL(blob);
|
|
119441
119724
|
const a2 = document.createElement("a");
|
|
119442
119725
|
a2.href = url;
|
|
119443
|
-
a2.download =
|
|
119726
|
+
a2.download = safeName;
|
|
119444
119727
|
document.body.appendChild(a2);
|
|
119445
119728
|
a2.click();
|
|
119446
119729
|
setTimeout(() => {
|
|
@@ -119871,27 +120154,82 @@ function openAutosaveDb2() {
|
|
|
119871
120154
|
req.onerror = () => reject(req.error);
|
|
119872
120155
|
});
|
|
119873
120156
|
}
|
|
119874
|
-
async function
|
|
120157
|
+
async function deleteOldestAutosaveEntry() {
|
|
119875
120158
|
const db = await openAutosaveDb2();
|
|
119876
|
-
return new Promise((resolve2
|
|
119877
|
-
|
|
119878
|
-
|
|
119879
|
-
|
|
119880
|
-
|
|
119881
|
-
|
|
119882
|
-
|
|
119883
|
-
|
|
119884
|
-
|
|
119885
|
-
|
|
119886
|
-
|
|
119887
|
-
|
|
119888
|
-
|
|
119889
|
-
|
|
119890
|
-
|
|
119891
|
-
|
|
119892
|
-
|
|
120159
|
+
return new Promise((resolve2) => {
|
|
120160
|
+
try {
|
|
120161
|
+
const tx = db.transaction(STORE_NAME3, "readwrite");
|
|
120162
|
+
const store = tx.objectStore(STORE_NAME3);
|
|
120163
|
+
let oldestKey = null;
|
|
120164
|
+
let oldestTimestamp = Infinity;
|
|
120165
|
+
const cursorReq = store.openCursor();
|
|
120166
|
+
cursorReq.onsuccess = () => {
|
|
120167
|
+
const cursor = cursorReq.result;
|
|
120168
|
+
if (cursor) {
|
|
120169
|
+
const value = cursor.value;
|
|
120170
|
+
if (typeof value.timestamp === "number" && value.timestamp < oldestTimestamp) {
|
|
120171
|
+
oldestTimestamp = value.timestamp;
|
|
120172
|
+
oldestKey = cursor.primaryKey;
|
|
120173
|
+
}
|
|
120174
|
+
cursor.continue();
|
|
120175
|
+
} else if (oldestKey !== null) {
|
|
120176
|
+
store.delete(oldestKey);
|
|
120177
|
+
}
|
|
120178
|
+
};
|
|
120179
|
+
tx.oncomplete = () => {
|
|
120180
|
+
db.close();
|
|
120181
|
+
resolve2(oldestKey !== null);
|
|
120182
|
+
};
|
|
120183
|
+
tx.onerror = () => {
|
|
120184
|
+
db.close();
|
|
120185
|
+
resolve2(false);
|
|
120186
|
+
};
|
|
120187
|
+
} catch {
|
|
120188
|
+
try {
|
|
120189
|
+
db.close();
|
|
120190
|
+
} catch {
|
|
120191
|
+
}
|
|
120192
|
+
resolve2(false);
|
|
120193
|
+
}
|
|
119893
120194
|
});
|
|
119894
120195
|
}
|
|
120196
|
+
function putAutosaveRecord(filePath, data) {
|
|
120197
|
+
return openAutosaveDb2().then(
|
|
120198
|
+
(db) => new Promise((resolve2, reject) => {
|
|
120199
|
+
const tx = db.transaction(STORE_NAME3, "readwrite");
|
|
120200
|
+
const store = tx.objectStore(STORE_NAME3);
|
|
120201
|
+
store.put({
|
|
120202
|
+
key: filePath,
|
|
120203
|
+
data,
|
|
120204
|
+
timestamp: Date.now(),
|
|
120205
|
+
size: data.byteLength
|
|
120206
|
+
});
|
|
120207
|
+
tx.oncomplete = () => {
|
|
120208
|
+
db.close();
|
|
120209
|
+
resolve2(true);
|
|
120210
|
+
};
|
|
120211
|
+
tx.onerror = () => {
|
|
120212
|
+
db.close();
|
|
120213
|
+
reject(tx.error);
|
|
120214
|
+
};
|
|
120215
|
+
})
|
|
120216
|
+
);
|
|
120217
|
+
}
|
|
120218
|
+
async function saveToIndexedDb(filePath, data) {
|
|
120219
|
+
try {
|
|
120220
|
+
return await putAutosaveRecord(filePath, data);
|
|
120221
|
+
} catch (err) {
|
|
120222
|
+
const errName = err instanceof Error || err instanceof DOMException ? err.name : "";
|
|
120223
|
+
if (errName !== "QuotaExceededError") {
|
|
120224
|
+
throw err;
|
|
120225
|
+
}
|
|
120226
|
+
const deleted = await deleteOldestAutosaveEntry();
|
|
120227
|
+
if (!deleted) {
|
|
120228
|
+
throw err;
|
|
120229
|
+
}
|
|
120230
|
+
return putAutosaveRecord(filePath, data);
|
|
120231
|
+
}
|
|
120232
|
+
}
|
|
119895
120233
|
function useAutosave(input) {
|
|
119896
120234
|
const {
|
|
119897
120235
|
isDirty,
|
|
@@ -120008,6 +120346,25 @@ function collectReferencedFontFamilies(slides) {
|
|
|
120008
120346
|
}
|
|
120009
120347
|
return families;
|
|
120010
120348
|
}
|
|
120349
|
+
var FONT_NAME_UNSAFE_CHARS = /["\\\n\r;}<>]/;
|
|
120350
|
+
var FONT_FORMAT_ALLOWED = /* @__PURE__ */ new Set([
|
|
120351
|
+
"truetype",
|
|
120352
|
+
"opentype",
|
|
120353
|
+
"woff",
|
|
120354
|
+
"woff2",
|
|
120355
|
+
"svg",
|
|
120356
|
+
"embedded-opentype"
|
|
120357
|
+
]);
|
|
120358
|
+
var FONT_DATA_URL_PATTERN = /^data:font\/[a-z0-9+.-]+(?:;charset=[a-z0-9-]+)?;base64,[A-Za-z0-9+/=]+$/i;
|
|
120359
|
+
function isFontDataUrlSafe(url) {
|
|
120360
|
+
if (typeof url !== "string" || url.length === 0) {
|
|
120361
|
+
return false;
|
|
120362
|
+
}
|
|
120363
|
+
if (url.startsWith("blob:")) {
|
|
120364
|
+
return true;
|
|
120365
|
+
}
|
|
120366
|
+
return FONT_DATA_URL_PATTERN.test(url);
|
|
120367
|
+
}
|
|
120011
120368
|
function useFontInjection({ embeddedFonts, slides }) {
|
|
120012
120369
|
React10.useEffect(() => {
|
|
120013
120370
|
if (!embeddedFonts.length) {
|
|
@@ -120015,17 +120372,28 @@ function useFontInjection({ embeddedFonts, slides }) {
|
|
|
120015
120372
|
}
|
|
120016
120373
|
const styleEl = document.createElement("style");
|
|
120017
120374
|
styleEl.id = EMBEDDED_FONTS_STYLE_ID;
|
|
120018
|
-
const cssRules = embeddedFonts.
|
|
120375
|
+
const cssRules = embeddedFonts.flatMap((font) => {
|
|
120376
|
+
if (typeof font.name !== "string" || font.name.length === 0 || FONT_NAME_UNSAFE_CHARS.test(font.name)) {
|
|
120377
|
+
return [];
|
|
120378
|
+
}
|
|
120379
|
+
if (!isFontDataUrlSafe(font.dataUrl)) {
|
|
120380
|
+
return [];
|
|
120381
|
+
}
|
|
120382
|
+
const fontFormat = font.format ?? "truetype";
|
|
120383
|
+
if (!FONT_FORMAT_ALLOWED.has(fontFormat)) {
|
|
120384
|
+
return [];
|
|
120385
|
+
}
|
|
120019
120386
|
const fontWeight = font.bold ? "700" : "400";
|
|
120020
120387
|
const fontStyleCss = font.italic ? "italic" : "normal";
|
|
120021
|
-
|
|
120022
|
-
|
|
120388
|
+
return [
|
|
120389
|
+
`@font-face {
|
|
120023
120390
|
font-family: "${font.name}";
|
|
120024
120391
|
src: url("${font.dataUrl}") format("${fontFormat}");
|
|
120025
120392
|
font-weight: ${fontWeight};
|
|
120026
120393
|
font-style: ${fontStyleCss};
|
|
120027
120394
|
font-display: swap;
|
|
120028
|
-
}
|
|
120395
|
+
}`
|
|
120396
|
+
];
|
|
120029
120397
|
}).join("\n");
|
|
120030
120398
|
styleEl.textContent = cssRules;
|
|
120031
120399
|
document.head.appendChild(styleEl);
|
|
@@ -120054,7 +120422,7 @@ function useFontInjection({ embeddedFonts, slides }) {
|
|
|
120054
120422
|
const linkEl = document.createElement("link");
|
|
120055
120423
|
linkEl.id = GOOGLE_FONTS_LINK_ID;
|
|
120056
120424
|
linkEl.rel = "stylesheet";
|
|
120057
|
-
linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${GOOGLE_FONTS_AVAILABLE[f]}`).join("&")}&display=swap`;
|
|
120425
|
+
linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${encodeURIComponent(GOOGLE_FONTS_AVAILABLE[f])}`).join("&")}&display=swap`;
|
|
120058
120426
|
document.head.appendChild(linkEl);
|
|
120059
120427
|
return () => {
|
|
120060
120428
|
const existing = document.getElementById(GOOGLE_FONTS_LINK_ID);
|
|
@@ -120070,13 +120438,18 @@ function useFontInjection({ embeddedFonts, slides }) {
|
|
|
120070
120438
|
}
|
|
120071
120439
|
const styleEl = document.createElement("style");
|
|
120072
120440
|
styleEl.id = SYMBOL_FONTS_STYLE_ID;
|
|
120073
|
-
const rules = neededSymbolFonts.
|
|
120074
|
-
(font
|
|
120441
|
+
const rules = neededSymbolFonts.flatMap((font) => {
|
|
120442
|
+
if (typeof font !== "string" || FONT_NAME_UNSAFE_CHARS.test(font)) {
|
|
120443
|
+
return [];
|
|
120444
|
+
}
|
|
120445
|
+
return [
|
|
120446
|
+
`@font-face {
|
|
120075
120447
|
font-family: "${font}";
|
|
120076
120448
|
src: local("${font}"), local("${font} Regular");
|
|
120077
120449
|
font-display: swap;
|
|
120078
120450
|
}`
|
|
120079
|
-
|
|
120451
|
+
];
|
|
120452
|
+
}).join("\n");
|
|
120080
120453
|
styleEl.textContent = rules;
|
|
120081
120454
|
document.head.appendChild(styleEl);
|
|
120082
120455
|
return () => {
|
|
@@ -120219,16 +120592,17 @@ function useLoadContent({
|
|
|
120219
120592
|
`[pptx] Large file detected (${fileSizeMB.toFixed(1)} MB). Loading may use significant memory.`
|
|
120220
120593
|
);
|
|
120221
120594
|
}
|
|
120222
|
-
|
|
120223
|
-
handlerRef.current.dispose();
|
|
120224
|
-
handlerRef.current = null;
|
|
120225
|
-
}
|
|
120595
|
+
const previousHandler = handlerRef.current;
|
|
120226
120596
|
const handler = new pptxViewerCore.PptxHandler();
|
|
120227
120597
|
const parsed = await handler.load(buffer);
|
|
120228
120598
|
if (cancelled || token !== renderTokenRef.current) {
|
|
120229
120599
|
handler.dispose();
|
|
120230
120600
|
return;
|
|
120231
120601
|
}
|
|
120602
|
+
if (previousHandler) {
|
|
120603
|
+
previousHandler.dispose();
|
|
120604
|
+
}
|
|
120605
|
+
handlerRef.current = null;
|
|
120232
120606
|
const mediaElements = [];
|
|
120233
120607
|
for (const slide of parsed.slides) {
|
|
120234
120608
|
collectMediaElements(slide.elements, mediaElements);
|
|
@@ -120273,6 +120647,7 @@ function useLoadContent({
|
|
|
120273
120647
|
})
|
|
120274
120648
|
);
|
|
120275
120649
|
const { paths: imagePaths, refs: imageRefs } = collectImagePaths(parsed.slides);
|
|
120650
|
+
let nextSlides = parsed.slides;
|
|
120276
120651
|
if (imagePaths.size > 0) {
|
|
120277
120652
|
const resolvedMap = /* @__PURE__ */ new Map();
|
|
120278
120653
|
await Promise.all(
|
|
@@ -120286,15 +120661,47 @@ function useLoadContent({
|
|
|
120286
120661
|
}
|
|
120287
120662
|
})
|
|
120288
120663
|
);
|
|
120664
|
+
const elementPatches = /* @__PURE__ */ new Map();
|
|
120289
120665
|
for (const ref of imageRefs) {
|
|
120290
120666
|
const url = resolvedMap.get(ref.path);
|
|
120291
|
-
if (url) {
|
|
120292
|
-
|
|
120667
|
+
if (!url) {
|
|
120668
|
+
continue;
|
|
120293
120669
|
}
|
|
120670
|
+
const id2 = ref.element.id;
|
|
120671
|
+
const existing = elementPatches.get(id2) ?? {};
|
|
120672
|
+
existing[ref.field] = url;
|
|
120673
|
+
elementPatches.set(id2, existing);
|
|
120674
|
+
}
|
|
120675
|
+
if (elementPatches.size > 0) {
|
|
120676
|
+
const patchElements = (elements) => {
|
|
120677
|
+
let mutated = false;
|
|
120678
|
+
const next = elements.map((el) => {
|
|
120679
|
+
let updated = el;
|
|
120680
|
+
const patch = elementPatches.get(el.id);
|
|
120681
|
+
if (patch) {
|
|
120682
|
+
updated = { ...el, ...patch };
|
|
120683
|
+
}
|
|
120684
|
+
if (updated.type === "group" && updated.children?.length) {
|
|
120685
|
+
const newChildren = patchElements(updated.children);
|
|
120686
|
+
if (newChildren !== updated.children) {
|
|
120687
|
+
updated = { ...updated, children: newChildren };
|
|
120688
|
+
}
|
|
120689
|
+
}
|
|
120690
|
+
if (updated !== el) {
|
|
120691
|
+
mutated = true;
|
|
120692
|
+
}
|
|
120693
|
+
return updated;
|
|
120694
|
+
});
|
|
120695
|
+
return mutated ? next : elements;
|
|
120696
|
+
};
|
|
120697
|
+
nextSlides = parsed.slides.map((s) => {
|
|
120698
|
+
const newElements = patchElements(s.elements);
|
|
120699
|
+
return newElements === s.elements ? s : { ...s, elements: newElements };
|
|
120700
|
+
});
|
|
120294
120701
|
}
|
|
120295
120702
|
}
|
|
120296
120703
|
handlerRef.current = handler;
|
|
120297
|
-
setSlides(
|
|
120704
|
+
setSlides(nextSlides);
|
|
120298
120705
|
setTemplateElementsBySlideId({});
|
|
120299
120706
|
setCanvasSize({
|
|
120300
120707
|
width: parsed.width ?? DEFAULT_CANVAS_WIDTH,
|
|
@@ -121468,7 +121875,19 @@ function injectFontFaces(svg, fontFaces) {
|
|
|
121468
121875
|
if (!fontFaces.length) {
|
|
121469
121876
|
return svg;
|
|
121470
121877
|
}
|
|
121471
|
-
const
|
|
121878
|
+
const safeFontFaces = fontFaces.filter((f) => {
|
|
121879
|
+
if (f.css.toLowerCase().includes("</style")) {
|
|
121880
|
+
console.warn(
|
|
121881
|
+
`[export-svg] Dropping @font-face entry for "${f.family}" containing "</style" \u2014 would break out of the <style> block.`
|
|
121882
|
+
);
|
|
121883
|
+
return false;
|
|
121884
|
+
}
|
|
121885
|
+
return true;
|
|
121886
|
+
});
|
|
121887
|
+
if (!safeFontFaces.length) {
|
|
121888
|
+
return svg;
|
|
121889
|
+
}
|
|
121890
|
+
const styleBlock = `<style type="text/css">${safeFontFaces.map((f) => f.css).join("\n")}</style>`;
|
|
121472
121891
|
if (svg.includes("<defs>")) {
|
|
121473
121892
|
return svg.replace("<defs>", `<defs>${styleBlock}`);
|
|
121474
121893
|
}
|
|
@@ -121794,7 +122213,7 @@ function useExportHandlers(input) {
|
|
|
121794
122213
|
activeSlideIndexForGuides,
|
|
121795
122214
|
modalControls
|
|
121796
122215
|
});
|
|
121797
|
-
const handleExportPng = async () => {
|
|
122216
|
+
const handleExportPng = React10.useCallback(async () => {
|
|
121798
122217
|
const stageEl = canvasStageRef.current;
|
|
121799
122218
|
if (!stageEl) {
|
|
121800
122219
|
return;
|
|
@@ -121806,8 +122225,8 @@ function useExportHandlers(input) {
|
|
|
121806
122225
|
} catch (err) {
|
|
121807
122226
|
console.error("[PowerPointViewer] PNG export failed:", err);
|
|
121808
122227
|
}
|
|
121809
|
-
};
|
|
121810
|
-
const handleExportPdf = async () => {
|
|
122228
|
+
}, [canvasStageRef, activeSlideIndex, activeSlide?.backgroundColor]);
|
|
122229
|
+
const handleExportPdf = React10.useCallback(async () => {
|
|
121811
122230
|
if (!canvasStageRef.current) {
|
|
121812
122231
|
return;
|
|
121813
122232
|
}
|
|
@@ -121848,8 +122267,8 @@ function useExportHandlers(input) {
|
|
|
121848
122267
|
exportAbortRef.current = null;
|
|
121849
122268
|
setExportModalOpen(false);
|
|
121850
122269
|
}
|
|
121851
|
-
};
|
|
121852
|
-
const handleExportNotesPdf = async () => {
|
|
122270
|
+
}, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
|
|
122271
|
+
const handleExportNotesPdf = React10.useCallback(async () => {
|
|
121853
122272
|
if (!canvasStageRef.current) {
|
|
121854
122273
|
return;
|
|
121855
122274
|
}
|
|
@@ -121892,8 +122311,8 @@ function useExportHandlers(input) {
|
|
|
121892
122311
|
exportAbortRef.current = null;
|
|
121893
122312
|
setExportModalOpen(false);
|
|
121894
122313
|
}
|
|
121895
|
-
};
|
|
121896
|
-
const handleCopySlideAsImage = async () => {
|
|
122314
|
+
}, [canvasStageRef, slides, setActiveSlideIndex, activeSlideIndex]);
|
|
122315
|
+
const handleCopySlideAsImage = React10.useCallback(async () => {
|
|
121897
122316
|
const stageEl = canvasStageRef.current;
|
|
121898
122317
|
if (!stageEl) {
|
|
121899
122318
|
return;
|
|
@@ -121905,8 +122324,8 @@ function useExportHandlers(input) {
|
|
|
121905
122324
|
} catch (err) {
|
|
121906
122325
|
console.error("[PowerPointViewer] Copy slide as image failed:", err);
|
|
121907
122326
|
}
|
|
121908
|
-
};
|
|
121909
|
-
const handleExportVideo = async () => {
|
|
122327
|
+
}, [canvasStageRef, activeSlide?.backgroundColor]);
|
|
122328
|
+
const handleExportVideo = React10.useCallback(async () => {
|
|
121910
122329
|
if (!canvasStageRef.current) {
|
|
121911
122330
|
return;
|
|
121912
122331
|
}
|
|
@@ -121948,8 +122367,8 @@ function useExportHandlers(input) {
|
|
|
121948
122367
|
exportAbortRef.current = null;
|
|
121949
122368
|
setExportModalOpen(false);
|
|
121950
122369
|
}
|
|
121951
|
-
};
|
|
121952
|
-
const handleExportGif = async () => {
|
|
122370
|
+
}, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
|
|
122371
|
+
const handleExportGif = React10.useCallback(async () => {
|
|
121953
122372
|
if (!canvasStageRef.current) {
|
|
121954
122373
|
return;
|
|
121955
122374
|
}
|
|
@@ -121987,7 +122406,7 @@ function useExportHandlers(input) {
|
|
|
121987
122406
|
exportAbortRef.current = null;
|
|
121988
122407
|
setExportModalOpen(false);
|
|
121989
122408
|
}
|
|
121990
|
-
};
|
|
122409
|
+
}, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
|
|
121991
122410
|
const handleCancelExport = React10.useCallback(() => {
|
|
121992
122411
|
exportAbortRef.current?.abort();
|
|
121993
122412
|
exportAbortRef.current = null;
|
|
@@ -122013,6 +122432,15 @@ function useExportHandlers(input) {
|
|
|
122013
122432
|
exportStatusMessage
|
|
122014
122433
|
};
|
|
122015
122434
|
}
|
|
122435
|
+
function escapeHtmlAttr(value) {
|
|
122436
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
122437
|
+
}
|
|
122438
|
+
function safeDataImageSrc(src) {
|
|
122439
|
+
if (typeof src !== "string" || !src.startsWith("data:image/")) {
|
|
122440
|
+
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=";
|
|
122441
|
+
}
|
|
122442
|
+
return escapeHtmlAttr(src);
|
|
122443
|
+
}
|
|
122016
122444
|
function openPrintWindow(title, bodyHtml, orientation, colorFilter, frameSlides) {
|
|
122017
122445
|
const printWindow = window.open("", "_blank", "noopener,noreferrer");
|
|
122018
122446
|
if (!printWindow) {
|
|
@@ -122195,7 +122623,7 @@ function usePrintHandlers(input) {
|
|
|
122195
122623
|
const slideImages = slideIndices.map((idx) => allImages[idx]).filter(Boolean);
|
|
122196
122624
|
if (settings.printWhat === "slides") {
|
|
122197
122625
|
const bodyHtml = slideImages.map(
|
|
122198
|
-
(img, i3) => `<section class="page slide-page"><img class="slide-img" src="${img}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
|
|
122626
|
+
(img, i3) => `<section class="page slide-page"><img class="slide-img" src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
|
|
122199
122627
|
).join("");
|
|
122200
122628
|
openPrintWindow(
|
|
122201
122629
|
"Slides",
|
|
@@ -122211,7 +122639,7 @@ function usePrintHandlers(input) {
|
|
|
122211
122639
|
const idx = slideIndices[i3];
|
|
122212
122640
|
const notes = slides[idx]?.notes?.trim() || "";
|
|
122213
122641
|
return `<section class="page notes-page">
|
|
122214
|
-
<img class="notes-slide" src="${img}" alt="Slide ${idx + 1}" />
|
|
122642
|
+
<img class="notes-slide" src="${safeDataImageSrc(img)}" alt="Slide ${idx + 1}" />
|
|
122215
122643
|
<div class="notes-text">${escapeHtml2(notes)}</div>
|
|
122216
122644
|
</section>`;
|
|
122217
122645
|
}).join("");
|
|
@@ -122243,14 +122671,14 @@ function usePrintHandlers(input) {
|
|
|
122243
122671
|
if (isThreePerPage) {
|
|
122244
122672
|
const rows = Array.from({ length: spp }, (_, cellIndex) => {
|
|
122245
122673
|
const img = pageImgs[cellIndex];
|
|
122246
|
-
const slideCell = img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122674
|
+
const slideCell = img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122247
122675
|
return `<div class="handout-row-3">${slideCell}${buildNoteLines()}</div>`;
|
|
122248
122676
|
}).join("");
|
|
122249
122677
|
pages.push(`<section class="page"><div class="handout-grid-3">${rows}</div></section>`);
|
|
122250
122678
|
} else {
|
|
122251
122679
|
const cells = Array.from({ length: spp }, (_, cellIndex) => {
|
|
122252
122680
|
const img = pageImgs[cellIndex];
|
|
122253
|
-
return img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122681
|
+
return img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122254
122682
|
}).join("");
|
|
122255
122683
|
pages.push(
|
|
122256
122684
|
`<section class="page"><div class="handout-grid" style="grid-template-columns: repeat(${grid.columns}, minmax(0, 1fr)); grid-template-rows: repeat(${grid.rows}, minmax(0, 1fr));">${cells}</div></section>`
|