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/index.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as React10 from 'react';
|
|
2
|
-
import React10__default, { createContext, useState, useEffect, useMemo, Suspense, useCallback, forwardRef, useRef, useSyncExternalStore, useImperativeHandle, useContext, useLayoutEffect } from 'react';
|
|
2
|
+
import React10__default, { createContext, useState, useEffect, useMemo, Suspense, useCallback, forwardRef, useRef, useSyncExternalStore, useImperativeHandle, useContext, useLayoutEffect, useDeferredValue } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import * as ReactDOM from 'react-dom/client';
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
6
|
import { twMerge } from 'tailwind-merge';
|
|
7
7
|
import { LuMessageSquare, LuEyeOff, LuSettings, LuX, LuCast, LuShieldCheck, LuPlus, LuPanelLeftClose, LuStickyNote, LuMonitor, LuColumns2, LuPresentation, LuMinus, LuClock, LuDownload, LuTrash2, LuCheck, LuLock, LuEye, LuFileText, LuType, LuLoader, LuShieldAlert, LuSignature, LuInfo, LuTriangleAlert, LuPrinter, LuPenTool, LuWifi, LuWifiOff, LuUsers, LuCopy, LuMonitorOff, LuChevronLeft, LuChevronRight, LuPlay, LuPause, LuPanelLeft, LuUndo, LuRedo, LuSearch, LuShare2, LuPanelRight, LuFolderOpen, LuVideo, LuImage, LuClipboardPaste, LuScissors, LuPaintbrush, LuChevronDown, LuSquare, LuDatabase, LuLayers, LuAArrowUp, LuAArrowDown, LuRemoveFormatting, LuHighlighter, LuList, LuListOrdered, LuIndentDecrease, LuIndentIncrease, LuChevronUp, LuPalette, LuPencil, LuPaintBucket, LuSparkles, LuCaptions, LuSpellCheck, LuGitCompare, LuPipette, LuCaseSensitive, LuReplace, LuTimer, LuMousePointer2, LuEraser, LuGripVertical, LuUpload, LuBold, LuItalic, LuUnderline, LuStrikethrough, LuLink, LuGrid2X2, LuCopyPlus, LuEllipsis, LuCircle, LuMoveRight, LuTriangle, LuDiamond, LuAlignLeft, LuAlignCenter, LuAlignRight, LuAlignJustify, LuSpline, LuSettings2, LuMove, LuRadio, LuArrowDown, LuArrowUp, LuArrowRight, LuArrowLeft, LuReply, LuRotateCw, LuBookmark } from 'react-icons/lu';
|
|
8
8
|
import { hasShapeProperties, hasTextProperties, SWITCHABLE_LAYOUT_TYPES, isCalloutShape, getCalloutLeaderLineGeometry, buildCalloutLeaderLineSvgPath, getCalloutViewBoxBounds, isInkElement, getLinkedTextBoxSegments, isImageLikeElement, getSubstituteFontFamily, PptxHandler, EncryptedFileError, guidePxToEmu, guideEmuToPx, THEME_COLOR_SCHEME_KEYS, hslToRgb, PRESET_COLOR_MAP, elementActionToPptxAction, mergeShapes, SvgExporter, applyDrawingColorTransforms as applyDrawingColorTransforms$1, getPresetShapeClipPath, svgPathToPolygons, polygonsToSvgPath, EMU_PER_PX as EMU_PER_PX$1, chartDataChangeType, chartDataUpdatePoint, chartDataAddCategory, chartDataRemoveCategory, chartDataAddSeries, chartDataRemoveSeries, getOleObjectTypeLabel, pptxActionToElementAction, hasNonTrivialOverride, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, addSmartArtNodeAsChild, updateSmartArtNodeText, removeSmartArtNode, switchSmartArtLayout } from 'pptx-viewer-core';
|
|
9
|
+
import DOMPurify from 'dompurify';
|
|
9
10
|
import { useTranslation } from 'react-i18next';
|
|
10
11
|
import html2canvasPro from 'html2canvas-pro';
|
|
11
12
|
import JSZip from 'jszip';
|
|
@@ -43570,7 +43571,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
|
|
|
43570
43571
|
return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
|
|
43571
43572
|
}
|
|
43572
43573
|
function useSyncExternalStore$2(subscribe3, getSnapshot2) {
|
|
43573
|
-
didWarnOld18Alpha || void 0 ===
|
|
43574
|
+
didWarnOld18Alpha || void 0 === React100.startTransition || (didWarnOld18Alpha = true, console.error(
|
|
43574
43575
|
"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."
|
|
43575
43576
|
));
|
|
43576
43577
|
var value = getSnapshot2();
|
|
@@ -43580,7 +43581,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
|
|
|
43580
43581
|
"The result of getSnapshot should be cached to avoid an infinite loop"
|
|
43581
43582
|
), didWarnUncachedGetSnapshot = true);
|
|
43582
43583
|
}
|
|
43583
|
-
cachedValue =
|
|
43584
|
+
cachedValue = useState86({
|
|
43584
43585
|
inst: { value, getSnapshot: getSnapshot2 }
|
|
43585
43586
|
});
|
|
43586
43587
|
var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
|
|
@@ -43618,8 +43619,8 @@ var require_use_sync_external_store_shim_development = __commonJS({
|
|
|
43618
43619
|
return getSnapshot2();
|
|
43619
43620
|
}
|
|
43620
43621
|
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
43621
|
-
var
|
|
43622
|
-
exports$1.useSyncExternalStore = void 0 !==
|
|
43622
|
+
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;
|
|
43623
|
+
exports$1.useSyncExternalStore = void 0 !== React100.useSyncExternalStore ? React100.useSyncExternalStore : shim;
|
|
43623
43624
|
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
43624
43625
|
})();
|
|
43625
43626
|
}
|
|
@@ -43642,7 +43643,7 @@ var require_with_selector_development = __commonJS({
|
|
|
43642
43643
|
return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
|
|
43643
43644
|
}
|
|
43644
43645
|
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
43645
|
-
var
|
|
43646
|
+
var React100 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef73 = React100.useRef, useEffect72 = React100.useEffect, useMemo41 = React100.useMemo, useDebugValue = React100.useDebugValue;
|
|
43646
43647
|
exports$1.useSyncExternalStoreWithSelector = function(subscribe3, getSnapshot2, getServerSnapshot2, selector, isEqual) {
|
|
43647
43648
|
var instRef = useRef73(null);
|
|
43648
43649
|
if (null === instRef.current) {
|
|
@@ -74074,6 +74075,13 @@ function hasDistinctScriptFonts(fonts) {
|
|
|
74074
74075
|
}
|
|
74075
74076
|
return Boolean(fonts.eastAsia) && fonts.eastAsia !== base || Boolean(fonts.complexScript) && fonts.complexScript !== base || Boolean(fonts.symbol) && fonts.symbol !== base;
|
|
74076
74077
|
}
|
|
74078
|
+
function sanitizeMathMl(markup) {
|
|
74079
|
+
const purify = DOMPurify;
|
|
74080
|
+
if (typeof purify.sanitize !== "function") {
|
|
74081
|
+
return markup;
|
|
74082
|
+
}
|
|
74083
|
+
return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
|
|
74084
|
+
}
|
|
74077
74085
|
function renderScriptAwareText(text2, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix) {
|
|
74078
74086
|
if (!needsScriptFonts || !text2) {
|
|
74079
74087
|
return text2;
|
|
@@ -74164,14 +74172,15 @@ function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsSc
|
|
|
74164
74172
|
}
|
|
74165
74173
|
function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber) {
|
|
74166
74174
|
const mathml = convertOmmlToMathMl(equationXml);
|
|
74167
|
-
const
|
|
74175
|
+
const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
|
|
74176
|
+
const equationContent = safeMathml ? /* @__PURE__ */ jsx(
|
|
74168
74177
|
"span",
|
|
74169
74178
|
{
|
|
74170
74179
|
className: "inline-block align-middle",
|
|
74171
74180
|
style: {
|
|
74172
74181
|
fontFamily: '"Cambria Math", "STIX Two Math", serif'
|
|
74173
74182
|
},
|
|
74174
|
-
dangerouslySetInnerHTML: { __html:
|
|
74183
|
+
dangerouslySetInnerHTML: { __html: safeMathml }
|
|
74175
74184
|
}
|
|
74176
74185
|
) : /* @__PURE__ */ jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: "Equation" });
|
|
74177
74186
|
if (equationNumber) {
|
|
@@ -86528,7 +86537,7 @@ function ResizeHandle({
|
|
|
86528
86537
|
}
|
|
86529
86538
|
);
|
|
86530
86539
|
}
|
|
86531
|
-
function
|
|
86540
|
+
function SlideThumbnailImpl({
|
|
86532
86541
|
slide,
|
|
86533
86542
|
templateElements,
|
|
86534
86543
|
canvasSize
|
|
@@ -86766,6 +86775,37 @@ function ThumbnailTable({
|
|
|
86766
86775
|
}
|
|
86767
86776
|
return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] text-muted-foreground pointer-events-none", children: "Table" });
|
|
86768
86777
|
}
|
|
86778
|
+
function arePropsEqual(prev, next) {
|
|
86779
|
+
if (prev.slide.id !== next.slide.id) {
|
|
86780
|
+
return false;
|
|
86781
|
+
}
|
|
86782
|
+
if (prev.slide.isDirty !== next.slide.isDirty) {
|
|
86783
|
+
return false;
|
|
86784
|
+
}
|
|
86785
|
+
if (prev.slide.hidden !== next.slide.hidden) {
|
|
86786
|
+
return false;
|
|
86787
|
+
}
|
|
86788
|
+
if (prev.slide.elements !== next.slide.elements) {
|
|
86789
|
+
return false;
|
|
86790
|
+
}
|
|
86791
|
+
if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
|
|
86792
|
+
return false;
|
|
86793
|
+
}
|
|
86794
|
+
if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
|
|
86795
|
+
return false;
|
|
86796
|
+
}
|
|
86797
|
+
if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
|
|
86798
|
+
return false;
|
|
86799
|
+
}
|
|
86800
|
+
if (prev.templateElements !== next.templateElements) {
|
|
86801
|
+
return false;
|
|
86802
|
+
}
|
|
86803
|
+
if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
|
|
86804
|
+
return false;
|
|
86805
|
+
}
|
|
86806
|
+
return true;
|
|
86807
|
+
}
|
|
86808
|
+
var SlideThumbnail = React10__default.memo(SlideThumbnailImpl, arePropsEqual);
|
|
86769
86809
|
function ContextMenu({
|
|
86770
86810
|
contextMenuState,
|
|
86771
86811
|
mode,
|
|
@@ -90499,7 +90539,7 @@ function BendingProcessRenderer({
|
|
|
90499
90539
|
}
|
|
90500
90540
|
);
|
|
90501
90541
|
}
|
|
90502
|
-
function
|
|
90542
|
+
function SmartArtRendererImpl({
|
|
90503
90543
|
element: element2,
|
|
90504
90544
|
className = ""
|
|
90505
90545
|
}) {
|
|
@@ -90610,6 +90650,30 @@ function renderLayout(layoutType, element2, nodes, palette, style) {
|
|
|
90610
90650
|
}
|
|
90611
90651
|
return /* @__PURE__ */ jsx(ListRenderer, { element: element2, nodes, palette, style });
|
|
90612
90652
|
}
|
|
90653
|
+
function arePropsEqual2(prev, next) {
|
|
90654
|
+
if (prev.className !== next.className) {
|
|
90655
|
+
return false;
|
|
90656
|
+
}
|
|
90657
|
+
if (prev.element.id !== next.element.id) {
|
|
90658
|
+
return false;
|
|
90659
|
+
}
|
|
90660
|
+
if (prev.element.type !== next.element.type) {
|
|
90661
|
+
return false;
|
|
90662
|
+
}
|
|
90663
|
+
if (prev.element.width !== next.element.width || prev.element.height !== next.element.height) {
|
|
90664
|
+
return false;
|
|
90665
|
+
}
|
|
90666
|
+
if (prev.element.x !== next.element.x || prev.element.y !== next.element.y) {
|
|
90667
|
+
return false;
|
|
90668
|
+
}
|
|
90669
|
+
const prevData = prev.element.type === "smartArt" ? prev.element.smartArtData : void 0;
|
|
90670
|
+
const nextData = next.element.type === "smartArt" ? next.element.smartArtData : void 0;
|
|
90671
|
+
if (prevData !== nextData) {
|
|
90672
|
+
return false;
|
|
90673
|
+
}
|
|
90674
|
+
return true;
|
|
90675
|
+
}
|
|
90676
|
+
var SmartArtRenderer = React10__default.memo(SmartArtRendererImpl, arePropsEqual2);
|
|
90613
90677
|
function ZoomElementRenderer({
|
|
90614
90678
|
element: element2,
|
|
90615
90679
|
slides,
|
|
@@ -93553,7 +93617,7 @@ function SectionBlock({
|
|
|
93553
93617
|
)
|
|
93554
93618
|
] });
|
|
93555
93619
|
}
|
|
93556
|
-
function
|
|
93620
|
+
function SlideCardImpl({
|
|
93557
93621
|
slide,
|
|
93558
93622
|
index,
|
|
93559
93623
|
isActive,
|
|
@@ -93607,6 +93671,49 @@ function SlideCard({
|
|
|
93607
93671
|
}
|
|
93608
93672
|
);
|
|
93609
93673
|
}
|
|
93674
|
+
function arePropsEqual3(prev, next) {
|
|
93675
|
+
if (prev.slide.id !== next.slide.id) {
|
|
93676
|
+
return false;
|
|
93677
|
+
}
|
|
93678
|
+
if (prev.slide.isDirty !== next.slide.isDirty) {
|
|
93679
|
+
return false;
|
|
93680
|
+
}
|
|
93681
|
+
if (prev.slide.hidden !== next.slide.hidden) {
|
|
93682
|
+
return false;
|
|
93683
|
+
}
|
|
93684
|
+
if (prev.slide.elements !== next.slide.elements) {
|
|
93685
|
+
return false;
|
|
93686
|
+
}
|
|
93687
|
+
if (prev.index !== next.index) {
|
|
93688
|
+
return false;
|
|
93689
|
+
}
|
|
93690
|
+
if (prev.isActive !== next.isActive) {
|
|
93691
|
+
return false;
|
|
93692
|
+
}
|
|
93693
|
+
if (prev.isDragTarget !== next.isDragTarget) {
|
|
93694
|
+
return false;
|
|
93695
|
+
}
|
|
93696
|
+
if (prev.isSelected !== next.isSelected) {
|
|
93697
|
+
return false;
|
|
93698
|
+
}
|
|
93699
|
+
if (prev.selectedCount !== next.selectedCount) {
|
|
93700
|
+
return false;
|
|
93701
|
+
}
|
|
93702
|
+
if (prev.selectionOrder !== next.selectionOrder) {
|
|
93703
|
+
return false;
|
|
93704
|
+
}
|
|
93705
|
+
if (prev.canEdit !== next.canEdit) {
|
|
93706
|
+
return false;
|
|
93707
|
+
}
|
|
93708
|
+
if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
|
|
93709
|
+
return false;
|
|
93710
|
+
}
|
|
93711
|
+
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) {
|
|
93712
|
+
return false;
|
|
93713
|
+
}
|
|
93714
|
+
return true;
|
|
93715
|
+
}
|
|
93716
|
+
var SlideCard = React10__default.memo(SlideCardImpl, arePropsEqual3);
|
|
93610
93717
|
function SorterContextMenu({
|
|
93611
93718
|
x: x2,
|
|
93612
93719
|
y,
|
|
@@ -94400,6 +94507,14 @@ function getCurrentParagraphIndex(editorEl, segments) {
|
|
|
94400
94507
|
}
|
|
94401
94508
|
return paraIdx;
|
|
94402
94509
|
}
|
|
94510
|
+
var CSS_VALUE_SAFE = /^[a-zA-Z0-9 _,.\-+#'%/]{1,100}$/;
|
|
94511
|
+
function isCssValueSafe(value) {
|
|
94512
|
+
if (value === void 0 || value === null) {
|
|
94513
|
+
return false;
|
|
94514
|
+
}
|
|
94515
|
+
const str = String(value);
|
|
94516
|
+
return str.length > 0 && CSS_VALUE_SAFE.test(str);
|
|
94517
|
+
}
|
|
94403
94518
|
function deriveStyleFromElement(element2, inheritedStyle) {
|
|
94404
94519
|
const style = { ...inheritedStyle };
|
|
94405
94520
|
const tagName = element2.tagName.toLowerCase();
|
|
@@ -94532,17 +94647,17 @@ function segmentsToEditorHtml(segments) {
|
|
|
94532
94647
|
if (segment.style.strikethrough) {
|
|
94533
94648
|
inlineStyles.push("text-decoration:line-through");
|
|
94534
94649
|
}
|
|
94535
|
-
if (segment.style.color) {
|
|
94650
|
+
if (segment.style.color && isCssValueSafe(segment.style.color)) {
|
|
94536
94651
|
inlineStyles.push(`color:${segment.style.color}`);
|
|
94537
94652
|
}
|
|
94538
|
-
if (segment.style.fontSize) {
|
|
94539
|
-
inlineStyles.push(`font-size:${segment.style.fontSize}pt`);
|
|
94653
|
+
if (segment.style.fontSize && Number.isFinite(Number(segment.style.fontSize))) {
|
|
94654
|
+
inlineStyles.push(`font-size:${Number(segment.style.fontSize)}pt`);
|
|
94540
94655
|
}
|
|
94541
|
-
if (segment.style.fontFamily) {
|
|
94656
|
+
if (segment.style.fontFamily && isCssValueSafe(segment.style.fontFamily)) {
|
|
94542
94657
|
inlineStyles.push(`font-family:${segment.style.fontFamily}`);
|
|
94543
94658
|
}
|
|
94544
94659
|
const text2 = escapeHtml(segment.text);
|
|
94545
|
-
if (segment.style.hyperlink) {
|
|
94660
|
+
if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
|
|
94546
94661
|
const href = escapeHtml(segment.style.hyperlink);
|
|
94547
94662
|
return `<a href="${href}" style="color:#4a9eff;text-decoration:underline;cursor:pointer" data-hyperlink="${href}">${text2}</a>`;
|
|
94548
94663
|
}
|
|
@@ -94625,7 +94740,8 @@ function renderRichNotesSegments(segments) {
|
|
|
94625
94740
|
if (segment.style.fontFamily) {
|
|
94626
94741
|
style.fontFamily = segment.style.fontFamily;
|
|
94627
94742
|
}
|
|
94628
|
-
if (segment.style.hyperlink) {
|
|
94743
|
+
if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
|
|
94744
|
+
const safeHref = segment.style.hyperlink;
|
|
94629
94745
|
style.color = "#4a9eff";
|
|
94630
94746
|
style.textDecoration = "underline";
|
|
94631
94747
|
style.cursor = "pointer";
|
|
@@ -94633,11 +94749,11 @@ function renderRichNotesSegments(segments) {
|
|
|
94633
94749
|
/* @__PURE__ */ jsx(
|
|
94634
94750
|
"a",
|
|
94635
94751
|
{
|
|
94636
|
-
href:
|
|
94752
|
+
href: safeHref,
|
|
94637
94753
|
style,
|
|
94638
94754
|
onClick: (e2) => {
|
|
94639
94755
|
e2.preventDefault();
|
|
94640
|
-
|
|
94756
|
+
safeOpenUrl(safeHref);
|
|
94641
94757
|
},
|
|
94642
94758
|
children: segment.text
|
|
94643
94759
|
},
|
|
@@ -95189,7 +95305,7 @@ function useSlideNotes({
|
|
|
95189
95305
|
const href = target.getAttribute("data-hyperlink") || target.getAttribute("href");
|
|
95190
95306
|
if (href && (e2.ctrlKey || e2.metaKey)) {
|
|
95191
95307
|
e2.preventDefault();
|
|
95192
|
-
|
|
95308
|
+
safeOpenUrl(href);
|
|
95193
95309
|
}
|
|
95194
95310
|
}, []);
|
|
95195
95311
|
return {
|
|
@@ -96862,7 +96978,7 @@ function renderNotesSegments(segments) {
|
|
|
96862
96978
|
return React10__default.createElement("span", { key: `seg-${index}`, style }, segment.text);
|
|
96863
96979
|
});
|
|
96864
96980
|
}
|
|
96865
|
-
function
|
|
96981
|
+
function ScaledSlidePreviewImpl({
|
|
96866
96982
|
slide,
|
|
96867
96983
|
templateElements,
|
|
96868
96984
|
canvasSize,
|
|
@@ -97009,6 +97125,40 @@ function ScaledSlidePreview({
|
|
|
97009
97125
|
}
|
|
97010
97126
|
);
|
|
97011
97127
|
}
|
|
97128
|
+
function arePropsEqual4(prev, next) {
|
|
97129
|
+
if (prev.slide.id !== next.slide.id) {
|
|
97130
|
+
return false;
|
|
97131
|
+
}
|
|
97132
|
+
if (prev.slide.isDirty !== next.slide.isDirty) {
|
|
97133
|
+
return false;
|
|
97134
|
+
}
|
|
97135
|
+
if (prev.slide.hidden !== next.slide.hidden) {
|
|
97136
|
+
return false;
|
|
97137
|
+
}
|
|
97138
|
+
if (prev.slide.elements !== next.slide.elements) {
|
|
97139
|
+
return false;
|
|
97140
|
+
}
|
|
97141
|
+
if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
|
|
97142
|
+
return false;
|
|
97143
|
+
}
|
|
97144
|
+
if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
|
|
97145
|
+
return false;
|
|
97146
|
+
}
|
|
97147
|
+
if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
|
|
97148
|
+
return false;
|
|
97149
|
+
}
|
|
97150
|
+
if (prev.templateElements !== next.templateElements) {
|
|
97151
|
+
return false;
|
|
97152
|
+
}
|
|
97153
|
+
if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
|
|
97154
|
+
return false;
|
|
97155
|
+
}
|
|
97156
|
+
if (prev.className !== next.className) {
|
|
97157
|
+
return false;
|
|
97158
|
+
}
|
|
97159
|
+
return true;
|
|
97160
|
+
}
|
|
97161
|
+
var ScaledSlidePreview = React10__default.memo(ScaledSlidePreviewImpl, arePropsEqual4);
|
|
97012
97162
|
function PresenterView({
|
|
97013
97163
|
slides,
|
|
97014
97164
|
currentSlideIndex,
|
|
@@ -111312,6 +111462,13 @@ function convertOmmlToLatex(omml) {
|
|
|
111312
111462
|
}
|
|
111313
111463
|
return ommlChildrenToLatex(oMath);
|
|
111314
111464
|
}
|
|
111465
|
+
function sanitizeMathMl2(markup) {
|
|
111466
|
+
const purify = DOMPurify;
|
|
111467
|
+
if (typeof purify.sanitize !== "function") {
|
|
111468
|
+
return markup;
|
|
111469
|
+
}
|
|
111470
|
+
return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
|
|
111471
|
+
}
|
|
111315
111472
|
var TEMPLATES = [
|
|
111316
111473
|
{
|
|
111317
111474
|
label: "Fraction",
|
|
@@ -111377,7 +111534,8 @@ var TEMPLATES = [
|
|
|
111377
111534
|
var TEMPLATE_MATHML = TEMPLATES.map((tmpl) => {
|
|
111378
111535
|
try {
|
|
111379
111536
|
const tmplOmml = convertLatexToOmml(tmpl.latex);
|
|
111380
|
-
|
|
111537
|
+
const raw = convertOmmlToMathMl(tmplOmml);
|
|
111538
|
+
return raw ? sanitizeMathMl2(raw) : "";
|
|
111381
111539
|
} catch {
|
|
111382
111540
|
return "";
|
|
111383
111541
|
}
|
|
@@ -111389,7 +111547,7 @@ function MathMlPreview({ mathml }) {
|
|
|
111389
111547
|
return;
|
|
111390
111548
|
}
|
|
111391
111549
|
if (mathml) {
|
|
111392
|
-
containerRef.current.innerHTML = mathml;
|
|
111550
|
+
containerRef.current.innerHTML = sanitizeMathMl2(mathml);
|
|
111393
111551
|
} else {
|
|
111394
111552
|
containerRef.current.innerHTML = "";
|
|
111395
111553
|
}
|
|
@@ -111417,6 +111575,7 @@ function EquationEditorDialog({
|
|
|
111417
111575
|
return convertOmmlToLatex(existingOmml);
|
|
111418
111576
|
}, [existingOmml]);
|
|
111419
111577
|
const [latex, setLatex] = useState(initialLatex);
|
|
111578
|
+
const deferredLatex = useDeferredValue(latex);
|
|
111420
111579
|
const textareaRef = useRef(null);
|
|
111421
111580
|
useEffect(() => {
|
|
111422
111581
|
if (isOpen) {
|
|
@@ -111425,17 +111584,17 @@ function EquationEditorDialog({
|
|
|
111425
111584
|
}
|
|
111426
111585
|
}, [isOpen, initialLatex]);
|
|
111427
111586
|
const { mathml, omml } = useMemo(() => {
|
|
111428
|
-
if (!
|
|
111587
|
+
if (!deferredLatex.trim()) {
|
|
111429
111588
|
return { mathml: "", omml: {} };
|
|
111430
111589
|
}
|
|
111431
111590
|
try {
|
|
111432
|
-
const ommlObj = convertLatexToOmml(
|
|
111591
|
+
const ommlObj = convertLatexToOmml(deferredLatex);
|
|
111433
111592
|
const mathmlStr = convertOmmlToMathMl(ommlObj);
|
|
111434
111593
|
return { mathml: mathmlStr, omml: ommlObj };
|
|
111435
111594
|
} catch {
|
|
111436
111595
|
return { mathml: "", omml: {} };
|
|
111437
111596
|
}
|
|
111438
|
-
}, [
|
|
111597
|
+
}, [deferredLatex]);
|
|
111439
111598
|
const handleInsert = useCallback(() => {
|
|
111440
111599
|
if (!latex.trim()) {
|
|
111441
111600
|
return;
|
|
@@ -114170,6 +114329,7 @@ function useEditorHistory(input) {
|
|
|
114170
114329
|
const historyFutureRef = useRef([]);
|
|
114171
114330
|
const lastHistorySnapshotRef = useRef(null);
|
|
114172
114331
|
const lastHistorySerializedRef = useRef("");
|
|
114332
|
+
const lastCheapHashRef = useRef("");
|
|
114173
114333
|
const isApplyingHistoryRef = useRef(false);
|
|
114174
114334
|
const unlockHistoryTimerRef = useRef(null);
|
|
114175
114335
|
const [canUndo, setCanUndo] = useState(false);
|
|
@@ -114311,15 +114471,21 @@ function useEditorHistory(input) {
|
|
|
114311
114471
|
if (hasActivePointerInteraction()) {
|
|
114312
114472
|
return;
|
|
114313
114473
|
}
|
|
114474
|
+
const cheapHash = `${slides.length}|${activeSlideIndex}|${canvasSize.width}x${canvasSize.height}|${slides.map((s) => `${s.id}:${s.elements.length}`).join("/")}`;
|
|
114475
|
+
if (cheapHash === lastCheapHashRef.current) {
|
|
114476
|
+
return;
|
|
114477
|
+
}
|
|
114314
114478
|
const snapshot2 = buildHistorySnapshot();
|
|
114315
114479
|
const serialized = JSON.stringify(snapshot2);
|
|
114316
114480
|
if (serialized === lastHistorySerializedRef.current) {
|
|
114481
|
+
lastCheapHashRef.current = cheapHash;
|
|
114317
114482
|
return;
|
|
114318
114483
|
}
|
|
114319
114484
|
const previousSnapshot = lastHistorySnapshotRef.current;
|
|
114320
114485
|
if (!previousSnapshot) {
|
|
114321
114486
|
lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
|
|
114322
114487
|
lastHistorySerializedRef.current = serialized;
|
|
114488
|
+
lastCheapHashRef.current = cheapHash;
|
|
114323
114489
|
updateHistoryAvailability();
|
|
114324
114490
|
return;
|
|
114325
114491
|
}
|
|
@@ -114330,13 +114496,18 @@ function useEditorHistory(input) {
|
|
|
114330
114496
|
historyFutureRef.current = [];
|
|
114331
114497
|
lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
|
|
114332
114498
|
lastHistorySerializedRef.current = serialized;
|
|
114499
|
+
lastCheapHashRef.current = cheapHash;
|
|
114333
114500
|
updateHistoryAvailability();
|
|
114334
114501
|
}, [
|
|
114502
|
+
activeSlideIndex,
|
|
114335
114503
|
buildHistorySnapshot,
|
|
114504
|
+
canvasSize.height,
|
|
114505
|
+
canvasSize.width,
|
|
114336
114506
|
error2,
|
|
114337
114507
|
hasActivePointerInteraction,
|
|
114338
114508
|
loading2,
|
|
114339
114509
|
pointerCommitNonce,
|
|
114510
|
+
slides,
|
|
114340
114511
|
updateHistoryAvailability
|
|
114341
114512
|
]);
|
|
114342
114513
|
return {
|
|
@@ -118048,7 +118219,8 @@ async function storeAudienceContent(content) {
|
|
|
118048
118219
|
const tx = db.transaction(STORE_NAME2, "readwrite");
|
|
118049
118220
|
const store = tx.objectStore(STORE_NAME2);
|
|
118050
118221
|
const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
|
|
118051
|
-
|
|
118222
|
+
const record = { bytes, createdAt: Date.now() };
|
|
118223
|
+
store.put(record, CONTENT_KEY);
|
|
118052
118224
|
tx.oncomplete = () => {
|
|
118053
118225
|
db.close();
|
|
118054
118226
|
resolve2();
|
|
@@ -118081,14 +118253,34 @@ async function clearAudienceContent() {
|
|
|
118081
118253
|
var PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
|
|
118082
118254
|
var AUDIENCE_HASH = "#pptx-audience";
|
|
118083
118255
|
var PRESENTER_MSG_ORIGIN = "pptx-viewer-presenter";
|
|
118256
|
+
var AUDIENCE_NONCE_KEY = "nonce";
|
|
118257
|
+
function generateSessionId() {
|
|
118258
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
118259
|
+
return crypto.randomUUID();
|
|
118260
|
+
}
|
|
118261
|
+
return `s${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
118262
|
+
}
|
|
118263
|
+
function parseAudienceNonce() {
|
|
118264
|
+
const hash = window.location.hash;
|
|
118265
|
+
if (!hash.startsWith(AUDIENCE_HASH)) {
|
|
118266
|
+
return null;
|
|
118267
|
+
}
|
|
118268
|
+
const trailing = hash.slice(AUDIENCE_HASH.length);
|
|
118269
|
+
if (!trailing) {
|
|
118270
|
+
return null;
|
|
118271
|
+
}
|
|
118272
|
+
const params2 = new URLSearchParams(trailing.replace(/^[&;?]/, ""));
|
|
118273
|
+
return params2.get(AUDIENCE_NONCE_KEY);
|
|
118274
|
+
}
|
|
118084
118275
|
function isAudienceTab() {
|
|
118085
|
-
return window.location.hash
|
|
118276
|
+
return window.location.hash.startsWith(AUDIENCE_HASH);
|
|
118086
118277
|
}
|
|
118087
118278
|
function usePresenterWindow(input) {
|
|
118088
118279
|
const { currentSlideIndex, isPresenterMode, content } = input;
|
|
118089
118280
|
const audienceWindowRef = useRef(null);
|
|
118090
118281
|
const channelRef = useRef(null);
|
|
118091
118282
|
const pollTimerRef = useRef(null);
|
|
118283
|
+
const sessionIdRef = useRef("");
|
|
118092
118284
|
const getChannel2 = useCallback(() => {
|
|
118093
118285
|
if (!channelRef.current) {
|
|
118094
118286
|
channelRef.current = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
|
|
@@ -118100,10 +118292,14 @@ function usePresenterWindow(input) {
|
|
|
118100
118292
|
}, []);
|
|
118101
118293
|
const syncSlideToAudience = useCallback(
|
|
118102
118294
|
(slideIndex) => {
|
|
118295
|
+
if (!sessionIdRef.current) {
|
|
118296
|
+
return;
|
|
118297
|
+
}
|
|
118103
118298
|
const msg = {
|
|
118104
118299
|
origin: PRESENTER_MSG_ORIGIN,
|
|
118105
118300
|
type: "presenter-slide-change",
|
|
118106
|
-
slideIndex
|
|
118301
|
+
slideIndex,
|
|
118302
|
+
sessionId: sessionIdRef.current
|
|
118107
118303
|
};
|
|
118108
118304
|
try {
|
|
118109
118305
|
getChannel2().postMessage(msg);
|
|
@@ -118113,13 +118309,16 @@ function usePresenterWindow(input) {
|
|
|
118113
118309
|
[getChannel2]
|
|
118114
118310
|
);
|
|
118115
118311
|
const closeAudienceWindow = useCallback(() => {
|
|
118116
|
-
|
|
118117
|
-
|
|
118118
|
-
|
|
118119
|
-
|
|
118120
|
-
|
|
118121
|
-
|
|
118122
|
-
|
|
118312
|
+
if (sessionIdRef.current) {
|
|
118313
|
+
try {
|
|
118314
|
+
const exitMsg = {
|
|
118315
|
+
origin: PRESENTER_MSG_ORIGIN,
|
|
118316
|
+
type: "presenter-exit",
|
|
118317
|
+
sessionId: sessionIdRef.current
|
|
118318
|
+
};
|
|
118319
|
+
getChannel2().postMessage(exitMsg);
|
|
118320
|
+
} catch {
|
|
118321
|
+
}
|
|
118123
118322
|
}
|
|
118124
118323
|
const win = audienceWindowRef.current;
|
|
118125
118324
|
if (win && !win.closed) {
|
|
@@ -118129,6 +118328,7 @@ function usePresenterWindow(input) {
|
|
|
118129
118328
|
}
|
|
118130
118329
|
}
|
|
118131
118330
|
audienceWindowRef.current = null;
|
|
118331
|
+
sessionIdRef.current = "";
|
|
118132
118332
|
if (pollTimerRef.current !== null) {
|
|
118133
118333
|
clearInterval(pollTimerRef.current);
|
|
118134
118334
|
pollTimerRef.current = null;
|
|
@@ -118139,20 +118339,53 @@ function usePresenterWindow(input) {
|
|
|
118139
118339
|
if (isAudienceWindowOpen()) {
|
|
118140
118340
|
closeAudienceWindow();
|
|
118141
118341
|
}
|
|
118142
|
-
|
|
118143
|
-
|
|
118144
|
-
}
|
|
118145
|
-
const url = new URL(window.location.href);
|
|
118146
|
-
url.hash = AUDIENCE_HASH;
|
|
118147
|
-
const win = window.open(url.toString(), "_blank");
|
|
118148
|
-
if (!win) {
|
|
118342
|
+
const blankWin = window.open("about:blank", "_blank");
|
|
118343
|
+
if (!blankWin) {
|
|
118149
118344
|
return false;
|
|
118150
118345
|
}
|
|
118151
|
-
audienceWindowRef.current =
|
|
118346
|
+
audienceWindowRef.current = blankWin;
|
|
118347
|
+
const sessionId = generateSessionId();
|
|
118348
|
+
sessionIdRef.current = sessionId;
|
|
118349
|
+
const audienceUrl = new URL(window.location.href);
|
|
118350
|
+
const params2 = new URLSearchParams();
|
|
118351
|
+
params2.set(AUDIENCE_NONCE_KEY, sessionId);
|
|
118352
|
+
audienceUrl.hash = `${AUDIENCE_HASH}&${params2.toString()}`;
|
|
118353
|
+
const navigateOrClose = (ok) => {
|
|
118354
|
+
const win = audienceWindowRef.current;
|
|
118355
|
+
if (!win || win.closed) {
|
|
118356
|
+
return;
|
|
118357
|
+
}
|
|
118358
|
+
if (!ok) {
|
|
118359
|
+
try {
|
|
118360
|
+
win.close();
|
|
118361
|
+
} catch {
|
|
118362
|
+
}
|
|
118363
|
+
audienceWindowRef.current = null;
|
|
118364
|
+
sessionIdRef.current = "";
|
|
118365
|
+
return;
|
|
118366
|
+
}
|
|
118367
|
+
try {
|
|
118368
|
+
win.location.replace(audienceUrl.toString());
|
|
118369
|
+
} catch {
|
|
118370
|
+
try {
|
|
118371
|
+
win.close();
|
|
118372
|
+
} catch {
|
|
118373
|
+
}
|
|
118374
|
+
audienceWindowRef.current = null;
|
|
118375
|
+
sessionIdRef.current = "";
|
|
118376
|
+
}
|
|
118377
|
+
};
|
|
118378
|
+
if (content) {
|
|
118379
|
+
void storeAudienceContent(content).then(() => navigateOrClose(true)).catch(() => navigateOrClose(false));
|
|
118380
|
+
} else {
|
|
118381
|
+
navigateOrClose(true);
|
|
118382
|
+
}
|
|
118152
118383
|
window.setTimeout(() => syncSlideToAudience(currentSlideIndex), 1500);
|
|
118153
118384
|
pollTimerRef.current = setInterval(() => {
|
|
118154
|
-
|
|
118385
|
+
const win = audienceWindowRef.current;
|
|
118386
|
+
if (!win || win.closed) {
|
|
118155
118387
|
audienceWindowRef.current = null;
|
|
118388
|
+
sessionIdRef.current = "";
|
|
118156
118389
|
if (pollTimerRef.current !== null) {
|
|
118157
118390
|
clearInterval(pollTimerRef.current);
|
|
118158
118391
|
pollTimerRef.current = null;
|
|
@@ -118180,6 +118413,13 @@ function usePresenterWindow(input) {
|
|
|
118180
118413
|
closeAudienceWindow();
|
|
118181
118414
|
}
|
|
118182
118415
|
}, [isPresenterMode, closeAudienceWindow]);
|
|
118416
|
+
useEffect(() => {
|
|
118417
|
+
const handleBeforeUnload = () => {
|
|
118418
|
+
void clearAudienceContent();
|
|
118419
|
+
};
|
|
118420
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
118421
|
+
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
118422
|
+
}, []);
|
|
118183
118423
|
return {
|
|
118184
118424
|
openAudienceWindow,
|
|
118185
118425
|
closeAudienceWindow,
|
|
@@ -118217,6 +118457,7 @@ function useAudienceMode(input) {
|
|
|
118217
118457
|
if (!isAudienceTab()) {
|
|
118218
118458
|
return;
|
|
118219
118459
|
}
|
|
118460
|
+
const expectedSessionId = parseAudienceNonce();
|
|
118220
118461
|
let channel;
|
|
118221
118462
|
try {
|
|
118222
118463
|
channel = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
|
|
@@ -118228,6 +118469,9 @@ function useAudienceMode(input) {
|
|
|
118228
118469
|
if (!data || data.origin !== PRESENTER_MSG_ORIGIN) {
|
|
118229
118470
|
return;
|
|
118230
118471
|
}
|
|
118472
|
+
if (expectedSessionId && data.sessionId !== expectedSessionId) {
|
|
118473
|
+
return;
|
|
118474
|
+
}
|
|
118231
118475
|
if (data.type === "presenter-slide-change") {
|
|
118232
118476
|
onSetActiveSlideIndex(data.slideIndex);
|
|
118233
118477
|
}
|
|
@@ -119161,6 +119405,12 @@ function useTouchGestures(input) {
|
|
|
119161
119405
|
callbacksRef.current = callbacks;
|
|
119162
119406
|
const scaleRef = useRef(currentScale);
|
|
119163
119407
|
scaleRef.current = currentScale;
|
|
119408
|
+
const [targetVersion, setTargetVersion] = useState(0);
|
|
119409
|
+
const lastTargetRef = useRef(null);
|
|
119410
|
+
if (targetRef.current !== lastTargetRef.current) {
|
|
119411
|
+
lastTargetRef.current = targetRef.current;
|
|
119412
|
+
queueMicrotask(() => setTargetVersion((v) => v + 1));
|
|
119413
|
+
}
|
|
119164
119414
|
useEffect(() => {
|
|
119165
119415
|
const el = targetRef.current;
|
|
119166
119416
|
if (!el || !enabled) {
|
|
@@ -119249,7 +119499,7 @@ function useTouchGestures(input) {
|
|
|
119249
119499
|
el.removeEventListener("touchcancel", handleTouchCancel);
|
|
119250
119500
|
cancelLongPress();
|
|
119251
119501
|
};
|
|
119252
|
-
}, [targetRef, enabled]);
|
|
119502
|
+
}, [targetRef, enabled, targetVersion]);
|
|
119253
119503
|
}
|
|
119254
119504
|
|
|
119255
119505
|
// src/viewer/utils/dom-helpers.ts
|
|
@@ -119270,11 +119520,31 @@ function safeConfirm(message) {
|
|
|
119270
119520
|
return false;
|
|
119271
119521
|
}
|
|
119272
119522
|
}
|
|
119523
|
+
function sanitizeDownloadFilename(input) {
|
|
119524
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
119525
|
+
return "presentation.pptx";
|
|
119526
|
+
}
|
|
119527
|
+
let cleaned = input.replace(/[\x00-\x1f\x7f"\\/:*?<>|]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, "").trim();
|
|
119528
|
+
if (cleaned.length === 0) {
|
|
119529
|
+
return "presentation.pptx";
|
|
119530
|
+
}
|
|
119531
|
+
if (cleaned.length > 200) {
|
|
119532
|
+
const dot = cleaned.lastIndexOf(".");
|
|
119533
|
+
if (dot > 0 && cleaned.length - dot <= 16) {
|
|
119534
|
+
const ext = cleaned.slice(dot);
|
|
119535
|
+
cleaned = cleaned.slice(0, 200 - ext.length) + ext;
|
|
119536
|
+
} else {
|
|
119537
|
+
cleaned = cleaned.slice(0, 200);
|
|
119538
|
+
}
|
|
119539
|
+
}
|
|
119540
|
+
return cleaned;
|
|
119541
|
+
}
|
|
119273
119542
|
function downloadBlob(blob, filename) {
|
|
119543
|
+
const safeName = sanitizeDownloadFilename(filename);
|
|
119274
119544
|
const url = URL.createObjectURL(blob);
|
|
119275
119545
|
const a2 = document.createElement("a");
|
|
119276
119546
|
a2.href = url;
|
|
119277
|
-
a2.download =
|
|
119547
|
+
a2.download = safeName;
|
|
119278
119548
|
document.body.appendChild(a2);
|
|
119279
119549
|
a2.click();
|
|
119280
119550
|
setTimeout(() => {
|
|
@@ -119705,27 +119975,82 @@ function openAutosaveDb2() {
|
|
|
119705
119975
|
req.onerror = () => reject(req.error);
|
|
119706
119976
|
});
|
|
119707
119977
|
}
|
|
119708
|
-
async function
|
|
119978
|
+
async function deleteOldestAutosaveEntry() {
|
|
119709
119979
|
const db = await openAutosaveDb2();
|
|
119710
|
-
return new Promise((resolve2
|
|
119711
|
-
|
|
119712
|
-
|
|
119713
|
-
|
|
119714
|
-
|
|
119715
|
-
|
|
119716
|
-
|
|
119717
|
-
|
|
119718
|
-
|
|
119719
|
-
|
|
119720
|
-
|
|
119721
|
-
|
|
119722
|
-
|
|
119723
|
-
|
|
119724
|
-
|
|
119725
|
-
|
|
119726
|
-
|
|
119980
|
+
return new Promise((resolve2) => {
|
|
119981
|
+
try {
|
|
119982
|
+
const tx = db.transaction(STORE_NAME3, "readwrite");
|
|
119983
|
+
const store = tx.objectStore(STORE_NAME3);
|
|
119984
|
+
let oldestKey = null;
|
|
119985
|
+
let oldestTimestamp = Infinity;
|
|
119986
|
+
const cursorReq = store.openCursor();
|
|
119987
|
+
cursorReq.onsuccess = () => {
|
|
119988
|
+
const cursor = cursorReq.result;
|
|
119989
|
+
if (cursor) {
|
|
119990
|
+
const value = cursor.value;
|
|
119991
|
+
if (typeof value.timestamp === "number" && value.timestamp < oldestTimestamp) {
|
|
119992
|
+
oldestTimestamp = value.timestamp;
|
|
119993
|
+
oldestKey = cursor.primaryKey;
|
|
119994
|
+
}
|
|
119995
|
+
cursor.continue();
|
|
119996
|
+
} else if (oldestKey !== null) {
|
|
119997
|
+
store.delete(oldestKey);
|
|
119998
|
+
}
|
|
119999
|
+
};
|
|
120000
|
+
tx.oncomplete = () => {
|
|
120001
|
+
db.close();
|
|
120002
|
+
resolve2(oldestKey !== null);
|
|
120003
|
+
};
|
|
120004
|
+
tx.onerror = () => {
|
|
120005
|
+
db.close();
|
|
120006
|
+
resolve2(false);
|
|
120007
|
+
};
|
|
120008
|
+
} catch {
|
|
120009
|
+
try {
|
|
120010
|
+
db.close();
|
|
120011
|
+
} catch {
|
|
120012
|
+
}
|
|
120013
|
+
resolve2(false);
|
|
120014
|
+
}
|
|
119727
120015
|
});
|
|
119728
120016
|
}
|
|
120017
|
+
function putAutosaveRecord(filePath, data) {
|
|
120018
|
+
return openAutosaveDb2().then(
|
|
120019
|
+
(db) => new Promise((resolve2, reject) => {
|
|
120020
|
+
const tx = db.transaction(STORE_NAME3, "readwrite");
|
|
120021
|
+
const store = tx.objectStore(STORE_NAME3);
|
|
120022
|
+
store.put({
|
|
120023
|
+
key: filePath,
|
|
120024
|
+
data,
|
|
120025
|
+
timestamp: Date.now(),
|
|
120026
|
+
size: data.byteLength
|
|
120027
|
+
});
|
|
120028
|
+
tx.oncomplete = () => {
|
|
120029
|
+
db.close();
|
|
120030
|
+
resolve2(true);
|
|
120031
|
+
};
|
|
120032
|
+
tx.onerror = () => {
|
|
120033
|
+
db.close();
|
|
120034
|
+
reject(tx.error);
|
|
120035
|
+
};
|
|
120036
|
+
})
|
|
120037
|
+
);
|
|
120038
|
+
}
|
|
120039
|
+
async function saveToIndexedDb(filePath, data) {
|
|
120040
|
+
try {
|
|
120041
|
+
return await putAutosaveRecord(filePath, data);
|
|
120042
|
+
} catch (err) {
|
|
120043
|
+
const errName = err instanceof Error || err instanceof DOMException ? err.name : "";
|
|
120044
|
+
if (errName !== "QuotaExceededError") {
|
|
120045
|
+
throw err;
|
|
120046
|
+
}
|
|
120047
|
+
const deleted = await deleteOldestAutosaveEntry();
|
|
120048
|
+
if (!deleted) {
|
|
120049
|
+
throw err;
|
|
120050
|
+
}
|
|
120051
|
+
return putAutosaveRecord(filePath, data);
|
|
120052
|
+
}
|
|
120053
|
+
}
|
|
119729
120054
|
function useAutosave(input) {
|
|
119730
120055
|
const {
|
|
119731
120056
|
isDirty,
|
|
@@ -119842,6 +120167,25 @@ function collectReferencedFontFamilies(slides) {
|
|
|
119842
120167
|
}
|
|
119843
120168
|
return families;
|
|
119844
120169
|
}
|
|
120170
|
+
var FONT_NAME_UNSAFE_CHARS = /["\\\n\r;}<>]/;
|
|
120171
|
+
var FONT_FORMAT_ALLOWED = /* @__PURE__ */ new Set([
|
|
120172
|
+
"truetype",
|
|
120173
|
+
"opentype",
|
|
120174
|
+
"woff",
|
|
120175
|
+
"woff2",
|
|
120176
|
+
"svg",
|
|
120177
|
+
"embedded-opentype"
|
|
120178
|
+
]);
|
|
120179
|
+
var FONT_DATA_URL_PATTERN = /^data:font\/[a-z0-9+.-]+(?:;charset=[a-z0-9-]+)?;base64,[A-Za-z0-9+/=]+$/i;
|
|
120180
|
+
function isFontDataUrlSafe(url) {
|
|
120181
|
+
if (typeof url !== "string" || url.length === 0) {
|
|
120182
|
+
return false;
|
|
120183
|
+
}
|
|
120184
|
+
if (url.startsWith("blob:")) {
|
|
120185
|
+
return true;
|
|
120186
|
+
}
|
|
120187
|
+
return FONT_DATA_URL_PATTERN.test(url);
|
|
120188
|
+
}
|
|
119845
120189
|
function useFontInjection({ embeddedFonts, slides }) {
|
|
119846
120190
|
useEffect(() => {
|
|
119847
120191
|
if (!embeddedFonts.length) {
|
|
@@ -119849,17 +120193,28 @@ function useFontInjection({ embeddedFonts, slides }) {
|
|
|
119849
120193
|
}
|
|
119850
120194
|
const styleEl = document.createElement("style");
|
|
119851
120195
|
styleEl.id = EMBEDDED_FONTS_STYLE_ID;
|
|
119852
|
-
const cssRules = embeddedFonts.
|
|
120196
|
+
const cssRules = embeddedFonts.flatMap((font) => {
|
|
120197
|
+
if (typeof font.name !== "string" || font.name.length === 0 || FONT_NAME_UNSAFE_CHARS.test(font.name)) {
|
|
120198
|
+
return [];
|
|
120199
|
+
}
|
|
120200
|
+
if (!isFontDataUrlSafe(font.dataUrl)) {
|
|
120201
|
+
return [];
|
|
120202
|
+
}
|
|
120203
|
+
const fontFormat = font.format ?? "truetype";
|
|
120204
|
+
if (!FONT_FORMAT_ALLOWED.has(fontFormat)) {
|
|
120205
|
+
return [];
|
|
120206
|
+
}
|
|
119853
120207
|
const fontWeight = font.bold ? "700" : "400";
|
|
119854
120208
|
const fontStyleCss = font.italic ? "italic" : "normal";
|
|
119855
|
-
|
|
119856
|
-
|
|
120209
|
+
return [
|
|
120210
|
+
`@font-face {
|
|
119857
120211
|
font-family: "${font.name}";
|
|
119858
120212
|
src: url("${font.dataUrl}") format("${fontFormat}");
|
|
119859
120213
|
font-weight: ${fontWeight};
|
|
119860
120214
|
font-style: ${fontStyleCss};
|
|
119861
120215
|
font-display: swap;
|
|
119862
|
-
}
|
|
120216
|
+
}`
|
|
120217
|
+
];
|
|
119863
120218
|
}).join("\n");
|
|
119864
120219
|
styleEl.textContent = cssRules;
|
|
119865
120220
|
document.head.appendChild(styleEl);
|
|
@@ -119888,7 +120243,7 @@ function useFontInjection({ embeddedFonts, slides }) {
|
|
|
119888
120243
|
const linkEl = document.createElement("link");
|
|
119889
120244
|
linkEl.id = GOOGLE_FONTS_LINK_ID;
|
|
119890
120245
|
linkEl.rel = "stylesheet";
|
|
119891
|
-
linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${GOOGLE_FONTS_AVAILABLE[f]}`).join("&")}&display=swap`;
|
|
120246
|
+
linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${encodeURIComponent(GOOGLE_FONTS_AVAILABLE[f])}`).join("&")}&display=swap`;
|
|
119892
120247
|
document.head.appendChild(linkEl);
|
|
119893
120248
|
return () => {
|
|
119894
120249
|
const existing = document.getElementById(GOOGLE_FONTS_LINK_ID);
|
|
@@ -119904,13 +120259,18 @@ function useFontInjection({ embeddedFonts, slides }) {
|
|
|
119904
120259
|
}
|
|
119905
120260
|
const styleEl = document.createElement("style");
|
|
119906
120261
|
styleEl.id = SYMBOL_FONTS_STYLE_ID;
|
|
119907
|
-
const rules = neededSymbolFonts.
|
|
119908
|
-
(font
|
|
120262
|
+
const rules = neededSymbolFonts.flatMap((font) => {
|
|
120263
|
+
if (typeof font !== "string" || FONT_NAME_UNSAFE_CHARS.test(font)) {
|
|
120264
|
+
return [];
|
|
120265
|
+
}
|
|
120266
|
+
return [
|
|
120267
|
+
`@font-face {
|
|
119909
120268
|
font-family: "${font}";
|
|
119910
120269
|
src: local("${font}"), local("${font} Regular");
|
|
119911
120270
|
font-display: swap;
|
|
119912
120271
|
}`
|
|
119913
|
-
|
|
120272
|
+
];
|
|
120273
|
+
}).join("\n");
|
|
119914
120274
|
styleEl.textContent = rules;
|
|
119915
120275
|
document.head.appendChild(styleEl);
|
|
119916
120276
|
return () => {
|
|
@@ -120053,16 +120413,17 @@ function useLoadContent({
|
|
|
120053
120413
|
`[pptx] Large file detected (${fileSizeMB.toFixed(1)} MB). Loading may use significant memory.`
|
|
120054
120414
|
);
|
|
120055
120415
|
}
|
|
120056
|
-
|
|
120057
|
-
handlerRef.current.dispose();
|
|
120058
|
-
handlerRef.current = null;
|
|
120059
|
-
}
|
|
120416
|
+
const previousHandler = handlerRef.current;
|
|
120060
120417
|
const handler = new PptxHandler();
|
|
120061
120418
|
const parsed = await handler.load(buffer);
|
|
120062
120419
|
if (cancelled || token !== renderTokenRef.current) {
|
|
120063
120420
|
handler.dispose();
|
|
120064
120421
|
return;
|
|
120065
120422
|
}
|
|
120423
|
+
if (previousHandler) {
|
|
120424
|
+
previousHandler.dispose();
|
|
120425
|
+
}
|
|
120426
|
+
handlerRef.current = null;
|
|
120066
120427
|
const mediaElements = [];
|
|
120067
120428
|
for (const slide of parsed.slides) {
|
|
120068
120429
|
collectMediaElements(slide.elements, mediaElements);
|
|
@@ -120107,6 +120468,7 @@ function useLoadContent({
|
|
|
120107
120468
|
})
|
|
120108
120469
|
);
|
|
120109
120470
|
const { paths: imagePaths, refs: imageRefs } = collectImagePaths(parsed.slides);
|
|
120471
|
+
let nextSlides = parsed.slides;
|
|
120110
120472
|
if (imagePaths.size > 0) {
|
|
120111
120473
|
const resolvedMap = /* @__PURE__ */ new Map();
|
|
120112
120474
|
await Promise.all(
|
|
@@ -120120,15 +120482,47 @@ function useLoadContent({
|
|
|
120120
120482
|
}
|
|
120121
120483
|
})
|
|
120122
120484
|
);
|
|
120485
|
+
const elementPatches = /* @__PURE__ */ new Map();
|
|
120123
120486
|
for (const ref of imageRefs) {
|
|
120124
120487
|
const url = resolvedMap.get(ref.path);
|
|
120125
|
-
if (url) {
|
|
120126
|
-
|
|
120488
|
+
if (!url) {
|
|
120489
|
+
continue;
|
|
120127
120490
|
}
|
|
120491
|
+
const id2 = ref.element.id;
|
|
120492
|
+
const existing = elementPatches.get(id2) ?? {};
|
|
120493
|
+
existing[ref.field] = url;
|
|
120494
|
+
elementPatches.set(id2, existing);
|
|
120495
|
+
}
|
|
120496
|
+
if (elementPatches.size > 0) {
|
|
120497
|
+
const patchElements = (elements) => {
|
|
120498
|
+
let mutated = false;
|
|
120499
|
+
const next = elements.map((el) => {
|
|
120500
|
+
let updated = el;
|
|
120501
|
+
const patch = elementPatches.get(el.id);
|
|
120502
|
+
if (patch) {
|
|
120503
|
+
updated = { ...el, ...patch };
|
|
120504
|
+
}
|
|
120505
|
+
if (updated.type === "group" && updated.children?.length) {
|
|
120506
|
+
const newChildren = patchElements(updated.children);
|
|
120507
|
+
if (newChildren !== updated.children) {
|
|
120508
|
+
updated = { ...updated, children: newChildren };
|
|
120509
|
+
}
|
|
120510
|
+
}
|
|
120511
|
+
if (updated !== el) {
|
|
120512
|
+
mutated = true;
|
|
120513
|
+
}
|
|
120514
|
+
return updated;
|
|
120515
|
+
});
|
|
120516
|
+
return mutated ? next : elements;
|
|
120517
|
+
};
|
|
120518
|
+
nextSlides = parsed.slides.map((s) => {
|
|
120519
|
+
const newElements = patchElements(s.elements);
|
|
120520
|
+
return newElements === s.elements ? s : { ...s, elements: newElements };
|
|
120521
|
+
});
|
|
120128
120522
|
}
|
|
120129
120523
|
}
|
|
120130
120524
|
handlerRef.current = handler;
|
|
120131
|
-
setSlides(
|
|
120525
|
+
setSlides(nextSlides);
|
|
120132
120526
|
setTemplateElementsBySlideId({});
|
|
120133
120527
|
setCanvasSize({
|
|
120134
120528
|
width: parsed.width ?? DEFAULT_CANVAS_WIDTH,
|
|
@@ -121302,7 +121696,19 @@ function injectFontFaces(svg, fontFaces) {
|
|
|
121302
121696
|
if (!fontFaces.length) {
|
|
121303
121697
|
return svg;
|
|
121304
121698
|
}
|
|
121305
|
-
const
|
|
121699
|
+
const safeFontFaces = fontFaces.filter((f) => {
|
|
121700
|
+
if (f.css.toLowerCase().includes("</style")) {
|
|
121701
|
+
console.warn(
|
|
121702
|
+
`[export-svg] Dropping @font-face entry for "${f.family}" containing "</style" \u2014 would break out of the <style> block.`
|
|
121703
|
+
);
|
|
121704
|
+
return false;
|
|
121705
|
+
}
|
|
121706
|
+
return true;
|
|
121707
|
+
});
|
|
121708
|
+
if (!safeFontFaces.length) {
|
|
121709
|
+
return svg;
|
|
121710
|
+
}
|
|
121711
|
+
const styleBlock = `<style type="text/css">${safeFontFaces.map((f) => f.css).join("\n")}</style>`;
|
|
121306
121712
|
if (svg.includes("<defs>")) {
|
|
121307
121713
|
return svg.replace("<defs>", `<defs>${styleBlock}`);
|
|
121308
121714
|
}
|
|
@@ -121628,7 +122034,7 @@ function useExportHandlers(input) {
|
|
|
121628
122034
|
activeSlideIndexForGuides,
|
|
121629
122035
|
modalControls
|
|
121630
122036
|
});
|
|
121631
|
-
const handleExportPng = async () => {
|
|
122037
|
+
const handleExportPng = useCallback(async () => {
|
|
121632
122038
|
const stageEl = canvasStageRef.current;
|
|
121633
122039
|
if (!stageEl) {
|
|
121634
122040
|
return;
|
|
@@ -121640,8 +122046,8 @@ function useExportHandlers(input) {
|
|
|
121640
122046
|
} catch (err) {
|
|
121641
122047
|
console.error("[PowerPointViewer] PNG export failed:", err);
|
|
121642
122048
|
}
|
|
121643
|
-
};
|
|
121644
|
-
const handleExportPdf = async () => {
|
|
122049
|
+
}, [canvasStageRef, activeSlideIndex, activeSlide?.backgroundColor]);
|
|
122050
|
+
const handleExportPdf = useCallback(async () => {
|
|
121645
122051
|
if (!canvasStageRef.current) {
|
|
121646
122052
|
return;
|
|
121647
122053
|
}
|
|
@@ -121682,8 +122088,8 @@ function useExportHandlers(input) {
|
|
|
121682
122088
|
exportAbortRef.current = null;
|
|
121683
122089
|
setExportModalOpen(false);
|
|
121684
122090
|
}
|
|
121685
|
-
};
|
|
121686
|
-
const handleExportNotesPdf = async () => {
|
|
122091
|
+
}, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
|
|
122092
|
+
const handleExportNotesPdf = useCallback(async () => {
|
|
121687
122093
|
if (!canvasStageRef.current) {
|
|
121688
122094
|
return;
|
|
121689
122095
|
}
|
|
@@ -121726,8 +122132,8 @@ function useExportHandlers(input) {
|
|
|
121726
122132
|
exportAbortRef.current = null;
|
|
121727
122133
|
setExportModalOpen(false);
|
|
121728
122134
|
}
|
|
121729
|
-
};
|
|
121730
|
-
const handleCopySlideAsImage = async () => {
|
|
122135
|
+
}, [canvasStageRef, slides, setActiveSlideIndex, activeSlideIndex]);
|
|
122136
|
+
const handleCopySlideAsImage = useCallback(async () => {
|
|
121731
122137
|
const stageEl = canvasStageRef.current;
|
|
121732
122138
|
if (!stageEl) {
|
|
121733
122139
|
return;
|
|
@@ -121739,8 +122145,8 @@ function useExportHandlers(input) {
|
|
|
121739
122145
|
} catch (err) {
|
|
121740
122146
|
console.error("[PowerPointViewer] Copy slide as image failed:", err);
|
|
121741
122147
|
}
|
|
121742
|
-
};
|
|
121743
|
-
const handleExportVideo = async () => {
|
|
122148
|
+
}, [canvasStageRef, activeSlide?.backgroundColor]);
|
|
122149
|
+
const handleExportVideo = useCallback(async () => {
|
|
121744
122150
|
if (!canvasStageRef.current) {
|
|
121745
122151
|
return;
|
|
121746
122152
|
}
|
|
@@ -121782,8 +122188,8 @@ function useExportHandlers(input) {
|
|
|
121782
122188
|
exportAbortRef.current = null;
|
|
121783
122189
|
setExportModalOpen(false);
|
|
121784
122190
|
}
|
|
121785
|
-
};
|
|
121786
|
-
const handleExportGif = async () => {
|
|
122191
|
+
}, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
|
|
122192
|
+
const handleExportGif = useCallback(async () => {
|
|
121787
122193
|
if (!canvasStageRef.current) {
|
|
121788
122194
|
return;
|
|
121789
122195
|
}
|
|
@@ -121821,7 +122227,7 @@ function useExportHandlers(input) {
|
|
|
121821
122227
|
exportAbortRef.current = null;
|
|
121822
122228
|
setExportModalOpen(false);
|
|
121823
122229
|
}
|
|
121824
|
-
};
|
|
122230
|
+
}, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
|
|
121825
122231
|
const handleCancelExport = useCallback(() => {
|
|
121826
122232
|
exportAbortRef.current?.abort();
|
|
121827
122233
|
exportAbortRef.current = null;
|
|
@@ -121847,6 +122253,15 @@ function useExportHandlers(input) {
|
|
|
121847
122253
|
exportStatusMessage
|
|
121848
122254
|
};
|
|
121849
122255
|
}
|
|
122256
|
+
function escapeHtmlAttr(value) {
|
|
122257
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
122258
|
+
}
|
|
122259
|
+
function safeDataImageSrc(src) {
|
|
122260
|
+
if (typeof src !== "string" || !src.startsWith("data:image/")) {
|
|
122261
|
+
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=";
|
|
122262
|
+
}
|
|
122263
|
+
return escapeHtmlAttr(src);
|
|
122264
|
+
}
|
|
121850
122265
|
function openPrintWindow(title, bodyHtml, orientation, colorFilter, frameSlides) {
|
|
121851
122266
|
const printWindow = window.open("", "_blank", "noopener,noreferrer");
|
|
121852
122267
|
if (!printWindow) {
|
|
@@ -122029,7 +122444,7 @@ function usePrintHandlers(input) {
|
|
|
122029
122444
|
const slideImages = slideIndices.map((idx) => allImages[idx]).filter(Boolean);
|
|
122030
122445
|
if (settings.printWhat === "slides") {
|
|
122031
122446
|
const bodyHtml = slideImages.map(
|
|
122032
|
-
(img, i3) => `<section class="page slide-page"><img class="slide-img" src="${img}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
|
|
122447
|
+
(img, i3) => `<section class="page slide-page"><img class="slide-img" src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
|
|
122033
122448
|
).join("");
|
|
122034
122449
|
openPrintWindow(
|
|
122035
122450
|
"Slides",
|
|
@@ -122045,7 +122460,7 @@ function usePrintHandlers(input) {
|
|
|
122045
122460
|
const idx = slideIndices[i3];
|
|
122046
122461
|
const notes = slides[idx]?.notes?.trim() || "";
|
|
122047
122462
|
return `<section class="page notes-page">
|
|
122048
|
-
<img class="notes-slide" src="${img}" alt="Slide ${idx + 1}" />
|
|
122463
|
+
<img class="notes-slide" src="${safeDataImageSrc(img)}" alt="Slide ${idx + 1}" />
|
|
122049
122464
|
<div class="notes-text">${escapeHtml2(notes)}</div>
|
|
122050
122465
|
</section>`;
|
|
122051
122466
|
}).join("");
|
|
@@ -122077,14 +122492,14 @@ function usePrintHandlers(input) {
|
|
|
122077
122492
|
if (isThreePerPage) {
|
|
122078
122493
|
const rows = Array.from({ length: spp }, (_, cellIndex) => {
|
|
122079
122494
|
const img = pageImgs[cellIndex];
|
|
122080
|
-
const slideCell = img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122495
|
+
const slideCell = img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122081
122496
|
return `<div class="handout-row-3">${slideCell}${buildNoteLines()}</div>`;
|
|
122082
122497
|
}).join("");
|
|
122083
122498
|
pages.push(`<section class="page"><div class="handout-grid-3">${rows}</div></section>`);
|
|
122084
122499
|
} else {
|
|
122085
122500
|
const cells = Array.from({ length: spp }, (_, cellIndex) => {
|
|
122086
122501
|
const img = pageImgs[cellIndex];
|
|
122087
|
-
return img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122502
|
+
return img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
|
|
122088
122503
|
}).join("");
|
|
122089
122504
|
pages.push(
|
|
122090
122505
|
`<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>`
|