pptx-react-viewer 1.1.6 → 1.1.7

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.
Files changed (31) hide show
  1. package/dist/{PowerPointViewer-CX0a7wz_.d.mts → PowerPointViewer-C5jGuKGB.d.mts} +3 -1
  2. package/dist/{PowerPointViewer-CX0a7wz_.d.ts → PowerPointViewer-C5jGuKGB.d.ts} +3 -1
  3. package/dist/index.d.mts +3 -2
  4. package/dist/index.d.ts +3 -2
  5. package/dist/index.js +1224 -454
  6. package/dist/index.mjs +1225 -455
  7. package/dist/pptx-viewer.css +1 -1
  8. package/dist/viewer/index.d.mts +6 -25
  9. package/dist/viewer/index.d.ts +6 -25
  10. package/dist/viewer/index.js +1224 -454
  11. package/dist/viewer/index.mjs +1225 -455
  12. package/node_modules/emf-converter/package.json +1 -1
  13. package/node_modules/mtx-decompressor/package.json +1 -1
  14. package/node_modules/pptx-viewer-core/dist/{SvgExporter-CTDG-t_z.d.ts → SvgExporter-BtZczTlB.d.ts} +1 -1
  15. package/node_modules/pptx-viewer-core/dist/{SvgExporter-BTkk4oNQ.d.mts → SvgExporter-D4mBWJHE.d.mts} +1 -1
  16. package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +2 -2
  17. package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +2 -2
  18. package/node_modules/pptx-viewer-core/dist/cli/index.js +0 -0
  19. package/node_modules/pptx-viewer-core/dist/cli/index.mjs +0 -0
  20. package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +3 -3
  21. package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +3 -3
  22. package/node_modules/pptx-viewer-core/dist/index.d.mts +108 -19
  23. package/node_modules/pptx-viewer-core/dist/index.d.ts +108 -19
  24. package/node_modules/pptx-viewer-core/dist/index.js +532 -306
  25. package/node_modules/pptx-viewer-core/dist/index.mjs +524 -307
  26. package/node_modules/pptx-viewer-core/dist/{presentation-4fhI3din.d.mts → presentation-nZxgWvXq.d.mts} +40 -1
  27. package/node_modules/pptx-viewer-core/dist/{presentation-4fhI3din.d.ts → presentation-nZxgWvXq.d.ts} +40 -1
  28. package/node_modules/pptx-viewer-core/dist/{text-operations-C89Jn6S0.d.mts → text-operations-DCTGMltY.d.mts} +1 -1
  29. package/node_modules/pptx-viewer-core/dist/{text-operations-B9EwbptL.d.ts → text-operations-DYmhoi7U.d.ts} +1 -1
  30. package/node_modules/pptx-viewer-core/package.json +1 -1
  31. package/package.json +4 -4
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ 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
- 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';
7
+ import { LuMessageSquare, LuEyeOff, LuSettings, LuX, LuCast, LuShieldCheck, LuPlus, LuPanelLeftClose, LuStickyNote, LuMonitor, LuColumns2, LuPresentation, LuMinus, LuLayers, LuSettings2, LuClock, LuDownload, LuTrash2, LuCheck, LuLock, LuEye, LuFileText, LuType, LuLoader, LuShieldAlert, LuSignature, LuInfo, LuTriangleAlert, LuPrinter, LuPenTool, LuWifi, LuWifiOff, LuUsers, LuCopy, LuMonitorOff, LuChevronLeft, LuChevronRight, LuPlay, LuPause, LuMenu, LuUndo, LuRedo, LuShare2, LuPanelLeft, LuSearch, LuPanelRight, LuFolderOpen, LuVideo, LuImage, LuClipboardPaste, LuScissors, LuPaintbrush, LuChevronDown, LuSquare, LuDatabase, 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, LuClipboardCopy, LuShapes, LuLayoutGrid, LuWand, LuTextCursorInput, LuFile, LuEllipsis, LuCircle, LuMoveRight, LuTriangle, LuDiamond, LuAlignLeft, LuAlignCenter, LuAlignRight, LuAlignJustify, LuSpline, 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, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, 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
9
  import DOMPurify from 'dompurify';
10
10
  import { useTranslation } from 'react-i18next';
@@ -43571,7 +43571,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43571
43571
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43572
43572
  }
43573
43573
  function useSyncExternalStore$2(subscribe3, getSnapshot2) {
43574
- didWarnOld18Alpha || void 0 === React100.startTransition || (didWarnOld18Alpha = true, console.error(
43574
+ didWarnOld18Alpha || void 0 === React103.startTransition || (didWarnOld18Alpha = true, console.error(
43575
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."
43576
43576
  ));
43577
43577
  var value = getSnapshot2();
@@ -43581,7 +43581,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43581
43581
  "The result of getSnapshot should be cached to avoid an infinite loop"
43582
43582
  ), didWarnUncachedGetSnapshot = true);
43583
43583
  }
43584
- cachedValue = useState86({
43584
+ cachedValue = useState89({
43585
43585
  inst: { value, getSnapshot: getSnapshot2 }
43586
43586
  });
43587
43587
  var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
@@ -43593,7 +43593,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43593
43593
  },
43594
43594
  [subscribe3, value, getSnapshot2]
43595
43595
  );
43596
- useEffect72(
43596
+ useEffect73(
43597
43597
  function() {
43598
43598
  checkIfSnapshotChanged(inst) && forceUpdate({ inst });
43599
43599
  return subscribe3(function() {
@@ -43619,8 +43619,8 @@ var require_use_sync_external_store_shim_development = __commonJS({
43619
43619
  return getSnapshot2();
43620
43620
  }
43621
43621
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
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;
43622
+ var React103 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is2, useState89 = React103.useState, useEffect73 = React103.useEffect, useLayoutEffect7 = React103.useLayoutEffect, useDebugValue = React103.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 !== React103.useSyncExternalStore ? React103.useSyncExternalStore : shim;
43624
43624
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
43625
43625
  })();
43626
43626
  }
@@ -43643,9 +43643,9 @@ var require_with_selector_development = __commonJS({
43643
43643
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43644
43644
  }
43645
43645
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
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
+ var React103 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef74 = React103.useRef, useEffect73 = React103.useEffect, useMemo41 = React103.useMemo, useDebugValue = React103.useDebugValue;
43647
43647
  exports$1.useSyncExternalStoreWithSelector = function(subscribe3, getSnapshot2, getServerSnapshot2, selector, isEqual) {
43648
- var instRef = useRef73(null);
43648
+ var instRef = useRef74(null);
43649
43649
  if (null === instRef.current) {
43650
43650
  var inst = { hasValue: false, value: null };
43651
43651
  instRef.current = inst;
@@ -43686,7 +43686,7 @@ var require_with_selector_development = __commonJS({
43686
43686
  [getSnapshot2, getServerSnapshot2, selector, isEqual]
43687
43687
  );
43688
43688
  var value = useSyncExternalStore3(subscribe3, instRef[0], instRef[1]);
43689
- useEffect72(
43689
+ useEffect73(
43690
43690
  function() {
43691
43691
  inst.hasValue = true;
43692
43692
  inst.value = value;
@@ -75508,9 +75508,9 @@ function renderImageAlphaSvgFilter(element2) {
75508
75508
  const primitives = [];
75509
75509
  let resultIdx = 0;
75510
75510
  let inputRef = "SourceGraphic";
75511
- const next = (jsx229) => {
75511
+ const next = (jsx235) => {
75512
75512
  const result = `r${resultIdx++}`;
75513
- primitives.push(jsx229(inputRef, result));
75513
+ primitives.push(jsx235(inputRef, result));
75514
75514
  inputRef = result;
75515
75515
  };
75516
75516
  if (typeof e2.alphaModFix === "number") {
@@ -79551,8 +79551,8 @@ function renderTableElement(element2, textStyle, options) {
79551
79551
  {
79552
79552
  style: rowHeight ? { height: rowHeight } : void 0,
79553
79553
  children: cells.map((cell, cellIndex) => {
79554
- const isHMerged = cell["@_hMerge"] === "1" || cell["@_hMerge"] === true;
79555
- const isVMerged = cell["@_vMerge"] === "1" || cell["@_vMerge"] === true;
79554
+ const isHMerged = cell["@_hMerge"] === "1";
79555
+ const isVMerged = cell["@_vMerge"] === "1";
79556
79556
  if (isHMerged || isVMerged) {
79557
79557
  return null;
79558
79558
  }
@@ -85561,6 +85561,15 @@ function copyFormatFromElement(element2) {
85561
85561
  }
85562
85562
  return result;
85563
85563
  }
85564
+ function definedEntries(obj) {
85565
+ const out = {};
85566
+ for (const key in obj) {
85567
+ if (obj[key] !== void 0) {
85568
+ out[key] = obj[key];
85569
+ }
85570
+ }
85571
+ return out;
85572
+ }
85564
85573
  function applyFormatToElement(element2, format) {
85565
85574
  let updated = { ...element2 };
85566
85575
  if (format.shapeStyle && hasShapeProperties(updated)) {
@@ -85568,7 +85577,7 @@ function applyFormatToElement(element2, format) {
85568
85577
  ...updated,
85569
85578
  shapeStyle: {
85570
85579
  ...updated.shapeStyle,
85571
- ...format.shapeStyle
85580
+ ...definedEntries(format.shapeStyle)
85572
85581
  }
85573
85582
  };
85574
85583
  }
@@ -85577,12 +85586,18 @@ function applyFormatToElement(element2, format) {
85577
85586
  ...updated,
85578
85587
  textStyle: {
85579
85588
  ...updated.textStyle,
85580
- ...format.textStyle
85589
+ ...definedEntries(format.textStyle)
85581
85590
  }
85582
85591
  };
85583
85592
  }
85584
85593
  return updated;
85585
85594
  }
85595
+ function hasCopyableFormat(element2) {
85596
+ if (!element2) {
85597
+ return false;
85598
+ }
85599
+ return hasShapeProperties(element2) || hasTextProperties(element2);
85600
+ }
85586
85601
 
85587
85602
  // src/viewer/utils/animation-preview.ts
85588
85603
  var PRESET_TO_EFFECT = {
@@ -86931,8 +86946,8 @@ function ThumbnailTable({
86931
86946
  {
86932
86947
  style: rowHeight ? { height: rowHeight } : void 0,
86933
86948
  children: cells.map((cell, ci) => {
86934
- const isHMerged = cell["@_hMerge"] === "1" || cell["@_hMerge"] === true;
86935
- const isVMerged = cell["@_vMerge"] === "1" || cell["@_vMerge"] === true;
86949
+ const isHMerged = cell["@_hMerge"] === "1";
86950
+ const isVMerged = cell["@_vMerge"] === "1";
86936
86951
  if (isHMerged || isVMerged) {
86937
86952
  return null;
86938
86953
  }
@@ -92023,11 +92038,16 @@ function usePresenceTracking({
92023
92038
  if (cid === localClientId) {
92024
92039
  return;
92025
92040
  }
92026
- const raw = state2?.presence;
92041
+ const stateRecord = state2;
92042
+ const raw = stateRecord?.presence;
92027
92043
  if (!raw || typeof raw !== "object") {
92028
92044
  return;
92029
92045
  }
92030
- const sanitized = sanitizePresence({ ...raw, clientId: cid }, canvasWidth, canvasHeight);
92046
+ const sanitized = sanitizePresence(
92047
+ { ...raw, clientId: cid },
92048
+ canvasWidth,
92049
+ canvasHeight
92050
+ );
92031
92051
  if (!sanitized) {
92032
92052
  return;
92033
92053
  }
@@ -92095,15 +92115,9 @@ function useYjsProvider({ config }) {
92095
92115
  try {
92096
92116
  const [Y, { WebsocketProvider: WebsocketProvider2 }] = await Promise.all([Promise.resolve().then(() => (init_yjs(), yjs_exports)), Promise.resolve().then(() => (init_y_websocket(), y_websocket_exports))]);
92097
92117
  const yDoc = new Y.Doc();
92098
- const provider = new WebsocketProvider2(
92099
- config.serverUrl,
92100
- roomId,
92101
- yDoc,
92102
- // eslint-disable-line @typescript-eslint/no-explicit-any
92103
- {
92104
- params: config.authToken ? { token: config.authToken } : void 0
92105
- }
92106
- );
92118
+ const provider = new WebsocketProvider2(config.serverUrl, roomId, yDoc, {
92119
+ params: config.authToken ? { token: config.authToken } : void 0
92120
+ });
92107
92121
  let connected = false;
92108
92122
  const handleStatus = (event) => {
92109
92123
  if (event.status === "connected") {
@@ -95669,90 +95683,101 @@ var SlideNotesPanel = ({
95669
95683
  });
95670
95684
  const hasNotes = draft.trim().length > 0;
95671
95685
  const slideLabel = activeSlide ? t2("pptx.notes.slideN", { n: activeSlide.slideNumber }) : t2("pptx.notes.noSlide");
95672
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col border-t border-border/60 bg-background select-none", children: [
95673
- /* @__PURE__ */ jsxs(
95674
- "button",
95675
- {
95676
- type: "button",
95677
- onClick: onToggle,
95678
- className: "flex items-center gap-1.5 px-3 py-1 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30 transition-colors w-full text-left shrink-0",
95679
- "aria-expanded": isExpanded,
95680
- "aria-controls": "slide-notes-content",
95681
- children: [
95682
- "Notes",
95683
- !isExpanded && hasNotes && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/50 text-[10px]", children: "(has notes)" })
95684
- ]
95685
- }
95686
- ),
95687
- isExpanded && /* @__PURE__ */ jsxs(
95688
- "div",
95689
- {
95690
- id: "slide-notes-content",
95691
- className: "px-3 pb-2 overflow-y-auto",
95692
- style: { maxHeight: panelHeight ?? EXPANDED_MAX_HEIGHT + 40 },
95693
- children: [
95694
- /* @__PURE__ */ jsx("div", { className: "text-[10px] text-muted-foreground mb-1", children: slideLabel }),
95695
- canEdit ? /* @__PURE__ */ jsxs(Fragment, { children: [
95696
- /* @__PURE__ */ jsx(
95697
- NotesToolbar,
95698
- {
95699
- isRichEditEnabled,
95700
- showLinkPopover,
95701
- savedSelectionText: savedSelectionRef.current?.text ?? "",
95702
- hasAllSlides: allSlides !== void 0 && allSlides.length > 0,
95703
- onApplyRichCommand: applyRichCommand,
95704
- onToggleBulletList: toggleBulletList,
95705
- onToggleNumberedList: toggleNumberedList,
95706
- onIndent: handleIndent,
95707
- onOutdent: handleOutdent,
95708
- onLinkButtonClick: handleLinkButtonClick,
95709
- onInsertLink: handleInsertLink,
95710
- onCloseLinkPopover: () => setShowLinkPopover(false),
95711
- onPrintClick: () => setShowPrintDialog(true),
95712
- onToggleRichEdit: () => setIsRichEditEnabled((prev) => !prev)
95713
- }
95714
- ),
95715
- isRichEditEnabled ? /* @__PURE__ */ jsx(
95716
- "div",
95717
- {
95718
- ref: richEditorRef,
95719
- contentEditable: true,
95720
- suppressContentEditableWarning: true,
95721
- onInput: handleRichInput,
95722
- onBlur: handleBlur,
95723
- onKeyDown: handleKeyDownRich,
95724
- onClick: handleEditorClick,
95725
- className: "w-full overflow-y-auto rounded-md border border-border/50 bg-muted/60 px-2.5 py-1.5 text-xs text-foreground focus:border-primary/50 focus:outline-none focus:ring-1 focus:ring-primary/30 transition-colors whitespace-pre-wrap",
95726
- style: { maxHeight: EXPANDED_MAX_HEIGHT - 8, minHeight: 72 }
95727
- }
95728
- ) : /* @__PURE__ */ jsx(
95729
- "textarea",
95730
- {
95731
- ref: textareaRef,
95732
- name: "slide-notes",
95733
- value: draft,
95734
- onChange: handlePlainChange,
95735
- onBlur: handleBlur,
95736
- onKeyDown: handleKeyDownPlain,
95737
- placeholder: t2("pptx.notes.clickToAddNotes"),
95738
- rows: 4,
95739
- className: "w-full resize-none rounded-md border border-border/50 bg-muted/60 px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:outline-none focus:ring-1 focus:ring-primary/30 transition-colors",
95740
- style: { maxHeight: EXPANDED_MAX_HEIGHT - 8 }
95741
- }
95742
- )
95743
- ] }) : /* @__PURE__ */ jsx(
95744
- "div",
95745
- {
95746
- className: "w-full rounded-md border border-border/30 bg-muted/40 px-2.5 py-1.5 text-xs text-muted-foreground overflow-y-auto whitespace-pre-wrap",
95747
- style: { maxHeight: EXPANDED_MAX_HEIGHT - 32, minHeight: 60 },
95748
- children: hasNotes ? renderRichNotesSegments(draftSegments) : /* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: t2("pptx.notes.noNotes") })
95749
- }
95750
- )
95751
- ]
95752
- }
95753
- ),
95754
- showPrintDialog && allSlides && /* @__PURE__ */ jsx(NotesPrintDialog, { slides: allSlides, onClose: () => setShowPrintDialog(false) })
95755
- ] });
95686
+ return /* @__PURE__ */ jsxs(
95687
+ "div",
95688
+ {
95689
+ className: cn(
95690
+ "flex flex-col border-t border-border/60 bg-background select-none",
95691
+ // On mobile, hide the entire notes strip when collapsed — the
95692
+ // MobileBottomBar's Notes button is the entry point instead.
95693
+ !isExpanded && "max-md:hidden"
95694
+ ),
95695
+ children: [
95696
+ /* @__PURE__ */ jsxs(
95697
+ "button",
95698
+ {
95699
+ type: "button",
95700
+ onClick: onToggle,
95701
+ className: "flex items-center gap-1.5 px-3 py-1 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30 transition-colors w-full text-left shrink-0 max-md:hidden",
95702
+ "aria-expanded": isExpanded,
95703
+ "aria-controls": "slide-notes-content",
95704
+ children: [
95705
+ "Notes",
95706
+ !isExpanded && hasNotes && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/50 text-[10px]", children: "(has notes)" })
95707
+ ]
95708
+ }
95709
+ ),
95710
+ isExpanded && /* @__PURE__ */ jsxs(
95711
+ "div",
95712
+ {
95713
+ id: "slide-notes-content",
95714
+ className: "px-3 pb-2 overflow-y-auto",
95715
+ style: { maxHeight: panelHeight ?? EXPANDED_MAX_HEIGHT + 40 },
95716
+ children: [
95717
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] text-muted-foreground mb-1", children: slideLabel }),
95718
+ canEdit ? /* @__PURE__ */ jsxs(Fragment, { children: [
95719
+ /* @__PURE__ */ jsx(
95720
+ NotesToolbar,
95721
+ {
95722
+ isRichEditEnabled,
95723
+ showLinkPopover,
95724
+ savedSelectionText: savedSelectionRef.current?.text ?? "",
95725
+ hasAllSlides: allSlides !== void 0 && allSlides.length > 0,
95726
+ onApplyRichCommand: applyRichCommand,
95727
+ onToggleBulletList: toggleBulletList,
95728
+ onToggleNumberedList: toggleNumberedList,
95729
+ onIndent: handleIndent,
95730
+ onOutdent: handleOutdent,
95731
+ onLinkButtonClick: handleLinkButtonClick,
95732
+ onInsertLink: handleInsertLink,
95733
+ onCloseLinkPopover: () => setShowLinkPopover(false),
95734
+ onPrintClick: () => setShowPrintDialog(true),
95735
+ onToggleRichEdit: () => setIsRichEditEnabled((prev) => !prev)
95736
+ }
95737
+ ),
95738
+ isRichEditEnabled ? /* @__PURE__ */ jsx(
95739
+ "div",
95740
+ {
95741
+ ref: richEditorRef,
95742
+ contentEditable: true,
95743
+ suppressContentEditableWarning: true,
95744
+ onInput: handleRichInput,
95745
+ onBlur: handleBlur,
95746
+ onKeyDown: handleKeyDownRich,
95747
+ onClick: handleEditorClick,
95748
+ className: "w-full overflow-y-auto rounded-md border border-border/50 bg-muted/60 px-2.5 py-1.5 text-xs text-foreground focus:border-primary/50 focus:outline-none focus:ring-1 focus:ring-primary/30 transition-colors whitespace-pre-wrap",
95749
+ style: { maxHeight: EXPANDED_MAX_HEIGHT - 8, minHeight: 72 }
95750
+ }
95751
+ ) : /* @__PURE__ */ jsx(
95752
+ "textarea",
95753
+ {
95754
+ ref: textareaRef,
95755
+ name: "slide-notes",
95756
+ value: draft,
95757
+ onChange: handlePlainChange,
95758
+ onBlur: handleBlur,
95759
+ onKeyDown: handleKeyDownPlain,
95760
+ placeholder: t2("pptx.notes.clickToAddNotes"),
95761
+ rows: 4,
95762
+ className: "w-full resize-none rounded-md border border-border/50 bg-muted/60 px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus:border-primary/50 focus:outline-none focus:ring-1 focus:ring-primary/30 transition-colors",
95763
+ style: { maxHeight: EXPANDED_MAX_HEIGHT - 8 }
95764
+ }
95765
+ )
95766
+ ] }) : /* @__PURE__ */ jsx(
95767
+ "div",
95768
+ {
95769
+ className: "w-full rounded-md border border-border/30 bg-muted/40 px-2.5 py-1.5 text-xs text-muted-foreground overflow-y-auto whitespace-pre-wrap",
95770
+ style: { maxHeight: EXPANDED_MAX_HEIGHT - 32, minHeight: 60 },
95771
+ children: hasNotes ? renderRichNotesSegments(draftSegments) : /* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: t2("pptx.notes.noNotes") })
95772
+ }
95773
+ )
95774
+ ]
95775
+ }
95776
+ ),
95777
+ showPrintDialog && allSlides && /* @__PURE__ */ jsx(NotesPrintDialog, { slides: allSlides, onClose: () => setShowPrintDialog(false) })
95778
+ ]
95779
+ }
95780
+ );
95756
95781
  };
95757
95782
  var DB_NAME = "pptx-viewer-autosave";
95758
95783
  var DB_VERSION = 1;
@@ -98289,7 +98314,8 @@ function ArrangeSection(p3) {
98289
98314
  {
98290
98315
  type: "button",
98291
98316
  onClick: p3.onToggleFormatPainter,
98292
- disabled: !p3.canEdit,
98317
+ disabled: !p3.canEdit || p3.canActivateFormatPainter === false && !p3.formatPainterActive,
98318
+ "data-testid": "format-painter-toggle",
98293
98319
  className: cn(
98294
98320
  pill,
98295
98321
  p3.formatPainterActive ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
@@ -98741,7 +98767,8 @@ function HomeSection(p3) {
98741
98767
  const { fontFamily, fontSize } = extractFontInfo(p3.selectedElement);
98742
98768
  const handleNewSlide = useCallback(() => {
98743
98769
  if (p3.layoutOptions.length > 0) {
98744
- p3.onInsertSlideFromLayout(p3.layoutOptions[0].path);
98770
+ const first = p3.layoutOptions[0];
98771
+ p3.onInsertSlideFromLayout(first.path, first.name);
98745
98772
  }
98746
98773
  }, [p3]);
98747
98774
  useEffect(() => {
@@ -98828,7 +98855,8 @@ function HomeSection(p3) {
98828
98855
  {
98829
98856
  type: "button",
98830
98857
  onClick: p3.onToggleFormatPainter,
98831
- disabled: !p3.canEdit,
98858
+ disabled: !p3.canEdit || p3.canActivateFormatPainter === false && !p3.formatPainterActive,
98859
+ "data-testid": "format-painter-toggle",
98832
98860
  className: cn(
98833
98861
  gL,
98834
98862
  p3.formatPainterActive ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
@@ -98878,7 +98906,7 @@ function HomeSection(p3) {
98878
98906
  type: "button",
98879
98907
  className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
98880
98908
  onClick: () => {
98881
- p3.onInsertSlideFromLayout(lo.path);
98909
+ p3.onInsertSlideFromLayout(lo.path, lo.name);
98882
98910
  setLayoutMenuOpen(false);
98883
98911
  },
98884
98912
  children: lo.name
@@ -99778,6 +99806,561 @@ function TextSection(p3) {
99778
99806
  ] })
99779
99807
  ] });
99780
99808
  }
99809
+ function ViewSection(p3) {
99810
+ const { t: t2 } = useTranslation();
99811
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
99812
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99813
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
99814
+ /* @__PURE__ */ jsx("button", { className: pill, title: "Normal view", children: "Normal" }),
99815
+ p3.onToggleSlideSorter ? /* @__PURE__ */ jsx("button", { className: pill, onClick: p3.onToggleSlideSorter, title: "Slide Sorter view", children: "Slide Sorter" }) : /* @__PURE__ */ jsx("button", { className: pill, title: "Slide Sorter view", children: "Slide Sorter" }),
99816
+ /* @__PURE__ */ jsx("button", { className: pill, title: "Reading View", children: "Reading View" })
99817
+ ] }),
99818
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Presentation Views" })
99819
+ ] }),
99820
+ sep,
99821
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99822
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: /* @__PURE__ */ jsx(
99823
+ "button",
99824
+ {
99825
+ onClick: p3.onEnterMasterView,
99826
+ disabled: !p3.canEdit,
99827
+ className: pill,
99828
+ title: "Edit slide masters and layouts",
99829
+ children: "Slide Master"
99830
+ }
99831
+ ) }),
99832
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Master Views" })
99833
+ ] }),
99834
+ sep,
99835
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99836
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: p3.onZoomToFit && /* @__PURE__ */ jsx("button", { className: pill, onClick: p3.onZoomToFit, title: "Zoom to fit slide in window", children: "Zoom to Fit" }) }),
99837
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Zoom" })
99838
+ ] }),
99839
+ sep,
99840
+ /* @__PURE__ */ jsx(
99841
+ "button",
99842
+ {
99843
+ onClick: () => p3.onSetEditTemplateMode(!p3.editTemplateMode),
99844
+ disabled: !p3.canEdit,
99845
+ className: cn(
99846
+ pill,
99847
+ p3.editTemplateMode ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
99848
+ ),
99849
+ title: "Toggle template/master element editing",
99850
+ children: p3.editTemplateMode ? "Templates On" : "Templates Off"
99851
+ }
99852
+ ),
99853
+ p3.onToggleSelectionPane && /* @__PURE__ */ jsxs(
99854
+ "button",
99855
+ {
99856
+ type: "button",
99857
+ onClick: p3.onToggleSelectionPane,
99858
+ className: cn(
99859
+ pill,
99860
+ p3.isSelectionPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
99861
+ ),
99862
+ title: "Selection Pane",
99863
+ children: [
99864
+ /* @__PURE__ */ jsx(LuList, { className: ic2 }),
99865
+ "Selection"
99866
+ ]
99867
+ }
99868
+ ),
99869
+ p3.onToggleEyedropper && /* @__PURE__ */ jsxs(
99870
+ "button",
99871
+ {
99872
+ type: "button",
99873
+ onClick: p3.onToggleEyedropper,
99874
+ disabled: !p3.canEdit,
99875
+ className: cn(
99876
+ pill,
99877
+ p3.eyedropperActive ? "bg-purple-600 hover:bg-purple-500 text-purple-50" : ""
99878
+ ),
99879
+ title: "Eyedropper \u2014 sample a colour from the slide",
99880
+ children: [
99881
+ /* @__PURE__ */ jsx(LuPipette, { className: ic2 }),
99882
+ "Eyedropper"
99883
+ ]
99884
+ }
99885
+ ),
99886
+ /* @__PURE__ */ jsx(
99887
+ "button",
99888
+ {
99889
+ onClick: () => p3.onSetShowGrid(!p3.showGrid),
99890
+ className: cn(pill, p3.showGrid ? "bg-primary text-primary-foreground" : ""),
99891
+ title: t2("pptx.grid.toggleGrid"),
99892
+ children: t2("pptx.grid.grid")
99893
+ }
99894
+ ),
99895
+ /* @__PURE__ */ jsx(
99896
+ "button",
99897
+ {
99898
+ onClick: () => p3.onSetShowRulers(!p3.showRulers),
99899
+ className: cn(pill, p3.showRulers ? "bg-primary text-primary-foreground" : ""),
99900
+ title: t2("pptx.ruler.toggleRulers"),
99901
+ children: t2("pptx.ruler.rulers")
99902
+ }
99903
+ ),
99904
+ /* @__PURE__ */ jsx(
99905
+ "button",
99906
+ {
99907
+ onClick: () => p3.onSetSnapToGrid(!p3.snapToGrid),
99908
+ className: cn(pill, p3.snapToGrid ? "bg-primary text-primary-foreground" : ""),
99909
+ title: t2("pptx.grid.snapToGrid"),
99910
+ children: t2("pptx.grid.snapToGrid")
99911
+ }
99912
+ ),
99913
+ /* @__PURE__ */ jsx(
99914
+ "button",
99915
+ {
99916
+ onClick: () => p3.onSetSnapToShape(!p3.snapToShape),
99917
+ className: cn(pill, p3.snapToShape ? "bg-primary text-primary-foreground" : ""),
99918
+ title: t2("pptx.grid.snapToShape"),
99919
+ children: t2("pptx.grid.snapToShape")
99920
+ }
99921
+ ),
99922
+ /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("h"), className: pill, title: "Add horizontal guide", children: "H Guide" }),
99923
+ /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("v"), className: pill, title: "Add vertical guide", children: "V Guide" }),
99924
+ /* @__PURE__ */ jsx(
99925
+ "button",
99926
+ {
99927
+ onClick: () => p3.onSetSpellCheckEnabled(!p3.spellCheckEnabled),
99928
+ className: cn(pill, p3.spellCheckEnabled ? "bg-primary text-primary-foreground" : ""),
99929
+ title: "Toggle spell check",
99930
+ children: "Spell"
99931
+ }
99932
+ )
99933
+ ] });
99934
+ }
99935
+ function MobileSheet({
99936
+ open,
99937
+ onClose,
99938
+ title,
99939
+ children,
99940
+ heightFraction = 0.6,
99941
+ fullScreen = false,
99942
+ className,
99943
+ headerRight
99944
+ }) {
99945
+ const sheetRef = useRef(null);
99946
+ const [dragY, setDragY] = useState(0);
99947
+ const dragStartRef = useRef(null);
99948
+ const onPointerDown = useCallback((e2) => {
99949
+ dragStartRef.current = e2.clientY;
99950
+ e2.target.setPointerCapture?.(e2.pointerId);
99951
+ }, []);
99952
+ const onPointerMove = useCallback((e2) => {
99953
+ if (dragStartRef.current === null) {
99954
+ return;
99955
+ }
99956
+ const delta = e2.clientY - dragStartRef.current;
99957
+ setDragY(Math.max(0, delta));
99958
+ }, []);
99959
+ const onPointerUp = useCallback(
99960
+ (e2) => {
99961
+ if (dragStartRef.current === null) {
99962
+ return;
99963
+ }
99964
+ const delta = e2.clientY - dragStartRef.current;
99965
+ dragStartRef.current = null;
99966
+ e2.target.releasePointerCapture?.(e2.pointerId);
99967
+ if (delta > 120) {
99968
+ onClose();
99969
+ }
99970
+ setDragY(0);
99971
+ },
99972
+ [onClose]
99973
+ );
99974
+ useEffect(() => {
99975
+ if (!open) {
99976
+ return;
99977
+ }
99978
+ const handleKey = (e2) => {
99979
+ if (e2.key === "Escape") {
99980
+ onClose();
99981
+ }
99982
+ };
99983
+ window.addEventListener("keydown", handleKey);
99984
+ return () => window.removeEventListener("keydown", handleKey);
99985
+ }, [open, onClose]);
99986
+ if (!open) {
99987
+ return null;
99988
+ }
99989
+ const heightStyle = fullScreen ? { height: "calc(100dvh - env(safe-area-inset-top))" } : { height: `${Math.round(heightFraction * 100)}dvh` };
99990
+ return /* @__PURE__ */ jsxs(
99991
+ "div",
99992
+ {
99993
+ className: "fixed inset-0 z-50 flex flex-col justify-end md:hidden",
99994
+ role: "dialog",
99995
+ "aria-modal": "true",
99996
+ children: [
99997
+ /* @__PURE__ */ jsx(
99998
+ "button",
99999
+ {
100000
+ type: "button",
100001
+ "aria-label": "Close",
100002
+ className: "absolute inset-0 bg-black/40 backdrop-blur-[2px] animate-in fade-in duration-150",
100003
+ onClick: onClose
100004
+ }
100005
+ ),
100006
+ /* @__PURE__ */ jsxs(
100007
+ "div",
100008
+ {
100009
+ ref: sheetRef,
100010
+ className: cn(
100011
+ "relative bg-background border-t border-border rounded-t-2xl shadow-2xl flex flex-col overflow-hidden",
100012
+ "animate-in slide-in-from-bottom duration-200",
100013
+ className
100014
+ ),
100015
+ style: {
100016
+ ...heightStyle,
100017
+ transform: dragY > 0 ? `translateY(${dragY}px)` : void 0,
100018
+ transition: dragStartRef.current === null ? "transform 150ms ease-out" : "none"
100019
+ },
100020
+ children: [
100021
+ /* @__PURE__ */ jsx(
100022
+ "div",
100023
+ {
100024
+ className: "flex items-center justify-center pt-2 pb-1 cursor-grab active:cursor-grabbing touch-none",
100025
+ onPointerDown,
100026
+ onPointerMove,
100027
+ onPointerUp,
100028
+ onPointerCancel: onPointerUp,
100029
+ children: /* @__PURE__ */ jsx("div", { className: "h-1 w-10 rounded-full bg-muted-foreground/40" })
100030
+ }
100031
+ ),
100032
+ (title || headerRight) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-4 pb-2 border-b border-border/60", children: [
100033
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground truncate", children: title }),
100034
+ headerRight
100035
+ ] }),
100036
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overscroll-contain", children })
100037
+ ]
100038
+ }
100039
+ )
100040
+ ]
100041
+ }
100042
+ );
100043
+ }
100044
+ var MENU_ITEMS = [
100045
+ { key: "home", label: "Home", icon: LuClipboardCopy },
100046
+ { key: "insert", label: "Insert", icon: LuPlus },
100047
+ { key: "text", label: "Text", icon: LuType },
100048
+ { key: "draw", label: "Draw", icon: LuPaintbrush },
100049
+ { key: "arrange", label: "Arrange", icon: LuShapes },
100050
+ { key: "design", label: "Design", icon: LuLayoutGrid },
100051
+ { key: "transitions", label: "Transitions", icon: LuSparkles },
100052
+ { key: "animations", label: "Animations", icon: LuWand },
100053
+ { key: "slideShow", label: "Slide Show", icon: LuPresentation },
100054
+ { key: "review", label: "Review", icon: LuTextCursorInput },
100055
+ { key: "view", label: "View", icon: LuSettings },
100056
+ { key: "file", label: "File", icon: LuFile }
100057
+ ];
100058
+ function MobileMenuSheet(props) {
100059
+ const { open, onClose } = props;
100060
+ const [active, setActive] = useState("home");
100061
+ return /* @__PURE__ */ jsx(MobileSheet, { open, onClose, fullScreen: true, title: "Menu", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
100062
+ /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-background border-b border-border", children: /* @__PURE__ */ jsx("div", { className: "flex gap-1.5 overflow-x-auto px-3 py-2 scrollbar-none", children: MENU_ITEMS.map(({ key, label, icon: Icon }) => /* @__PURE__ */ jsxs(
100063
+ "button",
100064
+ {
100065
+ type: "button",
100066
+ onClick: () => setActive(active === key ? null : key),
100067
+ className: cn(
100068
+ "inline-flex items-center gap-1.5 shrink-0 px-3 py-2 rounded-full text-[12px] font-medium border transition-colors min-h-[36px]",
100069
+ active === key ? "bg-primary text-primary-foreground border-primary" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent/40"
100070
+ ),
100071
+ children: [
100072
+ /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }),
100073
+ label
100074
+ ]
100075
+ },
100076
+ key
100077
+ )) }) }),
100078
+ /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsx(MobileSectionBody, { active, ...props }) })
100079
+ ] }) });
100080
+ }
100081
+ function MobileSectionBody({
100082
+ active,
100083
+ ...p3
100084
+ }) {
100085
+ const wrap = "flex flex-wrap items-center gap-2";
100086
+ switch (active) {
100087
+ case "home":
100088
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100089
+ HomeSection,
100090
+ {
100091
+ canEdit: p3.canEdit,
100092
+ clipboardPayload: p3.clipboardPayload,
100093
+ formatPainterActive: p3.formatPainterActive,
100094
+ canActivateFormatPainter: p3.canActivateFormatPainter,
100095
+ onCopy: p3.onCopy,
100096
+ onCut: p3.onCut,
100097
+ onPaste: p3.onPaste,
100098
+ onToggleFormatPainter: p3.onToggleFormatPainter,
100099
+ layoutOptions: p3.layoutOptions,
100100
+ onInsertSlideFromLayout: p3.onInsertSlideFromLayout,
100101
+ selectedElement: p3.selectedElement,
100102
+ onUpdateTextStyle: p3.onUpdateTextStyle
100103
+ }
100104
+ ) });
100105
+ case "insert":
100106
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100107
+ InsertSection,
100108
+ {
100109
+ canEdit: p3.canEdit,
100110
+ newShapeType: p3.newShapeType,
100111
+ onSetNewShapeType: p3.onSetNewShapeType,
100112
+ onAddTextBox: p3.onAddTextBox,
100113
+ onAddShape: p3.onAddShape,
100114
+ onAddTable: p3.onAddTable,
100115
+ onAddSmartArt: p3.onAddSmartArt,
100116
+ onAddEquation: p3.onAddEquation,
100117
+ onAddActionButton: p3.onAddActionButton,
100118
+ onInsertField: p3.onInsertField,
100119
+ onOpenImagePicker: p3.onOpenImagePicker,
100120
+ onOpenMediaPicker: p3.onOpenMediaPicker
100121
+ }
100122
+ ) });
100123
+ case "text":
100124
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100125
+ TextSection,
100126
+ {
100127
+ canEdit: p3.canEdit,
100128
+ selectedElement: p3.selectedElement,
100129
+ tableEditorState: p3.tableEditorState,
100130
+ onUpdateTextStyle: p3.onUpdateTextStyle
100131
+ }
100132
+ ) });
100133
+ case "draw":
100134
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100135
+ DrawSection,
100136
+ {
100137
+ activeTool: p3.activeTool,
100138
+ drawingColor: p3.drawingColor,
100139
+ drawingWidth: p3.drawingWidth,
100140
+ onSetActiveTool: p3.onSetActiveTool,
100141
+ onSetDrawingColor: p3.onSetDrawingColor,
100142
+ onSetDrawingWidth: p3.onSetDrawingWidth
100143
+ }
100144
+ ) });
100145
+ case "arrange":
100146
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100147
+ ArrangeSection,
100148
+ {
100149
+ canEdit: p3.canEdit,
100150
+ selectedElement: p3.selectedElement,
100151
+ clipboardPayload: p3.clipboardPayload,
100152
+ onAlignElements: p3.onAlignElements,
100153
+ onCopy: p3.onCopy,
100154
+ onCut: p3.onCut,
100155
+ onPaste: p3.onPaste,
100156
+ onFlip: p3.onFlip,
100157
+ onMoveLayer: p3.onMoveLayer,
100158
+ onMoveLayerToEdge: p3.onMoveLayerToEdge,
100159
+ onDuplicate: p3.onDuplicate,
100160
+ onDelete: p3.onDelete,
100161
+ formatPainterActive: p3.formatPainterActive,
100162
+ onToggleFormatPainter: p3.onToggleFormatPainter,
100163
+ canActivateFormatPainter: p3.canActivateFormatPainter
100164
+ }
100165
+ ) });
100166
+ case "design":
100167
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100168
+ DesignSection,
100169
+ {
100170
+ canEdit: p3.canEdit,
100171
+ onToggleThemeGallery: p3.onToggleThemeGallery,
100172
+ isThemeGalleryOpen: p3.isThemeGalleryOpen,
100173
+ onToggleThemeEditor: p3.onToggleThemeEditor,
100174
+ isThemeEditorOpen: p3.isThemeEditorOpen,
100175
+ onOpenDocumentProperties: p3.onOpenDocumentProperties,
100176
+ onToggleInspector: p3.onToggleInspector,
100177
+ isInspectorPaneOpen: p3.isInspectorPaneOpen
100178
+ }
100179
+ ) });
100180
+ case "transitions":
100181
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100182
+ TransitionsSection,
100183
+ {
100184
+ isInspectorPaneOpen: p3.isInspectorPaneOpen,
100185
+ onToggleInspector: p3.onToggleInspector
100186
+ }
100187
+ ) });
100188
+ case "animations":
100189
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100190
+ AnimationsSection,
100191
+ {
100192
+ canEdit: p3.canEdit,
100193
+ selectedElement: p3.selectedElement,
100194
+ isInspectorPaneOpen: p3.isInspectorPaneOpen,
100195
+ onToggleInspector: p3.onToggleInspector,
100196
+ onOpenAnimationPanel: p3.onOpenAnimationPanel,
100197
+ onAddAnimation: p3.onAddAnimation,
100198
+ onRemoveAnimation: p3.onRemoveAnimation
100199
+ }
100200
+ ) });
100201
+ case "slideShow":
100202
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100203
+ SlideShowSection,
100204
+ {
100205
+ onPresent: () => p3.onSetMode("present"),
100206
+ onEnterPresenterView: p3.onEnterPresenterView ?? (() => {
100207
+ }),
100208
+ onEnterRehearsalMode: p3.onEnterRehearsalMode ?? (() => {
100209
+ }),
100210
+ onOpenSetUpSlideShow: p3.onOpenSetUpSlideShow ?? (() => {
100211
+ }),
100212
+ onOpenBroadcastDialog: p3.onOpenBroadcastDialog ?? (() => {
100213
+ }),
100214
+ onToggleSubtitles: p3.onToggleSubtitles ?? (() => {
100215
+ }),
100216
+ showSubtitles: p3.showSubtitles ?? false,
100217
+ onSetMode: p3.onSetMode
100218
+ }
100219
+ ) });
100220
+ case "review":
100221
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100222
+ ReviewSection,
100223
+ {
100224
+ canEdit: p3.canEdit,
100225
+ spellCheckEnabled: p3.spellCheckEnabled,
100226
+ onSetSpellCheckEnabled: p3.onSetSpellCheckEnabled,
100227
+ onToggleComments: p3.onToggleComments,
100228
+ isCommentsPanelOpen: p3.isCommentsPanelOpen,
100229
+ slideCommentCount: p3.slideCommentCount,
100230
+ onCompare: p3.onCompare
100231
+ }
100232
+ ) });
100233
+ case "view":
100234
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100235
+ ViewSection,
100236
+ {
100237
+ canEdit: p3.canEdit,
100238
+ editTemplateMode: p3.editTemplateMode,
100239
+ onSetEditTemplateMode: p3.onSetEditTemplateMode,
100240
+ spellCheckEnabled: p3.spellCheckEnabled,
100241
+ onSetSpellCheckEnabled: p3.onSetSpellCheckEnabled,
100242
+ showGrid: p3.showGrid,
100243
+ showRulers: p3.showRulers,
100244
+ snapToGrid: p3.snapToGrid,
100245
+ snapToShape: p3.snapToShape,
100246
+ onSetShowGrid: p3.onSetShowGrid,
100247
+ onSetShowRulers: p3.onSetShowRulers,
100248
+ onSetSnapToGrid: p3.onSetSnapToGrid,
100249
+ onSetSnapToShape: p3.onSetSnapToShape,
100250
+ onAddGuide: p3.onAddGuide,
100251
+ onEnterMasterView: p3.onEnterMasterView,
100252
+ isSelectionPaneOpen: p3.isSelectionPaneOpen,
100253
+ onToggleSelectionPane: p3.onToggleSelectionPane,
100254
+ eyedropperActive: p3.eyedropperActive,
100255
+ onToggleEyedropper: p3.onToggleEyedropper
100256
+ }
100257
+ ) });
100258
+ case "file":
100259
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100260
+ FileSection,
100261
+ {
100262
+ onExportPng: p3.onExportPng,
100263
+ onExportPdf: p3.onExportPdf,
100264
+ onExportVideo: p3.onExportVideo,
100265
+ onExportGif: p3.onExportGif,
100266
+ onPackageForSharing: p3.onPackageForSharing,
100267
+ onSaveAsPptx: p3.onSaveAsPptx,
100268
+ onSaveAsPpsx: p3.onSaveAsPpsx,
100269
+ onSaveAsPptm: p3.onSaveAsPptm,
100270
+ hasMacros: p3.hasMacros,
100271
+ onCopySlideAsImage: p3.onCopySlideAsImage,
100272
+ onPrint: p3.onPrint,
100273
+ onOpenDocumentProperties: p3.onOpenDocumentProperties,
100274
+ onOpenPasswordProtection: p3.onOpenPasswordProtection,
100275
+ onOpenFontEmbedding: p3.onOpenFontEmbedding,
100276
+ onOpenDigitalSignatures: p3.onOpenDigitalSignatures
100277
+ }
100278
+ ) });
100279
+ default:
100280
+ return /* @__PURE__ */ jsxs("div", { className: "text-center text-sm text-muted-foreground py-8", children: [
100281
+ /* @__PURE__ */ jsx(LuChevronRight, { className: "w-5 h-5 inline-block opacity-50" }),
100282
+ " Select a section above"
100283
+ ] });
100284
+ }
100285
+ }
100286
+ function MobileToolbar(props) {
100287
+ const { t: t2 } = useTranslation();
100288
+ const { mode, canUndo, canRedo, onUndo, onRedo, onSetMode } = props;
100289
+ const [menuOpen, setMenuOpen] = useState(false);
100290
+ const showEdit = mode === "edit" || mode === "master";
100291
+ const btn = "inline-flex items-center justify-center min-w-[44px] min-h-[44px] rounded-md text-foreground/80 hover:bg-accent/60 disabled:opacity-40 disabled:cursor-not-allowed active:scale-95 transition-transform";
100292
+ return /* @__PURE__ */ jsxs(
100293
+ "div",
100294
+ {
100295
+ role: "toolbar",
100296
+ "aria-label": "Toolbar",
100297
+ className: "relative z-20 flex items-center gap-1 px-2 py-1 border-b border-border bg-secondary/50 min-h-[52px] pt-[max(env(safe-area-inset-top),0px)]",
100298
+ children: [
100299
+ showEdit && /* @__PURE__ */ jsx(
100300
+ "button",
100301
+ {
100302
+ type: "button",
100303
+ onClick: () => setMenuOpen(true),
100304
+ className: btn,
100305
+ title: "Menu",
100306
+ "aria-label": "Menu",
100307
+ children: /* @__PURE__ */ jsx(LuMenu, { className: "w-5 h-5" })
100308
+ }
100309
+ ),
100310
+ showEdit && /* @__PURE__ */ jsxs(Fragment, { children: [
100311
+ /* @__PURE__ */ jsx(
100312
+ "button",
100313
+ {
100314
+ type: "button",
100315
+ onClick: onUndo,
100316
+ disabled: !canUndo,
100317
+ className: btn,
100318
+ title: t2("pptx.toolbar.undo"),
100319
+ "aria-label": t2("pptx.toolbar.undo"),
100320
+ children: /* @__PURE__ */ jsx(LuUndo, { className: "w-5 h-5" })
100321
+ }
100322
+ ),
100323
+ /* @__PURE__ */ jsx(
100324
+ "button",
100325
+ {
100326
+ type: "button",
100327
+ onClick: onRedo,
100328
+ disabled: !canRedo,
100329
+ className: btn,
100330
+ title: t2("pptx.toolbar.redo"),
100331
+ "aria-label": t2("pptx.toolbar.redo"),
100332
+ children: /* @__PURE__ */ jsx(LuRedo, { className: "w-5 h-5" })
100333
+ }
100334
+ )
100335
+ ] }),
100336
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
100337
+ /* @__PURE__ */ jsx(
100338
+ "button",
100339
+ {
100340
+ type: "button",
100341
+ onClick: () => onSetMode("present"),
100342
+ className: cn(btn, "text-primary"),
100343
+ title: t2("pptx.toolbar.present"),
100344
+ "aria-label": t2("pptx.toolbar.present"),
100345
+ children: /* @__PURE__ */ jsx(LuPresentation, { className: "w-5 h-5" })
100346
+ }
100347
+ ),
100348
+ showEdit && /* @__PURE__ */ jsx(
100349
+ "button",
100350
+ {
100351
+ type: "button",
100352
+ onClick: props.onOpenShareDialog ?? props.onPackageForSharing,
100353
+ className: cn(btn, "bg-primary text-primary-foreground hover:bg-primary/90 px-3"),
100354
+ title: t2("pptx.toolbar.share"),
100355
+ "aria-label": t2("pptx.toolbar.share"),
100356
+ children: /* @__PURE__ */ jsx(LuShare2, { className: "w-4 h-4" })
100357
+ }
100358
+ ),
100359
+ /* @__PURE__ */ jsx(MobileMenuSheet, { open: menuOpen, onClose: () => setMenuOpen(false), ...props })
100360
+ ]
100361
+ }
100362
+ );
100363
+ }
99781
100364
  function CustomShowsControls({
99782
100365
  customShows,
99783
100366
  activeCustomShowId,
@@ -100206,7 +100789,7 @@ function ToolbarPrimaryRow(p3) {
100206
100789
  onClick: p3.onToggleComments,
100207
100790
  className: cn(
100208
100791
  qab,
100209
- "max-md:hidden",
100792
+ "relative max-md:hidden",
100210
100793
  p3.isCommentsPanelOpen ? "text-foreground" : "text-muted-foreground"
100211
100794
  ),
100212
100795
  title: t2("pptx.toolbar.comments"),
@@ -100322,134 +100905,11 @@ function ToolbarPrimaryRow(p3) {
100322
100905
  /* @__PURE__ */ jsx(OverflowMenu, { ...p3 })
100323
100906
  ] });
100324
100907
  }
100325
- function ViewSection(p3) {
100326
- const { t: t2 } = useTranslation();
100327
- return /* @__PURE__ */ jsxs(Fragment, { children: [
100328
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
100329
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
100330
- /* @__PURE__ */ jsx("button", { className: pill, title: "Normal view", children: "Normal" }),
100331
- p3.onToggleSlideSorter ? /* @__PURE__ */ jsx("button", { className: pill, onClick: p3.onToggleSlideSorter, title: "Slide Sorter view", children: "Slide Sorter" }) : /* @__PURE__ */ jsx("button", { className: pill, title: "Slide Sorter view", children: "Slide Sorter" }),
100332
- /* @__PURE__ */ jsx("button", { className: pill, title: "Reading View", children: "Reading View" })
100333
- ] }),
100334
- /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Presentation Views" })
100335
- ] }),
100336
- sep,
100337
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
100338
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: /* @__PURE__ */ jsx(
100339
- "button",
100340
- {
100341
- onClick: p3.onEnterMasterView,
100342
- disabled: !p3.canEdit,
100343
- className: pill,
100344
- title: "Edit slide masters and layouts",
100345
- children: "Slide Master"
100346
- }
100347
- ) }),
100348
- /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Master Views" })
100349
- ] }),
100350
- sep,
100351
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
100352
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: p3.onZoomToFit && /* @__PURE__ */ jsx("button", { className: pill, onClick: p3.onZoomToFit, title: "Zoom to fit slide in window", children: "Zoom to Fit" }) }),
100353
- /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Zoom" })
100354
- ] }),
100355
- sep,
100356
- /* @__PURE__ */ jsx(
100357
- "button",
100358
- {
100359
- onClick: () => p3.onSetEditTemplateMode(!p3.editTemplateMode),
100360
- disabled: !p3.canEdit,
100361
- className: cn(
100362
- pill,
100363
- p3.editTemplateMode ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
100364
- ),
100365
- title: "Toggle template/master element editing",
100366
- children: p3.editTemplateMode ? "Templates On" : "Templates Off"
100367
- }
100368
- ),
100369
- p3.onToggleSelectionPane && /* @__PURE__ */ jsxs(
100370
- "button",
100371
- {
100372
- type: "button",
100373
- onClick: p3.onToggleSelectionPane,
100374
- className: cn(
100375
- pill,
100376
- p3.isSelectionPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
100377
- ),
100378
- title: "Selection Pane",
100379
- children: [
100380
- /* @__PURE__ */ jsx(LuList, { className: ic2 }),
100381
- "Selection"
100382
- ]
100383
- }
100384
- ),
100385
- p3.onToggleEyedropper && /* @__PURE__ */ jsxs(
100386
- "button",
100387
- {
100388
- type: "button",
100389
- onClick: p3.onToggleEyedropper,
100390
- disabled: !p3.canEdit,
100391
- className: cn(
100392
- pill,
100393
- p3.eyedropperActive ? "bg-purple-600 hover:bg-purple-500 text-purple-50" : ""
100394
- ),
100395
- title: "Eyedropper \u2014 sample a colour from the slide",
100396
- children: [
100397
- /* @__PURE__ */ jsx(LuPipette, { className: ic2 }),
100398
- "Eyedropper"
100399
- ]
100400
- }
100401
- ),
100402
- /* @__PURE__ */ jsx(
100403
- "button",
100404
- {
100405
- onClick: () => p3.onSetShowGrid(!p3.showGrid),
100406
- className: cn(pill, p3.showGrid ? "bg-primary text-primary-foreground" : ""),
100407
- title: t2("pptx.grid.toggleGrid"),
100408
- children: t2("pptx.grid.grid")
100409
- }
100410
- ),
100411
- /* @__PURE__ */ jsx(
100412
- "button",
100413
- {
100414
- onClick: () => p3.onSetShowRulers(!p3.showRulers),
100415
- className: cn(pill, p3.showRulers ? "bg-primary text-primary-foreground" : ""),
100416
- title: t2("pptx.ruler.toggleRulers"),
100417
- children: t2("pptx.ruler.rulers")
100418
- }
100419
- ),
100420
- /* @__PURE__ */ jsx(
100421
- "button",
100422
- {
100423
- onClick: () => p3.onSetSnapToGrid(!p3.snapToGrid),
100424
- className: cn(pill, p3.snapToGrid ? "bg-primary text-primary-foreground" : ""),
100425
- title: t2("pptx.grid.snapToGrid"),
100426
- children: t2("pptx.grid.snapToGrid")
100427
- }
100428
- ),
100429
- /* @__PURE__ */ jsx(
100430
- "button",
100431
- {
100432
- onClick: () => p3.onSetSnapToShape(!p3.snapToShape),
100433
- className: cn(pill, p3.snapToShape ? "bg-primary text-primary-foreground" : ""),
100434
- title: t2("pptx.grid.snapToShape"),
100435
- children: t2("pptx.grid.snapToShape")
100436
- }
100437
- ),
100438
- /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("h"), className: pill, title: "Add horizontal guide", children: "H Guide" }),
100439
- /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("v"), className: pill, title: "Add vertical guide", children: "V Guide" }),
100440
- /* @__PURE__ */ jsx(
100441
- "button",
100442
- {
100443
- onClick: () => p3.onSetSpellCheckEnabled(!p3.spellCheckEnabled),
100444
- className: cn(pill, p3.spellCheckEnabled ? "bg-primary text-primary-foreground" : ""),
100445
- title: "Toggle spell check",
100446
- children: "Spell"
100447
- }
100448
- )
100449
- ] });
100450
- }
100451
100908
  function Toolbar(p3) {
100452
100909
  const { mode, isNarrowViewport, isCompactToolbarOpen, toolbarSection, onSetToolbarSection } = p3;
100910
+ if (isNarrowViewport && mode !== "present") {
100911
+ return /* @__PURE__ */ jsx(MobileToolbar, { ...p3 });
100912
+ }
100453
100913
  const sFil = toolbarSection === "file";
100454
100914
  const sHome = toolbarSection === "home";
100455
100915
  const sIns = toolbarSection === "insert";
@@ -100535,6 +100995,7 @@ function Toolbar(p3) {
100535
100995
  canEdit: p3.canEdit,
100536
100996
  clipboardPayload: p3.clipboardPayload,
100537
100997
  formatPainterActive: p3.formatPainterActive,
100998
+ canActivateFormatPainter: p3.canActivateFormatPainter,
100538
100999
  onCopy: p3.onCopy,
100539
101000
  onCut: p3.onCut,
100540
101001
  onPaste: p3.onPaste,
@@ -100598,7 +101059,8 @@ function Toolbar(p3) {
100598
101059
  onDuplicate: p3.onDuplicate,
100599
101060
  onDelete: p3.onDelete,
100600
101061
  formatPainterActive: p3.formatPainterActive,
100601
- onToggleFormatPainter: p3.onToggleFormatPainter
101062
+ onToggleFormatPainter: p3.onToggleFormatPainter,
101063
+ canActivateFormatPainter: p3.canActivateFormatPainter
100602
101064
  }
100603
101065
  ),
100604
101066
  sDes && /* @__PURE__ */ jsx(
@@ -106311,181 +106773,194 @@ function InspectorPane(props) {
106311
106773
  const fallback = themeOptions[0]?.path ?? "";
106312
106774
  setSelectedThemePath((previous) => previous || fallback);
106313
106775
  }, [slideMasters, themeOptions]);
106314
- return /* @__PURE__ */ jsxs(
106315
- "div",
106316
- {
106317
- className: cn(
106318
- // Shared styles
106319
- "bg-background flex flex-col text-xs text-foreground shadow-xl",
106320
- // Mobile: absolute bottom sheet overlay
106321
- "max-md:fixed max-md:inset-x-0 max-md:bottom-0 max-md:top-auto max-md:w-full max-md:max-h-[60vh] max-md:rounded-t-xl max-md:border-t max-md:border-border max-md:z-30",
106322
- "max-md:transition-transform max-md:duration-200 max-md:ease-in-out",
106323
- isOpen ? "max-md:translate-y-0" : "max-md:translate-y-full",
106324
- // Desktop: flow-based flex child (takes space from canvas)
106325
- "md:h-full md:border-l md:border-border",
106326
- !panelWidth && "md:w-72"
106327
- ),
106328
- style: panelWidth ? { width: panelWidth } : void 0,
106329
- children: [
106330
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border", children: [
106331
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 rounded bg-muted p-0.5", children: INSPECTOR_TABS.map(({ key, label, icon: Icon }) => /* @__PURE__ */ jsxs(
106332
- "button",
106333
- {
106334
- type: "button",
106335
- title: label,
106336
- className: cn(
106337
- "flex items-center gap-1 px-2 py-1 rounded text-[11px] transition-colors",
106338
- activeTab === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent"
106339
- ),
106340
- onClick: () => onSetActiveTab(key),
106341
- children: [
106342
- /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
106343
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: label })
106344
- ]
106345
- },
106346
- key
106347
- )) }),
106348
- /* @__PURE__ */ jsx(
106349
- "button",
106350
- {
106351
- type: "button",
106352
- onClick: onClose,
106353
- title: t2("common.close"),
106354
- className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
106355
- children: /* @__PURE__ */ jsx(LuX, { className: "w-4 h-4" })
106356
- }
106357
- )
106358
- ] }),
106359
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3 space-y-3", children: [
106360
- activeTab === "elements" && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
106361
- /* @__PURE__ */ jsx("div", { className: cn(HEADING, "mb-2"), children: t2("pptx.inspector.layerOrder") }),
106362
- activeSlide ? [...activeSlide.elements || []].reverse().map((el, ri) => {
106363
- const idx = (activeSlide.elements || []).length - 1 - ri;
106364
- const sel = selectedElement?.id === el.id || selectedElementIds.includes(el.id);
106365
- const label = (hasTextProperties(el) ? (el.text || "").slice(0, 24) : void 0) || el.type;
106366
- return /* @__PURE__ */ jsxs(
106367
- "div",
106368
- {
106369
- title: `${el.type} \u2014 ${el.id}`,
106370
- className: cn(
106371
- "flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors",
106372
- sel ? "bg-primary/30 text-primary" : "hover:bg-muted text-foreground"
106373
- ),
106374
- onClick: () => onSelectElement(el.id),
106375
- children: [
106376
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground w-4 text-right", children: idx + 1 }),
106377
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: label })
106378
- ]
106379
- },
106380
- el.id
106381
- );
106382
- }) : /* @__PURE__ */ jsx("div", { className: "text-muted-foreground italic", children: t2("pptx.inspector.noSlideSelected") })
106383
- ] }),
106384
- activeTab === "properties" && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: hasSelection && selectedElement ? /* @__PURE__ */ jsx(
106385
- ElementInspectorBody,
106386
- {
106387
- selectedElement,
106388
- canEdit,
106389
- slides,
106390
- tableEditorState,
106391
- mediaDataUrls,
106392
- onUpdateElement,
106393
- onUpdateElementStyle,
106394
- onUpdateTextStyle,
106395
- onMoveLayer
106396
- }
106397
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
106776
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
106777
+ isOpen && /* @__PURE__ */ jsx(
106778
+ "button",
106779
+ {
106780
+ type: "button",
106781
+ "aria-label": t2("common.close"),
106782
+ onClick: onClose,
106783
+ className: "md:hidden fixed inset-0 z-20 bg-black/40 backdrop-blur-[2px] animate-in fade-in duration-150"
106784
+ }
106785
+ ),
106786
+ /* @__PURE__ */ jsxs(
106787
+ "div",
106788
+ {
106789
+ className: cn(
106790
+ // Shared styles
106791
+ "bg-background flex flex-col text-xs text-foreground shadow-xl",
106792
+ // Mobile: absolute bottom sheet overlay sized via dvh so it
106793
+ // adapts to the on-screen keyboard / dynamic browser chrome.
106794
+ "max-md:fixed max-md:inset-x-0 max-md:bottom-0 max-md:top-auto max-md:w-full max-md:max-h-[75dvh] max-md:rounded-t-2xl max-md:border-t max-md:border-border max-md:z-30 max-md:pb-[max(env(safe-area-inset-bottom),0px)]",
106795
+ "max-md:transition-transform max-md:duration-200 max-md:ease-in-out",
106796
+ isOpen ? "max-md:translate-y-0" : "max-md:translate-y-full",
106797
+ // Desktop: flow-based flex child (takes space from canvas)
106798
+ "md:h-full md:border-l md:border-border",
106799
+ !panelWidth && "md:w-72"
106800
+ ),
106801
+ style: panelWidth ? { width: panelWidth } : void 0,
106802
+ children: [
106803
+ /* @__PURE__ */ jsx("div", { className: "md:hidden flex items-center justify-center pt-2 pb-1", children: /* @__PURE__ */ jsx("div", { className: "h-1 w-10 rounded-full bg-muted-foreground/40" }) }),
106804
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border", children: [
106805
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 rounded bg-muted p-0.5", children: INSPECTOR_TABS.map(({ key, label, icon: Icon }) => /* @__PURE__ */ jsxs(
106806
+ "button",
106807
+ {
106808
+ type: "button",
106809
+ title: label,
106810
+ className: cn(
106811
+ "flex items-center gap-1 px-2 py-1 rounded text-[11px] transition-colors",
106812
+ activeTab === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent"
106813
+ ),
106814
+ onClick: () => onSetActiveTab(key),
106815
+ children: [
106816
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
106817
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: label })
106818
+ ]
106819
+ },
106820
+ key
106821
+ )) }),
106398
106822
  /* @__PURE__ */ jsx(
106399
- PresentationPropertiesPanel,
106823
+ "button",
106400
106824
  {
106401
- canEdit,
106402
- canvasSize,
106403
- presentationProperties,
106404
- onUpdatePresentationProperties,
106405
- notesMaster,
106406
- handoutMaster,
106407
- notesCanvasSize,
106408
- coreProperties,
106409
- appProperties,
106410
- customProperties,
106411
- themeOptions,
106412
- selectedThemePath,
106413
- setSelectedThemePath,
106414
- onApplyTheme,
106415
- onUpdateCoreProperties,
106416
- onUpdateAppProperties,
106417
- onUpdateCustomProperties,
106418
- tagCollections,
106419
- onUpdateTagCollections,
106420
- onUpdateCanvasSize,
106421
- activeSlide,
106422
- theme,
106423
- onUpdateSlide
106825
+ type: "button",
106826
+ onClick: onClose,
106827
+ title: t2("common.close"),
106828
+ className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
106829
+ children: /* @__PURE__ */ jsx(LuX, { className: "w-4 h-4" })
106424
106830
  }
106425
- ),
106426
- activeSlide && /* @__PURE__ */ jsx(
106427
- SlideBackgroundPanel,
106831
+ )
106832
+ ] }),
106833
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3 space-y-3", children: [
106834
+ activeTab === "elements" && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
106835
+ /* @__PURE__ */ jsx("div", { className: cn(HEADING, "mb-2"), children: t2("pptx.inspector.layerOrder") }),
106836
+ activeSlide ? [...activeSlide.elements || []].reverse().map((el, ri) => {
106837
+ const idx = (activeSlide.elements || []).length - 1 - ri;
106838
+ const sel = selectedElement?.id === el.id || selectedElementIds.includes(el.id);
106839
+ const label = (hasTextProperties(el) ? (el.text || "").slice(0, 24) : void 0) || el.type;
106840
+ return /* @__PURE__ */ jsxs(
106841
+ "div",
106842
+ {
106843
+ title: `${el.type} \u2014 ${el.id}`,
106844
+ className: cn(
106845
+ "flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors",
106846
+ sel ? "bg-primary/30 text-primary" : "hover:bg-muted text-foreground"
106847
+ ),
106848
+ onClick: () => onSelectElement(el.id),
106849
+ children: [
106850
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground w-4 text-right", children: idx + 1 }),
106851
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: label })
106852
+ ]
106853
+ },
106854
+ el.id
106855
+ );
106856
+ }) : /* @__PURE__ */ jsx("div", { className: "text-muted-foreground italic", children: t2("pptx.inspector.noSlideSelected") })
106857
+ ] }),
106858
+ activeTab === "properties" && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: hasSelection && selectedElement ? /* @__PURE__ */ jsx(
106859
+ ElementInspectorBody,
106428
106860
  {
106429
- activeSlide,
106861
+ selectedElement,
106430
106862
  canEdit,
106431
- onUpdateSlide,
106432
- editTemplateMode,
106433
- slideMasters,
106434
- onSetTemplateBackground,
106435
- onGetTemplateBackgroundColor
106863
+ slides,
106864
+ tableEditorState,
106865
+ mediaDataUrls,
106866
+ onUpdateElement,
106867
+ onUpdateElementStyle,
106868
+ onUpdateTextStyle,
106869
+ onMoveLayer
106436
106870
  }
106437
- )
106438
- ] }) }),
106439
- activeTab === "comments" && /* @__PURE__ */ jsx(
106440
- InspectorCommentsSection,
106441
- {
106442
- comments,
106443
- canEdit,
106444
- activeSlide,
106445
- selectedElement,
106446
- editingCommentId,
106447
- commentEditDraft,
106448
- commentDraft,
106449
- replyingToCommentId: replyingToCommentId ?? null,
106450
- replyDraftByCommentId: replyDraftByCommentId ?? {},
106451
- onSetCommentDraft,
106452
- onAddComment,
106453
- onDeleteComment,
106454
- onStartEditComment,
106455
- onSaveEditComment,
106456
- onCancelEditComment,
106457
- onSetCommentEditDraft,
106458
- onToggleCommentResolved,
106459
- onStartReply,
106460
- onCancelReply,
106461
- onReplyDraftChange,
106462
- onSubmitReply,
106463
- onSelectElement
106464
- }
106465
- )
106466
- ] }),
106467
- hasSelection && selectedElement && activeSlide && /* @__PURE__ */ jsxs(Fragment, { children: [
106468
- /* @__PURE__ */ jsx(ResizeHandle, { direction: "vertical", onResize: onResizeAnim }),
106469
- /* @__PURE__ */ jsx(
106470
- "div",
106471
- {
106472
- className: "border-t border-border p-3 overflow-y-auto flex-shrink-0",
106473
- style: { height: animPanelHeight },
106474
- children: /* @__PURE__ */ jsx(
106475
- AnimationPanel,
106871
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
106872
+ /* @__PURE__ */ jsx(
106873
+ PresentationPropertiesPanel,
106476
106874
  {
106477
- selectedElement,
106478
- activeSlide,
106479
106875
  canEdit,
106876
+ canvasSize,
106877
+ presentationProperties,
106878
+ onUpdatePresentationProperties,
106879
+ notesMaster,
106880
+ handoutMaster,
106881
+ notesCanvasSize,
106882
+ coreProperties,
106883
+ appProperties,
106884
+ customProperties,
106885
+ themeOptions,
106886
+ selectedThemePath,
106887
+ setSelectedThemePath,
106888
+ onApplyTheme,
106889
+ onUpdateCoreProperties,
106890
+ onUpdateAppProperties,
106891
+ onUpdateCustomProperties,
106892
+ tagCollections,
106893
+ onUpdateTagCollections,
106894
+ onUpdateCanvasSize,
106895
+ activeSlide,
106896
+ theme,
106480
106897
  onUpdateSlide
106481
106898
  }
106899
+ ),
106900
+ activeSlide && /* @__PURE__ */ jsx(
106901
+ SlideBackgroundPanel,
106902
+ {
106903
+ activeSlide,
106904
+ canEdit,
106905
+ onUpdateSlide,
106906
+ editTemplateMode,
106907
+ slideMasters,
106908
+ onSetTemplateBackground,
106909
+ onGetTemplateBackgroundColor
106910
+ }
106482
106911
  )
106483
- }
106484
- )
106485
- ] })
106486
- ]
106487
- }
106488
- );
106912
+ ] }) }),
106913
+ activeTab === "comments" && /* @__PURE__ */ jsx(
106914
+ InspectorCommentsSection,
106915
+ {
106916
+ comments,
106917
+ canEdit,
106918
+ activeSlide,
106919
+ selectedElement,
106920
+ editingCommentId,
106921
+ commentEditDraft,
106922
+ commentDraft,
106923
+ replyingToCommentId: replyingToCommentId ?? null,
106924
+ replyDraftByCommentId: replyDraftByCommentId ?? {},
106925
+ onSetCommentDraft,
106926
+ onAddComment,
106927
+ onDeleteComment,
106928
+ onStartEditComment,
106929
+ onSaveEditComment,
106930
+ onCancelEditComment,
106931
+ onSetCommentEditDraft,
106932
+ onToggleCommentResolved,
106933
+ onStartReply,
106934
+ onCancelReply,
106935
+ onReplyDraftChange,
106936
+ onSubmitReply,
106937
+ onSelectElement
106938
+ }
106939
+ )
106940
+ ] }),
106941
+ hasSelection && selectedElement && activeSlide && /* @__PURE__ */ jsxs(Fragment, { children: [
106942
+ /* @__PURE__ */ jsx(ResizeHandle, { direction: "vertical", onResize: onResizeAnim }),
106943
+ /* @__PURE__ */ jsx(
106944
+ "div",
106945
+ {
106946
+ className: "border-t border-border p-3 overflow-y-auto flex-shrink-0",
106947
+ style: { height: animPanelHeight },
106948
+ children: /* @__PURE__ */ jsx(
106949
+ AnimationPanel,
106950
+ {
106951
+ selectedElement,
106952
+ activeSlide,
106953
+ canEdit,
106954
+ onUpdateSlide
106955
+ }
106956
+ )
106957
+ }
106958
+ )
106959
+ ] })
106960
+ ]
106961
+ }
106962
+ )
106963
+ ] });
106489
106964
  }
106490
106965
  function buildPathD(points) {
106491
106966
  if (points.length === 0) {
@@ -110619,7 +111094,8 @@ function ViewerBottomPanels({
110619
111094
  onZoomToFit,
110620
111095
  mode,
110621
111096
  onSetMode,
110622
- onToggleSlideSorter
111097
+ onToggleSlideSorter,
111098
+ hideStatusBar = false
110623
111099
  }) {
110624
111100
  return /* @__PURE__ */ jsxs(Fragment, { children: [
110625
111101
  onResizeBottom && !isSlideNotesCollapsed && /* @__PURE__ */ jsx(ResizeHandle, { direction: "vertical", onResize: onResizeBottom }),
@@ -110635,7 +111111,7 @@ function ViewerBottomPanels({
110635
111111
  panelHeight: notesPanelHeight
110636
111112
  }
110637
111113
  ),
110638
- /* @__PURE__ */ jsx(
111114
+ !hideStatusBar && /* @__PURE__ */ jsx(
110639
111115
  StatusBar,
110640
111116
  {
110641
111117
  slideCount,
@@ -110792,6 +111268,45 @@ function ViewerInspector({
110792
111268
  }
110793
111269
  );
110794
111270
  }
111271
+
111272
+ // src/viewer/hooks/useScopedLayoutOptions.ts
111273
+ function scopeLayoutOptionsToActiveSlide(options, activeSlide) {
111274
+ if (!activeSlide?.layoutPath) {
111275
+ return options;
111276
+ }
111277
+ const hasAnyMasterInfo = options.some((o3) => o3.masterPath);
111278
+ if (!hasAnyMasterInfo) {
111279
+ return options;
111280
+ }
111281
+ const activeOption = options.find((o3) => o3.path === activeSlide.layoutPath);
111282
+ const activeMaster = activeOption?.masterPath;
111283
+ if (!activeMaster) {
111284
+ return options;
111285
+ }
111286
+ const scoped = options.filter((o3) => o3.masterPath === activeMaster);
111287
+ const seen = /* @__PURE__ */ new Map();
111288
+ for (const opt of scoped) {
111289
+ const isActive = opt.path === activeSlide.layoutPath;
111290
+ const existing = seen.get(opt.name);
111291
+ if (!existing || isActive) {
111292
+ seen.set(opt.name, opt);
111293
+ }
111294
+ }
111295
+ const chosen = new Set(Array.from(seen.values()).map((o3) => o3.path));
111296
+ const result = [];
111297
+ const usedNames = /* @__PURE__ */ new Set();
111298
+ for (const opt of scoped) {
111299
+ if (!chosen.has(opt.path)) {
111300
+ continue;
111301
+ }
111302
+ if (usedNames.has(opt.name)) {
111303
+ continue;
111304
+ }
111305
+ usedNames.add(opt.name);
111306
+ result.push(opt);
111307
+ }
111308
+ return result;
111309
+ }
110795
111310
  function ViewerToolbarSection(props) {
110796
111311
  const {
110797
111312
  mode,
@@ -110861,6 +111376,10 @@ function ViewerToolbarSection(props) {
110861
111376
  },
110862
111377
  [activeSlide, propertyHandlers]
110863
111378
  );
111379
+ const scopedLayoutOptions = React10__default.useMemo(
111380
+ () => scopeLayoutOptionsToActiveSlide(s.layoutOptions, activeSlide),
111381
+ [s.layoutOptions, activeSlide]
111382
+ );
110864
111383
  const handleApplyTransitionToAll = useCallback(() => {
110865
111384
  const transition = activeSlide?.transition;
110866
111385
  if (!transition) {
@@ -110967,7 +111486,7 @@ function ViewerToolbarSection(props) {
110967
111486
  onUpdateTextStyle: ops.updateSelectedTextStyle,
110968
111487
  isOverflowMenuOpen: s.isOverflowMenuOpen,
110969
111488
  onSetOverflowMenuOpen: s.setIsOverflowMenuOpen,
110970
- layoutOptions: s.layoutOptions,
111489
+ layoutOptions: scopedLayoutOptions,
110971
111490
  onInsertSlideFromLayout: slideOps.handleInsertSlideFromLayout,
110972
111491
  customShows: s.customShows,
110973
111492
  activeCustomShowId: s.activeCustomShowId,
@@ -111000,6 +111519,7 @@ function ViewerToolbarSection(props) {
111000
111519
  isCommentsPanelOpen: s.isInspectorPaneOpen,
111001
111520
  slideCommentCount: activeSlide?.comments?.length ?? 0,
111002
111521
  formatPainterActive: s.formatPainterActive,
111522
+ canActivateFormatPainter: hasCopyableFormat(selectedElement),
111003
111523
  onToggleFormatPainter: onToggleFormatPainterProp ?? (() => s.setFormatPainterActive((p3) => !p3)),
111004
111524
  isSelectionPaneOpen: s.isSelectionPaneOpen,
111005
111525
  onToggleSelectionPane: () => s.setIsSelectionPaneOpen((p3) => !p3),
@@ -114148,6 +114668,166 @@ function ViewerMainContent(props) {
114148
114668
  )
114149
114669
  ] });
114150
114670
  }
114671
+ function MobileBottomBar({
114672
+ onOpenSlides,
114673
+ onOpenInsert,
114674
+ onOpenInspector,
114675
+ onOpenComments,
114676
+ onToggleNotes,
114677
+ activeSheet,
114678
+ commentCount
114679
+ }) {
114680
+ const actions = [
114681
+ {
114682
+ key: "slides",
114683
+ label: "Slides",
114684
+ icon: LuLayers,
114685
+ onClick: onOpenSlides
114686
+ },
114687
+ {
114688
+ key: "insert",
114689
+ label: "Insert",
114690
+ icon: LuPlus,
114691
+ onClick: onOpenInsert
114692
+ },
114693
+ {
114694
+ key: "inspector",
114695
+ label: "Format",
114696
+ icon: LuSettings2,
114697
+ onClick: onOpenInspector
114698
+ },
114699
+ {
114700
+ key: "comments",
114701
+ label: "Comments",
114702
+ icon: LuMessageSquare,
114703
+ onClick: onOpenComments,
114704
+ badge: commentCount
114705
+ },
114706
+ {
114707
+ key: "notes",
114708
+ label: "Notes",
114709
+ icon: LuStickyNote,
114710
+ onClick: onToggleNotes
114711
+ }
114712
+ ];
114713
+ return /* @__PURE__ */ jsx(
114714
+ "nav",
114715
+ {
114716
+ "aria-label": "Editor actions",
114717
+ className: "md:hidden flex items-stretch justify-around border-t border-border bg-secondary/80 backdrop-blur supports-[backdrop-filter]:bg-secondary/60 pb-[max(env(safe-area-inset-bottom),0px)]",
114718
+ children: actions.map(({ key, label, icon: Icon, onClick, badge }) => {
114719
+ const active = activeSheet === key;
114720
+ return /* @__PURE__ */ jsxs(
114721
+ "button",
114722
+ {
114723
+ type: "button",
114724
+ onClick,
114725
+ className: cn(
114726
+ "relative flex flex-col items-center justify-center gap-0.5 flex-1 min-h-[56px] py-1.5 text-[10px] font-medium transition-colors active:scale-95",
114727
+ active ? "text-primary" : "text-muted-foreground hover:text-foreground"
114728
+ ),
114729
+ "aria-pressed": active,
114730
+ children: [
114731
+ /* @__PURE__ */ jsx(Icon, { className: "w-5 h-5" }),
114732
+ /* @__PURE__ */ jsx("span", { children: label }),
114733
+ badge !== void 0 && badge > 0 && /* @__PURE__ */ jsx("span", { className: "absolute top-1 right-1/4 flex items-center justify-center min-w-[16px] h-4 px-1 rounded-full bg-primary text-[9px] font-semibold text-primary-foreground", children: badge > 99 ? "99+" : badge }),
114734
+ active && /* @__PURE__ */ jsx("span", { className: "absolute top-0 left-1/2 -translate-x-1/2 w-8 h-0.5 rounded-full bg-primary" })
114735
+ ]
114736
+ },
114737
+ key
114738
+ );
114739
+ })
114740
+ }
114741
+ );
114742
+ }
114743
+ function MobileSlidesSheet({
114744
+ open,
114745
+ onClose,
114746
+ ...sidebar
114747
+ }) {
114748
+ return /* @__PURE__ */ jsx(MobileSheet, { open, onClose, heightFraction: 0.7, title: "Slides", children: /* @__PURE__ */ jsx("div", { className: "h-full", children: /* @__PURE__ */ jsx(SlidesPaneSidebar, { ...sidebar }) }) });
114749
+ }
114750
+ function MobileChromeOverlay(props) {
114751
+ const {
114752
+ state: s,
114753
+ editorOps,
114754
+ presentation,
114755
+ slides,
114756
+ activeSlideIndex,
114757
+ canvasSize,
114758
+ slideSectionGroups,
114759
+ canEdit,
114760
+ commentCount
114761
+ } = props;
114762
+ const activeSheet = s.isSlidesPaneOpen ? "slides" : s.isInspectorPaneOpen ? s.sidebarPanelMode === "comments" ? "comments" : "inspector" : !s.isSlideNotesCollapsed ? "notes" : null;
114763
+ const closeAllSheets = () => {
114764
+ s.setIsSlidesPaneOpen(false);
114765
+ s.setIsInspectorPaneOpen(false);
114766
+ s.setIsSlideNotesCollapsed(true);
114767
+ };
114768
+ const openSheet = (which) => {
114769
+ closeAllSheets();
114770
+ switch (which) {
114771
+ case "slides":
114772
+ s.setIsSlidesPaneOpen(true);
114773
+ break;
114774
+ case "inspector":
114775
+ s.setSidebarPanelMode("properties");
114776
+ s.setIsInspectorPaneOpen(true);
114777
+ break;
114778
+ case "comments":
114779
+ s.setSidebarPanelMode("comments");
114780
+ s.setIsInspectorPaneOpen(true);
114781
+ break;
114782
+ case "notes":
114783
+ s.setIsSlideNotesCollapsed(false);
114784
+ break;
114785
+ }
114786
+ };
114787
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
114788
+ /* @__PURE__ */ jsx(
114789
+ MobileSlidesSheet,
114790
+ {
114791
+ open: s.isSlidesPaneOpen,
114792
+ onClose: () => s.setIsSlidesPaneOpen(false),
114793
+ slides,
114794
+ activeSlideIndex,
114795
+ canvasSize,
114796
+ sectionGroups: slideSectionGroups,
114797
+ isOpen: true,
114798
+ canEdit,
114799
+ onSelectSlide: (index) => {
114800
+ s.setActiveSlideIndex(index);
114801
+ s.setIsSlidesPaneOpen(false);
114802
+ },
114803
+ onSlideContextMenu: editorOps.slideOps.handleSlideContextMenu,
114804
+ onMoveSlide: editorOps.slideOps.handleMoveSlide,
114805
+ onAddSlide: editorOps.slideOps.handleAddSlide,
114806
+ onCollapse: () => s.setIsSlidesPaneOpen(false),
114807
+ onAddSection: editorOps.sectionOps.addSection,
114808
+ onRenameSection: editorOps.sectionOps.renameSection,
114809
+ onDeleteSection: editorOps.sectionOps.deleteSection,
114810
+ onMoveSectionUp: editorOps.sectionOps.moveSectionUp,
114811
+ onMoveSectionDown: editorOps.sectionOps.moveSectionDown,
114812
+ rehearsalTimings: Object.keys(presentation.recordedTimings).length > 0 ? presentation.recordedTimings : void 0
114813
+ }
114814
+ ),
114815
+ /* @__PURE__ */ jsx(
114816
+ MobileBottomBar,
114817
+ {
114818
+ activeSheet,
114819
+ commentCount,
114820
+ onOpenSlides: () => s.isSlidesPaneOpen ? s.setIsSlidesPaneOpen(false) : openSheet("slides"),
114821
+ onOpenInsert: () => {
114822
+ editorOps.insertHandlers.handleAddTextBox();
114823
+ },
114824
+ onOpenInspector: () => s.isInspectorPaneOpen && s.sidebarPanelMode !== "comments" ? s.setIsInspectorPaneOpen(false) : openSheet("inspector"),
114825
+ onOpenComments: () => s.isInspectorPaneOpen && s.sidebarPanelMode === "comments" ? s.setIsInspectorPaneOpen(false) : openSheet("comments"),
114826
+ onToggleNotes: () => !s.isSlideNotesCollapsed ? s.setIsSlideNotesCollapsed(true) : openSheet("notes")
114827
+ }
114828
+ )
114829
+ ] });
114830
+ }
114151
114831
  function ToggleSwitch({
114152
114832
  label,
114153
114833
  enabled,
@@ -114340,14 +115020,10 @@ function useYjsDocumentSync({
114340
115020
  const lastSyncedRef = useRef("");
114341
115021
  const hasInitializedRef = useRef(false);
114342
115022
  const getDocMap = useCallback(() => {
114343
- if (!doc2 || typeof doc2 !== "object") {
115023
+ if (!doc2) {
114344
115024
  return null;
114345
115025
  }
114346
- const d = doc2;
114347
- if (typeof d.getMap !== "function") {
114348
- return null;
114349
- }
114350
- return d.getMap("slides-data");
115026
+ return doc2.getMap("slides-data");
114351
115027
  }, [doc2]);
114352
115028
  useEffect(() => {
114353
115029
  if (!isConnected || !doc2) {
@@ -114368,10 +115044,9 @@ function useYjsDocumentSync({
114368
115044
  return;
114369
115045
  }
114370
115046
  lastSyncedRef.current = serialized;
114371
- const d = doc2;
114372
- d.transact(() => {
115047
+ doc2.transact(() => {
114373
115048
  const currentCount = map3.get("count");
114374
- if (currentCount && currentCount > slides.length) {
115049
+ if (typeof currentCount === "number" && currentCount > slides.length) {
114375
115050
  for (let i3 = slides.length; i3 < currentCount; i3++) {
114376
115051
  map3.delete(`slide-${i3}`);
114377
115052
  }
@@ -114396,13 +115071,13 @@ function useYjsDocumentSync({
114396
115071
  }
114397
115072
  const handleUpdate = () => {
114398
115073
  const count = map3.get("count");
114399
- if (!count || count === 0) {
115074
+ if (typeof count !== "number" || count === 0) {
114400
115075
  return;
114401
115076
  }
114402
115077
  const remoteSlides = [];
114403
115078
  for (let i3 = 0; i3 < count; i3++) {
114404
115079
  const slideJson = map3.get(`slide-${i3}`);
114405
- if (slideJson) {
115080
+ if (typeof slideJson === "string") {
114406
115081
  try {
114407
115082
  remoteSlides.push(JSON.parse(slideJson));
114408
115083
  } catch {
@@ -114427,7 +115102,7 @@ function useYjsDocumentSync({
114427
115102
  if (!hasInitializedRef.current) {
114428
115103
  hasInitializedRef.current = true;
114429
115104
  const count = map3.get("count");
114430
- if (count && count > 0) {
115105
+ if (typeof count === "number" && count > 0) {
114431
115106
  handleUpdate();
114432
115107
  }
114433
115108
  }
@@ -114988,13 +115663,14 @@ function useCanvasInteractions(input) {
114988
115663
  if (e2.button !== 0) {
114989
115664
  return;
114990
115665
  }
114991
- if (!selectedElementIdSet.has(elementId)) {
115666
+ const wasSelected = selectedElementIdSet.has(elementId);
115667
+ if (!wasSelected) {
114992
115668
  ops.applySelection(elementId);
114993
115669
  justSelectedRef.current = true;
114994
115670
  } else {
114995
115671
  justSelectedRef.current = false;
114996
115672
  }
114997
- const ids = effectiveSelectedIds.length ? effectiveSelectedIds : [elementId];
115673
+ const ids = !wasSelected ? [elementId] : effectiveSelectedIds.length ? effectiveSelectedIds : [elementId];
114998
115674
  const startPositions = {};
114999
115675
  const domEls = /* @__PURE__ */ new Map();
115000
115676
  for (const id2 of ids) {
@@ -117031,8 +117707,14 @@ function useSectionOperations(input) {
117031
117707
  }
117032
117708
 
117033
117709
  // src/viewer/hooks/useSlideManagement.ts
117710
+ function insertSlideFromLayoutUpdater(slides, activeIndex, draft) {
117711
+ const next = [...slides];
117712
+ const insertAt = Math.max(0, Math.min(activeIndex + 1, next.length));
117713
+ next.splice(insertAt, 0, draft);
117714
+ return next;
117715
+ }
117034
117716
  function useSlideManagement(input) {
117035
- const { slides, activeSlideIndex, setActiveSlideIndex, ops, history } = input;
117717
+ const { slides, activeSlideIndex, setActiveSlideIndex, ops, history, handlerRef } = input;
117036
117718
  const handleAddSlide = () => {
117037
117719
  const newSlide = {
117038
117720
  id: `slide-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
@@ -117131,8 +117813,42 @@ function useSlideManagement(input) {
117131
117813
  });
117132
117814
  history.markDirty();
117133
117815
  };
117134
- const handleInsertSlideFromLayout = (_layoutPath) => {
117135
- handleAddSlide();
117816
+ const handleInsertSlideFromLayout = (layoutPath, layoutName) => {
117817
+ const insertAt = activeSlideIndex + 1;
117818
+ const draft = {
117819
+ id: `slide-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
117820
+ rId: "",
117821
+ slideNumber: slides.length + 1,
117822
+ elements: [],
117823
+ layoutPath,
117824
+ ...layoutName ? { layoutName } : {}
117825
+ };
117826
+ let inserted = [];
117827
+ ops.updateSlides((prev) => {
117828
+ inserted = insertSlideFromLayoutUpdater(prev, activeSlideIndex, draft);
117829
+ return inserted;
117830
+ });
117831
+ setActiveSlideIndex(insertAt);
117832
+ history.markDirty();
117833
+ const handler = handlerRef?.current;
117834
+ if (handler) {
117835
+ void handler.applyLayoutToSlide(insertAt, layoutPath, inserted).then(
117836
+ (updated) => {
117837
+ ops.updateSlides((prev) => {
117838
+ if (prev[insertAt]?.id !== draft.id) {
117839
+ return prev;
117840
+ }
117841
+ const next = [...prev];
117842
+ next[insertAt] = updated;
117843
+ return next;
117844
+ });
117845
+ return void 0;
117846
+ },
117847
+ () => {
117848
+ return void 0;
117849
+ }
117850
+ );
117851
+ }
117136
117852
  };
117137
117853
  return {
117138
117854
  handleAddSlide,
@@ -117668,7 +118384,8 @@ function useEditorOperations(input) {
117668
118384
  canvasSize,
117669
118385
  dialogs,
117670
118386
  presentation,
117671
- userName
118387
+ userName,
118388
+ handlerRef
117672
118389
  } = input;
117673
118390
  const ops = useElementOperations({
117674
118391
  activeSlide,
@@ -117770,7 +118487,8 @@ function useEditorOperations(input) {
117770
118487
  activeSlideIndex,
117771
118488
  setActiveSlideIndex: state2.setActiveSlideIndex,
117772
118489
  ops,
117773
- history
118490
+ history,
118491
+ handlerRef
117774
118492
  });
117775
118493
  const tableOps = useTableOperations({
117776
118494
  selectedElement,
@@ -117799,21 +118517,34 @@ function useEditorOperations(input) {
117799
118517
  );
117800
118518
  const copiedFormatRef = useRef(null);
117801
118519
  const prevFormatPainterRef = useRef(false);
118520
+ const { formatPainterActive, setFormatPainterActive, elementLookup } = state2;
117802
118521
  useEffect(() => {
117803
- if (state2.formatPainterActive && !prevFormatPainterRef.current && selectedElement) {
118522
+ if (formatPainterActive && !prevFormatPainterRef.current && selectedElement) {
117804
118523
  copiedFormatRef.current = copyFormatFromElement(selectedElement);
117805
- } else if (!state2.formatPainterActive) {
118524
+ } else if (!formatPainterActive) {
117806
118525
  copiedFormatRef.current = null;
117807
118526
  }
117808
- prevFormatPainterRef.current = state2.formatPainterActive;
117809
- }, [state2.formatPainterActive, selectedElement]);
118527
+ prevFormatPainterRef.current = formatPainterActive;
118528
+ }, [formatPainterActive, selectedElement]);
118529
+ useEffect(() => {
118530
+ if (!formatPainterActive) {
118531
+ return;
118532
+ }
118533
+ const onKey = (e2) => {
118534
+ if (e2.key === "Escape") {
118535
+ setFormatPainterActive(false);
118536
+ }
118537
+ };
118538
+ window.addEventListener("keydown", onKey);
118539
+ return () => window.removeEventListener("keydown", onKey);
118540
+ }, [formatPainterActive, setFormatPainterActive]);
117810
118541
  const formatPainterCanvasHandlers = useMemo(
117811
118542
  () => ({
117812
118543
  ...canvasHandlers,
117813
118544
  handleElementClick: (elementId, e2) => {
117814
- if (state2.formatPainterActive && copiedFormatRef.current) {
118545
+ if (formatPainterActive && copiedFormatRef.current) {
117815
118546
  e2.stopPropagation();
117816
- const element2 = state2.elementLookup.get(elementId);
118547
+ const element2 = elementLookup.get(elementId);
117817
118548
  if (element2) {
117818
118549
  const updated = applyFormatToElement(element2, copiedFormatRef.current);
117819
118550
  const updates = {};
@@ -117826,14 +118557,21 @@ function useEditorOperations(input) {
117826
118557
  ops.updateElementById(elementId, updates);
117827
118558
  }
117828
118559
  copiedFormatRef.current = null;
117829
- state2.setFormatPainterActive(false);
118560
+ setFormatPainterActive(false);
117830
118561
  ops.applySelection(elementId);
117831
118562
  return;
117832
118563
  }
117833
118564
  canvasHandlers.handleElementClick(elementId, e2);
118565
+ },
118566
+ handleCanvasMouseDown: (e2) => {
118567
+ if (formatPainterActive) {
118568
+ setFormatPainterActive(false);
118569
+ return;
118570
+ }
118571
+ canvasHandlers.handleCanvasMouseDown(e2);
117834
118572
  }
117835
118573
  }),
117836
- [canvasHandlers, ops, state2]
118574
+ [canvasHandlers, ops, formatPainterActive, setFormatPainterActive, elementLookup]
117837
118575
  );
117838
118576
  return {
117839
118577
  ops: combinedOps,
@@ -119972,21 +120710,35 @@ function useViewerDialogs(input) {
119972
120710
  null
119973
120711
  );
119974
120712
  const [embedFontsEnabled, setEmbedFontsEnabled] = useState(false);
119975
- const [isNarrowViewport, setIsNarrowViewport] = useState(false);
120713
+ const [isNarrowViewport, setIsNarrowViewport] = useState(
120714
+ () => typeof window !== "undefined" ? window.innerWidth < 768 : false
120715
+ );
119976
120716
  useEffect(() => {
119977
- const el = containerRef.current;
119978
- if (!el) {
119979
- return;
119980
- }
119981
- const observer = new ResizeObserver((entries) => {
119982
- const entry = entries[0];
119983
- if (entry) {
119984
- setIsNarrowViewport(entry.contentRect.width < 768);
120717
+ const handleWindow = () => setIsNarrowViewport(window.innerWidth < 768);
120718
+ let observer = null;
120719
+ let raf = 0;
120720
+ const attach2 = () => {
120721
+ const el = containerRef.current;
120722
+ if (!el) {
120723
+ raf = requestAnimationFrame(attach2);
120724
+ return;
119985
120725
  }
119986
- });
119987
- observer.observe(el);
119988
- setIsNarrowViewport(el.clientWidth < 768);
119989
- return () => observer.disconnect();
120726
+ observer = new ResizeObserver((entries) => {
120727
+ const entry = entries[0];
120728
+ if (entry) {
120729
+ setIsNarrowViewport(entry.contentRect.width < 768);
120730
+ }
120731
+ });
120732
+ observer.observe(el);
120733
+ setIsNarrowViewport(el.clientWidth < 768);
120734
+ };
120735
+ attach2();
120736
+ window.addEventListener("resize", handleWindow);
120737
+ return () => {
120738
+ cancelAnimationFrame(raf);
120739
+ observer?.disconnect();
120740
+ window.removeEventListener("resize", handleWindow);
120741
+ };
119990
120742
  }, []);
119991
120743
  useEffect(() => {
119992
120744
  if (isDirty && hasDigitalSignatures && !signatureStripAcknowledgedRef.current) {
@@ -124458,7 +125210,9 @@ function useViewerCoreState(_input) {
124458
125210
  function useViewerUIState() {
124459
125211
  const [isCompactToolbarOpen, setIsCompactToolbarOpen] = useState(false);
124460
125212
  const [toolbarSection, setToolbarSection] = useState("home");
124461
- const [isSlidesPaneOpen, setIsSlidesPaneOpen] = useState(true);
125213
+ const [isSlidesPaneOpen, setIsSlidesPaneOpen] = useState(
125214
+ () => typeof window === "undefined" ? true : window.innerWidth >= 768
125215
+ );
124462
125216
  const [isInspectorPaneOpen, setIsInspectorPaneOpen] = useState(false);
124463
125217
  const [isSlideNotesCollapsed, setIsSlideNotesCollapsed] = useState(true);
124464
125218
  const [isOverflowMenuOpen, setIsOverflowMenuOpen] = useState(false);
@@ -124901,7 +125655,8 @@ var PowerPointViewer = forwardRef(
124901
125655
  canvasSize,
124902
125656
  dialogs,
124903
125657
  presentation,
124904
- userName: authorName ?? collaboration?.userName
125658
+ userName: authorName ?? collaboration?.userName,
125659
+ handlerRef: actionSoundHandlerRef
124905
125660
  });
124906
125661
  const {
124907
125662
  exportHandlers,
@@ -125024,7 +125779,7 @@ var PowerPointViewer = forwardRef(
125024
125779
  {
125025
125780
  activeSlide,
125026
125781
  allSlides: slides,
125027
- isSlideNotesCollapsed: isMobile || state2.isSlideNotesCollapsed,
125782
+ isSlideNotesCollapsed: state2.isSlideNotesCollapsed,
125028
125783
  canEdit,
125029
125784
  slideCount: slides.length,
125030
125785
  activeSlideIndex,
@@ -125041,7 +125796,22 @@ var PowerPointViewer = forwardRef(
125041
125796
  onZoomToFit: zoom.handleZoomToFit,
125042
125797
  mode,
125043
125798
  onSetMode: handleSetMode,
125044
- onToggleSlideSorter: () => state2.setShowSlideSorter((p3) => !p3)
125799
+ onToggleSlideSorter: () => state2.setShowSlideSorter((p3) => !p3),
125800
+ hideStatusBar: isMobile
125801
+ }
125802
+ ),
125803
+ mode !== "present" && isMobile && /* @__PURE__ */ jsx(
125804
+ MobileChromeOverlay,
125805
+ {
125806
+ state: state2,
125807
+ editorOps,
125808
+ presentation,
125809
+ slides,
125810
+ activeSlideIndex,
125811
+ canvasSize,
125812
+ slideSectionGroups,
125813
+ canEdit,
125814
+ commentCount: activeSlide?.comments?.length ?? 0
125045
125815
  }
125046
125816
  ),
125047
125817
  /* @__PURE__ */ jsx(