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
@@ -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, applyThemeToData, THEME_PRESETS } 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, useMemo42 = 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, useMemo42 = 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;
@@ -75497,9 +75497,9 @@ function renderImageAlphaSvgFilter(element2) {
75497
75497
  const primitives = [];
75498
75498
  let resultIdx = 0;
75499
75499
  let inputRef = "SourceGraphic";
75500
- const next = (jsx229) => {
75500
+ const next = (jsx235) => {
75501
75501
  const result = `r${resultIdx++}`;
75502
- primitives.push(jsx229(inputRef, result));
75502
+ primitives.push(jsx235(inputRef, result));
75503
75503
  inputRef = result;
75504
75504
  };
75505
75505
  if (typeof e2.alphaModFix === "number") {
@@ -79540,8 +79540,8 @@ function renderTableElement(element2, textStyle, options) {
79540
79540
  {
79541
79541
  style: rowHeight ? { height: rowHeight } : void 0,
79542
79542
  children: cells.map((cell, cellIndex) => {
79543
- const isHMerged = cell["@_hMerge"] === "1" || cell["@_hMerge"] === true;
79544
- const isVMerged = cell["@_vMerge"] === "1" || cell["@_vMerge"] === true;
79543
+ const isHMerged = cell["@_hMerge"] === "1";
79544
+ const isVMerged = cell["@_vMerge"] === "1";
79545
79545
  if (isHMerged || isVMerged) {
79546
79546
  return null;
79547
79547
  }
@@ -85550,6 +85550,15 @@ function copyFormatFromElement(element2) {
85550
85550
  }
85551
85551
  return result;
85552
85552
  }
85553
+ function definedEntries(obj) {
85554
+ const out = {};
85555
+ for (const key in obj) {
85556
+ if (obj[key] !== void 0) {
85557
+ out[key] = obj[key];
85558
+ }
85559
+ }
85560
+ return out;
85561
+ }
85553
85562
  function applyFormatToElement(element2, format) {
85554
85563
  let updated = { ...element2 };
85555
85564
  if (format.shapeStyle && hasShapeProperties(updated)) {
@@ -85557,7 +85566,7 @@ function applyFormatToElement(element2, format) {
85557
85566
  ...updated,
85558
85567
  shapeStyle: {
85559
85568
  ...updated.shapeStyle,
85560
- ...format.shapeStyle
85569
+ ...definedEntries(format.shapeStyle)
85561
85570
  }
85562
85571
  };
85563
85572
  }
@@ -85566,12 +85575,18 @@ function applyFormatToElement(element2, format) {
85566
85575
  ...updated,
85567
85576
  textStyle: {
85568
85577
  ...updated.textStyle,
85569
- ...format.textStyle
85578
+ ...definedEntries(format.textStyle)
85570
85579
  }
85571
85580
  };
85572
85581
  }
85573
85582
  return updated;
85574
85583
  }
85584
+ function hasCopyableFormat(element2) {
85585
+ if (!element2) {
85586
+ return false;
85587
+ }
85588
+ return hasShapeProperties(element2) || hasTextProperties(element2);
85589
+ }
85575
85590
 
85576
85591
  // src/viewer/utils/animation-preview.ts
85577
85592
  var PRESET_TO_EFFECT = {
@@ -86920,8 +86935,8 @@ function ThumbnailTable({
86920
86935
  {
86921
86936
  style: rowHeight ? { height: rowHeight } : void 0,
86922
86937
  children: cells.map((cell, ci) => {
86923
- const isHMerged = cell["@_hMerge"] === "1" || cell["@_hMerge"] === true;
86924
- const isVMerged = cell["@_vMerge"] === "1" || cell["@_vMerge"] === true;
86938
+ const isHMerged = cell["@_hMerge"] === "1";
86939
+ const isVMerged = cell["@_vMerge"] === "1";
86925
86940
  if (isHMerged || isVMerged) {
86926
86941
  return null;
86927
86942
  }
@@ -92012,11 +92027,16 @@ function usePresenceTracking({
92012
92027
  if (cid === localClientId) {
92013
92028
  return;
92014
92029
  }
92015
- const raw = state2?.presence;
92030
+ const stateRecord = state2;
92031
+ const raw = stateRecord?.presence;
92016
92032
  if (!raw || typeof raw !== "object") {
92017
92033
  return;
92018
92034
  }
92019
- const sanitized = sanitizePresence({ ...raw, clientId: cid }, canvasWidth, canvasHeight);
92035
+ const sanitized = sanitizePresence(
92036
+ { ...raw, clientId: cid },
92037
+ canvasWidth,
92038
+ canvasHeight
92039
+ );
92020
92040
  if (!sanitized) {
92021
92041
  return;
92022
92042
  }
@@ -92084,15 +92104,9 @@ function useYjsProvider({ config }) {
92084
92104
  try {
92085
92105
  const [Y, { WebsocketProvider: WebsocketProvider2 }] = await Promise.all([Promise.resolve().then(() => (init_yjs(), yjs_exports)), Promise.resolve().then(() => (init_y_websocket(), y_websocket_exports))]);
92086
92106
  const yDoc = new Y.Doc();
92087
- const provider = new WebsocketProvider2(
92088
- config.serverUrl,
92089
- roomId,
92090
- yDoc,
92091
- // eslint-disable-line @typescript-eslint/no-explicit-any
92092
- {
92093
- params: config.authToken ? { token: config.authToken } : void 0
92094
- }
92095
- );
92107
+ const provider = new WebsocketProvider2(config.serverUrl, roomId, yDoc, {
92108
+ params: config.authToken ? { token: config.authToken } : void 0
92109
+ });
92096
92110
  let connected = false;
92097
92111
  const handleStatus = (event) => {
92098
92112
  if (event.status === "connected") {
@@ -95753,90 +95767,101 @@ var SlideNotesPanel = ({
95753
95767
  });
95754
95768
  const hasNotes = draft.trim().length > 0;
95755
95769
  const slideLabel = activeSlide ? t2("pptx.notes.slideN", { n: activeSlide.slideNumber }) : t2("pptx.notes.noSlide");
95756
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col border-t border-border/60 bg-background select-none", children: [
95757
- /* @__PURE__ */ jsxs(
95758
- "button",
95759
- {
95760
- type: "button",
95761
- onClick: onToggle,
95762
- 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",
95763
- "aria-expanded": isExpanded,
95764
- "aria-controls": "slide-notes-content",
95765
- children: [
95766
- "Notes",
95767
- !isExpanded && hasNotes && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/50 text-[10px]", children: "(has notes)" })
95768
- ]
95769
- }
95770
- ),
95771
- isExpanded && /* @__PURE__ */ jsxs(
95772
- "div",
95773
- {
95774
- id: "slide-notes-content",
95775
- className: "px-3 pb-2 overflow-y-auto",
95776
- style: { maxHeight: panelHeight ?? EXPANDED_MAX_HEIGHT + 40 },
95777
- children: [
95778
- /* @__PURE__ */ jsx("div", { className: "text-[10px] text-muted-foreground mb-1", children: slideLabel }),
95779
- canEdit ? /* @__PURE__ */ jsxs(Fragment, { children: [
95780
- /* @__PURE__ */ jsx(
95781
- NotesToolbar,
95782
- {
95783
- isRichEditEnabled,
95784
- showLinkPopover,
95785
- savedSelectionText: savedSelectionRef.current?.text ?? "",
95786
- hasAllSlides: allSlides !== void 0 && allSlides.length > 0,
95787
- onApplyRichCommand: applyRichCommand,
95788
- onToggleBulletList: toggleBulletList,
95789
- onToggleNumberedList: toggleNumberedList,
95790
- onIndent: handleIndent,
95791
- onOutdent: handleOutdent,
95792
- onLinkButtonClick: handleLinkButtonClick,
95793
- onInsertLink: handleInsertLink,
95794
- onCloseLinkPopover: () => setShowLinkPopover(false),
95795
- onPrintClick: () => setShowPrintDialog(true),
95796
- onToggleRichEdit: () => setIsRichEditEnabled((prev) => !prev)
95797
- }
95798
- ),
95799
- isRichEditEnabled ? /* @__PURE__ */ jsx(
95800
- "div",
95801
- {
95802
- ref: richEditorRef,
95803
- contentEditable: true,
95804
- suppressContentEditableWarning: true,
95805
- onInput: handleRichInput,
95806
- onBlur: handleBlur,
95807
- onKeyDown: handleKeyDownRich,
95808
- onClick: handleEditorClick,
95809
- 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",
95810
- style: { maxHeight: EXPANDED_MAX_HEIGHT - 8, minHeight: 72 }
95811
- }
95812
- ) : /* @__PURE__ */ jsx(
95813
- "textarea",
95814
- {
95815
- ref: textareaRef,
95816
- name: "slide-notes",
95817
- value: draft,
95818
- onChange: handlePlainChange,
95819
- onBlur: handleBlur,
95820
- onKeyDown: handleKeyDownPlain,
95821
- placeholder: t2("pptx.notes.clickToAddNotes"),
95822
- rows: 4,
95823
- 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",
95824
- style: { maxHeight: EXPANDED_MAX_HEIGHT - 8 }
95825
- }
95826
- )
95827
- ] }) : /* @__PURE__ */ jsx(
95828
- "div",
95829
- {
95830
- 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",
95831
- style: { maxHeight: EXPANDED_MAX_HEIGHT - 32, minHeight: 60 },
95832
- children: hasNotes ? renderRichNotesSegments(draftSegments) : /* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: t2("pptx.notes.noNotes") })
95833
- }
95834
- )
95835
- ]
95836
- }
95837
- ),
95838
- showPrintDialog && allSlides && /* @__PURE__ */ jsx(NotesPrintDialog, { slides: allSlides, onClose: () => setShowPrintDialog(false) })
95839
- ] });
95770
+ return /* @__PURE__ */ jsxs(
95771
+ "div",
95772
+ {
95773
+ className: cn(
95774
+ "flex flex-col border-t border-border/60 bg-background select-none",
95775
+ // On mobile, hide the entire notes strip when collapsed — the
95776
+ // MobileBottomBar's Notes button is the entry point instead.
95777
+ !isExpanded && "max-md:hidden"
95778
+ ),
95779
+ children: [
95780
+ /* @__PURE__ */ jsxs(
95781
+ "button",
95782
+ {
95783
+ type: "button",
95784
+ onClick: onToggle,
95785
+ 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",
95786
+ "aria-expanded": isExpanded,
95787
+ "aria-controls": "slide-notes-content",
95788
+ children: [
95789
+ "Notes",
95790
+ !isExpanded && hasNotes && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/50 text-[10px]", children: "(has notes)" })
95791
+ ]
95792
+ }
95793
+ ),
95794
+ isExpanded && /* @__PURE__ */ jsxs(
95795
+ "div",
95796
+ {
95797
+ id: "slide-notes-content",
95798
+ className: "px-3 pb-2 overflow-y-auto",
95799
+ style: { maxHeight: panelHeight ?? EXPANDED_MAX_HEIGHT + 40 },
95800
+ children: [
95801
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] text-muted-foreground mb-1", children: slideLabel }),
95802
+ canEdit ? /* @__PURE__ */ jsxs(Fragment, { children: [
95803
+ /* @__PURE__ */ jsx(
95804
+ NotesToolbar,
95805
+ {
95806
+ isRichEditEnabled,
95807
+ showLinkPopover,
95808
+ savedSelectionText: savedSelectionRef.current?.text ?? "",
95809
+ hasAllSlides: allSlides !== void 0 && allSlides.length > 0,
95810
+ onApplyRichCommand: applyRichCommand,
95811
+ onToggleBulletList: toggleBulletList,
95812
+ onToggleNumberedList: toggleNumberedList,
95813
+ onIndent: handleIndent,
95814
+ onOutdent: handleOutdent,
95815
+ onLinkButtonClick: handleLinkButtonClick,
95816
+ onInsertLink: handleInsertLink,
95817
+ onCloseLinkPopover: () => setShowLinkPopover(false),
95818
+ onPrintClick: () => setShowPrintDialog(true),
95819
+ onToggleRichEdit: () => setIsRichEditEnabled((prev) => !prev)
95820
+ }
95821
+ ),
95822
+ isRichEditEnabled ? /* @__PURE__ */ jsx(
95823
+ "div",
95824
+ {
95825
+ ref: richEditorRef,
95826
+ contentEditable: true,
95827
+ suppressContentEditableWarning: true,
95828
+ onInput: handleRichInput,
95829
+ onBlur: handleBlur,
95830
+ onKeyDown: handleKeyDownRich,
95831
+ onClick: handleEditorClick,
95832
+ 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",
95833
+ style: { maxHeight: EXPANDED_MAX_HEIGHT - 8, minHeight: 72 }
95834
+ }
95835
+ ) : /* @__PURE__ */ jsx(
95836
+ "textarea",
95837
+ {
95838
+ ref: textareaRef,
95839
+ name: "slide-notes",
95840
+ value: draft,
95841
+ onChange: handlePlainChange,
95842
+ onBlur: handleBlur,
95843
+ onKeyDown: handleKeyDownPlain,
95844
+ placeholder: t2("pptx.notes.clickToAddNotes"),
95845
+ rows: 4,
95846
+ 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",
95847
+ style: { maxHeight: EXPANDED_MAX_HEIGHT - 8 }
95848
+ }
95849
+ )
95850
+ ] }) : /* @__PURE__ */ jsx(
95851
+ "div",
95852
+ {
95853
+ 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",
95854
+ style: { maxHeight: EXPANDED_MAX_HEIGHT - 32, minHeight: 60 },
95855
+ children: hasNotes ? renderRichNotesSegments(draftSegments) : /* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: t2("pptx.notes.noNotes") })
95856
+ }
95857
+ )
95858
+ ]
95859
+ }
95860
+ ),
95861
+ showPrintDialog && allSlides && /* @__PURE__ */ jsx(NotesPrintDialog, { slides: allSlides, onClose: () => setShowPrintDialog(false) })
95862
+ ]
95863
+ }
95864
+ );
95840
95865
  };
95841
95866
  var DB_NAME = "pptx-viewer-autosave";
95842
95867
  var DB_VERSION = 1;
@@ -98373,7 +98398,8 @@ function ArrangeSection(p3) {
98373
98398
  {
98374
98399
  type: "button",
98375
98400
  onClick: p3.onToggleFormatPainter,
98376
- disabled: !p3.canEdit,
98401
+ disabled: !p3.canEdit || p3.canActivateFormatPainter === false && !p3.formatPainterActive,
98402
+ "data-testid": "format-painter-toggle",
98377
98403
  className: cn(
98378
98404
  pill,
98379
98405
  p3.formatPainterActive ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
@@ -98825,7 +98851,8 @@ function HomeSection(p3) {
98825
98851
  const { fontFamily, fontSize } = extractFontInfo(p3.selectedElement);
98826
98852
  const handleNewSlide = useCallback(() => {
98827
98853
  if (p3.layoutOptions.length > 0) {
98828
- p3.onInsertSlideFromLayout(p3.layoutOptions[0].path);
98854
+ const first = p3.layoutOptions[0];
98855
+ p3.onInsertSlideFromLayout(first.path, first.name);
98829
98856
  }
98830
98857
  }, [p3]);
98831
98858
  useEffect(() => {
@@ -98912,7 +98939,8 @@ function HomeSection(p3) {
98912
98939
  {
98913
98940
  type: "button",
98914
98941
  onClick: p3.onToggleFormatPainter,
98915
- disabled: !p3.canEdit,
98942
+ disabled: !p3.canEdit || p3.canActivateFormatPainter === false && !p3.formatPainterActive,
98943
+ "data-testid": "format-painter-toggle",
98916
98944
  className: cn(
98917
98945
  gL,
98918
98946
  p3.formatPainterActive ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
@@ -98962,7 +98990,7 @@ function HomeSection(p3) {
98962
98990
  type: "button",
98963
98991
  className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
98964
98992
  onClick: () => {
98965
- p3.onInsertSlideFromLayout(lo.path);
98993
+ p3.onInsertSlideFromLayout(lo.path, lo.name);
98966
98994
  setLayoutMenuOpen(false);
98967
98995
  },
98968
98996
  children: lo.name
@@ -99862,6 +99890,561 @@ function TextSection(p3) {
99862
99890
  ] })
99863
99891
  ] });
99864
99892
  }
99893
+ function ViewSection(p3) {
99894
+ const { t: t2 } = useTranslation();
99895
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
99896
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99897
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
99898
+ /* @__PURE__ */ jsx("button", { className: pill, title: "Normal view", children: "Normal" }),
99899
+ 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" }),
99900
+ /* @__PURE__ */ jsx("button", { className: pill, title: "Reading View", children: "Reading View" })
99901
+ ] }),
99902
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Presentation Views" })
99903
+ ] }),
99904
+ sep,
99905
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99906
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: /* @__PURE__ */ jsx(
99907
+ "button",
99908
+ {
99909
+ onClick: p3.onEnterMasterView,
99910
+ disabled: !p3.canEdit,
99911
+ className: pill,
99912
+ title: "Edit slide masters and layouts",
99913
+ children: "Slide Master"
99914
+ }
99915
+ ) }),
99916
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Master Views" })
99917
+ ] }),
99918
+ sep,
99919
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99920
+ /* @__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" }) }),
99921
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Zoom" })
99922
+ ] }),
99923
+ sep,
99924
+ /* @__PURE__ */ jsx(
99925
+ "button",
99926
+ {
99927
+ onClick: () => p3.onSetEditTemplateMode(!p3.editTemplateMode),
99928
+ disabled: !p3.canEdit,
99929
+ className: cn(
99930
+ pill,
99931
+ p3.editTemplateMode ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
99932
+ ),
99933
+ title: "Toggle template/master element editing",
99934
+ children: p3.editTemplateMode ? "Templates On" : "Templates Off"
99935
+ }
99936
+ ),
99937
+ p3.onToggleSelectionPane && /* @__PURE__ */ jsxs(
99938
+ "button",
99939
+ {
99940
+ type: "button",
99941
+ onClick: p3.onToggleSelectionPane,
99942
+ className: cn(
99943
+ pill,
99944
+ p3.isSelectionPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
99945
+ ),
99946
+ title: "Selection Pane",
99947
+ children: [
99948
+ /* @__PURE__ */ jsx(LuList, { className: ic2 }),
99949
+ "Selection"
99950
+ ]
99951
+ }
99952
+ ),
99953
+ p3.onToggleEyedropper && /* @__PURE__ */ jsxs(
99954
+ "button",
99955
+ {
99956
+ type: "button",
99957
+ onClick: p3.onToggleEyedropper,
99958
+ disabled: !p3.canEdit,
99959
+ className: cn(
99960
+ pill,
99961
+ p3.eyedropperActive ? "bg-purple-600 hover:bg-purple-500 text-purple-50" : ""
99962
+ ),
99963
+ title: "Eyedropper \u2014 sample a colour from the slide",
99964
+ children: [
99965
+ /* @__PURE__ */ jsx(LuPipette, { className: ic2 }),
99966
+ "Eyedropper"
99967
+ ]
99968
+ }
99969
+ ),
99970
+ /* @__PURE__ */ jsx(
99971
+ "button",
99972
+ {
99973
+ onClick: () => p3.onSetShowGrid(!p3.showGrid),
99974
+ className: cn(pill, p3.showGrid ? "bg-primary text-primary-foreground" : ""),
99975
+ title: t2("pptx.grid.toggleGrid"),
99976
+ children: t2("pptx.grid.grid")
99977
+ }
99978
+ ),
99979
+ /* @__PURE__ */ jsx(
99980
+ "button",
99981
+ {
99982
+ onClick: () => p3.onSetShowRulers(!p3.showRulers),
99983
+ className: cn(pill, p3.showRulers ? "bg-primary text-primary-foreground" : ""),
99984
+ title: t2("pptx.ruler.toggleRulers"),
99985
+ children: t2("pptx.ruler.rulers")
99986
+ }
99987
+ ),
99988
+ /* @__PURE__ */ jsx(
99989
+ "button",
99990
+ {
99991
+ onClick: () => p3.onSetSnapToGrid(!p3.snapToGrid),
99992
+ className: cn(pill, p3.snapToGrid ? "bg-primary text-primary-foreground" : ""),
99993
+ title: t2("pptx.grid.snapToGrid"),
99994
+ children: t2("pptx.grid.snapToGrid")
99995
+ }
99996
+ ),
99997
+ /* @__PURE__ */ jsx(
99998
+ "button",
99999
+ {
100000
+ onClick: () => p3.onSetSnapToShape(!p3.snapToShape),
100001
+ className: cn(pill, p3.snapToShape ? "bg-primary text-primary-foreground" : ""),
100002
+ title: t2("pptx.grid.snapToShape"),
100003
+ children: t2("pptx.grid.snapToShape")
100004
+ }
100005
+ ),
100006
+ /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("h"), className: pill, title: "Add horizontal guide", children: "H Guide" }),
100007
+ /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("v"), className: pill, title: "Add vertical guide", children: "V Guide" }),
100008
+ /* @__PURE__ */ jsx(
100009
+ "button",
100010
+ {
100011
+ onClick: () => p3.onSetSpellCheckEnabled(!p3.spellCheckEnabled),
100012
+ className: cn(pill, p3.spellCheckEnabled ? "bg-primary text-primary-foreground" : ""),
100013
+ title: "Toggle spell check",
100014
+ children: "Spell"
100015
+ }
100016
+ )
100017
+ ] });
100018
+ }
100019
+ function MobileSheet({
100020
+ open,
100021
+ onClose,
100022
+ title,
100023
+ children,
100024
+ heightFraction = 0.6,
100025
+ fullScreen = false,
100026
+ className,
100027
+ headerRight
100028
+ }) {
100029
+ const sheetRef = useRef(null);
100030
+ const [dragY, setDragY] = useState(0);
100031
+ const dragStartRef = useRef(null);
100032
+ const onPointerDown = useCallback((e2) => {
100033
+ dragStartRef.current = e2.clientY;
100034
+ e2.target.setPointerCapture?.(e2.pointerId);
100035
+ }, []);
100036
+ const onPointerMove = useCallback((e2) => {
100037
+ if (dragStartRef.current === null) {
100038
+ return;
100039
+ }
100040
+ const delta = e2.clientY - dragStartRef.current;
100041
+ setDragY(Math.max(0, delta));
100042
+ }, []);
100043
+ const onPointerUp = useCallback(
100044
+ (e2) => {
100045
+ if (dragStartRef.current === null) {
100046
+ return;
100047
+ }
100048
+ const delta = e2.clientY - dragStartRef.current;
100049
+ dragStartRef.current = null;
100050
+ e2.target.releasePointerCapture?.(e2.pointerId);
100051
+ if (delta > 120) {
100052
+ onClose();
100053
+ }
100054
+ setDragY(0);
100055
+ },
100056
+ [onClose]
100057
+ );
100058
+ useEffect(() => {
100059
+ if (!open) {
100060
+ return;
100061
+ }
100062
+ const handleKey = (e2) => {
100063
+ if (e2.key === "Escape") {
100064
+ onClose();
100065
+ }
100066
+ };
100067
+ window.addEventListener("keydown", handleKey);
100068
+ return () => window.removeEventListener("keydown", handleKey);
100069
+ }, [open, onClose]);
100070
+ if (!open) {
100071
+ return null;
100072
+ }
100073
+ const heightStyle = fullScreen ? { height: "calc(100dvh - env(safe-area-inset-top))" } : { height: `${Math.round(heightFraction * 100)}dvh` };
100074
+ return /* @__PURE__ */ jsxs(
100075
+ "div",
100076
+ {
100077
+ className: "fixed inset-0 z-50 flex flex-col justify-end md:hidden",
100078
+ role: "dialog",
100079
+ "aria-modal": "true",
100080
+ children: [
100081
+ /* @__PURE__ */ jsx(
100082
+ "button",
100083
+ {
100084
+ type: "button",
100085
+ "aria-label": "Close",
100086
+ className: "absolute inset-0 bg-black/40 backdrop-blur-[2px] animate-in fade-in duration-150",
100087
+ onClick: onClose
100088
+ }
100089
+ ),
100090
+ /* @__PURE__ */ jsxs(
100091
+ "div",
100092
+ {
100093
+ ref: sheetRef,
100094
+ className: cn(
100095
+ "relative bg-background border-t border-border rounded-t-2xl shadow-2xl flex flex-col overflow-hidden",
100096
+ "animate-in slide-in-from-bottom duration-200",
100097
+ className
100098
+ ),
100099
+ style: {
100100
+ ...heightStyle,
100101
+ transform: dragY > 0 ? `translateY(${dragY}px)` : void 0,
100102
+ transition: dragStartRef.current === null ? "transform 150ms ease-out" : "none"
100103
+ },
100104
+ children: [
100105
+ /* @__PURE__ */ jsx(
100106
+ "div",
100107
+ {
100108
+ className: "flex items-center justify-center pt-2 pb-1 cursor-grab active:cursor-grabbing touch-none",
100109
+ onPointerDown,
100110
+ onPointerMove,
100111
+ onPointerUp,
100112
+ onPointerCancel: onPointerUp,
100113
+ children: /* @__PURE__ */ jsx("div", { className: "h-1 w-10 rounded-full bg-muted-foreground/40" })
100114
+ }
100115
+ ),
100116
+ (title || headerRight) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-4 pb-2 border-b border-border/60", children: [
100117
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground truncate", children: title }),
100118
+ headerRight
100119
+ ] }),
100120
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overscroll-contain", children })
100121
+ ]
100122
+ }
100123
+ )
100124
+ ]
100125
+ }
100126
+ );
100127
+ }
100128
+ var MENU_ITEMS = [
100129
+ { key: "home", label: "Home", icon: LuClipboardCopy },
100130
+ { key: "insert", label: "Insert", icon: LuPlus },
100131
+ { key: "text", label: "Text", icon: LuType },
100132
+ { key: "draw", label: "Draw", icon: LuPaintbrush },
100133
+ { key: "arrange", label: "Arrange", icon: LuShapes },
100134
+ { key: "design", label: "Design", icon: LuLayoutGrid },
100135
+ { key: "transitions", label: "Transitions", icon: LuSparkles },
100136
+ { key: "animations", label: "Animations", icon: LuWand },
100137
+ { key: "slideShow", label: "Slide Show", icon: LuPresentation },
100138
+ { key: "review", label: "Review", icon: LuTextCursorInput },
100139
+ { key: "view", label: "View", icon: LuSettings },
100140
+ { key: "file", label: "File", icon: LuFile }
100141
+ ];
100142
+ function MobileMenuSheet(props) {
100143
+ const { open, onClose } = props;
100144
+ const [active, setActive] = useState("home");
100145
+ return /* @__PURE__ */ jsx(MobileSheet, { open, onClose, fullScreen: true, title: "Menu", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
100146
+ /* @__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(
100147
+ "button",
100148
+ {
100149
+ type: "button",
100150
+ onClick: () => setActive(active === key ? null : key),
100151
+ className: cn(
100152
+ "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]",
100153
+ active === key ? "bg-primary text-primary-foreground border-primary" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent/40"
100154
+ ),
100155
+ children: [
100156
+ /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }),
100157
+ label
100158
+ ]
100159
+ },
100160
+ key
100161
+ )) }) }),
100162
+ /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsx(MobileSectionBody, { active, ...props }) })
100163
+ ] }) });
100164
+ }
100165
+ function MobileSectionBody({
100166
+ active,
100167
+ ...p3
100168
+ }) {
100169
+ const wrap = "flex flex-wrap items-center gap-2";
100170
+ switch (active) {
100171
+ case "home":
100172
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100173
+ HomeSection,
100174
+ {
100175
+ canEdit: p3.canEdit,
100176
+ clipboardPayload: p3.clipboardPayload,
100177
+ formatPainterActive: p3.formatPainterActive,
100178
+ canActivateFormatPainter: p3.canActivateFormatPainter,
100179
+ onCopy: p3.onCopy,
100180
+ onCut: p3.onCut,
100181
+ onPaste: p3.onPaste,
100182
+ onToggleFormatPainter: p3.onToggleFormatPainter,
100183
+ layoutOptions: p3.layoutOptions,
100184
+ onInsertSlideFromLayout: p3.onInsertSlideFromLayout,
100185
+ selectedElement: p3.selectedElement,
100186
+ onUpdateTextStyle: p3.onUpdateTextStyle
100187
+ }
100188
+ ) });
100189
+ case "insert":
100190
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100191
+ InsertSection,
100192
+ {
100193
+ canEdit: p3.canEdit,
100194
+ newShapeType: p3.newShapeType,
100195
+ onSetNewShapeType: p3.onSetNewShapeType,
100196
+ onAddTextBox: p3.onAddTextBox,
100197
+ onAddShape: p3.onAddShape,
100198
+ onAddTable: p3.onAddTable,
100199
+ onAddSmartArt: p3.onAddSmartArt,
100200
+ onAddEquation: p3.onAddEquation,
100201
+ onAddActionButton: p3.onAddActionButton,
100202
+ onInsertField: p3.onInsertField,
100203
+ onOpenImagePicker: p3.onOpenImagePicker,
100204
+ onOpenMediaPicker: p3.onOpenMediaPicker
100205
+ }
100206
+ ) });
100207
+ case "text":
100208
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100209
+ TextSection,
100210
+ {
100211
+ canEdit: p3.canEdit,
100212
+ selectedElement: p3.selectedElement,
100213
+ tableEditorState: p3.tableEditorState,
100214
+ onUpdateTextStyle: p3.onUpdateTextStyle
100215
+ }
100216
+ ) });
100217
+ case "draw":
100218
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100219
+ DrawSection,
100220
+ {
100221
+ activeTool: p3.activeTool,
100222
+ drawingColor: p3.drawingColor,
100223
+ drawingWidth: p3.drawingWidth,
100224
+ onSetActiveTool: p3.onSetActiveTool,
100225
+ onSetDrawingColor: p3.onSetDrawingColor,
100226
+ onSetDrawingWidth: p3.onSetDrawingWidth
100227
+ }
100228
+ ) });
100229
+ case "arrange":
100230
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100231
+ ArrangeSection,
100232
+ {
100233
+ canEdit: p3.canEdit,
100234
+ selectedElement: p3.selectedElement,
100235
+ clipboardPayload: p3.clipboardPayload,
100236
+ onAlignElements: p3.onAlignElements,
100237
+ onCopy: p3.onCopy,
100238
+ onCut: p3.onCut,
100239
+ onPaste: p3.onPaste,
100240
+ onFlip: p3.onFlip,
100241
+ onMoveLayer: p3.onMoveLayer,
100242
+ onMoveLayerToEdge: p3.onMoveLayerToEdge,
100243
+ onDuplicate: p3.onDuplicate,
100244
+ onDelete: p3.onDelete,
100245
+ formatPainterActive: p3.formatPainterActive,
100246
+ onToggleFormatPainter: p3.onToggleFormatPainter,
100247
+ canActivateFormatPainter: p3.canActivateFormatPainter
100248
+ }
100249
+ ) });
100250
+ case "design":
100251
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100252
+ DesignSection,
100253
+ {
100254
+ canEdit: p3.canEdit,
100255
+ onToggleThemeGallery: p3.onToggleThemeGallery,
100256
+ isThemeGalleryOpen: p3.isThemeGalleryOpen,
100257
+ onToggleThemeEditor: p3.onToggleThemeEditor,
100258
+ isThemeEditorOpen: p3.isThemeEditorOpen,
100259
+ onOpenDocumentProperties: p3.onOpenDocumentProperties,
100260
+ onToggleInspector: p3.onToggleInspector,
100261
+ isInspectorPaneOpen: p3.isInspectorPaneOpen
100262
+ }
100263
+ ) });
100264
+ case "transitions":
100265
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100266
+ TransitionsSection,
100267
+ {
100268
+ isInspectorPaneOpen: p3.isInspectorPaneOpen,
100269
+ onToggleInspector: p3.onToggleInspector
100270
+ }
100271
+ ) });
100272
+ case "animations":
100273
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100274
+ AnimationsSection,
100275
+ {
100276
+ canEdit: p3.canEdit,
100277
+ selectedElement: p3.selectedElement,
100278
+ isInspectorPaneOpen: p3.isInspectorPaneOpen,
100279
+ onToggleInspector: p3.onToggleInspector,
100280
+ onOpenAnimationPanel: p3.onOpenAnimationPanel,
100281
+ onAddAnimation: p3.onAddAnimation,
100282
+ onRemoveAnimation: p3.onRemoveAnimation
100283
+ }
100284
+ ) });
100285
+ case "slideShow":
100286
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100287
+ SlideShowSection,
100288
+ {
100289
+ onPresent: () => p3.onSetMode("present"),
100290
+ onEnterPresenterView: p3.onEnterPresenterView ?? (() => {
100291
+ }),
100292
+ onEnterRehearsalMode: p3.onEnterRehearsalMode ?? (() => {
100293
+ }),
100294
+ onOpenSetUpSlideShow: p3.onOpenSetUpSlideShow ?? (() => {
100295
+ }),
100296
+ onOpenBroadcastDialog: p3.onOpenBroadcastDialog ?? (() => {
100297
+ }),
100298
+ onToggleSubtitles: p3.onToggleSubtitles ?? (() => {
100299
+ }),
100300
+ showSubtitles: p3.showSubtitles ?? false,
100301
+ onSetMode: p3.onSetMode
100302
+ }
100303
+ ) });
100304
+ case "review":
100305
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100306
+ ReviewSection,
100307
+ {
100308
+ canEdit: p3.canEdit,
100309
+ spellCheckEnabled: p3.spellCheckEnabled,
100310
+ onSetSpellCheckEnabled: p3.onSetSpellCheckEnabled,
100311
+ onToggleComments: p3.onToggleComments,
100312
+ isCommentsPanelOpen: p3.isCommentsPanelOpen,
100313
+ slideCommentCount: p3.slideCommentCount,
100314
+ onCompare: p3.onCompare
100315
+ }
100316
+ ) });
100317
+ case "view":
100318
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100319
+ ViewSection,
100320
+ {
100321
+ canEdit: p3.canEdit,
100322
+ editTemplateMode: p3.editTemplateMode,
100323
+ onSetEditTemplateMode: p3.onSetEditTemplateMode,
100324
+ spellCheckEnabled: p3.spellCheckEnabled,
100325
+ onSetSpellCheckEnabled: p3.onSetSpellCheckEnabled,
100326
+ showGrid: p3.showGrid,
100327
+ showRulers: p3.showRulers,
100328
+ snapToGrid: p3.snapToGrid,
100329
+ snapToShape: p3.snapToShape,
100330
+ onSetShowGrid: p3.onSetShowGrid,
100331
+ onSetShowRulers: p3.onSetShowRulers,
100332
+ onSetSnapToGrid: p3.onSetSnapToGrid,
100333
+ onSetSnapToShape: p3.onSetSnapToShape,
100334
+ onAddGuide: p3.onAddGuide,
100335
+ onEnterMasterView: p3.onEnterMasterView,
100336
+ isSelectionPaneOpen: p3.isSelectionPaneOpen,
100337
+ onToggleSelectionPane: p3.onToggleSelectionPane,
100338
+ eyedropperActive: p3.eyedropperActive,
100339
+ onToggleEyedropper: p3.onToggleEyedropper
100340
+ }
100341
+ ) });
100342
+ case "file":
100343
+ return /* @__PURE__ */ jsx("div", { className: wrap, children: /* @__PURE__ */ jsx(
100344
+ FileSection,
100345
+ {
100346
+ onExportPng: p3.onExportPng,
100347
+ onExportPdf: p3.onExportPdf,
100348
+ onExportVideo: p3.onExportVideo,
100349
+ onExportGif: p3.onExportGif,
100350
+ onPackageForSharing: p3.onPackageForSharing,
100351
+ onSaveAsPptx: p3.onSaveAsPptx,
100352
+ onSaveAsPpsx: p3.onSaveAsPpsx,
100353
+ onSaveAsPptm: p3.onSaveAsPptm,
100354
+ hasMacros: p3.hasMacros,
100355
+ onCopySlideAsImage: p3.onCopySlideAsImage,
100356
+ onPrint: p3.onPrint,
100357
+ onOpenDocumentProperties: p3.onOpenDocumentProperties,
100358
+ onOpenPasswordProtection: p3.onOpenPasswordProtection,
100359
+ onOpenFontEmbedding: p3.onOpenFontEmbedding,
100360
+ onOpenDigitalSignatures: p3.onOpenDigitalSignatures
100361
+ }
100362
+ ) });
100363
+ default:
100364
+ return /* @__PURE__ */ jsxs("div", { className: "text-center text-sm text-muted-foreground py-8", children: [
100365
+ /* @__PURE__ */ jsx(LuChevronRight, { className: "w-5 h-5 inline-block opacity-50" }),
100366
+ " Select a section above"
100367
+ ] });
100368
+ }
100369
+ }
100370
+ function MobileToolbar(props) {
100371
+ const { t: t2 } = useTranslation();
100372
+ const { mode, canUndo, canRedo, onUndo, onRedo, onSetMode } = props;
100373
+ const [menuOpen, setMenuOpen] = useState(false);
100374
+ const showEdit = mode === "edit" || mode === "master";
100375
+ 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";
100376
+ return /* @__PURE__ */ jsxs(
100377
+ "div",
100378
+ {
100379
+ role: "toolbar",
100380
+ "aria-label": "Toolbar",
100381
+ 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)]",
100382
+ children: [
100383
+ showEdit && /* @__PURE__ */ jsx(
100384
+ "button",
100385
+ {
100386
+ type: "button",
100387
+ onClick: () => setMenuOpen(true),
100388
+ className: btn,
100389
+ title: "Menu",
100390
+ "aria-label": "Menu",
100391
+ children: /* @__PURE__ */ jsx(LuMenu, { className: "w-5 h-5" })
100392
+ }
100393
+ ),
100394
+ showEdit && /* @__PURE__ */ jsxs(Fragment, { children: [
100395
+ /* @__PURE__ */ jsx(
100396
+ "button",
100397
+ {
100398
+ type: "button",
100399
+ onClick: onUndo,
100400
+ disabled: !canUndo,
100401
+ className: btn,
100402
+ title: t2("pptx.toolbar.undo"),
100403
+ "aria-label": t2("pptx.toolbar.undo"),
100404
+ children: /* @__PURE__ */ jsx(LuUndo, { className: "w-5 h-5" })
100405
+ }
100406
+ ),
100407
+ /* @__PURE__ */ jsx(
100408
+ "button",
100409
+ {
100410
+ type: "button",
100411
+ onClick: onRedo,
100412
+ disabled: !canRedo,
100413
+ className: btn,
100414
+ title: t2("pptx.toolbar.redo"),
100415
+ "aria-label": t2("pptx.toolbar.redo"),
100416
+ children: /* @__PURE__ */ jsx(LuRedo, { className: "w-5 h-5" })
100417
+ }
100418
+ )
100419
+ ] }),
100420
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
100421
+ /* @__PURE__ */ jsx(
100422
+ "button",
100423
+ {
100424
+ type: "button",
100425
+ onClick: () => onSetMode("present"),
100426
+ className: cn(btn, "text-primary"),
100427
+ title: t2("pptx.toolbar.present"),
100428
+ "aria-label": t2("pptx.toolbar.present"),
100429
+ children: /* @__PURE__ */ jsx(LuPresentation, { className: "w-5 h-5" })
100430
+ }
100431
+ ),
100432
+ showEdit && /* @__PURE__ */ jsx(
100433
+ "button",
100434
+ {
100435
+ type: "button",
100436
+ onClick: props.onOpenShareDialog ?? props.onPackageForSharing,
100437
+ className: cn(btn, "bg-primary text-primary-foreground hover:bg-primary/90 px-3"),
100438
+ title: t2("pptx.toolbar.share"),
100439
+ "aria-label": t2("pptx.toolbar.share"),
100440
+ children: /* @__PURE__ */ jsx(LuShare2, { className: "w-4 h-4" })
100441
+ }
100442
+ ),
100443
+ /* @__PURE__ */ jsx(MobileMenuSheet, { open: menuOpen, onClose: () => setMenuOpen(false), ...props })
100444
+ ]
100445
+ }
100446
+ );
100447
+ }
99865
100448
  function CustomShowsControls({
99866
100449
  customShows,
99867
100450
  activeCustomShowId,
@@ -100290,7 +100873,7 @@ function ToolbarPrimaryRow(p3) {
100290
100873
  onClick: p3.onToggleComments,
100291
100874
  className: cn(
100292
100875
  qab,
100293
- "max-md:hidden",
100876
+ "relative max-md:hidden",
100294
100877
  p3.isCommentsPanelOpen ? "text-foreground" : "text-muted-foreground"
100295
100878
  ),
100296
100879
  title: t2("pptx.toolbar.comments"),
@@ -100406,134 +100989,11 @@ function ToolbarPrimaryRow(p3) {
100406
100989
  /* @__PURE__ */ jsx(OverflowMenu, { ...p3 })
100407
100990
  ] });
100408
100991
  }
100409
- function ViewSection(p3) {
100410
- const { t: t2 } = useTranslation();
100411
- return /* @__PURE__ */ jsxs(Fragment, { children: [
100412
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
100413
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
100414
- /* @__PURE__ */ jsx("button", { className: pill, title: "Normal view", children: "Normal" }),
100415
- 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" }),
100416
- /* @__PURE__ */ jsx("button", { className: pill, title: "Reading View", children: "Reading View" })
100417
- ] }),
100418
- /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Presentation Views" })
100419
- ] }),
100420
- sep,
100421
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
100422
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: /* @__PURE__ */ jsx(
100423
- "button",
100424
- {
100425
- onClick: p3.onEnterMasterView,
100426
- disabled: !p3.canEdit,
100427
- className: pill,
100428
- title: "Edit slide masters and layouts",
100429
- children: "Slide Master"
100430
- }
100431
- ) }),
100432
- /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Master Views" })
100433
- ] }),
100434
- sep,
100435
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
100436
- /* @__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" }) }),
100437
- /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Zoom" })
100438
- ] }),
100439
- sep,
100440
- /* @__PURE__ */ jsx(
100441
- "button",
100442
- {
100443
- onClick: () => p3.onSetEditTemplateMode(!p3.editTemplateMode),
100444
- disabled: !p3.canEdit,
100445
- className: cn(
100446
- pill,
100447
- p3.editTemplateMode ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
100448
- ),
100449
- title: "Toggle template/master element editing",
100450
- children: p3.editTemplateMode ? "Templates On" : "Templates Off"
100451
- }
100452
- ),
100453
- p3.onToggleSelectionPane && /* @__PURE__ */ jsxs(
100454
- "button",
100455
- {
100456
- type: "button",
100457
- onClick: p3.onToggleSelectionPane,
100458
- className: cn(
100459
- pill,
100460
- p3.isSelectionPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
100461
- ),
100462
- title: "Selection Pane",
100463
- children: [
100464
- /* @__PURE__ */ jsx(LuList, { className: ic2 }),
100465
- "Selection"
100466
- ]
100467
- }
100468
- ),
100469
- p3.onToggleEyedropper && /* @__PURE__ */ jsxs(
100470
- "button",
100471
- {
100472
- type: "button",
100473
- onClick: p3.onToggleEyedropper,
100474
- disabled: !p3.canEdit,
100475
- className: cn(
100476
- pill,
100477
- p3.eyedropperActive ? "bg-purple-600 hover:bg-purple-500 text-purple-50" : ""
100478
- ),
100479
- title: "Eyedropper \u2014 sample a colour from the slide",
100480
- children: [
100481
- /* @__PURE__ */ jsx(LuPipette, { className: ic2 }),
100482
- "Eyedropper"
100483
- ]
100484
- }
100485
- ),
100486
- /* @__PURE__ */ jsx(
100487
- "button",
100488
- {
100489
- onClick: () => p3.onSetShowGrid(!p3.showGrid),
100490
- className: cn(pill, p3.showGrid ? "bg-primary text-primary-foreground" : ""),
100491
- title: t2("pptx.grid.toggleGrid"),
100492
- children: t2("pptx.grid.grid")
100493
- }
100494
- ),
100495
- /* @__PURE__ */ jsx(
100496
- "button",
100497
- {
100498
- onClick: () => p3.onSetShowRulers(!p3.showRulers),
100499
- className: cn(pill, p3.showRulers ? "bg-primary text-primary-foreground" : ""),
100500
- title: t2("pptx.ruler.toggleRulers"),
100501
- children: t2("pptx.ruler.rulers")
100502
- }
100503
- ),
100504
- /* @__PURE__ */ jsx(
100505
- "button",
100506
- {
100507
- onClick: () => p3.onSetSnapToGrid(!p3.snapToGrid),
100508
- className: cn(pill, p3.snapToGrid ? "bg-primary text-primary-foreground" : ""),
100509
- title: t2("pptx.grid.snapToGrid"),
100510
- children: t2("pptx.grid.snapToGrid")
100511
- }
100512
- ),
100513
- /* @__PURE__ */ jsx(
100514
- "button",
100515
- {
100516
- onClick: () => p3.onSetSnapToShape(!p3.snapToShape),
100517
- className: cn(pill, p3.snapToShape ? "bg-primary text-primary-foreground" : ""),
100518
- title: t2("pptx.grid.snapToShape"),
100519
- children: t2("pptx.grid.snapToShape")
100520
- }
100521
- ),
100522
- /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("h"), className: pill, title: "Add horizontal guide", children: "H Guide" }),
100523
- /* @__PURE__ */ jsx("button", { onClick: () => p3.onAddGuide("v"), className: pill, title: "Add vertical guide", children: "V Guide" }),
100524
- /* @__PURE__ */ jsx(
100525
- "button",
100526
- {
100527
- onClick: () => p3.onSetSpellCheckEnabled(!p3.spellCheckEnabled),
100528
- className: cn(pill, p3.spellCheckEnabled ? "bg-primary text-primary-foreground" : ""),
100529
- title: "Toggle spell check",
100530
- children: "Spell"
100531
- }
100532
- )
100533
- ] });
100534
- }
100535
100992
  function Toolbar(p3) {
100536
100993
  const { mode, isNarrowViewport, isCompactToolbarOpen, toolbarSection, onSetToolbarSection } = p3;
100994
+ if (isNarrowViewport && mode !== "present") {
100995
+ return /* @__PURE__ */ jsx(MobileToolbar, { ...p3 });
100996
+ }
100537
100997
  const sFil = toolbarSection === "file";
100538
100998
  const sHome = toolbarSection === "home";
100539
100999
  const sIns = toolbarSection === "insert";
@@ -100619,6 +101079,7 @@ function Toolbar(p3) {
100619
101079
  canEdit: p3.canEdit,
100620
101080
  clipboardPayload: p3.clipboardPayload,
100621
101081
  formatPainterActive: p3.formatPainterActive,
101082
+ canActivateFormatPainter: p3.canActivateFormatPainter,
100622
101083
  onCopy: p3.onCopy,
100623
101084
  onCut: p3.onCut,
100624
101085
  onPaste: p3.onPaste,
@@ -100682,7 +101143,8 @@ function Toolbar(p3) {
100682
101143
  onDuplicate: p3.onDuplicate,
100683
101144
  onDelete: p3.onDelete,
100684
101145
  formatPainterActive: p3.formatPainterActive,
100685
- onToggleFormatPainter: p3.onToggleFormatPainter
101146
+ onToggleFormatPainter: p3.onToggleFormatPainter,
101147
+ canActivateFormatPainter: p3.canActivateFormatPainter
100686
101148
  }
100687
101149
  ),
100688
101150
  sDes && /* @__PURE__ */ jsx(
@@ -106395,181 +106857,194 @@ function InspectorPane(props) {
106395
106857
  const fallback = themeOptions[0]?.path ?? "";
106396
106858
  setSelectedThemePath((previous) => previous || fallback);
106397
106859
  }, [slideMasters, themeOptions]);
106398
- return /* @__PURE__ */ jsxs(
106399
- "div",
106400
- {
106401
- className: cn(
106402
- // Shared styles
106403
- "bg-background flex flex-col text-xs text-foreground shadow-xl",
106404
- // Mobile: absolute bottom sheet overlay
106405
- "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",
106406
- "max-md:transition-transform max-md:duration-200 max-md:ease-in-out",
106407
- isOpen ? "max-md:translate-y-0" : "max-md:translate-y-full",
106408
- // Desktop: flow-based flex child (takes space from canvas)
106409
- "md:h-full md:border-l md:border-border",
106410
- !panelWidth && "md:w-72"
106411
- ),
106412
- style: panelWidth ? { width: panelWidth } : void 0,
106413
- children: [
106414
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border", children: [
106415
- /* @__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(
106416
- "button",
106417
- {
106418
- type: "button",
106419
- title: label,
106420
- className: cn(
106421
- "flex items-center gap-1 px-2 py-1 rounded text-[11px] transition-colors",
106422
- activeTab === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent"
106423
- ),
106424
- onClick: () => onSetActiveTab(key),
106425
- children: [
106426
- /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
106427
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: label })
106428
- ]
106429
- },
106430
- key
106431
- )) }),
106432
- /* @__PURE__ */ jsx(
106433
- "button",
106434
- {
106435
- type: "button",
106436
- onClick: onClose,
106437
- title: t2("common.close"),
106438
- className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
106439
- children: /* @__PURE__ */ jsx(LuX, { className: "w-4 h-4" })
106440
- }
106441
- )
106442
- ] }),
106443
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3 space-y-3", children: [
106444
- activeTab === "elements" && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
106445
- /* @__PURE__ */ jsx("div", { className: cn(HEADING, "mb-2"), children: t2("pptx.inspector.layerOrder") }),
106446
- activeSlide ? [...activeSlide.elements || []].reverse().map((el, ri) => {
106447
- const idx = (activeSlide.elements || []).length - 1 - ri;
106448
- const sel = selectedElement?.id === el.id || selectedElementIds.includes(el.id);
106449
- const label = (hasTextProperties(el) ? (el.text || "").slice(0, 24) : void 0) || el.type;
106450
- return /* @__PURE__ */ jsxs(
106451
- "div",
106452
- {
106453
- title: `${el.type} \u2014 ${el.id}`,
106454
- className: cn(
106455
- "flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors",
106456
- sel ? "bg-primary/30 text-primary" : "hover:bg-muted text-foreground"
106457
- ),
106458
- onClick: () => onSelectElement(el.id),
106459
- children: [
106460
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground w-4 text-right", children: idx + 1 }),
106461
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: label })
106462
- ]
106463
- },
106464
- el.id
106465
- );
106466
- }) : /* @__PURE__ */ jsx("div", { className: "text-muted-foreground italic", children: t2("pptx.inspector.noSlideSelected") })
106467
- ] }),
106468
- activeTab === "properties" && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: hasSelection && selectedElement ? /* @__PURE__ */ jsx(
106469
- ElementInspectorBody,
106470
- {
106471
- selectedElement,
106472
- canEdit,
106473
- slides,
106474
- tableEditorState,
106475
- mediaDataUrls,
106476
- onUpdateElement,
106477
- onUpdateElementStyle,
106478
- onUpdateTextStyle,
106479
- onMoveLayer
106480
- }
106481
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
106860
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
106861
+ isOpen && /* @__PURE__ */ jsx(
106862
+ "button",
106863
+ {
106864
+ type: "button",
106865
+ "aria-label": t2("common.close"),
106866
+ onClick: onClose,
106867
+ className: "md:hidden fixed inset-0 z-20 bg-black/40 backdrop-blur-[2px] animate-in fade-in duration-150"
106868
+ }
106869
+ ),
106870
+ /* @__PURE__ */ jsxs(
106871
+ "div",
106872
+ {
106873
+ className: cn(
106874
+ // Shared styles
106875
+ "bg-background flex flex-col text-xs text-foreground shadow-xl",
106876
+ // Mobile: absolute bottom sheet overlay sized via dvh so it
106877
+ // adapts to the on-screen keyboard / dynamic browser chrome.
106878
+ "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)]",
106879
+ "max-md:transition-transform max-md:duration-200 max-md:ease-in-out",
106880
+ isOpen ? "max-md:translate-y-0" : "max-md:translate-y-full",
106881
+ // Desktop: flow-based flex child (takes space from canvas)
106882
+ "md:h-full md:border-l md:border-border",
106883
+ !panelWidth && "md:w-72"
106884
+ ),
106885
+ style: panelWidth ? { width: panelWidth } : void 0,
106886
+ children: [
106887
+ /* @__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" }) }),
106888
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border", children: [
106889
+ /* @__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(
106890
+ "button",
106891
+ {
106892
+ type: "button",
106893
+ title: label,
106894
+ className: cn(
106895
+ "flex items-center gap-1 px-2 py-1 rounded text-[11px] transition-colors",
106896
+ activeTab === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent"
106897
+ ),
106898
+ onClick: () => onSetActiveTab(key),
106899
+ children: [
106900
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
106901
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: label })
106902
+ ]
106903
+ },
106904
+ key
106905
+ )) }),
106482
106906
  /* @__PURE__ */ jsx(
106483
- PresentationPropertiesPanel,
106907
+ "button",
106484
106908
  {
106485
- canEdit,
106486
- canvasSize,
106487
- presentationProperties,
106488
- onUpdatePresentationProperties,
106489
- notesMaster,
106490
- handoutMaster,
106491
- notesCanvasSize,
106492
- coreProperties,
106493
- appProperties,
106494
- customProperties,
106495
- themeOptions,
106496
- selectedThemePath,
106497
- setSelectedThemePath,
106498
- onApplyTheme,
106499
- onUpdateCoreProperties,
106500
- onUpdateAppProperties,
106501
- onUpdateCustomProperties,
106502
- tagCollections,
106503
- onUpdateTagCollections,
106504
- onUpdateCanvasSize,
106505
- activeSlide,
106506
- theme,
106507
- onUpdateSlide
106909
+ type: "button",
106910
+ onClick: onClose,
106911
+ title: t2("common.close"),
106912
+ className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
106913
+ children: /* @__PURE__ */ jsx(LuX, { className: "w-4 h-4" })
106508
106914
  }
106509
- ),
106510
- activeSlide && /* @__PURE__ */ jsx(
106511
- SlideBackgroundPanel,
106915
+ )
106916
+ ] }),
106917
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3 space-y-3", children: [
106918
+ activeTab === "elements" && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
106919
+ /* @__PURE__ */ jsx("div", { className: cn(HEADING, "mb-2"), children: t2("pptx.inspector.layerOrder") }),
106920
+ activeSlide ? [...activeSlide.elements || []].reverse().map((el, ri) => {
106921
+ const idx = (activeSlide.elements || []).length - 1 - ri;
106922
+ const sel = selectedElement?.id === el.id || selectedElementIds.includes(el.id);
106923
+ const label = (hasTextProperties(el) ? (el.text || "").slice(0, 24) : void 0) || el.type;
106924
+ return /* @__PURE__ */ jsxs(
106925
+ "div",
106926
+ {
106927
+ title: `${el.type} \u2014 ${el.id}`,
106928
+ className: cn(
106929
+ "flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors",
106930
+ sel ? "bg-primary/30 text-primary" : "hover:bg-muted text-foreground"
106931
+ ),
106932
+ onClick: () => onSelectElement(el.id),
106933
+ children: [
106934
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground w-4 text-right", children: idx + 1 }),
106935
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: label })
106936
+ ]
106937
+ },
106938
+ el.id
106939
+ );
106940
+ }) : /* @__PURE__ */ jsx("div", { className: "text-muted-foreground italic", children: t2("pptx.inspector.noSlideSelected") })
106941
+ ] }),
106942
+ activeTab === "properties" && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: hasSelection && selectedElement ? /* @__PURE__ */ jsx(
106943
+ ElementInspectorBody,
106512
106944
  {
106513
- activeSlide,
106945
+ selectedElement,
106514
106946
  canEdit,
106515
- onUpdateSlide,
106516
- editTemplateMode,
106517
- slideMasters,
106518
- onSetTemplateBackground,
106519
- onGetTemplateBackgroundColor
106947
+ slides,
106948
+ tableEditorState,
106949
+ mediaDataUrls,
106950
+ onUpdateElement,
106951
+ onUpdateElementStyle,
106952
+ onUpdateTextStyle,
106953
+ onMoveLayer
106520
106954
  }
106521
- )
106522
- ] }) }),
106523
- activeTab === "comments" && /* @__PURE__ */ jsx(
106524
- InspectorCommentsSection,
106525
- {
106526
- comments,
106527
- canEdit,
106528
- activeSlide,
106529
- selectedElement,
106530
- editingCommentId,
106531
- commentEditDraft,
106532
- commentDraft,
106533
- replyingToCommentId: replyingToCommentId ?? null,
106534
- replyDraftByCommentId: replyDraftByCommentId ?? {},
106535
- onSetCommentDraft,
106536
- onAddComment,
106537
- onDeleteComment,
106538
- onStartEditComment,
106539
- onSaveEditComment,
106540
- onCancelEditComment,
106541
- onSetCommentEditDraft,
106542
- onToggleCommentResolved,
106543
- onStartReply,
106544
- onCancelReply,
106545
- onReplyDraftChange,
106546
- onSubmitReply,
106547
- onSelectElement
106548
- }
106549
- )
106550
- ] }),
106551
- hasSelection && selectedElement && activeSlide && /* @__PURE__ */ jsxs(Fragment, { children: [
106552
- /* @__PURE__ */ jsx(ResizeHandle, { direction: "vertical", onResize: onResizeAnim }),
106553
- /* @__PURE__ */ jsx(
106554
- "div",
106555
- {
106556
- className: "border-t border-border p-3 overflow-y-auto flex-shrink-0",
106557
- style: { height: animPanelHeight },
106558
- children: /* @__PURE__ */ jsx(
106559
- AnimationPanel,
106955
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
106956
+ /* @__PURE__ */ jsx(
106957
+ PresentationPropertiesPanel,
106560
106958
  {
106561
- selectedElement,
106562
- activeSlide,
106563
106959
  canEdit,
106960
+ canvasSize,
106961
+ presentationProperties,
106962
+ onUpdatePresentationProperties,
106963
+ notesMaster,
106964
+ handoutMaster,
106965
+ notesCanvasSize,
106966
+ coreProperties,
106967
+ appProperties,
106968
+ customProperties,
106969
+ themeOptions,
106970
+ selectedThemePath,
106971
+ setSelectedThemePath,
106972
+ onApplyTheme,
106973
+ onUpdateCoreProperties,
106974
+ onUpdateAppProperties,
106975
+ onUpdateCustomProperties,
106976
+ tagCollections,
106977
+ onUpdateTagCollections,
106978
+ onUpdateCanvasSize,
106979
+ activeSlide,
106980
+ theme,
106564
106981
  onUpdateSlide
106565
106982
  }
106983
+ ),
106984
+ activeSlide && /* @__PURE__ */ jsx(
106985
+ SlideBackgroundPanel,
106986
+ {
106987
+ activeSlide,
106988
+ canEdit,
106989
+ onUpdateSlide,
106990
+ editTemplateMode,
106991
+ slideMasters,
106992
+ onSetTemplateBackground,
106993
+ onGetTemplateBackgroundColor
106994
+ }
106566
106995
  )
106567
- }
106568
- )
106569
- ] })
106570
- ]
106571
- }
106572
- );
106996
+ ] }) }),
106997
+ activeTab === "comments" && /* @__PURE__ */ jsx(
106998
+ InspectorCommentsSection,
106999
+ {
107000
+ comments,
107001
+ canEdit,
107002
+ activeSlide,
107003
+ selectedElement,
107004
+ editingCommentId,
107005
+ commentEditDraft,
107006
+ commentDraft,
107007
+ replyingToCommentId: replyingToCommentId ?? null,
107008
+ replyDraftByCommentId: replyDraftByCommentId ?? {},
107009
+ onSetCommentDraft,
107010
+ onAddComment,
107011
+ onDeleteComment,
107012
+ onStartEditComment,
107013
+ onSaveEditComment,
107014
+ onCancelEditComment,
107015
+ onSetCommentEditDraft,
107016
+ onToggleCommentResolved,
107017
+ onStartReply,
107018
+ onCancelReply,
107019
+ onReplyDraftChange,
107020
+ onSubmitReply,
107021
+ onSelectElement
107022
+ }
107023
+ )
107024
+ ] }),
107025
+ hasSelection && selectedElement && activeSlide && /* @__PURE__ */ jsxs(Fragment, { children: [
107026
+ /* @__PURE__ */ jsx(ResizeHandle, { direction: "vertical", onResize: onResizeAnim }),
107027
+ /* @__PURE__ */ jsx(
107028
+ "div",
107029
+ {
107030
+ className: "border-t border-border p-3 overflow-y-auto flex-shrink-0",
107031
+ style: { height: animPanelHeight },
107032
+ children: /* @__PURE__ */ jsx(
107033
+ AnimationPanel,
107034
+ {
107035
+ selectedElement,
107036
+ activeSlide,
107037
+ canEdit,
107038
+ onUpdateSlide
107039
+ }
107040
+ )
107041
+ }
107042
+ )
107043
+ ] })
107044
+ ]
107045
+ }
107046
+ )
107047
+ ] });
106573
107048
  }
106574
107049
  function buildPathD(points) {
106575
107050
  if (points.length === 0) {
@@ -110703,7 +111178,8 @@ function ViewerBottomPanels({
110703
111178
  onZoomToFit,
110704
111179
  mode,
110705
111180
  onSetMode,
110706
- onToggleSlideSorter
111181
+ onToggleSlideSorter,
111182
+ hideStatusBar = false
110707
111183
  }) {
110708
111184
  return /* @__PURE__ */ jsxs(Fragment, { children: [
110709
111185
  onResizeBottom && !isSlideNotesCollapsed && /* @__PURE__ */ jsx(ResizeHandle, { direction: "vertical", onResize: onResizeBottom }),
@@ -110719,7 +111195,7 @@ function ViewerBottomPanels({
110719
111195
  panelHeight: notesPanelHeight
110720
111196
  }
110721
111197
  ),
110722
- /* @__PURE__ */ jsx(
111198
+ !hideStatusBar && /* @__PURE__ */ jsx(
110723
111199
  StatusBar,
110724
111200
  {
110725
111201
  slideCount,
@@ -110876,6 +111352,45 @@ function ViewerInspector({
110876
111352
  }
110877
111353
  );
110878
111354
  }
111355
+
111356
+ // src/viewer/hooks/useScopedLayoutOptions.ts
111357
+ function scopeLayoutOptionsToActiveSlide(options, activeSlide) {
111358
+ if (!activeSlide?.layoutPath) {
111359
+ return options;
111360
+ }
111361
+ const hasAnyMasterInfo = options.some((o3) => o3.masterPath);
111362
+ if (!hasAnyMasterInfo) {
111363
+ return options;
111364
+ }
111365
+ const activeOption = options.find((o3) => o3.path === activeSlide.layoutPath);
111366
+ const activeMaster = activeOption?.masterPath;
111367
+ if (!activeMaster) {
111368
+ return options;
111369
+ }
111370
+ const scoped = options.filter((o3) => o3.masterPath === activeMaster);
111371
+ const seen = /* @__PURE__ */ new Map();
111372
+ for (const opt of scoped) {
111373
+ const isActive = opt.path === activeSlide.layoutPath;
111374
+ const existing = seen.get(opt.name);
111375
+ if (!existing || isActive) {
111376
+ seen.set(opt.name, opt);
111377
+ }
111378
+ }
111379
+ const chosen = new Set(Array.from(seen.values()).map((o3) => o3.path));
111380
+ const result = [];
111381
+ const usedNames = /* @__PURE__ */ new Set();
111382
+ for (const opt of scoped) {
111383
+ if (!chosen.has(opt.path)) {
111384
+ continue;
111385
+ }
111386
+ if (usedNames.has(opt.name)) {
111387
+ continue;
111388
+ }
111389
+ usedNames.add(opt.name);
111390
+ result.push(opt);
111391
+ }
111392
+ return result;
111393
+ }
110879
111394
  function ViewerToolbarSection(props) {
110880
111395
  const {
110881
111396
  mode,
@@ -110945,6 +111460,10 @@ function ViewerToolbarSection(props) {
110945
111460
  },
110946
111461
  [activeSlide, propertyHandlers]
110947
111462
  );
111463
+ const scopedLayoutOptions = React10__default.useMemo(
111464
+ () => scopeLayoutOptionsToActiveSlide(s.layoutOptions, activeSlide),
111465
+ [s.layoutOptions, activeSlide]
111466
+ );
110948
111467
  const handleApplyTransitionToAll = useCallback(() => {
110949
111468
  const transition = activeSlide?.transition;
110950
111469
  if (!transition) {
@@ -111051,7 +111570,7 @@ function ViewerToolbarSection(props) {
111051
111570
  onUpdateTextStyle: ops.updateSelectedTextStyle,
111052
111571
  isOverflowMenuOpen: s.isOverflowMenuOpen,
111053
111572
  onSetOverflowMenuOpen: s.setIsOverflowMenuOpen,
111054
- layoutOptions: s.layoutOptions,
111573
+ layoutOptions: scopedLayoutOptions,
111055
111574
  onInsertSlideFromLayout: slideOps.handleInsertSlideFromLayout,
111056
111575
  customShows: s.customShows,
111057
111576
  activeCustomShowId: s.activeCustomShowId,
@@ -111084,6 +111603,7 @@ function ViewerToolbarSection(props) {
111084
111603
  isCommentsPanelOpen: s.isInspectorPaneOpen,
111085
111604
  slideCommentCount: activeSlide?.comments?.length ?? 0,
111086
111605
  formatPainterActive: s.formatPainterActive,
111606
+ canActivateFormatPainter: hasCopyableFormat(selectedElement),
111087
111607
  onToggleFormatPainter: onToggleFormatPainterProp ?? (() => s.setFormatPainterActive((p3) => !p3)),
111088
111608
  isSelectionPaneOpen: s.isSelectionPaneOpen,
111089
111609
  onToggleSelectionPane: () => s.setIsSelectionPaneOpen((p3) => !p3),
@@ -114232,6 +114752,166 @@ function ViewerMainContent(props) {
114232
114752
  )
114233
114753
  ] });
114234
114754
  }
114755
+ function MobileBottomBar({
114756
+ onOpenSlides,
114757
+ onOpenInsert,
114758
+ onOpenInspector,
114759
+ onOpenComments,
114760
+ onToggleNotes,
114761
+ activeSheet,
114762
+ commentCount
114763
+ }) {
114764
+ const actions = [
114765
+ {
114766
+ key: "slides",
114767
+ label: "Slides",
114768
+ icon: LuLayers,
114769
+ onClick: onOpenSlides
114770
+ },
114771
+ {
114772
+ key: "insert",
114773
+ label: "Insert",
114774
+ icon: LuPlus,
114775
+ onClick: onOpenInsert
114776
+ },
114777
+ {
114778
+ key: "inspector",
114779
+ label: "Format",
114780
+ icon: LuSettings2,
114781
+ onClick: onOpenInspector
114782
+ },
114783
+ {
114784
+ key: "comments",
114785
+ label: "Comments",
114786
+ icon: LuMessageSquare,
114787
+ onClick: onOpenComments,
114788
+ badge: commentCount
114789
+ },
114790
+ {
114791
+ key: "notes",
114792
+ label: "Notes",
114793
+ icon: LuStickyNote,
114794
+ onClick: onToggleNotes
114795
+ }
114796
+ ];
114797
+ return /* @__PURE__ */ jsx(
114798
+ "nav",
114799
+ {
114800
+ "aria-label": "Editor actions",
114801
+ 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)]",
114802
+ children: actions.map(({ key, label, icon: Icon, onClick, badge }) => {
114803
+ const active = activeSheet === key;
114804
+ return /* @__PURE__ */ jsxs(
114805
+ "button",
114806
+ {
114807
+ type: "button",
114808
+ onClick,
114809
+ className: cn(
114810
+ "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",
114811
+ active ? "text-primary" : "text-muted-foreground hover:text-foreground"
114812
+ ),
114813
+ "aria-pressed": active,
114814
+ children: [
114815
+ /* @__PURE__ */ jsx(Icon, { className: "w-5 h-5" }),
114816
+ /* @__PURE__ */ jsx("span", { children: label }),
114817
+ 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 }),
114818
+ active && /* @__PURE__ */ jsx("span", { className: "absolute top-0 left-1/2 -translate-x-1/2 w-8 h-0.5 rounded-full bg-primary" })
114819
+ ]
114820
+ },
114821
+ key
114822
+ );
114823
+ })
114824
+ }
114825
+ );
114826
+ }
114827
+ function MobileSlidesSheet({
114828
+ open,
114829
+ onClose,
114830
+ ...sidebar
114831
+ }) {
114832
+ return /* @__PURE__ */ jsx(MobileSheet, { open, onClose, heightFraction: 0.7, title: "Slides", children: /* @__PURE__ */ jsx("div", { className: "h-full", children: /* @__PURE__ */ jsx(SlidesPaneSidebar, { ...sidebar }) }) });
114833
+ }
114834
+ function MobileChromeOverlay(props) {
114835
+ const {
114836
+ state: s,
114837
+ editorOps,
114838
+ presentation,
114839
+ slides,
114840
+ activeSlideIndex,
114841
+ canvasSize,
114842
+ slideSectionGroups,
114843
+ canEdit,
114844
+ commentCount
114845
+ } = props;
114846
+ const activeSheet = s.isSlidesPaneOpen ? "slides" : s.isInspectorPaneOpen ? s.sidebarPanelMode === "comments" ? "comments" : "inspector" : !s.isSlideNotesCollapsed ? "notes" : null;
114847
+ const closeAllSheets = () => {
114848
+ s.setIsSlidesPaneOpen(false);
114849
+ s.setIsInspectorPaneOpen(false);
114850
+ s.setIsSlideNotesCollapsed(true);
114851
+ };
114852
+ const openSheet = (which) => {
114853
+ closeAllSheets();
114854
+ switch (which) {
114855
+ case "slides":
114856
+ s.setIsSlidesPaneOpen(true);
114857
+ break;
114858
+ case "inspector":
114859
+ s.setSidebarPanelMode("properties");
114860
+ s.setIsInspectorPaneOpen(true);
114861
+ break;
114862
+ case "comments":
114863
+ s.setSidebarPanelMode("comments");
114864
+ s.setIsInspectorPaneOpen(true);
114865
+ break;
114866
+ case "notes":
114867
+ s.setIsSlideNotesCollapsed(false);
114868
+ break;
114869
+ }
114870
+ };
114871
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
114872
+ /* @__PURE__ */ jsx(
114873
+ MobileSlidesSheet,
114874
+ {
114875
+ open: s.isSlidesPaneOpen,
114876
+ onClose: () => s.setIsSlidesPaneOpen(false),
114877
+ slides,
114878
+ activeSlideIndex,
114879
+ canvasSize,
114880
+ sectionGroups: slideSectionGroups,
114881
+ isOpen: true,
114882
+ canEdit,
114883
+ onSelectSlide: (index) => {
114884
+ s.setActiveSlideIndex(index);
114885
+ s.setIsSlidesPaneOpen(false);
114886
+ },
114887
+ onSlideContextMenu: editorOps.slideOps.handleSlideContextMenu,
114888
+ onMoveSlide: editorOps.slideOps.handleMoveSlide,
114889
+ onAddSlide: editorOps.slideOps.handleAddSlide,
114890
+ onCollapse: () => s.setIsSlidesPaneOpen(false),
114891
+ onAddSection: editorOps.sectionOps.addSection,
114892
+ onRenameSection: editorOps.sectionOps.renameSection,
114893
+ onDeleteSection: editorOps.sectionOps.deleteSection,
114894
+ onMoveSectionUp: editorOps.sectionOps.moveSectionUp,
114895
+ onMoveSectionDown: editorOps.sectionOps.moveSectionDown,
114896
+ rehearsalTimings: Object.keys(presentation.recordedTimings).length > 0 ? presentation.recordedTimings : void 0
114897
+ }
114898
+ ),
114899
+ /* @__PURE__ */ jsx(
114900
+ MobileBottomBar,
114901
+ {
114902
+ activeSheet,
114903
+ commentCount,
114904
+ onOpenSlides: () => s.isSlidesPaneOpen ? s.setIsSlidesPaneOpen(false) : openSheet("slides"),
114905
+ onOpenInsert: () => {
114906
+ editorOps.insertHandlers.handleAddTextBox();
114907
+ },
114908
+ onOpenInspector: () => s.isInspectorPaneOpen && s.sidebarPanelMode !== "comments" ? s.setIsInspectorPaneOpen(false) : openSheet("inspector"),
114909
+ onOpenComments: () => s.isInspectorPaneOpen && s.sidebarPanelMode === "comments" ? s.setIsInspectorPaneOpen(false) : openSheet("comments"),
114910
+ onToggleNotes: () => !s.isSlideNotesCollapsed ? s.setIsSlideNotesCollapsed(true) : openSheet("notes")
114911
+ }
114912
+ )
114913
+ ] });
114914
+ }
114235
114915
  function ToggleSwitch({
114236
114916
  label,
114237
114917
  enabled,
@@ -114453,14 +115133,10 @@ function useYjsDocumentSync({
114453
115133
  const lastSyncedRef = useRef("");
114454
115134
  const hasInitializedRef = useRef(false);
114455
115135
  const getDocMap = useCallback(() => {
114456
- if (!doc2 || typeof doc2 !== "object") {
115136
+ if (!doc2) {
114457
115137
  return null;
114458
115138
  }
114459
- const d = doc2;
114460
- if (typeof d.getMap !== "function") {
114461
- return null;
114462
- }
114463
- return d.getMap("slides-data");
115139
+ return doc2.getMap("slides-data");
114464
115140
  }, [doc2]);
114465
115141
  useEffect(() => {
114466
115142
  if (!isConnected || !doc2) {
@@ -114481,10 +115157,9 @@ function useYjsDocumentSync({
114481
115157
  return;
114482
115158
  }
114483
115159
  lastSyncedRef.current = serialized;
114484
- const d = doc2;
114485
- d.transact(() => {
115160
+ doc2.transact(() => {
114486
115161
  const currentCount = map3.get("count");
114487
- if (currentCount && currentCount > slides.length) {
115162
+ if (typeof currentCount === "number" && currentCount > slides.length) {
114488
115163
  for (let i3 = slides.length; i3 < currentCount; i3++) {
114489
115164
  map3.delete(`slide-${i3}`);
114490
115165
  }
@@ -114509,13 +115184,13 @@ function useYjsDocumentSync({
114509
115184
  }
114510
115185
  const handleUpdate = () => {
114511
115186
  const count = map3.get("count");
114512
- if (!count || count === 0) {
115187
+ if (typeof count !== "number" || count === 0) {
114513
115188
  return;
114514
115189
  }
114515
115190
  const remoteSlides = [];
114516
115191
  for (let i3 = 0; i3 < count; i3++) {
114517
115192
  const slideJson = map3.get(`slide-${i3}`);
114518
- if (slideJson) {
115193
+ if (typeof slideJson === "string") {
114519
115194
  try {
114520
115195
  remoteSlides.push(JSON.parse(slideJson));
114521
115196
  } catch {
@@ -114540,7 +115215,7 @@ function useYjsDocumentSync({
114540
115215
  if (!hasInitializedRef.current) {
114541
115216
  hasInitializedRef.current = true;
114542
115217
  const count = map3.get("count");
114543
- if (count && count > 0) {
115218
+ if (typeof count === "number" && count > 0) {
114544
115219
  handleUpdate();
114545
115220
  }
114546
115221
  }
@@ -115101,13 +115776,14 @@ function useCanvasInteractions(input) {
115101
115776
  if (e2.button !== 0) {
115102
115777
  return;
115103
115778
  }
115104
- if (!selectedElementIdSet.has(elementId)) {
115779
+ const wasSelected = selectedElementIdSet.has(elementId);
115780
+ if (!wasSelected) {
115105
115781
  ops.applySelection(elementId);
115106
115782
  justSelectedRef.current = true;
115107
115783
  } else {
115108
115784
  justSelectedRef.current = false;
115109
115785
  }
115110
- const ids = effectiveSelectedIds.length ? effectiveSelectedIds : [elementId];
115786
+ const ids = !wasSelected ? [elementId] : effectiveSelectedIds.length ? effectiveSelectedIds : [elementId];
115111
115787
  const startPositions = {};
115112
115788
  const domEls = /* @__PURE__ */ new Map();
115113
115789
  for (const id2 of ids) {
@@ -117144,8 +117820,14 @@ function useSectionOperations(input) {
117144
117820
  }
117145
117821
 
117146
117822
  // src/viewer/hooks/useSlideManagement.ts
117823
+ function insertSlideFromLayoutUpdater(slides, activeIndex, draft) {
117824
+ const next = [...slides];
117825
+ const insertAt = Math.max(0, Math.min(activeIndex + 1, next.length));
117826
+ next.splice(insertAt, 0, draft);
117827
+ return next;
117828
+ }
117147
117829
  function useSlideManagement(input) {
117148
- const { slides, activeSlideIndex, setActiveSlideIndex, ops, history } = input;
117830
+ const { slides, activeSlideIndex, setActiveSlideIndex, ops, history, handlerRef } = input;
117149
117831
  const handleAddSlide = () => {
117150
117832
  const newSlide = {
117151
117833
  id: `slide-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
@@ -117244,8 +117926,42 @@ function useSlideManagement(input) {
117244
117926
  });
117245
117927
  history.markDirty();
117246
117928
  };
117247
- const handleInsertSlideFromLayout = (_layoutPath) => {
117248
- handleAddSlide();
117929
+ const handleInsertSlideFromLayout = (layoutPath, layoutName) => {
117930
+ const insertAt = activeSlideIndex + 1;
117931
+ const draft = {
117932
+ id: `slide-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
117933
+ rId: "",
117934
+ slideNumber: slides.length + 1,
117935
+ elements: [],
117936
+ layoutPath,
117937
+ ...layoutName ? { layoutName } : {}
117938
+ };
117939
+ let inserted = [];
117940
+ ops.updateSlides((prev) => {
117941
+ inserted = insertSlideFromLayoutUpdater(prev, activeSlideIndex, draft);
117942
+ return inserted;
117943
+ });
117944
+ setActiveSlideIndex(insertAt);
117945
+ history.markDirty();
117946
+ const handler = handlerRef?.current;
117947
+ if (handler) {
117948
+ void handler.applyLayoutToSlide(insertAt, layoutPath, inserted).then(
117949
+ (updated) => {
117950
+ ops.updateSlides((prev) => {
117951
+ if (prev[insertAt]?.id !== draft.id) {
117952
+ return prev;
117953
+ }
117954
+ const next = [...prev];
117955
+ next[insertAt] = updated;
117956
+ return next;
117957
+ });
117958
+ return void 0;
117959
+ },
117960
+ () => {
117961
+ return void 0;
117962
+ }
117963
+ );
117964
+ }
117249
117965
  };
117250
117966
  return {
117251
117967
  handleAddSlide,
@@ -117781,7 +118497,8 @@ function useEditorOperations(input) {
117781
118497
  canvasSize,
117782
118498
  dialogs,
117783
118499
  presentation,
117784
- userName
118500
+ userName,
118501
+ handlerRef
117785
118502
  } = input;
117786
118503
  const ops = useElementOperations({
117787
118504
  activeSlide,
@@ -117883,7 +118600,8 @@ function useEditorOperations(input) {
117883
118600
  activeSlideIndex,
117884
118601
  setActiveSlideIndex: state2.setActiveSlideIndex,
117885
118602
  ops,
117886
- history
118603
+ history,
118604
+ handlerRef
117887
118605
  });
117888
118606
  const tableOps = useTableOperations({
117889
118607
  selectedElement,
@@ -117912,21 +118630,34 @@ function useEditorOperations(input) {
117912
118630
  );
117913
118631
  const copiedFormatRef = useRef(null);
117914
118632
  const prevFormatPainterRef = useRef(false);
118633
+ const { formatPainterActive, setFormatPainterActive, elementLookup } = state2;
117915
118634
  useEffect(() => {
117916
- if (state2.formatPainterActive && !prevFormatPainterRef.current && selectedElement) {
118635
+ if (formatPainterActive && !prevFormatPainterRef.current && selectedElement) {
117917
118636
  copiedFormatRef.current = copyFormatFromElement(selectedElement);
117918
- } else if (!state2.formatPainterActive) {
118637
+ } else if (!formatPainterActive) {
117919
118638
  copiedFormatRef.current = null;
117920
118639
  }
117921
- prevFormatPainterRef.current = state2.formatPainterActive;
117922
- }, [state2.formatPainterActive, selectedElement]);
118640
+ prevFormatPainterRef.current = formatPainterActive;
118641
+ }, [formatPainterActive, selectedElement]);
118642
+ useEffect(() => {
118643
+ if (!formatPainterActive) {
118644
+ return;
118645
+ }
118646
+ const onKey = (e2) => {
118647
+ if (e2.key === "Escape") {
118648
+ setFormatPainterActive(false);
118649
+ }
118650
+ };
118651
+ window.addEventListener("keydown", onKey);
118652
+ return () => window.removeEventListener("keydown", onKey);
118653
+ }, [formatPainterActive, setFormatPainterActive]);
117923
118654
  const formatPainterCanvasHandlers = useMemo(
117924
118655
  () => ({
117925
118656
  ...canvasHandlers,
117926
118657
  handleElementClick: (elementId, e2) => {
117927
- if (state2.formatPainterActive && copiedFormatRef.current) {
118658
+ if (formatPainterActive && copiedFormatRef.current) {
117928
118659
  e2.stopPropagation();
117929
- const element2 = state2.elementLookup.get(elementId);
118660
+ const element2 = elementLookup.get(elementId);
117930
118661
  if (element2) {
117931
118662
  const updated = applyFormatToElement(element2, copiedFormatRef.current);
117932
118663
  const updates = {};
@@ -117939,14 +118670,21 @@ function useEditorOperations(input) {
117939
118670
  ops.updateElementById(elementId, updates);
117940
118671
  }
117941
118672
  copiedFormatRef.current = null;
117942
- state2.setFormatPainterActive(false);
118673
+ setFormatPainterActive(false);
117943
118674
  ops.applySelection(elementId);
117944
118675
  return;
117945
118676
  }
117946
118677
  canvasHandlers.handleElementClick(elementId, e2);
118678
+ },
118679
+ handleCanvasMouseDown: (e2) => {
118680
+ if (formatPainterActive) {
118681
+ setFormatPainterActive(false);
118682
+ return;
118683
+ }
118684
+ canvasHandlers.handleCanvasMouseDown(e2);
117947
118685
  }
117948
118686
  }),
117949
- [canvasHandlers, ops, state2]
118687
+ [canvasHandlers, ops, formatPainterActive, setFormatPainterActive, elementLookup]
117950
118688
  );
117951
118689
  return {
117952
118690
  ops: combinedOps,
@@ -120124,21 +120862,35 @@ function useViewerDialogs(input) {
120124
120862
  null
120125
120863
  );
120126
120864
  const [embedFontsEnabled, setEmbedFontsEnabled] = useState(false);
120127
- const [isNarrowViewport, setIsNarrowViewport] = useState(false);
120865
+ const [isNarrowViewport, setIsNarrowViewport] = useState(
120866
+ () => typeof window !== "undefined" ? window.innerWidth < 768 : false
120867
+ );
120128
120868
  useEffect(() => {
120129
- const el = containerRef.current;
120130
- if (!el) {
120131
- return;
120132
- }
120133
- const observer = new ResizeObserver((entries) => {
120134
- const entry = entries[0];
120135
- if (entry) {
120136
- setIsNarrowViewport(entry.contentRect.width < 768);
120869
+ const handleWindow = () => setIsNarrowViewport(window.innerWidth < 768);
120870
+ let observer = null;
120871
+ let raf = 0;
120872
+ const attach2 = () => {
120873
+ const el = containerRef.current;
120874
+ if (!el) {
120875
+ raf = requestAnimationFrame(attach2);
120876
+ return;
120137
120877
  }
120138
- });
120139
- observer.observe(el);
120140
- setIsNarrowViewport(el.clientWidth < 768);
120141
- return () => observer.disconnect();
120878
+ observer = new ResizeObserver((entries) => {
120879
+ const entry = entries[0];
120880
+ if (entry) {
120881
+ setIsNarrowViewport(entry.contentRect.width < 768);
120882
+ }
120883
+ });
120884
+ observer.observe(el);
120885
+ setIsNarrowViewport(el.clientWidth < 768);
120886
+ };
120887
+ attach2();
120888
+ window.addEventListener("resize", handleWindow);
120889
+ return () => {
120890
+ cancelAnimationFrame(raf);
120891
+ observer?.disconnect();
120892
+ window.removeEventListener("resize", handleWindow);
120893
+ };
120142
120894
  }, []);
120143
120895
  useEffect(() => {
120144
120896
  if (isDirty && hasDigitalSignatures && !signatureStripAcknowledgedRef.current) {
@@ -124610,7 +125362,9 @@ function useViewerCoreState(_input) {
124610
125362
  function useViewerUIState() {
124611
125363
  const [isCompactToolbarOpen, setIsCompactToolbarOpen] = useState(false);
124612
125364
  const [toolbarSection, setToolbarSection] = useState("home");
124613
- const [isSlidesPaneOpen, setIsSlidesPaneOpen] = useState(true);
125365
+ const [isSlidesPaneOpen, setIsSlidesPaneOpen] = useState(
125366
+ () => typeof window === "undefined" ? true : window.innerWidth >= 768
125367
+ );
124614
125368
  const [isInspectorPaneOpen, setIsInspectorPaneOpen] = useState(false);
124615
125369
  const [isSlideNotesCollapsed, setIsSlideNotesCollapsed] = useState(true);
124616
125370
  const [isOverflowMenuOpen, setIsOverflowMenuOpen] = useState(false);
@@ -125053,7 +125807,8 @@ var PowerPointViewer = forwardRef(
125053
125807
  canvasSize,
125054
125808
  dialogs,
125055
125809
  presentation,
125056
- userName: authorName ?? collaboration?.userName
125810
+ userName: authorName ?? collaboration?.userName,
125811
+ handlerRef: actionSoundHandlerRef
125057
125812
  });
125058
125813
  const {
125059
125814
  exportHandlers,
@@ -125176,7 +125931,7 @@ var PowerPointViewer = forwardRef(
125176
125931
  {
125177
125932
  activeSlide,
125178
125933
  allSlides: slides,
125179
- isSlideNotesCollapsed: isMobile || state2.isSlideNotesCollapsed,
125934
+ isSlideNotesCollapsed: state2.isSlideNotesCollapsed,
125180
125935
  canEdit,
125181
125936
  slideCount: slides.length,
125182
125937
  activeSlideIndex,
@@ -125193,7 +125948,22 @@ var PowerPointViewer = forwardRef(
125193
125948
  onZoomToFit: zoom.handleZoomToFit,
125194
125949
  mode,
125195
125950
  onSetMode: handleSetMode,
125196
- onToggleSlideSorter: () => state2.setShowSlideSorter((p3) => !p3)
125951
+ onToggleSlideSorter: () => state2.setShowSlideSorter((p3) => !p3),
125952
+ hideStatusBar: isMobile
125953
+ }
125954
+ ),
125955
+ mode !== "present" && isMobile && /* @__PURE__ */ jsx(
125956
+ MobileChromeOverlay,
125957
+ {
125958
+ state: state2,
125959
+ editorOps,
125960
+ presentation,
125961
+ slides,
125962
+ activeSlideIndex,
125963
+ canvasSize,
125964
+ slideSectionGroups,
125965
+ canEdit,
125966
+ commentCount: activeSlide?.comments?.length ?? 0
125197
125967
  }
125198
125968
  ),
125199
125969
  /* @__PURE__ */ jsx(