pptx-react-viewer 1.0.9 → 1.0.11

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 (28) hide show
  1. package/README.md +68 -0
  2. package/dist/{PowerPointViewer-K2URyPlJ.d.mts → PowerPointViewer-gSKLhZDo.d.mts} +35 -1
  3. package/dist/{PowerPointViewer-K2URyPlJ.d.ts → PowerPointViewer-gSKLhZDo.d.ts} +35 -1
  4. package/dist/index.d.mts +2 -2
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +2532 -882
  7. package/dist/index.mjs +2533 -883
  8. package/dist/pptx-viewer.css +1 -1
  9. package/dist/viewer/index.d.mts +15 -3
  10. package/dist/viewer/index.d.ts +15 -3
  11. package/dist/viewer/index.js +2731 -985
  12. package/dist/viewer/index.mjs +2732 -987
  13. package/node_modules/emf-converter/package.json +1 -1
  14. package/node_modules/mtx-decompressor/package.json +1 -1
  15. package/node_modules/pptx-viewer-core/dist/{SvgExporter-DqcmwxFu.d.mts → SvgExporter-B4-1_Hjp.d.mts} +1 -1
  16. package/node_modules/pptx-viewer-core/dist/{SvgExporter-BZJguJbp.d.ts → SvgExporter-CPr1npgo.d.ts} +1 -1
  17. package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +2 -2
  18. package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +2 -2
  19. package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +3 -3
  20. package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +3 -3
  21. package/node_modules/pptx-viewer-core/dist/index.d.mts +5 -5
  22. package/node_modules/pptx-viewer-core/dist/index.d.ts +5 -5
  23. package/node_modules/pptx-viewer-core/dist/{presentation-Bo7cMMCe.d.mts → presentation-DgkIYhXo.d.mts} +6 -0
  24. package/node_modules/pptx-viewer-core/dist/{presentation-Bo7cMMCe.d.ts → presentation-DgkIYhXo.d.ts} +6 -0
  25. package/node_modules/pptx-viewer-core/dist/{text-operations-D0f1jred.d.ts → text-operations-B6U6XxWt.d.ts} +1 -1
  26. package/node_modules/pptx-viewer-core/dist/{text-operations-Bo-WG-Z8.d.mts → text-operations-dYKZp3zE.d.mts} +1 -1
  27. package/node_modules/pptx-viewer-core/package.json +1 -1
  28. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -43328,7 +43328,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43328
43328
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43329
43329
  }
43330
43330
  function useSyncExternalStore$2(subscribe3, getSnapshot2) {
43331
- didWarnOld18Alpha || void 0 === React90.startTransition || (didWarnOld18Alpha = true, console.error(
43331
+ didWarnOld18Alpha || void 0 === React95.startTransition || (didWarnOld18Alpha = true, console.error(
43332
43332
  "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."
43333
43333
  ));
43334
43334
  var value = getSnapshot2();
@@ -43338,7 +43338,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43338
43338
  "The result of getSnapshot should be cached to avoid an infinite loop"
43339
43339
  ), didWarnUncachedGetSnapshot = true);
43340
43340
  }
43341
- cachedValue = useState80({
43341
+ cachedValue = useState84({
43342
43342
  inst: { value, getSnapshot: getSnapshot2 }
43343
43343
  });
43344
43344
  var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
@@ -43350,7 +43350,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43350
43350
  },
43351
43351
  [subscribe3, value, getSnapshot2]
43352
43352
  );
43353
- useEffect64(
43353
+ useEffect68(
43354
43354
  function() {
43355
43355
  checkIfSnapshotChanged(inst) && forceUpdate({ inst });
43356
43356
  return subscribe3(function() {
@@ -43376,8 +43376,8 @@ var require_use_sync_external_store_shim_development = __commonJS({
43376
43376
  return getSnapshot2();
43377
43377
  }
43378
43378
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
43379
- var React90 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is2, useState80 = React90.useState, useEffect64 = React90.useEffect, useLayoutEffect7 = React90.useLayoutEffect, useDebugValue = React90.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
43380
- exports$1.useSyncExternalStore = void 0 !== React90.useSyncExternalStore ? React90.useSyncExternalStore : shim;
43379
+ var React95 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is2, useState84 = React95.useState, useEffect68 = React95.useEffect, useLayoutEffect7 = React95.useLayoutEffect, useDebugValue = React95.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
43380
+ exports$1.useSyncExternalStore = void 0 !== React95.useSyncExternalStore ? React95.useSyncExternalStore : shim;
43381
43381
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
43382
43382
  })();
43383
43383
  }
@@ -43400,9 +43400,9 @@ var require_with_selector_development = __commonJS({
43400
43400
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43401
43401
  }
43402
43402
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
43403
- var React90 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef65 = React90.useRef, useEffect64 = React90.useEffect, useMemo41 = React90.useMemo, useDebugValue = React90.useDebugValue;
43403
+ var React95 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef69 = React95.useRef, useEffect68 = React95.useEffect, useMemo41 = React95.useMemo, useDebugValue = React95.useDebugValue;
43404
43404
  exports$1.useSyncExternalStoreWithSelector = function(subscribe3, getSnapshot2, getServerSnapshot2, selector, isEqual) {
43405
- var instRef = useRef65(null);
43405
+ var instRef = useRef69(null);
43406
43406
  if (null === instRef.current) {
43407
43407
  var inst = { hasValue: false, value: null };
43408
43408
  instRef.current = inst;
@@ -43443,7 +43443,7 @@ var require_with_selector_development = __commonJS({
43443
43443
  [getSnapshot2, getServerSnapshot2, selector, isEqual]
43444
43444
  );
43445
43445
  var value = useSyncExternalStore3(subscribe3, instRef[0], instRef[1]);
43446
- useEffect64(
43446
+ useEffect68(
43447
43447
  function() {
43448
43448
  inst.hasValue = true;
43449
43449
  inst.value = value;
@@ -69742,6 +69742,7 @@ var UNGROUPED_SECTION_ID = "__ungrouped__";
69742
69742
 
69743
69743
  // src/viewer/constants/toolbar.ts
69744
69744
  var TOOLBAR_SECTIONS = [
69745
+ { id: "file", label: "File" },
69745
69746
  { id: "home", label: "Home" },
69746
69747
  { id: "insert", label: "Insert" },
69747
69748
  { id: "text", label: "Text" },
@@ -69749,8 +69750,11 @@ var TOOLBAR_SECTIONS = [
69749
69750
  { id: "arrange", label: "Arrange" },
69750
69751
  { id: "design", label: "Design" },
69751
69752
  { id: "transitions", label: "Transitions" },
69753
+ { id: "animations", label: "Animations" },
69754
+ { id: "slideShow", label: "Slide Show" },
69752
69755
  { id: "review", label: "Review" },
69753
- { id: "view", label: "View" }
69756
+ { id: "view", label: "View" },
69757
+ { id: "help", label: "Help" }
69754
69758
  ];
69755
69759
  var SHORTCUT_REFERENCE_ITEMS = [
69756
69760
  { action: "Undo", shortcut: "Ctrl/Cmd+Z" },
@@ -85788,6 +85792,7 @@ function AccessibilityPanel({
85788
85792
  reducedMotion,
85789
85793
  onToggleReducedMotion
85790
85794
  }) {
85795
+ const { t: t2 } = reactI18next.useTranslation();
85791
85796
  if (!isOpen) {
85792
85797
  return null;
85793
85798
  }
@@ -85795,31 +85800,27 @@ function AccessibilityPanel({
85795
85800
  "div",
85796
85801
  {
85797
85802
  role: "dialog",
85798
- "aria-label": "Accessibility Checker",
85803
+ "aria-label": t2("pptx.accessibility.title"),
85799
85804
  className: "absolute top-14 right-3 z-40 w-[min(28rem,calc(100%-1.5rem))] rounded border border-border bg-popover shadow-2xl",
85800
85805
  children: [
85801
85806
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2", children: [
85802
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs uppercase tracking-wide text-foreground", children: "Accessibility Checker" }),
85807
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs uppercase tracking-wide text-foreground", children: t2("pptx.accessibility.title") }),
85803
85808
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
85804
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
85805
- issues.length,
85806
- " issue",
85807
- issues.length !== 1 ? "s" : ""
85808
- ] }),
85809
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: t2("pptx.accessibility.issueCount", { count: issues.length }) }),
85809
85810
  /* @__PURE__ */ jsxRuntime.jsx(
85810
85811
  "button",
85811
85812
  {
85812
85813
  type: "button",
85813
85814
  onClick: onClose,
85814
- "aria-label": "Close accessibility panel",
85815
+ "aria-label": t2("pptx.accessibility.closePanel"),
85815
85816
  className: "rounded px-2 py-1 text-[11px] text-foreground hover:bg-muted hover:text-foreground",
85816
- children: "Close"
85817
+ children: t2("pptx.accessibility.close")
85817
85818
  }
85818
85819
  )
85819
85820
  ] })
85820
85821
  ] }),
85821
85822
  onToggleReducedMotion !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2", children: [
85822
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "reduced-motion-toggle", className: "text-xs text-foreground", children: "Reduce motion" }),
85823
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "reduced-motion-toggle", className: "text-xs text-foreground", children: t2("pptx.accessibility.reduceMotion") }),
85823
85824
  /* @__PURE__ */ jsxRuntime.jsx(
85824
85825
  "button",
85825
85826
  {
@@ -85848,9 +85849,9 @@ function AccessibilityPanel({
85848
85849
  "div",
85849
85850
  {
85850
85851
  role: "list",
85851
- "aria-label": "Accessibility issues",
85852
+ "aria-label": t2("pptx.accessibility.issuesList"),
85852
85853
  className: "max-h-72 overflow-y-auto p-2 space-y-1",
85853
- children: issues.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listitem", className: "text-center text-xs text-muted-foreground py-4", children: "No accessibility issues found." }) : issues.map((issue, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
85854
+ children: issues.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listitem", className: "text-center text-xs text-muted-foreground py-4", children: t2("pptx.accessibility.noIssues") }) : issues.map((issue, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
85854
85855
  "div",
85855
85856
  {
85856
85857
  role: "listitem",
@@ -85862,7 +85863,7 @@ function AccessibilityPanel({
85862
85863
  children: [
85863
85864
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 mt-0.5", "aria-hidden": "true", children: issue.severity === "error" ? "\u25CF" : issue.severity === "warning" ? "\u25B2" : "\u2139" }),
85864
85865
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
85865
- issue.severity === "error" ? "Error: " : issue.severity === "warning" ? "Warning: " : "Info: ",
85866
+ issue.severity === "error" ? t2("pptx.accessibility.error") : issue.severity === "warning" ? t2("pptx.accessibility.warning") : t2("pptx.accessibility.info"),
85866
85867
  issue.message
85867
85868
  ] })
85868
85869
  ]
@@ -85914,22 +85915,32 @@ function ExportProgressModal({
85914
85915
  ) })
85915
85916
  ] }) });
85916
85917
  }
85917
- function formatAutosaveAge(timestamp) {
85918
+ function formatAutosaveAge(timestamp, t2) {
85918
85919
  const diff = Date.now() - timestamp;
85919
85920
  const minutes = Math.floor(diff / 6e4);
85920
85921
  if (minutes < 1) {
85921
- return "just now";
85922
+ return t2("pptx.autosave.justNow");
85922
85923
  }
85923
85924
  if (minutes === 1) {
85924
- return "1 min ago";
85925
+ return t2("pptx.autosave.oneMinAgo");
85925
85926
  }
85926
- return `${minutes} min ago`;
85927
+ return t2("pptx.autosave.minutesAgo", { count: minutes });
85927
85928
  }
85928
85929
  function StatusBar({
85929
85930
  slideCount,
85930
85931
  activeSlideIndex,
85931
85932
  isDirty,
85932
- autosaveStatus
85933
+ autosaveStatus,
85934
+ scale,
85935
+ onZoomIn,
85936
+ onZoomOut,
85937
+ onZoomToFit,
85938
+ isNotesExpanded,
85939
+ onToggleNotes,
85940
+ mode,
85941
+ onSetMode,
85942
+ onToggleSlideSorter,
85943
+ collaborationSlot
85933
85944
  }) {
85934
85945
  const { t: t2 } = reactI18next.useTranslation();
85935
85946
  let statusText;
@@ -85937,7 +85948,7 @@ function StatusBar({
85937
85948
  statusText = t2("pptx.autosave.saving");
85938
85949
  } else if (autosaveStatus?.state === "saved") {
85939
85950
  statusText = t2("pptx.autosave.saved", {
85940
- time: formatAutosaveAge(autosaveStatus.timestamp)
85951
+ time: formatAutosaveAge(autosaveStatus.timestamp, t2)
85941
85952
  });
85942
85953
  } else if (autosaveStatus?.state === "error") {
85943
85954
  statusText = t2("pptx.autosave.error");
@@ -85946,15 +85957,124 @@ function StatusBar({
85946
85957
  } else {
85947
85958
  statusText = t2("pptx.statusBar.allSaved");
85948
85959
  }
85949
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 py-1 border-t border-border bg-background/50 text-[10px] text-muted-foreground flex items-center justify-between", children: [
85950
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: slideCount > 0 ? `Slide ${Math.min(activeSlideIndex + 1, slideCount)} of ${slideCount}` : "No slides" }),
85960
+ const vb = "p-1 rounded-sm transition-colors hover:bg-accent/60 text-muted-foreground active:scale-95 active:opacity-80";
85961
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full px-2 py-0.5 border-t border-border bg-secondary/50 text-[10px] text-muted-foreground flex items-center gap-1", children: [
85962
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: slideCount > 0 ? t2("pptx.statusBar.slideOf", {
85963
+ current: Math.min(activeSlideIndex + 1, slideCount),
85964
+ total: slideCount
85965
+ }) : t2("pptx.statusBar.noSlides") }),
85966
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-border/40 mx-1 max-md:hidden" }),
85967
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 max-md:hidden text-[10px]", children: t2("pptx.statusBar.language") }),
85968
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-border/60 mx-1 max-md:hidden" }),
85951
85969
  /* @__PURE__ */ jsxRuntime.jsx(
85952
85970
  "span",
85953
85971
  {
85954
- className: autosaveStatus?.state === "error" ? "text-red-400" : autosaveStatus?.state === "saving" ? "text-yellow-400" : "",
85972
+ className: cn(
85973
+ "shrink-0 max-md:hidden",
85974
+ autosaveStatus?.state === "error" ? "text-red-400" : autosaveStatus?.state === "saving" ? "text-yellow-400" : ""
85975
+ ),
85955
85976
  children: statusText
85956
85977
  }
85957
- )
85978
+ ),
85979
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
85980
+ onToggleNotes && /* @__PURE__ */ jsxRuntime.jsxs(
85981
+ "button",
85982
+ {
85983
+ type: "button",
85984
+ onClick: onToggleNotes,
85985
+ className: cn(
85986
+ vb,
85987
+ "flex items-center gap-1 text-[10px]",
85988
+ isNotesExpanded && "text-primary"
85989
+ ),
85990
+ title: t2("pptx.statusBar.toggleNotes"),
85991
+ "aria-label": t2("pptx.statusBar.toggleNotes"),
85992
+ children: [
85993
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuStickyNote, { className: "w-3 h-3" }),
85994
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-md:hidden", children: t2("pptx.notes.title") })
85995
+ ]
85996
+ }
85997
+ ),
85998
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-border/60 mx-0.5" }),
85999
+ onSetMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
86000
+ /* @__PURE__ */ jsxRuntime.jsx(
86001
+ "button",
86002
+ {
86003
+ type: "button",
86004
+ onClick: () => onSetMode("edit"),
86005
+ className: cn(vb, mode === "edit" && "text-primary"),
86006
+ title: t2("pptx.statusBar.normalView"),
86007
+ "aria-label": t2("pptx.statusBar.normalView"),
86008
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuMonitor, { className: "w-3.5 h-3.5" })
86009
+ }
86010
+ ),
86011
+ onToggleSlideSorter && /* @__PURE__ */ jsxRuntime.jsx(
86012
+ "button",
86013
+ {
86014
+ type: "button",
86015
+ onClick: onToggleSlideSorter,
86016
+ className: vb,
86017
+ title: t2("pptx.statusBar.slideSorter"),
86018
+ "aria-label": t2("pptx.statusBar.slideSorter"),
86019
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuColumns2, { className: "w-3.5 h-3.5" })
86020
+ }
86021
+ ),
86022
+ /* @__PURE__ */ jsxRuntime.jsx(
86023
+ "button",
86024
+ {
86025
+ type: "button",
86026
+ onClick: () => onSetMode("present"),
86027
+ className: cn(vb, mode === "present" && "text-primary"),
86028
+ title: t2("pptx.statusBar.slideShow"),
86029
+ "aria-label": t2("pptx.statusBar.slideShow"),
86030
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuPresentation, { className: "w-3.5 h-3.5" })
86031
+ }
86032
+ )
86033
+ ] }),
86034
+ collaborationSlot && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
86035
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-border/40 mx-0.5" }),
86036
+ collaborationSlot
86037
+ ] }),
86038
+ scale !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
86039
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-border/60 mx-0.5" }),
86040
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
86041
+ onZoomOut && /* @__PURE__ */ jsxRuntime.jsx(
86042
+ "button",
86043
+ {
86044
+ type: "button",
86045
+ onClick: onZoomOut,
86046
+ className: vb,
86047
+ title: t2("pptx.statusBar.zoomOut"),
86048
+ "aria-label": t2("pptx.statusBar.zoomOut"),
86049
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuMinus, { className: "w-3 h-3" })
86050
+ }
86051
+ ),
86052
+ /* @__PURE__ */ jsxRuntime.jsxs(
86053
+ "button",
86054
+ {
86055
+ type: "button",
86056
+ onClick: onZoomToFit,
86057
+ className: "px-1.5 py-0.5 rounded-sm hover:bg-accent/60 text-[10px] text-muted-foreground tabular-nums min-w-[3rem] text-center transition-colors",
86058
+ title: t2("pptx.statusBar.zoomToFit"),
86059
+ children: [
86060
+ Math.round((scale ?? 1) * 100),
86061
+ "%"
86062
+ ]
86063
+ }
86064
+ ),
86065
+ onZoomIn && /* @__PURE__ */ jsxRuntime.jsx(
86066
+ "button",
86067
+ {
86068
+ type: "button",
86069
+ onClick: onZoomIn,
86070
+ className: vb,
86071
+ title: t2("pptx.statusBar.zoomIn"),
86072
+ "aria-label": t2("pptx.statusBar.zoomIn"),
86073
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlus, { className: "w-3 h-3" })
86074
+ }
86075
+ )
86076
+ ] })
86077
+ ] })
85958
86078
  ] });
85959
86079
  }
85960
86080
  function ResizeHandle({
@@ -90770,6 +90890,7 @@ function FindReplacePanel({
90770
90890
  onReplaceAll,
90771
90891
  onClose
90772
90892
  }) {
90893
+ const { t: t2 } = reactI18next.useTranslation();
90773
90894
  const searchInputRef = React10.useRef(null);
90774
90895
  React10.useEffect(() => {
90775
90896
  searchInputRef.current?.focus();
@@ -90802,12 +90923,12 @@ function FindReplacePanel({
90802
90923
  [onClose, onReplace]
90803
90924
  );
90804
90925
  const hasResults = findResults.length > 0;
90805
- const matchCountLabel = hasResults ? `${findResultIndex + 1} of ${findResults.length}` : findQuery.length > 0 ? "No matches" : "";
90926
+ const matchCountLabel = hasResults ? t2("pptx.findReplace.matchCount", { current: findResultIndex + 1, total: findResults.length }) : findQuery.length > 0 ? t2("pptx.findReplace.noMatches") : "";
90806
90927
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 right-2 z-40 bg-popover border border-border rounded-lg shadow-lg p-3 text-xs text-foreground w-80 backdrop-blur", children: [
90807
90928
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
90808
90929
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-sm inline-flex items-center gap-1.5", children: [
90809
90930
  /* @__PURE__ */ jsxRuntime.jsx(lu.LuSearch, { className: ic }),
90810
- "Find & Replace"
90931
+ t2("pptx.findReplace.title")
90811
90932
  ] }),
90812
90933
  /* @__PURE__ */ jsxRuntime.jsx(
90813
90934
  "button",
@@ -90815,8 +90936,8 @@ function FindReplacePanel({
90815
90936
  type: "button",
90816
90937
  className: btnGhost,
90817
90938
  onClick: onClose,
90818
- title: "Close (Escape)",
90819
- "aria-label": "Close find and replace",
90939
+ title: t2("pptx.findReplace.closeEscape"),
90940
+ "aria-label": t2("pptx.findReplace.closeAriaLabel"),
90820
90941
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuX, { className: ic })
90821
90942
  }
90822
90943
  )
@@ -90831,9 +90952,9 @@ function FindReplacePanel({
90831
90952
  value: findQuery,
90832
90953
  onChange: (e2) => onSetFindQuery(e2.target.value),
90833
90954
  onKeyDown: handleSearchKeyDown,
90834
- placeholder: "Find\u2026",
90955
+ placeholder: t2("pptx.findReplace.findPlaceholder"),
90835
90956
  className: "w-full bg-muted border border-border rounded px-2 py-1 pr-7 text-xs text-foreground placeholder-muted-foreground focus:border-primary focus:outline-none",
90836
- "aria-label": "Search text"
90957
+ "aria-label": t2("pptx.findReplace.searchText")
90837
90958
  }
90838
90959
  ),
90839
90960
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -90842,8 +90963,8 @@ function FindReplacePanel({
90842
90963
  type: "button",
90843
90964
  className: `absolute right-1 top-1/2 -translate-y-1/2 p-0.5 rounded transition-colors ${findMatchCase ? "bg-primary/80 text-white" : "text-muted-foreground hover:text-foreground hover:bg-accent"}`,
90844
90965
  onClick: () => onSetFindMatchCase(!findMatchCase),
90845
- title: "Match case",
90846
- "aria-label": "Toggle match case",
90966
+ title: t2("pptx.findReplace.matchCase"),
90967
+ "aria-label": t2("pptx.findReplace.toggleMatchCase"),
90847
90968
  "aria-pressed": findMatchCase,
90848
90969
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCaseSensitive, { className: ic })
90849
90970
  }
@@ -90856,8 +90977,8 @@ function FindReplacePanel({
90856
90977
  className: btnGhost,
90857
90978
  onClick: () => onNavigateResult(-1),
90858
90979
  disabled: !hasResults,
90859
- title: "Previous match (Shift+Enter)",
90860
- "aria-label": "Previous match",
90980
+ title: t2("pptx.findReplace.previousMatch"),
90981
+ "aria-label": t2("pptx.findReplace.previousMatch"),
90861
90982
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronUp, { className: ic })
90862
90983
  }
90863
90984
  ),
@@ -90868,8 +90989,8 @@ function FindReplacePanel({
90868
90989
  className: btnGhost,
90869
90990
  onClick: () => onNavigateResult(1),
90870
90991
  disabled: !hasResults,
90871
- title: "Next match (Enter)",
90872
- "aria-label": "Next match",
90992
+ title: t2("pptx.findReplace.nextMatch"),
90993
+ "aria-label": t2("pptx.findReplace.nextMatch"),
90873
90994
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: ic })
90874
90995
  }
90875
90996
  )
@@ -90884,9 +91005,9 @@ function FindReplacePanel({
90884
91005
  value: replaceQuery,
90885
91006
  onChange: (e2) => onSetReplaceQuery(e2.target.value),
90886
91007
  onKeyDown: handleReplaceKeyDown,
90887
- placeholder: "Replace with\u2026",
91008
+ placeholder: t2("pptx.findReplace.replacePlaceholder"),
90888
91009
  className: "w-full bg-muted border border-border rounded pl-7 pr-2 py-1 text-xs text-foreground placeholder-muted-foreground focus:border-primary focus:outline-none",
90889
- "aria-label": "Replacement text"
91010
+ "aria-label": t2("pptx.findReplace.replacementText")
90890
91011
  }
90891
91012
  )
90892
91013
  ] }) }),
@@ -90898,8 +91019,8 @@ function FindReplacePanel({
90898
91019
  className: btnAction,
90899
91020
  onClick: onReplace,
90900
91021
  disabled: !hasResults,
90901
- title: "Replace current match",
90902
- children: "Replace"
91022
+ title: t2("pptx.findReplace.replaceCurrent"),
91023
+ children: t2("pptx.findReplace.replace")
90903
91024
  }
90904
91025
  ),
90905
91026
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -90909,8 +91030,8 @@ function FindReplacePanel({
90909
91030
  className: btnAction,
90910
91031
  onClick: onReplaceAll,
90911
91032
  disabled: !hasResults,
90912
- title: "Replace all matches",
90913
- children: "Replace All"
91033
+ title: t2("pptx.findReplace.replaceAllMatches"),
91034
+ children: t2("pptx.findReplace.replaceAll")
90914
91035
  }
90915
91036
  )
90916
91037
  ] })
@@ -91319,8 +91440,8 @@ function SlideItemInner({
91319
91440
  {
91320
91441
  ref: slideRef,
91321
91442
  className: cn(
91322
- "group relative cursor-pointer rounded-lg border-2 p-1 transition-all",
91323
- isActive ? "border-primary bg-primary/10" : "border-border bg-background/40 hover:border-muted-foreground",
91443
+ "group relative flex items-center gap-1 cursor-pointer py-0.5 px-1 transition-all",
91444
+ isActive && "bg-accent/40 before:absolute before:left-0 before:top-1 before:bottom-1 before:w-[3px] before:bg-primary before:rounded-r",
91324
91445
  isHidden && "opacity-50"
91325
91446
  ),
91326
91447
  draggable: canEdit,
@@ -91330,21 +91451,35 @@ function SlideItemInner({
91330
91451
  onDragOver,
91331
91452
  onDrop: (e2) => onDrop(e2, slideIndex),
91332
91453
  children: [
91333
- isHidden && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg pointer-events-none bg-[repeating-linear-gradient(135deg,transparent,transparent_4px,rgba(255,255,255,0.04)_4px,rgba(255,255,255,0.04)_8px)]" }),
91334
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative overflow-hidden rounded bg-white", children: [
91335
- /* @__PURE__ */ jsxRuntime.jsx(LazyThumbnail, { slide, canvasSize, previewHeight }),
91336
- (slide.comments?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0.5 right-0.5 flex items-center gap-0.5 rounded bg-amber-500/90 px-1 py-0.5 text-[8px] font-medium text-white leading-none", children: [
91337
- /* @__PURE__ */ jsxRuntime.jsx(lu.LuMessageSquare, { className: "w-2 h-2" }),
91338
- slide.comments?.length
91339
- ] })
91340
- ] }),
91341
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center justify-between px-1", children: [
91342
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-[10px]", isActive ? "text-primary" : "text-muted-foreground"), children: slideIndex + 1 }),
91343
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
91344
- rehearsalTimings && typeof rehearsalTimings[slideIndex] === "number" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] font-mono text-amber-400/80 tabular-nums", children: formatTimingMs(rehearsalTimings[slideIndex]) }),
91345
- isHidden && /* @__PURE__ */ jsxRuntime.jsx(lu.LuEyeOff, { className: "w-3 h-3 text-muted-foreground" })
91346
- ] })
91347
- ] })
91454
+ /* @__PURE__ */ jsxRuntime.jsx(
91455
+ "span",
91456
+ {
91457
+ className: cn(
91458
+ "text-[10px] tabular-nums w-5 text-right shrink-0 select-none",
91459
+ isActive ? "text-primary font-medium" : "text-muted-foreground"
91460
+ ),
91461
+ children: slideIndex + 1
91462
+ }
91463
+ ),
91464
+ /* @__PURE__ */ jsxRuntime.jsxs(
91465
+ "div",
91466
+ {
91467
+ className: cn(
91468
+ "relative flex-1 overflow-hidden border transition-colors bg-white",
91469
+ isActive ? "border-primary/60" : "border-transparent group-hover:border-border/40"
91470
+ ),
91471
+ children: [
91472
+ isHidden && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 pointer-events-none z-10 bg-[repeating-linear-gradient(135deg,transparent,transparent_4px,rgba(255,255,255,0.04)_4px,rgba(255,255,255,0.04)_8px)]" }),
91473
+ /* @__PURE__ */ jsxRuntime.jsx(LazyThumbnail, { slide, canvasSize, previewHeight }),
91474
+ (slide.comments?.length ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0.5 right-0.5 flex items-center gap-0.5 rounded bg-amber-500/90 px-1 py-0.5 text-[8px] font-medium text-white leading-none z-10", children: [
91475
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuMessageSquare, { className: "w-2 h-2" }),
91476
+ slide.comments?.length
91477
+ ] }),
91478
+ isHidden && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0.5 right-0.5 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuEyeOff, { className: "w-3 h-3 text-muted-foreground" }) }),
91479
+ rehearsalTimings && typeof rehearsalTimings[slideIndex] === "number" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0.5 left-0.5 z-10", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px] font-mono text-amber-400/80 tabular-nums bg-black/50 px-0.5 rounded", children: formatTimingMs(rehearsalTimings[slideIndex]) }) })
91480
+ ]
91481
+ }
91482
+ )
91348
91483
  ]
91349
91484
  }
91350
91485
  );
@@ -91465,7 +91600,7 @@ function SlidesPaneSidebar({
91465
91600
  onSlideContextMenu,
91466
91601
  onMoveSlide,
91467
91602
  onAddSlide,
91468
- onCollapse,
91603
+ onCollapse: _onCollapse,
91469
91604
  onAddSection,
91470
91605
  onRenameSection,
91471
91606
  onDeleteSection,
@@ -91670,50 +91805,23 @@ function SlidesPaneSidebar({
91670
91805
  {
91671
91806
  role: "navigation",
91672
91807
  "aria-label": "Slides",
91673
- className: "flex h-full flex-col border-r border-border bg-background/70 backdrop-blur-sm",
91808
+ className: "flex h-full flex-col border-r border-border bg-secondary/30",
91674
91809
  style: panelWidth ? { width: panelWidth, flexShrink: 0 } : void 0,
91675
91810
  children: [
91676
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [
91677
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: t2("pptx.sections.slides") }),
91678
- /* @__PURE__ */ jsxRuntime.jsx(
91679
- "button",
91680
- {
91681
- type: "button",
91682
- className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
91683
- title: t2("pptx.sections.collapsePane"),
91684
- onClick: onCollapse,
91685
- children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuPanelLeftClose, { className: "h-3.5 w-3.5" })
91686
- }
91687
- )
91688
- ] }),
91689
91811
  shouldVirtualize ? renderVirtualized() : renderNonVirtualized(),
91690
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/60 px-3 py-2 space-y-1", children: [
91691
- /* @__PURE__ */ jsxRuntime.jsxs(
91692
- "button",
91693
- {
91694
- type: "button",
91695
- className: "flex w-full items-center justify-center gap-1 rounded bg-muted/80 px-2 py-1.5 text-xs text-foreground hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40",
91696
- disabled: !canEdit,
91697
- onClick: onAddSlide,
91698
- children: [
91699
- /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlus, { className: "h-3.5 w-3.5" }),
91700
- t2("pptx.sections.addSlide")
91701
- ]
91702
- }
91703
- ),
91704
- canEdit && onAddSection && /* @__PURE__ */ jsxRuntime.jsxs(
91705
- "button",
91706
- {
91707
- type: "button",
91708
- className: "flex w-full items-center justify-center gap-1 rounded bg-muted/50 px-2 py-1 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground",
91709
- onClick: () => onAddSection(t2("pptx.sections.defaultName"), activeSlideIndex),
91710
- children: [
91711
- /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlus, { className: "h-3 w-3" }),
91712
- t2("pptx.sections.addSection")
91713
- ]
91714
- }
91715
- )
91716
- ] }),
91812
+ canEdit && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/60 px-2 py-1.5", children: /* @__PURE__ */ jsxRuntime.jsxs(
91813
+ "button",
91814
+ {
91815
+ type: "button",
91816
+ className: "flex w-full items-center justify-center gap-1 rounded-sm px-2 py-1 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground transition-colors disabled:cursor-not-allowed disabled:opacity-40",
91817
+ disabled: !canEdit,
91818
+ onClick: onAddSlide,
91819
+ children: [
91820
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlus, { className: "h-3 w-3" }),
91821
+ t2("pptx.sections.addSlide")
91822
+ ]
91823
+ }
91824
+ ) }),
91717
91825
  sectionContextMenu && /* @__PURE__ */ jsxRuntime.jsx(
91718
91826
  SectionContextMenu,
91719
91827
  {
@@ -94148,23 +94256,18 @@ var SlideNotesPanel = ({
94148
94256
  });
94149
94257
  const hasNotes = draft.trim().length > 0;
94150
94258
  const slideLabel = activeSlide ? t2("pptx.notes.slideN", { n: activeSlide.slideNumber }) : t2("pptx.notes.noSlide");
94151
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-t border-border/60 bg-background/80 select-none", children: [
94259
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-t border-border/60 bg-background select-none", children: [
94152
94260
  /* @__PURE__ */ jsxRuntime.jsxs(
94153
94261
  "button",
94154
94262
  {
94155
94263
  type: "button",
94156
94264
  onClick: onToggle,
94157
- className: "flex items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/60 transition-colors w-full text-left shrink-0",
94265
+ 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",
94158
94266
  "aria-expanded": isExpanded,
94159
94267
  "aria-controls": "slide-notes-content",
94160
94268
  children: [
94161
- /* @__PURE__ */ jsxRuntime.jsx(lu.LuStickyNote, { className: "w-3.5 h-3.5 shrink-0" }),
94162
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium tracking-wide uppercase", children: t2("pptx.notes.title") }),
94163
- !isExpanded && hasNotes && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 truncate max-w-[240px] text-muted-foreground font-normal normal-case", children: [
94164
- "- ",
94165
- draft.trim().split("\n")[0]
94166
- ] }),
94167
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0", children: isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronUp, { className: "w-3.5 h-3.5" }) })
94269
+ "Notes",
94270
+ !isExpanded && hasNotes && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/50 text-[10px]", children: "(has notes)" })
94168
94271
  ]
94169
94272
  }
94170
94273
  ),
@@ -96432,15 +96535,14 @@ function SelectionPane({
96432
96535
  }) })
96433
96536
  ] });
96434
96537
  }
96435
- var _b = "inline-flex items-center justify-center px-2.5 py-1.5 max-md:min-h-[44px] max-md:min-w-[44px]";
96538
+ var _b = "inline-flex items-center justify-center px-2.5 py-1.5 max-md:min-h-[44px] max-md:min-w-[44px] active:scale-95 active:opacity-80";
96436
96539
  var gB = `${_b} border-r border-border hover:bg-accent disabled:opacity-40 disabled:cursor-not-allowed`;
96437
96540
  var gL = `${_b} hover:bg-accent disabled:opacity-40 disabled:cursor-not-allowed`;
96438
96541
  var grp = "inline-flex items-center rounded bg-muted text-xs overflow-hidden";
96439
- var pill = "inline-flex items-center gap-1.5 px-2.5 py-1.5 max-md:min-h-[44px] rounded bg-muted hover:bg-accent text-xs transition-colors";
96440
- var sep = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-5 bg-border/60 mx-0.5 max-md:hidden" });
96542
+ var pill = "inline-flex items-center gap-1.5 px-2.5 py-1.5 max-md:min-h-[44px] rounded bg-muted hover:bg-accent text-xs transition-colors active:scale-95 active:opacity-80";
96543
+ var sep = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px self-stretch bg-border/40 mx-1 max-md:hidden" });
96441
96544
  var ic2 = "w-4 h-4";
96442
96545
  var ics = "w-3.5 h-3.5";
96443
- var MODES = ["edit", "preview", "present"];
96444
96546
  var ALIGN_BTNS = [
96445
96547
  { k: "left", el: /* @__PURE__ */ jsxRuntime.jsx(lu.LuAlignLeft, { className: ic2 }) },
96446
96548
  { k: "center", el: /* @__PURE__ */ jsxRuntime.jsx(lu.LuAlignCenter, { className: ic2 }) },
@@ -96559,7 +96661,128 @@ var ATXT = [
96559
96661
  { i: /* @__PURE__ */ jsxRuntime.jsx(lu.LuAlignRight, { className: ic2 }), t: "Align right" },
96560
96662
  { i: /* @__PURE__ */ jsxRuntime.jsx(lu.LuAlignJustify, { className: ic2 }), t: "Justify" }
96561
96663
  ];
96664
+ var ANIMATION_PRESETS = [
96665
+ {
96666
+ group: "Entrance",
96667
+ items: [
96668
+ { value: "appear", label: "Appear" },
96669
+ { value: "fadeIn", label: "Fade In" },
96670
+ { value: "flyIn", label: "Fly In" }
96671
+ ]
96672
+ },
96673
+ {
96674
+ group: "Emphasis",
96675
+ items: [
96676
+ { value: "pulse", label: "Pulse" },
96677
+ { value: "spin", label: "Spin" }
96678
+ ]
96679
+ },
96680
+ {
96681
+ group: "Exit",
96682
+ items: [
96683
+ { value: "disappear", label: "Disappear" },
96684
+ { value: "fadeOut", label: "Fade Out" }
96685
+ ]
96686
+ }
96687
+ ];
96688
+ function AnimationsSection(p3) {
96689
+ const { t: t2 } = reactI18next.useTranslation();
96690
+ const [previewActive, setPreviewActive] = React10.useState(false);
96691
+ const hasElement = p3.selectedElement !== null;
96692
+ const disabled = !p3.canEdit || !hasElement;
96693
+ const handlePreview = () => {
96694
+ if (disabled) {
96695
+ return;
96696
+ }
96697
+ setPreviewActive(true);
96698
+ setTimeout(() => setPreviewActive(false), 1200);
96699
+ };
96700
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
96701
+ /* @__PURE__ */ jsxRuntime.jsxs(
96702
+ "button",
96703
+ {
96704
+ type: "button",
96705
+ onClick: handlePreview,
96706
+ disabled,
96707
+ className: cn(
96708
+ pill,
96709
+ previewActive ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
96710
+ ),
96711
+ title: t2("pptx.animations.previewTooltip"),
96712
+ children: [
96713
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlay, { className: ic2 }),
96714
+ t2("pptx.animations.preview")
96715
+ ]
96716
+ }
96717
+ ),
96718
+ sep,
96719
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
96720
+ /* @__PURE__ */ jsxRuntime.jsxs(
96721
+ "button",
96722
+ {
96723
+ type: "button",
96724
+ disabled,
96725
+ className: pill,
96726
+ title: t2("pptx.animations.addTooltip"),
96727
+ children: [
96728
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuSparkles, { className: ic2 }),
96729
+ t2("pptx.animations.addAnimation"),
96730
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: "w-3 h-3" })
96731
+ ]
96732
+ }
96733
+ ),
96734
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 hidden group-hover:flex flex-col w-44 pt-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1", children: ANIMATION_PRESETS.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React10__namespace.default.Fragment, { children: [
96735
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pt-1.5 pb-0.5 text-[10px] font-semibold text-muted-foreground uppercase tracking-wider", children: t2(`pptx.animations.group.${group.group.toLowerCase()}`) }),
96736
+ group.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
96737
+ "button",
96738
+ {
96739
+ type: "button",
96740
+ disabled,
96741
+ className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
96742
+ title: t2("pptx.animations.applyAnimation", {
96743
+ name: t2(`pptx.animations.preset.${item.value}`)
96744
+ }),
96745
+ children: t2(`pptx.animations.preset.${item.value}`)
96746
+ },
96747
+ item.value
96748
+ ))
96749
+ ] }, group.group)) }) })
96750
+ ] }),
96751
+ sep,
96752
+ /* @__PURE__ */ jsxRuntime.jsxs(
96753
+ "button",
96754
+ {
96755
+ type: "button",
96756
+ disabled,
96757
+ className: pill,
96758
+ title: t2("pptx.animations.removeTooltip"),
96759
+ children: [
96760
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuTrash2, { className: ic2 }),
96761
+ t2("pptx.animations.remove")
96762
+ ]
96763
+ }
96764
+ ),
96765
+ sep,
96766
+ /* @__PURE__ */ jsxRuntime.jsxs(
96767
+ "button",
96768
+ {
96769
+ type: "button",
96770
+ onClick: p3.onToggleInspector,
96771
+ className: cn(
96772
+ pill,
96773
+ p3.isInspectorPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
96774
+ ),
96775
+ title: t2("pptx.animations.openPanelTooltip"),
96776
+ children: [
96777
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPanelRight, { className: ic2 }),
96778
+ t2("pptx.animations.animationPanel")
96779
+ ]
96780
+ }
96781
+ )
96782
+ ] });
96783
+ }
96562
96784
  function ArrangeSection(p3) {
96785
+ const { t: t2 } = reactI18next.useTranslation();
96563
96786
  const hasSel = Boolean(p3.selectedElement);
96564
96787
  const canMut = hasSel && p3.canEdit;
96565
96788
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -96570,21 +96793,21 @@ function ArrangeSection(p3) {
96570
96793
  onClick: () => p3.onAlignElements(a2.k),
96571
96794
  disabled: !canMut,
96572
96795
  className: i3 < arr.length - 1 ? gB : gL,
96573
- title: `Align ${a2.k}`,
96796
+ title: t2("pptx.arrange.align", { direction: a2.k }),
96574
96797
  children: a2.el
96575
96798
  },
96576
96799
  a2.k
96577
96800
  )) }),
96578
96801
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: grp, children: [
96579
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: p3.onCopy, disabled: !hasSel, className: gB, title: "Copy", children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ic2 }) }),
96580
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: p3.onCut, disabled: !canMut, className: gB, title: "Cut", children: "Cut" }),
96802
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: p3.onCopy, disabled: !hasSel, className: gB, title: t2("pptx.arrange.copy"), children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ic2 }) }),
96803
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: p3.onCut, disabled: !canMut, className: gB, title: t2("pptx.arrange.cut"), children: t2("pptx.arrange.cut") }),
96581
96804
  /* @__PURE__ */ jsxRuntime.jsx(
96582
96805
  "button",
96583
96806
  {
96584
96807
  onClick: p3.onPaste,
96585
96808
  disabled: !p3.clipboardPayload || !p3.canEdit,
96586
96809
  className: gL,
96587
- title: "Paste",
96810
+ title: t2("pptx.arrange.paste"),
96588
96811
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuClipboardPaste, { className: ic2 })
96589
96812
  }
96590
96813
  )
@@ -96599,10 +96822,10 @@ function ArrangeSection(p3) {
96599
96822
  pill,
96600
96823
  p3.formatPainterActive ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
96601
96824
  ),
96602
- title: "Format Painter",
96825
+ title: t2("pptx.arrange.formatPainter"),
96603
96826
  children: [
96604
96827
  /* @__PURE__ */ jsxRuntime.jsx(lu.LuPaintbrush, { className: ic2 }),
96605
- "Format"
96828
+ t2("pptx.arrange.format")
96606
96829
  ]
96607
96830
  }
96608
96831
  ),
@@ -96614,8 +96837,8 @@ function ArrangeSection(p3) {
96614
96837
  onClick: () => p3.onFlip("horizontal"),
96615
96838
  disabled: !canMut,
96616
96839
  className: gB,
96617
- title: "Flip horizontally",
96618
- children: "Flip H"
96840
+ title: t2("pptx.arrange.flipHorizontally"),
96841
+ children: t2("pptx.arrange.flipH")
96619
96842
  }
96620
96843
  ),
96621
96844
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -96625,8 +96848,8 @@ function ArrangeSection(p3) {
96625
96848
  onClick: () => p3.onFlip("vertical"),
96626
96849
  disabled: !canMut,
96627
96850
  className: gL,
96628
- title: "Flip vertically",
96629
- children: "Flip V"
96851
+ title: t2("pptx.arrange.flipVertically"),
96852
+ children: t2("pptx.arrange.flipV")
96630
96853
  }
96631
96854
  )
96632
96855
  ] }),
@@ -96637,7 +96860,7 @@ function ArrangeSection(p3) {
96637
96860
  onClick: () => p3.onMoveLayer("backward"),
96638
96861
  disabled: !canMut,
96639
96862
  className: gB,
96640
- title: "Send backward",
96863
+ title: t2("pptx.arrange.sendBackward"),
96641
96864
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: ic2 })
96642
96865
  }
96643
96866
  ),
@@ -96647,7 +96870,7 @@ function ArrangeSection(p3) {
96647
96870
  onClick: () => p3.onMoveLayer("forward"),
96648
96871
  disabled: !canMut,
96649
96872
  className: gB,
96650
- title: "Bring forward",
96873
+ title: t2("pptx.arrange.bringForward"),
96651
96874
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronUp, { className: ic2 })
96652
96875
  }
96653
96876
  ),
@@ -96657,8 +96880,8 @@ function ArrangeSection(p3) {
96657
96880
  onClick: () => p3.onMoveLayerToEdge("back"),
96658
96881
  disabled: !canMut,
96659
96882
  className: gB,
96660
- title: "Send to back",
96661
- children: "Back"
96883
+ title: t2("pptx.arrange.sendToBack"),
96884
+ children: t2("pptx.arrange.back")
96662
96885
  }
96663
96886
  ),
96664
96887
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -96667,25 +96890,34 @@ function ArrangeSection(p3) {
96667
96890
  onClick: () => p3.onMoveLayerToEdge("front"),
96668
96891
  disabled: !canMut,
96669
96892
  className: gL,
96670
- title: "Bring to front",
96671
- children: "Front"
96893
+ title: t2("pptx.arrange.bringToFront"),
96894
+ children: t2("pptx.arrange.front")
96672
96895
  }
96673
96896
  )
96674
96897
  ] }),
96675
- /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onDuplicate, disabled: !canMut, className: pill, title: "Duplicate", children: [
96676
- /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ic2 }),
96677
- "Duplicate"
96678
- ] }),
96898
+ /* @__PURE__ */ jsxRuntime.jsxs(
96899
+ "button",
96900
+ {
96901
+ onClick: p3.onDuplicate,
96902
+ disabled: !canMut,
96903
+ className: pill,
96904
+ title: t2("pptx.arrange.duplicate"),
96905
+ children: [
96906
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ic2 }),
96907
+ t2("pptx.arrange.duplicate")
96908
+ ]
96909
+ }
96910
+ ),
96679
96911
  /* @__PURE__ */ jsxRuntime.jsxs(
96680
96912
  "button",
96681
96913
  {
96682
96914
  onClick: p3.onDelete,
96683
96915
  disabled: !canMut,
96684
96916
  className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded bg-red-700/80 hover:bg-red-600 disabled:opacity-40 disabled:cursor-not-allowed text-xs transition-colors",
96685
- title: "Delete",
96917
+ title: t2("pptx.arrange.delete"),
96686
96918
  children: [
96687
96919
  /* @__PURE__ */ jsxRuntime.jsx(lu.LuTrash2, { className: ic2 }),
96688
- "Delete"
96920
+ t2("pptx.arrange.delete")
96689
96921
  ]
96690
96922
  }
96691
96923
  )
@@ -96724,12 +96956,91 @@ function DesignSection(p3) {
96724
96956
  "Edit Theme"
96725
96957
  ]
96726
96958
  }
96959
+ ),
96960
+ sep,
96961
+ p3.onOpenDocumentProperties && /* @__PURE__ */ jsxRuntime.jsxs(
96962
+ "button",
96963
+ {
96964
+ onClick: p3.onOpenDocumentProperties,
96965
+ className: pill,
96966
+ title: "Change slide dimensions (16:9, 4:3, custom)",
96967
+ children: [
96968
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuMonitor, { className: ics }),
96969
+ "Slide Size"
96970
+ ]
96971
+ }
96972
+ ),
96973
+ p3.onToggleInspector && /* @__PURE__ */ jsxRuntime.jsxs(
96974
+ "button",
96975
+ {
96976
+ onClick: p3.onToggleInspector,
96977
+ className: cn(
96978
+ pill,
96979
+ p3.isInspectorPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
96980
+ ),
96981
+ title: "Open inspector to edit slide background",
96982
+ children: [
96983
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPaintBucket, { className: ics }),
96984
+ "Format Background"
96985
+ ]
96986
+ }
96727
96987
  )
96728
96988
  ] });
96729
96989
  }
96990
+ var TRANSITION_PRESETS = [
96991
+ { value: "none", label: "None" },
96992
+ { value: "fade", label: "Fade" },
96993
+ { value: "push", label: "Push" },
96994
+ { value: "wipe", label: "Wipe" },
96995
+ { value: "split", label: "Split" },
96996
+ { value: "reveal", label: "Reveal" },
96997
+ { value: "cut", label: "Cut" },
96998
+ { value: "cover", label: "Cover" },
96999
+ { value: "uncover", label: "Uncover" }
97000
+ ];
96730
97001
  function TransitionsSection(p3) {
97002
+ const [selected, setSelected] = React10__namespace.default.useState("none");
97003
+ const [duration, setDuration] = React10__namespace.default.useState("00.50");
96731
97004
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
96732
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground px-2", children: "Configure transitions in the Inspector panel (Slide tab)." }),
97005
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: pill, title: "Preview transition", children: [
97006
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlay, { className: ics }),
97007
+ "Preview"
97008
+ ] }),
97009
+ sep,
97010
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center gap-0.5 overflow-x-auto max-w-[420px]", children: TRANSITION_PRESETS.map((t2) => /* @__PURE__ */ jsxRuntime.jsx(
97011
+ "button",
97012
+ {
97013
+ type: "button",
97014
+ onClick: () => setSelected(t2.value),
97015
+ className: cn(
97016
+ "flex-shrink-0 px-2 py-1 max-md:min-h-[44px] rounded border text-[11px] leading-tight transition-colors",
97017
+ selected === t2.value ? "border-primary bg-primary/10 text-primary font-medium" : "border-border bg-muted hover:bg-accent text-foreground"
97018
+ ),
97019
+ title: `${t2.label} transition`,
97020
+ children: t2.label
97021
+ },
97022
+ t2.value
97023
+ )) }),
97024
+ sep,
97025
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [
97026
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap", children: "Duration:" }),
97027
+ /* @__PURE__ */ jsxRuntime.jsx(
97028
+ "input",
97029
+ {
97030
+ type: "text",
97031
+ value: duration,
97032
+ onChange: (e2) => setDuration(e2.target.value),
97033
+ className: "w-14 px-1.5 py-1 rounded border border-border bg-muted text-xs text-foreground text-center",
97034
+ title: "Transition duration in seconds"
97035
+ }
97036
+ )
97037
+ ] }),
97038
+ sep,
97039
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: pill, title: "Apply transition to all slides", children: [
97040
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ics }),
97041
+ "Apply to All"
97042
+ ] }),
97043
+ sep,
96733
97044
  /* @__PURE__ */ jsxRuntime.jsxs(
96734
97045
  "button",
96735
97046
  {
@@ -96739,7 +97050,7 @@ function TransitionsSection(p3) {
96739
97050
  pill,
96740
97051
  p3.isInspectorPaneOpen ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
96741
97052
  ),
96742
- title: "Open Inspector to edit transitions",
97053
+ title: "Open Inspector for full transition options",
96743
97054
  children: [
96744
97055
  /* @__PURE__ */ jsxRuntime.jsx(lu.LuPanelRight, { className: ic2 }),
96745
97056
  "Inspector"
@@ -96850,6 +97161,321 @@ function DrawSection(p3) {
96850
97161
  ] })
96851
97162
  ] });
96852
97163
  }
97164
+ function FileSection(p3) {
97165
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
97166
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onSaveAsPpsx, className: pill, title: "Save as Slide Show (.ppsx)", children: [
97167
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlay, { className: ic2 }),
97168
+ "Save .ppsx"
97169
+ ] }),
97170
+ p3.hasMacros && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onSaveAsPptm, className: pill, title: "Save as Macro-Enabled (.pptm)", children: [
97171
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuFileText, { className: ic2 }),
97172
+ "Save .pptm"
97173
+ ] }),
97174
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onPackageForSharing, className: pill, title: "Package for Sharing", children: [
97175
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuFolderOpen, { className: ic2 }),
97176
+ "Package"
97177
+ ] }),
97178
+ sep,
97179
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onExportPng, className: pill, title: "Export as PNG", children: [
97180
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuDownload, { className: ic2 }),
97181
+ "PNG"
97182
+ ] }),
97183
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onExportPdf, className: pill, title: "Export as PDF", children: [
97184
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuFileText, { className: ic2 }),
97185
+ "PDF"
97186
+ ] }),
97187
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onExportVideo, className: pill, title: "Export as Video", children: [
97188
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuVideo, { className: ic2 }),
97189
+ "Video"
97190
+ ] }),
97191
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onExportGif, className: pill, title: "Export as GIF", children: [
97192
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuImage, { className: ic2 }),
97193
+ "GIF"
97194
+ ] }),
97195
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onCopySlideAsImage, className: pill, title: "Copy Slide as Image", children: [
97196
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ic2 }),
97197
+ "Copy Image"
97198
+ ] }),
97199
+ sep,
97200
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onPrint, className: pill, title: "Print", children: [
97201
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPrinter, { className: ic2 }),
97202
+ "Print"
97203
+ ] }),
97204
+ sep,
97205
+ p3.onOpenDocumentProperties && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onOpenDocumentProperties, className: pill, title: "Document Properties", children: [
97206
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuInfo, { className: ic2 }),
97207
+ "Properties"
97208
+ ] }),
97209
+ p3.onOpenPasswordProtection && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onOpenPasswordProtection, className: pill, title: "Protect Presentation", children: [
97210
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuLock, { className: ic2 }),
97211
+ "Protect"
97212
+ ] }),
97213
+ p3.onOpenFontEmbedding && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onOpenFontEmbedding, className: pill, title: "Embed Fonts", children: [
97214
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuType, { className: ic2 }),
97215
+ "Fonts"
97216
+ ] }),
97217
+ p3.onOpenDigitalSignatures && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onOpenDigitalSignatures, className: pill, title: "Digital Signatures", children: [
97218
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuShieldAlert, { className: ic2 }),
97219
+ "Signatures"
97220
+ ] })
97221
+ ] });
97222
+ }
97223
+ function extractFontInfo(element2) {
97224
+ const defaults = { fontFamily: "Segoe UI", fontSize: "24" };
97225
+ if (!element2) {
97226
+ return defaults;
97227
+ }
97228
+ if (!pptxViewerCore.hasTextProperties(element2)) {
97229
+ return defaults;
97230
+ }
97231
+ const segStyle = element2.textSegments?.[0]?.style;
97232
+ const textStyle = element2.textStyle;
97233
+ const fontFamily = segStyle?.fontFamily ?? textStyle?.fontFamily ?? defaults.fontFamily;
97234
+ const fontSize = segStyle?.fontSize ?? textStyle?.fontSize;
97235
+ return {
97236
+ fontFamily,
97237
+ fontSize: fontSize !== void 0 && fontSize !== null ? String(fontSize) : defaults.fontSize
97238
+ };
97239
+ }
97240
+ var COMMON_FONTS = [
97241
+ "Arial",
97242
+ "Calibri",
97243
+ "Cambria",
97244
+ "Comic Sans MS",
97245
+ "Courier New",
97246
+ "Georgia",
97247
+ "Helvetica",
97248
+ "Impact",
97249
+ "Segoe UI",
97250
+ "Tahoma",
97251
+ "Times New Roman",
97252
+ "Trebuchet MS",
97253
+ "Verdana"
97254
+ ];
97255
+ var COMMON_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 44, 48, 54, 60, 72, 96];
97256
+ function HomeSection(p3) {
97257
+ const [layoutMenuOpen, setLayoutMenuOpen] = React10.useState(false);
97258
+ const [fontMenuOpen, setFontMenuOpen] = React10.useState(false);
97259
+ const [sizeMenuOpen, setSizeMenuOpen] = React10.useState(false);
97260
+ const [copiedFeedback, setCopiedFeedback] = React10.useState(false);
97261
+ const [cutFeedback, setCutFeedback] = React10.useState(false);
97262
+ const layoutMenuRef = React10.useRef(null);
97263
+ const fontMenuRef = React10.useRef(null);
97264
+ const sizeMenuRef = React10.useRef(null);
97265
+ const { fontFamily, fontSize } = extractFontInfo(p3.selectedElement);
97266
+ const handleNewSlide = React10.useCallback(() => {
97267
+ if (p3.layoutOptions.length > 0) {
97268
+ p3.onInsertSlideFromLayout(p3.layoutOptions[0].path);
97269
+ }
97270
+ }, [p3]);
97271
+ React10.useEffect(() => {
97272
+ if (!layoutMenuOpen) {
97273
+ return;
97274
+ }
97275
+ const handler = (e2) => {
97276
+ if (layoutMenuRef.current && !layoutMenuRef.current.contains(e2.target)) {
97277
+ setLayoutMenuOpen(false);
97278
+ }
97279
+ };
97280
+ document.addEventListener("mousedown", handler);
97281
+ return () => document.removeEventListener("mousedown", handler);
97282
+ }, [layoutMenuOpen]);
97283
+ React10.useEffect(() => {
97284
+ if (!fontMenuOpen) {
97285
+ return;
97286
+ }
97287
+ const handler = (e2) => {
97288
+ if (fontMenuRef.current && !fontMenuRef.current.contains(e2.target)) {
97289
+ setFontMenuOpen(false);
97290
+ }
97291
+ };
97292
+ document.addEventListener("mousedown", handler);
97293
+ return () => document.removeEventListener("mousedown", handler);
97294
+ }, [fontMenuOpen]);
97295
+ React10.useEffect(() => {
97296
+ if (!sizeMenuOpen) {
97297
+ return;
97298
+ }
97299
+ const handler = (e2) => {
97300
+ if (sizeMenuRef.current && !sizeMenuRef.current.contains(e2.target)) {
97301
+ setSizeMenuOpen(false);
97302
+ }
97303
+ };
97304
+ document.addEventListener("mousedown", handler);
97305
+ return () => document.removeEventListener("mousedown", handler);
97306
+ }, [sizeMenuOpen]);
97307
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
97308
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
97309
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: grp, children: [
97310
+ /* @__PURE__ */ jsxRuntime.jsx(
97311
+ "button",
97312
+ {
97313
+ type: "button",
97314
+ onClick: p3.onPaste,
97315
+ disabled: !p3.clipboardPayload || !p3.canEdit,
97316
+ className: gB,
97317
+ title: "Paste",
97318
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuClipboardPaste, { className: ic2 })
97319
+ }
97320
+ ),
97321
+ /* @__PURE__ */ jsxRuntime.jsx(
97322
+ "button",
97323
+ {
97324
+ type: "button",
97325
+ onClick: () => {
97326
+ p3.onCut();
97327
+ setCutFeedback(true);
97328
+ setTimeout(() => setCutFeedback(false), 600);
97329
+ },
97330
+ disabled: !p3.canEdit,
97331
+ className: cn(gB, cutFeedback && "bg-green-600/20 text-green-400"),
97332
+ title: "Cut",
97333
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuScissors, { className: ic2 })
97334
+ }
97335
+ ),
97336
+ /* @__PURE__ */ jsxRuntime.jsx(
97337
+ "button",
97338
+ {
97339
+ type: "button",
97340
+ onClick: () => {
97341
+ p3.onCopy();
97342
+ setCopiedFeedback(true);
97343
+ setTimeout(() => setCopiedFeedback(false), 600);
97344
+ },
97345
+ className: cn(gB, copiedFeedback && "bg-green-600/20 text-green-400"),
97346
+ title: "Copy",
97347
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: ic2 })
97348
+ }
97349
+ ),
97350
+ p3.onToggleFormatPainter && /* @__PURE__ */ jsxRuntime.jsx(
97351
+ "button",
97352
+ {
97353
+ type: "button",
97354
+ onClick: p3.onToggleFormatPainter,
97355
+ disabled: !p3.canEdit,
97356
+ className: cn(
97357
+ gL,
97358
+ p3.formatPainterActive ? "bg-amber-600 hover:bg-amber-500 text-amber-50" : ""
97359
+ ),
97360
+ title: "Format Painter",
97361
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuPaintbrush, { className: ic2 })
97362
+ }
97363
+ )
97364
+ ] }),
97365
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Clipboard" })
97366
+ ] }),
97367
+ sep,
97368
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
97369
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-flex items-center", ref: layoutMenuRef, children: [
97370
+ /* @__PURE__ */ jsxRuntime.jsxs(
97371
+ "button",
97372
+ {
97373
+ type: "button",
97374
+ onClick: handleNewSlide,
97375
+ disabled: !p3.canEdit || p3.layoutOptions.length === 0,
97376
+ className: cn(
97377
+ pill,
97378
+ "whitespace-nowrap",
97379
+ p3.layoutOptions.length > 0 ? "rounded-r-none" : ""
97380
+ ),
97381
+ title: "New Slide",
97382
+ children: [
97383
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlus, { className: ic2 }),
97384
+ "New Slide"
97385
+ ]
97386
+ }
97387
+ ),
97388
+ p3.layoutOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
97389
+ "button",
97390
+ {
97391
+ type: "button",
97392
+ disabled: !p3.canEdit,
97393
+ className: "inline-flex items-center justify-center self-stretch px-1 rounded-r bg-muted hover:bg-accent text-xs transition-colors border-l border-border/40 active:scale-95 active:opacity-80",
97394
+ title: "Choose layout",
97395
+ onClick: () => setLayoutMenuOpen((v) => !v),
97396
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: "w-3 h-3" })
97397
+ }
97398
+ ),
97399
+ layoutMenuOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 flex flex-col w-48 pt-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-60 overflow-y-auto", children: p3.layoutOptions.map((lo) => /* @__PURE__ */ jsxRuntime.jsx(
97400
+ "button",
97401
+ {
97402
+ type: "button",
97403
+ className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
97404
+ onClick: () => {
97405
+ p3.onInsertSlideFromLayout(lo.path);
97406
+ setLayoutMenuOpen(false);
97407
+ },
97408
+ children: lo.name
97409
+ },
97410
+ lo.path
97411
+ )) }) })
97412
+ ] }),
97413
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Slides" })
97414
+ ] }),
97415
+ sep,
97416
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
97417
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
97418
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: fontMenuRef, children: [
97419
+ /* @__PURE__ */ jsxRuntime.jsxs(
97420
+ "button",
97421
+ {
97422
+ type: "button",
97423
+ onClick: () => setFontMenuOpen((v) => !v),
97424
+ className: "inline-flex items-center justify-between px-2 py-1 rounded-sm border border-border/60 bg-background/60 text-[11px] text-foreground min-w-[120px] truncate hover:bg-accent/40 transition-colors cursor-pointer",
97425
+ children: [
97426
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: fontFamily }),
97427
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: "w-3 h-3 ml-1 shrink-0 text-muted-foreground" })
97428
+ ]
97429
+ }
97430
+ ),
97431
+ fontMenuOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 flex flex-col w-48 pt-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-60 overflow-y-auto", children: COMMON_FONTS.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
97432
+ "button",
97433
+ {
97434
+ type: "button",
97435
+ className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
97436
+ style: { fontFamily: f },
97437
+ onClick: () => {
97438
+ p3.onUpdateTextStyle?.({ fontFamily: f });
97439
+ setFontMenuOpen(false);
97440
+ },
97441
+ children: f
97442
+ },
97443
+ f
97444
+ )) }) })
97445
+ ] }),
97446
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: sizeMenuRef, children: [
97447
+ /* @__PURE__ */ jsxRuntime.jsxs(
97448
+ "button",
97449
+ {
97450
+ type: "button",
97451
+ onClick: () => setSizeMenuOpen((v) => !v),
97452
+ className: "inline-flex items-center justify-between px-2 py-1 rounded-sm border border-border/60 bg-background/60 text-[11px] text-foreground min-w-[50px] text-center hover:bg-accent/40 transition-colors cursor-pointer",
97453
+ children: [
97454
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: fontSize }),
97455
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronDown, { className: "w-3 h-3 ml-1 shrink-0 text-muted-foreground" })
97456
+ ]
97457
+ }
97458
+ ),
97459
+ sizeMenuOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 flex flex-col w-48 pt-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-60 overflow-y-auto", children: COMMON_SIZES.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
97460
+ "button",
97461
+ {
97462
+ type: "button",
97463
+ className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
97464
+ onClick: () => {
97465
+ p3.onUpdateTextStyle?.({ fontSize: s });
97466
+ setSizeMenuOpen(false);
97467
+ },
97468
+ children: s
97469
+ },
97470
+ s
97471
+ )) }) })
97472
+ ] })
97473
+ ] }),
97474
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Font" })
97475
+ ] }),
97476
+ sep
97477
+ ] });
97478
+ }
96853
97479
  function InsertSection(p3) {
96854
97480
  const { t: t2 } = reactI18next.useTranslation();
96855
97481
  const { canEdit } = p3;
@@ -97208,6 +97834,60 @@ function InsertSection(p3) {
97208
97834
  )
97209
97835
  ] });
97210
97836
  }
97837
+ function SlideShowSection(p3) {
97838
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
97839
+ /* @__PURE__ */ jsxRuntime.jsxs(
97840
+ "button",
97841
+ {
97842
+ onClick: () => p3.onSetMode("present"),
97843
+ className: pill,
97844
+ title: "Start slide show from beginning",
97845
+ children: [
97846
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlay, { className: ic2 }),
97847
+ "From Beginning"
97848
+ ]
97849
+ }
97850
+ ),
97851
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onPresent, className: pill, title: "Start slide show from current slide", children: [
97852
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuPlay, { className: ic2 }),
97853
+ "From Current Slide"
97854
+ ] }),
97855
+ sep,
97856
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onEnterPresenterView, className: pill, title: "Presenter view", children: [
97857
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuMonitor, { className: ic2 }),
97858
+ "Presenter View"
97859
+ ] }),
97860
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onEnterRehearsalMode, className: pill, title: "Rehearse timings", children: [
97861
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuClock, { className: ic2 }),
97862
+ "Rehearse Timings"
97863
+ ] }),
97864
+ sep,
97865
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onOpenSetUpSlideShow, className: pill, title: "Set up slide show", children: [
97866
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuSettings, { className: ic2 }),
97867
+ "Set Up Slide Show"
97868
+ ] }),
97869
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: p3.onOpenBroadcastDialog, className: pill, title: "Broadcast slide show", children: [
97870
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCast, { className: ic2 }),
97871
+ "Broadcast"
97872
+ ] }),
97873
+ sep,
97874
+ /* @__PURE__ */ jsxRuntime.jsxs(
97875
+ "button",
97876
+ {
97877
+ onClick: p3.onToggleSubtitles,
97878
+ className: cn(
97879
+ pill,
97880
+ p3.showSubtitles ? "bg-primary hover:bg-primary/80 text-primary-foreground" : ""
97881
+ ),
97882
+ title: "Toggle subtitles",
97883
+ children: [
97884
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCaptions, { className: ic2 }),
97885
+ "Subtitles"
97886
+ ]
97887
+ }
97888
+ )
97889
+ ] });
97890
+ }
97211
97891
  var FONT_COLOR_PRESETS = [
97212
97892
  "#000000",
97213
97893
  "#ffffff",
@@ -97220,6 +97900,18 @@ var FONT_COLOR_PRESETS = [
97220
97900
  "#ff69b4",
97221
97901
  "#808080"
97222
97902
  ];
97903
+ var HIGHLIGHT_COLOR_PRESETS = [
97904
+ "#ffff00",
97905
+ "#00ff00",
97906
+ "#00ffff",
97907
+ "#ff00ff",
97908
+ "#0000ff",
97909
+ "#ff0000",
97910
+ "#000080",
97911
+ "#008080",
97912
+ "#008000",
97913
+ "#800080"
97914
+ ];
97223
97915
  function TextSection(p3) {
97224
97916
  const hasSel = Boolean(p3.selectedElement);
97225
97917
  const canMut = hasSel && p3.canEdit;
@@ -97227,7 +97919,9 @@ function TextSection(p3) {
97227
97919
  const isTable = hasSel && p3.selectedElement?.type === "table";
97228
97920
  const canFormat = isTextEl || isTable;
97229
97921
  const currentColor = isTextEl && p3.selectedElement && pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textSegments?.[0]?.style?.color ?? p3.selectedElement.textStyle?.color ?? "#000000" : "#000000";
97922
+ const currentHighlight = isTextEl && p3.selectedElement && pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textSegments?.[0]?.style?.highlightColor ?? p3.selectedElement.textStyle?.highlightColor ?? "#ffff00" : "#ffff00";
97230
97923
  const colorInputRef = React10.useRef(null);
97924
+ const highlightInputRef = React10.useRef(null);
97231
97925
  const handleColorChange = React10.useCallback(
97232
97926
  (color) => {
97233
97927
  if (!canFormat) {
@@ -97237,138 +97931,709 @@ function TextSection(p3) {
97237
97931
  },
97238
97932
  [canFormat, p3]
97239
97933
  );
97934
+ const handleHighlightChange = React10.useCallback(
97935
+ (highlightColor) => {
97936
+ if (!canFormat) {
97937
+ return;
97938
+ }
97939
+ p3.onUpdateTextStyle({ highlightColor });
97940
+ },
97941
+ [canFormat, p3]
97942
+ );
97240
97943
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
97241
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: grp, children: FMT.map((b2, i3, a2) => {
97242
- const handleClick = () => {
97243
- if (!canFormat || !p3.selectedElement) {
97244
- return;
97245
- }
97246
- const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
97247
- switch (b2.t) {
97248
- case "Bold":
97249
- p3.onUpdateTextStyle({ bold: !ts?.bold });
97250
- break;
97251
- case "Italic":
97252
- p3.onUpdateTextStyle({ italic: !ts?.italic });
97253
- break;
97254
- case "Underline":
97255
- p3.onUpdateTextStyle({
97256
- underline: !ts?.underline
97257
- });
97258
- break;
97259
- case "Strikethrough":
97260
- p3.onUpdateTextStyle({
97261
- strikethrough: !ts?.strikethrough
97262
- });
97263
- break;
97264
- }
97265
- };
97266
- return /* @__PURE__ */ jsxRuntime.jsx(
97267
- "button",
97268
- {
97269
- type: "button",
97270
- disabled: !canMut,
97271
- onMouseDown: (e2) => e2.preventDefault(),
97272
- onClick: handleClick,
97273
- className: i3 < a2.length - 1 ? gB : gL,
97274
- title: b2.t,
97275
- children: b2.i
97276
- },
97277
- b2.t
97278
- );
97279
- }) }),
97280
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
97281
- /* @__PURE__ */ jsxRuntime.jsxs(
97282
- "button",
97283
- {
97284
- type: "button",
97285
- disabled: !canMut,
97286
- onMouseDown: (e2) => e2.preventDefault(),
97287
- className: pill,
97288
- title: "Font color",
97289
- children: [
97944
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
97945
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
97946
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: grp, children: FMT.map((b2, i3, a2) => {
97947
+ const handleClick = () => {
97948
+ if (!canFormat || !p3.selectedElement) {
97949
+ return;
97950
+ }
97951
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
97952
+ switch (b2.t) {
97953
+ case "Bold":
97954
+ p3.onUpdateTextStyle({ bold: !ts?.bold });
97955
+ break;
97956
+ case "Italic":
97957
+ p3.onUpdateTextStyle({ italic: !ts?.italic });
97958
+ break;
97959
+ case "Underline":
97960
+ p3.onUpdateTextStyle({
97961
+ underline: !ts?.underline
97962
+ });
97963
+ break;
97964
+ case "Strikethrough":
97965
+ p3.onUpdateTextStyle({
97966
+ strikethrough: !ts?.strikethrough
97967
+ });
97968
+ break;
97969
+ }
97970
+ };
97971
+ return /* @__PURE__ */ jsxRuntime.jsx(
97972
+ "button",
97973
+ {
97974
+ type: "button",
97975
+ disabled: !canMut,
97976
+ onMouseDown: (e2) => e2.preventDefault(),
97977
+ onClick: handleClick,
97978
+ className: i3 < a2.length - 1 ? gB : gL,
97979
+ title: b2.t,
97980
+ children: b2.i
97981
+ },
97982
+ b2.t
97983
+ );
97984
+ }) }),
97985
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: grp, children: [
97986
+ /* @__PURE__ */ jsxRuntime.jsx(
97987
+ "button",
97988
+ {
97989
+ type: "button",
97990
+ disabled: !canMut,
97991
+ onMouseDown: (e2) => e2.preventDefault(),
97992
+ onClick: () => {
97993
+ if (!canFormat || !p3.selectedElement) {
97994
+ return;
97995
+ }
97996
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
97997
+ const current = ts?.fontSize ?? 18;
97998
+ p3.onUpdateTextStyle({ fontSize: current + 2 });
97999
+ },
98000
+ className: gB,
98001
+ title: "Increase Font Size",
98002
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuAArrowUp, { className: ic2 })
98003
+ }
98004
+ ),
98005
+ /* @__PURE__ */ jsxRuntime.jsx(
98006
+ "button",
98007
+ {
98008
+ type: "button",
98009
+ disabled: !canMut,
98010
+ onMouseDown: (e2) => e2.preventDefault(),
98011
+ onClick: () => {
98012
+ if (!canFormat || !p3.selectedElement) {
98013
+ return;
98014
+ }
98015
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
98016
+ const current = ts?.fontSize ?? 18;
98017
+ p3.onUpdateTextStyle({ fontSize: Math.max(1, current - 2) });
98018
+ },
98019
+ className: gB,
98020
+ title: "Decrease Font Size",
98021
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuAArrowDown, { className: ic2 })
98022
+ }
98023
+ ),
98024
+ /* @__PURE__ */ jsxRuntime.jsx(
98025
+ "button",
98026
+ {
98027
+ type: "button",
98028
+ disabled: !canMut,
98029
+ onMouseDown: (e2) => e2.preventDefault(),
98030
+ onClick: () => {
98031
+ if (!canFormat) {
98032
+ return;
98033
+ }
98034
+ p3.onUpdateTextStyle({
98035
+ bold: false,
98036
+ italic: false,
98037
+ underline: false,
98038
+ strikethrough: false,
98039
+ highlightColor: void 0
98040
+ });
98041
+ },
98042
+ className: gL,
98043
+ title: "Clear Formatting",
98044
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuRemoveFormatting, { className: ic2 })
98045
+ }
98046
+ )
98047
+ ] }),
98048
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
98049
+ /* @__PURE__ */ jsxRuntime.jsxs(
98050
+ "button",
98051
+ {
98052
+ type: "button",
98053
+ disabled: !canMut,
98054
+ onMouseDown: (e2) => e2.preventDefault(),
98055
+ className: pill,
98056
+ title: "Font Color",
98057
+ children: [
98058
+ /* @__PURE__ */ jsxRuntime.jsx(
98059
+ "svg",
98060
+ {
98061
+ className: ic2,
98062
+ viewBox: "0 0 24 24",
98063
+ fill: "none",
98064
+ stroke: "currentColor",
98065
+ strokeWidth: "2",
98066
+ strokeLinecap: "round",
98067
+ strokeLinejoin: "round",
98068
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 20h12M9.5 4h5L18 16H6L9.5 4z" })
98069
+ }
98070
+ ),
98071
+ /* @__PURE__ */ jsxRuntime.jsx(
98072
+ "div",
98073
+ {
98074
+ className: "w-4 h-1 rounded-sm -mt-0.5",
98075
+ style: { backgroundColor: currentColor }
98076
+ }
98077
+ )
98078
+ ]
98079
+ }
98080
+ ),
98081
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 hidden group-hover:block pt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 w-36", children: [
98082
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-5 gap-1.5 mb-2", children: FONT_COLOR_PRESETS.map((c2) => /* @__PURE__ */ jsxRuntime.jsx(
98083
+ "button",
98084
+ {
98085
+ type: "button",
98086
+ className: `w-5 h-5 rounded-full border transition-transform hover:scale-125 ${currentColor?.toLowerCase() === c2 ? "border-primary ring-1 ring-primary" : "border-border"}`,
98087
+ style: { backgroundColor: c2 },
98088
+ onMouseDown: (e2) => e2.preventDefault(),
98089
+ onClick: () => handleColorChange(c2)
98090
+ },
98091
+ c2
98092
+ )) }),
97290
98093
  /* @__PURE__ */ jsxRuntime.jsx(
97291
- "svg",
98094
+ "button",
97292
98095
  {
97293
- className: ic2,
97294
- viewBox: "0 0 24 24",
97295
- fill: "none",
97296
- stroke: "currentColor",
97297
- strokeWidth: "2",
97298
- strokeLinecap: "round",
97299
- strokeLinejoin: "round",
97300
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 20h12M9.5 4h5L18 16H6L9.5 4z" })
98096
+ type: "button",
98097
+ className: "w-full text-[10px] text-muted-foreground hover:text-foreground py-1 transition-colors",
98098
+ onMouseDown: (e2) => e2.preventDefault(),
98099
+ onClick: () => colorInputRef.current?.click(),
98100
+ children: "Custom colour..."
97301
98101
  }
97302
98102
  ),
97303
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-4 h-1 rounded-sm -mt-0.5", style: { backgroundColor: currentColor } })
97304
- ]
97305
- }
97306
- ),
97307
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 hidden group-hover:block pt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 w-36", children: [
97308
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-5 gap-1.5 mb-2", children: FONT_COLOR_PRESETS.map((c2) => /* @__PURE__ */ jsxRuntime.jsx(
97309
- "button",
97310
- {
97311
- type: "button",
97312
- className: `w-5 h-5 rounded-full border transition-transform hover:scale-125 ${currentColor?.toLowerCase() === c2 ? "border-primary ring-1 ring-primary" : "border-border"}`,
97313
- style: { backgroundColor: c2 },
97314
- onMouseDown: (e2) => e2.preventDefault(),
97315
- onClick: () => handleColorChange(c2)
97316
- },
97317
- c2
97318
- )) }),
97319
- /* @__PURE__ */ jsxRuntime.jsx(
97320
- "button",
97321
- {
97322
- type: "button",
97323
- className: "w-full text-[10px] text-muted-foreground hover:text-foreground py-1 transition-colors",
97324
- onMouseDown: (e2) => e2.preventDefault(),
97325
- onClick: () => colorInputRef.current?.click(),
97326
- children: "Custom colour\u2026"
97327
- }
97328
- ),
97329
- /* @__PURE__ */ jsxRuntime.jsx(
97330
- "input",
97331
- {
97332
- ref: colorInputRef,
97333
- type: "color",
97334
- className: "sr-only",
97335
- value: currentColor,
97336
- onChange: (e2) => handleColorChange(e2.target.value)
97337
- }
97338
- )
97339
- ] }) })
98103
+ /* @__PURE__ */ jsxRuntime.jsx(
98104
+ "input",
98105
+ {
98106
+ ref: colorInputRef,
98107
+ type: "color",
98108
+ className: "sr-only",
98109
+ value: currentColor,
98110
+ onChange: (e2) => handleColorChange(e2.target.value)
98111
+ }
98112
+ )
98113
+ ] }) })
98114
+ ] }),
98115
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
98116
+ /* @__PURE__ */ jsxRuntime.jsxs(
98117
+ "button",
98118
+ {
98119
+ type: "button",
98120
+ disabled: !canMut,
98121
+ onMouseDown: (e2) => e2.preventDefault(),
98122
+ className: pill,
98123
+ title: "Text Highlight Color",
98124
+ children: [
98125
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuHighlighter, { className: ic2 }),
98126
+ /* @__PURE__ */ jsxRuntime.jsx(
98127
+ "div",
98128
+ {
98129
+ className: "w-4 h-1 rounded-sm -mt-0.5",
98130
+ style: { backgroundColor: currentHighlight }
98131
+ }
98132
+ )
98133
+ ]
98134
+ }
98135
+ ),
98136
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 hidden group-hover:block pt-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 w-36", children: [
98137
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-5 gap-1.5 mb-2", children: HIGHLIGHT_COLOR_PRESETS.map((c2) => /* @__PURE__ */ jsxRuntime.jsx(
98138
+ "button",
98139
+ {
98140
+ type: "button",
98141
+ className: `w-5 h-5 rounded-full border transition-transform hover:scale-125 ${currentHighlight?.toLowerCase() === c2 ? "border-primary ring-1 ring-primary" : "border-border"}`,
98142
+ style: { backgroundColor: c2 },
98143
+ onMouseDown: (e2) => e2.preventDefault(),
98144
+ onClick: () => handleHighlightChange(c2)
98145
+ },
98146
+ c2
98147
+ )) }),
98148
+ /* @__PURE__ */ jsxRuntime.jsx(
98149
+ "button",
98150
+ {
98151
+ type: "button",
98152
+ className: "w-full text-[10px] text-muted-foreground hover:text-foreground py-1 transition-colors",
98153
+ onMouseDown: (e2) => e2.preventDefault(),
98154
+ onClick: () => highlightInputRef.current?.click(),
98155
+ children: "Custom colour..."
98156
+ }
98157
+ ),
98158
+ /* @__PURE__ */ jsxRuntime.jsx(
98159
+ "input",
98160
+ {
98161
+ ref: highlightInputRef,
98162
+ type: "color",
98163
+ className: "sr-only",
98164
+ value: currentHighlight,
98165
+ onChange: (e2) => handleHighlightChange(e2.target.value)
98166
+ }
98167
+ )
98168
+ ] }) })
98169
+ ] })
98170
+ ] }),
98171
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Font" })
97340
98172
  ] }),
97341
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: grp, children: ATXT.map((b2, i3, a2) => {
97342
- const handleClick = () => {
97343
- if (!canFormat) {
98173
+ sep,
98174
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
98175
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
98176
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: grp, children: [
98177
+ /* @__PURE__ */ jsxRuntime.jsx(
98178
+ "button",
98179
+ {
98180
+ type: "button",
98181
+ disabled: !canMut,
98182
+ onMouseDown: (e2) => e2.preventDefault(),
98183
+ onClick: () => {
98184
+ if (!canFormat || !p3.selectedElement) {
98185
+ return;
98186
+ }
98187
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
98188
+ p3.onUpdateTextStyle({
98189
+ listType: ts?.listType === "bullet" ? "none" : "bullet"
98190
+ });
98191
+ },
98192
+ className: gB,
98193
+ title: "Bullet List",
98194
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuList, { className: ic2 })
98195
+ }
98196
+ ),
98197
+ /* @__PURE__ */ jsxRuntime.jsx(
98198
+ "button",
98199
+ {
98200
+ type: "button",
98201
+ disabled: !canMut,
98202
+ onMouseDown: (e2) => e2.preventDefault(),
98203
+ onClick: () => {
98204
+ if (!canFormat || !p3.selectedElement) {
98205
+ return;
98206
+ }
98207
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
98208
+ p3.onUpdateTextStyle({
98209
+ listType: ts?.listType === "numbered" ? "none" : "numbered"
98210
+ });
98211
+ },
98212
+ className: gL,
98213
+ title: "Numbered List",
98214
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuListOrdered, { className: ic2 })
98215
+ }
98216
+ )
98217
+ ] }),
98218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: grp, children: [
98219
+ /* @__PURE__ */ jsxRuntime.jsx(
98220
+ "button",
98221
+ {
98222
+ type: "button",
98223
+ disabled: !canMut,
98224
+ onMouseDown: (e2) => e2.preventDefault(),
98225
+ onClick: () => {
98226
+ if (!canFormat || !p3.selectedElement) {
98227
+ return;
98228
+ }
98229
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
98230
+ const current = ts?.paragraphMarginLeft ?? 0;
98231
+ p3.onUpdateTextStyle({
98232
+ paragraphMarginLeft: Math.max(0, current - 24)
98233
+ });
98234
+ },
98235
+ className: gB,
98236
+ title: "Decrease Indent",
98237
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuIndentDecrease, { className: ic2 })
98238
+ }
98239
+ ),
98240
+ /* @__PURE__ */ jsxRuntime.jsx(
98241
+ "button",
98242
+ {
98243
+ type: "button",
98244
+ disabled: !canMut,
98245
+ onMouseDown: (e2) => e2.preventDefault(),
98246
+ onClick: () => {
98247
+ if (!canFormat || !p3.selectedElement) {
98248
+ return;
98249
+ }
98250
+ const ts = pptxViewerCore.hasTextProperties(p3.selectedElement) ? p3.selectedElement.textStyle : void 0;
98251
+ const current = ts?.paragraphMarginLeft ?? 0;
98252
+ p3.onUpdateTextStyle({
98253
+ paragraphMarginLeft: current + 24
98254
+ });
98255
+ },
98256
+ className: gL,
98257
+ title: "Increase Indent",
98258
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuIndentIncrease, { className: ic2 })
98259
+ }
98260
+ )
98261
+ ] }),
98262
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: grp, children: ATXT.map((b2, i3, a2) => {
98263
+ const handleClick = () => {
98264
+ if (!canFormat) {
98265
+ return;
98266
+ }
98267
+ const alignMap = {
98268
+ "Align left": "left",
98269
+ "Align center": "center",
98270
+ "Align right": "right",
98271
+ Justify: "justify"
98272
+ };
98273
+ const align = alignMap[b2.t];
98274
+ if (align) {
98275
+ p3.onUpdateTextStyle({ align });
98276
+ }
98277
+ };
98278
+ return /* @__PURE__ */ jsxRuntime.jsx(
98279
+ "button",
98280
+ {
98281
+ type: "button",
98282
+ disabled: !canMut,
98283
+ onMouseDown: (e2) => e2.preventDefault(),
98284
+ onClick: handleClick,
98285
+ className: i3 < a2.length - 1 ? gB : gL,
98286
+ title: b2.t,
98287
+ children: b2.i
98288
+ },
98289
+ b2.t
98290
+ );
98291
+ }) })
98292
+ ] }),
98293
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Paragraph" })
98294
+ ] })
98295
+ ] });
98296
+ }
98297
+
98298
+ // src/viewer/hooks/collaboration/sanitize.ts
98299
+ var ROOM_ID_REGEX = /^[a-zA-Z0-9_-]{1,128}$/;
98300
+ function validateRoomId(roomId) {
98301
+ if (!ROOM_ID_REGEX.test(roomId)) {
98302
+ throw new Error(
98303
+ `Invalid collaboration room ID: "${roomId}". Must be 1-128 alphanumeric characters, hyphens, or underscores.`
98304
+ );
98305
+ }
98306
+ return roomId;
98307
+ }
98308
+ function sanitizeUserName(name) {
98309
+ if (typeof name !== "string") {
98310
+ return "Anonymous";
98311
+ }
98312
+ const stripped = name.replace(/<[^>]*>/g, "");
98313
+ const trimmed = stripped.trim().slice(0, 64);
98314
+ return trimmed || "Anonymous";
98315
+ }
98316
+ function clampCursorPosition(value, min2, max2) {
98317
+ if (typeof value !== "number" || !Number.isFinite(value)) {
98318
+ return 0;
98319
+ }
98320
+ const margin = 20;
98321
+ return Math.max(min2 - margin, Math.min(max2 + margin, value));
98322
+ }
98323
+ var HEX_COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
98324
+ function sanitizeColor(color, fallback = "#6366f1") {
98325
+ if (typeof color !== "string") {
98326
+ return fallback;
98327
+ }
98328
+ return HEX_COLOR_REGEX.test(color) ? color : fallback;
98329
+ }
98330
+ function sanitizeAvatarUrl(url) {
98331
+ if (typeof url !== "string") {
98332
+ return void 0;
98333
+ }
98334
+ try {
98335
+ const parsed = new URL(url);
98336
+ if (parsed.protocol === "https:" || parsed.protocol === "http:" || parsed.protocol === "data:") {
98337
+ return url;
98338
+ }
98339
+ } catch {
98340
+ }
98341
+ return void 0;
98342
+ }
98343
+ function sanitizeSlideIndex(value) {
98344
+ if (typeof value !== "number" || !Number.isFinite(value)) {
98345
+ return 0;
98346
+ }
98347
+ return Math.max(0, Math.floor(value));
98348
+ }
98349
+ function sanitizePresence(raw, canvasWidth, canvasHeight) {
98350
+ if (typeof raw.clientId !== "number") {
98351
+ return null;
98352
+ }
98353
+ return {
98354
+ clientId: raw.clientId,
98355
+ userName: sanitizeUserName(raw.userName),
98356
+ userAvatar: sanitizeAvatarUrl(raw.userAvatar),
98357
+ userColor: sanitizeColor(raw.userColor),
98358
+ activeSlideIndex: sanitizeSlideIndex(raw.activeSlideIndex),
98359
+ cursorX: clampCursorPosition(raw.cursorX, 0, canvasWidth),
98360
+ cursorY: clampCursorPosition(raw.cursorY, 0, canvasHeight),
98361
+ lastUpdated: typeof raw.lastUpdated === "string" ? raw.lastUpdated : (/* @__PURE__ */ new Date()).toISOString(),
98362
+ selectedElementId: typeof raw.selectedElementId === "string" ? raw.selectedElementId.slice(0, 128) : void 0
98363
+ };
98364
+ }
98365
+ var BROADCAST_THROTTLE_MS = 50;
98366
+ var STALE_PRESENCE_MS = 3e4;
98367
+ function usePresenceTracking({
98368
+ awareness,
98369
+ localClientId,
98370
+ userName,
98371
+ userColor,
98372
+ userAvatar,
98373
+ canvasWidth,
98374
+ canvasHeight
98375
+ }) {
98376
+ const [remoteUsers, setRemoteUsers] = React10.useState([]);
98377
+ const lastBroadcastRef = React10.useRef(0);
98378
+ const pendingBroadcastRef = React10.useRef(null);
98379
+ const latestLocalState = React10.useRef({});
98380
+ const broadcastPresence = React10.useCallback(
98381
+ (update2) => {
98382
+ if (!awareness) {
98383
+ return;
98384
+ }
98385
+ Object.assign(latestLocalState.current, update2);
98386
+ const now = Date.now();
98387
+ const elapsed = now - lastBroadcastRef.current;
98388
+ const flush = () => {
98389
+ const state2 = {
98390
+ ...latestLocalState.current,
98391
+ userName,
98392
+ userColor,
98393
+ userAvatar,
98394
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
98395
+ };
98396
+ awareness.setLocalStateField("presence", state2);
98397
+ lastBroadcastRef.current = Date.now();
98398
+ };
98399
+ if (elapsed >= BROADCAST_THROTTLE_MS) {
98400
+ if (pendingBroadcastRef.current) {
98401
+ clearTimeout(pendingBroadcastRef.current);
98402
+ pendingBroadcastRef.current = null;
98403
+ }
98404
+ flush();
98405
+ } else if (!pendingBroadcastRef.current) {
98406
+ pendingBroadcastRef.current = setTimeout(() => {
98407
+ pendingBroadcastRef.current = null;
98408
+ flush();
98409
+ }, BROADCAST_THROTTLE_MS - elapsed);
98410
+ }
98411
+ },
98412
+ [awareness, userName, userColor, userAvatar]
98413
+ );
98414
+ React10.useEffect(() => {
98415
+ if (!awareness) {
98416
+ return;
98417
+ }
98418
+ awareness.setLocalStateField("presence", {
98419
+ userName,
98420
+ userColor,
98421
+ userAvatar,
98422
+ activeSlideIndex: 0,
98423
+ cursorX: 0,
98424
+ cursorY: 0,
98425
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
98426
+ });
98427
+ }, [awareness, userName, userColor, userAvatar]);
98428
+ React10.useEffect(() => {
98429
+ if (!awareness || localClientId === null) {
98430
+ return;
98431
+ }
98432
+ const handleChange = () => {
98433
+ const now = Date.now();
98434
+ const states = awareness.getStates();
98435
+ const users = [];
98436
+ states.forEach((state2, cid) => {
98437
+ if (cid === localClientId) {
97344
98438
  return;
97345
98439
  }
97346
- const alignMap = {
97347
- "Align left": "left",
97348
- "Align center": "center",
97349
- "Align right": "right",
97350
- Justify: "justify"
97351
- };
97352
- const align = alignMap[b2.t];
97353
- if (align) {
97354
- p3.onUpdateTextStyle({ align });
98440
+ const raw = state2?.presence;
98441
+ if (!raw || typeof raw !== "object") {
98442
+ return;
97355
98443
  }
97356
- };
97357
- return /* @__PURE__ */ jsxRuntime.jsx(
97358
- "button",
98444
+ const sanitized = sanitizePresence({ ...raw, clientId: cid }, canvasWidth, canvasHeight);
98445
+ if (!sanitized) {
98446
+ return;
98447
+ }
98448
+ const updatedAt = new Date(sanitized.lastUpdated).getTime();
98449
+ if (Number.isNaN(updatedAt) || now - updatedAt > STALE_PRESENCE_MS) {
98450
+ return;
98451
+ }
98452
+ users.push(sanitized);
98453
+ });
98454
+ setRemoteUsers(users);
98455
+ };
98456
+ awareness.on("change", handleChange);
98457
+ awareness.on("update", handleChange);
98458
+ handleChange();
98459
+ return () => {
98460
+ awareness.off("change", handleChange);
98461
+ awareness.off("update", handleChange);
98462
+ };
98463
+ }, [awareness, localClientId, canvasWidth, canvasHeight]);
98464
+ React10.useEffect(() => {
98465
+ if (!awareness) {
98466
+ return;
98467
+ }
98468
+ const interval = setInterval(() => {
98469
+ awareness.setLocalStateField("presence", {
98470
+ ...latestLocalState.current,
98471
+ userName,
98472
+ userColor,
98473
+ userAvatar,
98474
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
98475
+ });
98476
+ }, 1e4);
98477
+ return () => clearInterval(interval);
98478
+ }, [awareness, userName, userColor, userAvatar]);
98479
+ React10.useEffect(() => {
98480
+ return () => {
98481
+ if (pendingBroadcastRef.current) {
98482
+ clearTimeout(pendingBroadcastRef.current);
98483
+ }
98484
+ };
98485
+ }, []);
98486
+ return { remoteUsers, broadcastPresence };
98487
+ }
98488
+ function useYjsProvider({ config }) {
98489
+ const [status, setStatus] = React10.useState("disconnected");
98490
+ const [awareness, setAwareness] = React10.useState(null);
98491
+ const [doc2, setDoc] = React10.useState(null);
98492
+ const [clientId, setClientId] = React10.useState(null);
98493
+ const cleanupRef = React10.useRef(null);
98494
+ const init = React10.useCallback(async () => {
98495
+ const roomId = validateRoomId(config.roomId);
98496
+ setStatus("connecting");
98497
+ try {
98498
+ const [Y, { WebsocketProvider: WebsocketProvider2 }] = await Promise.all([Promise.resolve().then(() => (init_yjs(), yjs_exports)), Promise.resolve().then(() => (init_y_websocket(), y_websocket_exports))]);
98499
+ const yDoc = new Y.Doc();
98500
+ const provider = new WebsocketProvider2(
98501
+ config.serverUrl,
98502
+ roomId,
98503
+ yDoc,
98504
+ // eslint-disable-line @typescript-eslint/no-explicit-any
97359
98505
  {
97360
- type: "button",
97361
- disabled: !canMut,
97362
- onMouseDown: (e2) => e2.preventDefault(),
97363
- onClick: handleClick,
97364
- className: i3 < a2.length - 1 ? gB : gL,
97365
- title: b2.t,
97366
- children: b2.i
97367
- },
97368
- b2.t
98506
+ params: config.authToken ? { token: config.authToken } : void 0
98507
+ }
97369
98508
  );
97370
- }) })
97371
- ] });
98509
+ const handleStatus = (event) => {
98510
+ if (event.status === "connected") {
98511
+ setStatus("connected");
98512
+ } else if (event.status === "disconnected") {
98513
+ setStatus("disconnected");
98514
+ }
98515
+ };
98516
+ provider.on("status", handleStatus);
98517
+ if (provider.wsconnected) {
98518
+ setStatus("connected");
98519
+ }
98520
+ setDoc(yDoc);
98521
+ setAwareness(provider.awareness);
98522
+ setClientId(provider.awareness.clientID);
98523
+ cleanupRef.current = () => {
98524
+ provider.off("status", handleStatus);
98525
+ provider.destroy();
98526
+ yDoc.destroy();
98527
+ setDoc(null);
98528
+ setAwareness(null);
98529
+ setClientId(null);
98530
+ setStatus("disconnected");
98531
+ };
98532
+ } catch (err) {
98533
+ console.warn(
98534
+ "[pptx-viewer] Collaboration packages not available:",
98535
+ err instanceof Error ? err.message : err
98536
+ );
98537
+ setStatus("error");
98538
+ }
98539
+ }, [config.roomId, config.serverUrl, config.authToken]);
98540
+ React10.useEffect(() => {
98541
+ init();
98542
+ return () => {
98543
+ cleanupRef.current?.();
98544
+ cleanupRef.current = null;
98545
+ };
98546
+ }, [init]);
98547
+ return { status, awareness, doc: doc2, clientId };
98548
+ }
98549
+
98550
+ // src/viewer/hooks/collaboration/useCollaborativeState.ts
98551
+ function useCollaborativeState({
98552
+ config,
98553
+ canvasWidth,
98554
+ canvasHeight
98555
+ }) {
98556
+ const userColor = sanitizeColor(config.userColor, "#6366f1");
98557
+ const { status, awareness, doc: doc2, clientId } = useYjsProvider({ config });
98558
+ const { remoteUsers, broadcastPresence } = usePresenceTracking({
98559
+ awareness,
98560
+ localClientId: clientId,
98561
+ userName: config.userName,
98562
+ userColor,
98563
+ userAvatar: config.userAvatar,
98564
+ canvasWidth,
98565
+ canvasHeight
98566
+ });
98567
+ const connectedCount = status === "connected" ? remoteUsers.length + 1 : remoteUsers.length;
98568
+ return {
98569
+ status,
98570
+ remoteUsers,
98571
+ broadcastPresence,
98572
+ connectedCount,
98573
+ config,
98574
+ doc: doc2
98575
+ };
98576
+ }
98577
+ var CollaborationContext = React10.createContext(null);
98578
+ function useCollaboration() {
98579
+ return React10.useContext(CollaborationContext);
98580
+ }
98581
+ function CollaborationProvider({
98582
+ config,
98583
+ canvasWidth,
98584
+ canvasHeight,
98585
+ children
98586
+ }) {
98587
+ const value = useCollaborativeState({
98588
+ config,
98589
+ canvasWidth,
98590
+ canvasHeight
98591
+ });
98592
+ return /* @__PURE__ */ jsxRuntime.jsx(CollaborationContext.Provider, { value, children });
98593
+ }
98594
+ var STATUS_STYLES = {
98595
+ connected: {
98596
+ dot: "bg-green-400",
98597
+ text: "text-green-400",
98598
+ label: "Connected"
98599
+ },
98600
+ connecting: {
98601
+ dot: "bg-yellow-400 animate-pulse",
98602
+ text: "text-yellow-400",
98603
+ label: "Connecting..."
98604
+ },
98605
+ disconnected: {
98606
+ dot: "bg-gray-500",
98607
+ text: "text-gray-500",
98608
+ label: "Disconnected"
98609
+ },
98610
+ error: {
98611
+ dot: "bg-red-400",
98612
+ text: "text-red-400",
98613
+ label: "Connection error"
98614
+ }
98615
+ };
98616
+ function CollaborationStatusIndicator({
98617
+ status,
98618
+ connectedCount
98619
+ }) {
98620
+ const { t: t2 } = reactI18next.useTranslation();
98621
+ const style = STATUS_STYLES[status];
98622
+ return /* @__PURE__ */ jsxRuntime.jsxs(
98623
+ "div",
98624
+ {
98625
+ "data-testid": "collaboration-status",
98626
+ className: "flex items-center gap-1.5",
98627
+ "aria-label": t2("pptx.collaboration.statusAriaLabel", {
98628
+ status: t2(`pptx.collaboration.status.${status}`),
98629
+ count: connectedCount
98630
+ }),
98631
+ children: [
98632
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-block w-2 h-2 rounded-full ${style.dot}`, "aria-hidden": "true" }),
98633
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-[10px] ${style.text}`, children: status === "connected" ? t2("pptx.collaboration.userCount", { count: connectedCount }) : t2(`pptx.collaboration.status.${status}`) })
98634
+ ]
98635
+ }
98636
+ );
97372
98637
  }
97373
98638
  function CustomShowsControls({
97374
98639
  customShows,
@@ -97616,7 +98881,6 @@ function ModeSwitcher({
97616
98881
  mode,
97617
98882
  onSetMode,
97618
98883
  onCloseMasterView,
97619
- onToggleSlideSorter,
97620
98884
  onEnterPresenterView,
97621
98885
  onEnterRehearsalMode,
97622
98886
  onOpenSetUpSlideShow,
@@ -97625,64 +98889,33 @@ function ModeSwitcher({
97625
98889
  showSubtitles
97626
98890
  }) {
97627
98891
  if (mode === "master") {
97628
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-2", children: [
97629
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center px-2 py-1 rounded bg-amber-600/90 text-[11px] text-amber-50", children: "Slide Master View" }),
98892
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1.5", children: [
98893
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center px-2 py-0.5 rounded-sm bg-amber-600/90 text-[10px] text-amber-50", children: "Master View" }),
97630
98894
  /* @__PURE__ */ jsxRuntime.jsx(
97631
98895
  "button",
97632
98896
  {
97633
98897
  type: "button",
97634
98898
  onClick: onCloseMasterView,
97635
- className: "px-2.5 py-1 rounded bg-muted hover:bg-accent text-[11px] text-foreground transition-colors",
98899
+ className: "px-2 py-0.5 rounded-sm hover:bg-accent text-[10px] text-foreground transition-colors",
97636
98900
  title: "Close master view",
97637
- children: "Close Master View"
98901
+ children: "Close"
97638
98902
  }
97639
98903
  )
97640
98904
  ] });
97641
98905
  }
97642
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center rounded bg-muted text-[11px] overflow-hidden", children: [
97643
- MODES.map(
97644
- (m2) => m2 === "present" ? /* @__PURE__ */ jsxRuntime.jsx(
97645
- PresentDropdown,
97646
- {
97647
- isActive: mode === m2,
97648
- onPresent: () => onSetMode(m2),
97649
- onPresenterView: onEnterPresenterView,
97650
- onRehearse: onEnterRehearsalMode,
97651
- onSetUpSlideShow: onOpenSetUpSlideShow,
97652
- onBroadcast: onOpenBroadcastDialog,
97653
- onToggleSubtitles,
97654
- showSubtitles
97655
- },
97656
- m2
97657
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
97658
- "button",
97659
- {
97660
- type: "button",
97661
- onClick: () => onSetMode(m2),
97662
- className: cn(
97663
- "px-2 py-1 transition-colors border-l border-border first:border-l-0",
97664
- mode === m2 ? "bg-primary text-primary-foreground" : "hover:bg-accent text-foreground"
97665
- ),
97666
- title: `${m2[0].toUpperCase()}${m2.slice(1)} mode`,
97667
- children: [
97668
- m2[0].toUpperCase(),
97669
- m2.slice(1)
97670
- ]
97671
- },
97672
- m2
97673
- )
97674
- ),
97675
- /* @__PURE__ */ jsxRuntime.jsx(
97676
- "button",
97677
- {
97678
- type: "button",
97679
- onClick: onToggleSlideSorter,
97680
- className: "px-2 py-1 border-l border-border hover:bg-accent text-foreground transition-colors",
97681
- title: "Slide sorter",
97682
- children: "Sorter"
97683
- }
97684
- )
97685
- ] });
98906
+ return /* @__PURE__ */ jsxRuntime.jsx(
98907
+ PresentDropdown,
98908
+ {
98909
+ isActive: mode === "present",
98910
+ onPresent: () => onSetMode("present"),
98911
+ onPresenterView: onEnterPresenterView,
98912
+ onRehearse: onEnterRehearsalMode,
98913
+ onSetUpSlideShow: onOpenSetUpSlideShow,
98914
+ onBroadcast: onOpenBroadcastDialog,
98915
+ onToggleSubtitles,
98916
+ showSubtitles
98917
+ }
98918
+ );
97686
98919
  }
97687
98920
  function OverflowMenu(p3) {
97688
98921
  const ovAct = (k2) => {
@@ -97750,14 +98983,12 @@ function OverflowMenu(p3) {
97750
98983
  ] });
97751
98984
  }
97752
98985
  function ToolbarPrimaryRow(p3) {
98986
+ const { t: t2 } = reactI18next.useTranslation();
97753
98987
  const {
97754
98988
  mode,
97755
98989
  canEdit,
97756
- isNarrowViewport,
97757
98990
  isSidebarCollapsed,
97758
98991
  isInspectorPaneOpen,
97759
- isCompactToolbarOpen,
97760
- scale,
97761
98992
  canUndo,
97762
98993
  canRedo,
97763
98994
  undoLabel,
@@ -97765,26 +98996,21 @@ function ToolbarPrimaryRow(p3) {
97765
98996
  findReplaceOpen,
97766
98997
  onToggleSidebar,
97767
98998
  onToggleInspector,
97768
- onToggleCompactToolbar,
97769
- onZoomIn,
97770
- onZoomOut,
97771
- onZoomToFit,
97772
98999
  onUndo,
97773
99000
  onRedo,
97774
99001
  onToggleFindReplace
97775
99002
  } = p3;
97776
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 max-md:gap-0.5 flex-wrap", children: [
99003
+ const collab = useCollaboration();
99004
+ const qab = "p-1 max-md:p-2 max-md:min-h-[40px] max-md:min-w-[40px] rounded-sm transition-colors hover:bg-accent/60 disabled:opacity-40 disabled:cursor-not-allowed active:scale-90 active:opacity-70";
99005
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 max-md:gap-0 px-1.5 py-0.5 max-md:px-1", children: [
97777
99006
  mode !== "present" && /* @__PURE__ */ jsxRuntime.jsx(
97778
99007
  "button",
97779
99008
  {
97780
99009
  type: "button",
97781
99010
  onClick: onToggleSidebar,
97782
- className: cn(
97783
- "p-1.5 max-md:p-2.5 max-md:min-h-[44px] max-md:min-w-[44px] rounded transition-colors",
97784
- !isSidebarCollapsed ? "bg-primary/80 text-primary-foreground" : "bg-muted hover:bg-accent"
97785
- ),
97786
- title: "Toggle slides panel",
97787
- "aria-label": "Toggle slides panel",
99011
+ className: cn(qab, !isSidebarCollapsed ? "text-foreground" : "text-muted-foreground"),
99012
+ title: t2("pptx.toolbar.toggleSlidesPanel"),
99013
+ "aria-label": t2("pptx.toolbar.toggleSlidesPanel"),
97788
99014
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuPanelLeft, { className: ic2 })
97789
99015
  }
97790
99016
  ),
@@ -97792,77 +99018,84 @@ function ToolbarPrimaryRow(p3) {
97792
99018
  /* @__PURE__ */ jsxRuntime.jsx(
97793
99019
  "button",
97794
99020
  {
97795
- onClick: onZoomOut,
97796
- className: "p-1.5 max-md:p-2.5 max-md:min-h-[44px] max-md:min-w-[44px] rounded bg-muted hover:bg-accent transition-colors",
97797
- title: "Zoom out",
97798
- "aria-label": "Zoom out",
97799
- children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuZoomOut, { className: ics })
97800
- }
97801
- ),
97802
- /* @__PURE__ */ jsxRuntime.jsxs(
97803
- "button",
97804
- {
97805
- onClick: onZoomToFit,
97806
- className: "px-1.5 py-1 max-md:min-h-[44px] rounded bg-muted hover:bg-accent text-[11px] text-muted-foreground tabular-nums min-w-[3rem] text-center transition-colors",
97807
- title: "Zoom to fit",
97808
- children: [
97809
- Math.round(scale * 100),
97810
- "%"
97811
- ]
99021
+ type: "button",
99022
+ onClick: onUndo,
99023
+ disabled: !canEdit || !canUndo,
99024
+ className: cn(qab, "text-muted-foreground"),
99025
+ title: undoLabel ? t2("pptx.toolbar.undoAction", { action: undoLabel }) : t2("pptx.toolbar.undo"),
99026
+ "aria-label": t2("pptx.toolbar.undo"),
99027
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuUndo, { className: ics })
97812
99028
  }
97813
99029
  ),
97814
99030
  /* @__PURE__ */ jsxRuntime.jsx(
97815
99031
  "button",
97816
99032
  {
97817
- onClick: onZoomIn,
97818
- className: "p-1.5 max-md:p-2.5 max-md:min-h-[44px] max-md:min-w-[44px] rounded bg-muted hover:bg-accent transition-colors",
97819
- title: "Zoom in",
97820
- "aria-label": "Zoom in",
97821
- children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuZoomIn, { className: ics })
99033
+ type: "button",
99034
+ onClick: onRedo,
99035
+ disabled: !canEdit || !canRedo,
99036
+ className: cn(qab, "text-muted-foreground"),
99037
+ title: redoLabel ? t2("pptx.toolbar.redoAction", { action: redoLabel }) : t2("pptx.toolbar.redo"),
99038
+ "aria-label": t2("pptx.toolbar.redo"),
99039
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuRedo, { className: ics })
97822
99040
  }
97823
99041
  ),
97824
- sep,
97825
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: grp, children: [
97826
- /* @__PURE__ */ jsxRuntime.jsx(
97827
- "button",
97828
- {
97829
- type: "button",
97830
- onClick: onUndo,
97831
- disabled: !canEdit || !canUndo,
97832
- className: gB,
97833
- title: undoLabel ? `Undo: ${undoLabel}` : "Undo",
97834
- "aria-label": "Undo",
97835
- children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuUndo, { className: ics })
97836
- }
97837
- ),
97838
- /* @__PURE__ */ jsxRuntime.jsx(
97839
- "button",
97840
- {
97841
- type: "button",
97842
- onClick: onRedo,
97843
- disabled: !canEdit || !canRedo,
97844
- className: gL,
97845
- title: redoLabel ? `Redo: ${redoLabel}` : "Redo",
97846
- "aria-label": "Redo",
97847
- children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuRedo, { className: ics })
97848
- }
97849
- )
97850
- ] }),
97851
99042
  (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsx(
97852
99043
  "button",
97853
99044
  {
97854
99045
  type: "button",
97855
99046
  onClick: onToggleFindReplace,
97856
99047
  className: cn(
97857
- "p-1.5 max-md:p-2.5 max-md:min-h-[44px] max-md:min-w-[44px] rounded transition-colors max-md:hidden",
97858
- findReplaceOpen ? "bg-primary/80 text-primary-foreground" : "bg-muted hover:bg-accent"
99048
+ qab,
99049
+ "max-md:hidden",
99050
+ findReplaceOpen ? "text-foreground" : "text-muted-foreground"
97859
99051
  ),
97860
- title: "Find & Replace",
97861
- "aria-label": "Find and Replace",
99052
+ title: t2("pptx.findReplace.title"),
99053
+ "aria-label": t2("pptx.findReplace.title"),
97862
99054
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuSearch, { className: ics })
97863
99055
  }
97864
99056
  ),
97865
99057
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-2 max-md:min-w-1" }),
99058
+ (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsxs(
99059
+ "button",
99060
+ {
99061
+ type: "button",
99062
+ onClick: p3.onToggleComments,
99063
+ className: cn(
99064
+ qab,
99065
+ "max-md:hidden",
99066
+ p3.isCommentsPanelOpen ? "text-foreground" : "text-muted-foreground"
99067
+ ),
99068
+ title: t2("pptx.toolbar.comments"),
99069
+ "aria-label": t2("pptx.toolbar.comments"),
99070
+ children: [
99071
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuMessageSquare, { className: ics }),
99072
+ (p3.slideCommentCount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-0.5 -right-0.5 flex items-center justify-center w-3.5 h-3.5 rounded-full bg-primary text-[8px] text-primary-foreground leading-none", children: p3.slideCommentCount })
99073
+ ]
99074
+ }
99075
+ ),
99076
+ collab && (collab.status === "connected" || collab.status === "connecting") && collab.remoteUsers.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center -space-x-1.5 mx-1", children: [
99077
+ collab.remoteUsers.slice(0, 4).map((user) => /* @__PURE__ */ jsxRuntime.jsx(
99078
+ "div",
99079
+ {
99080
+ className: "w-6 h-6 rounded-full border-2 border-background flex items-center justify-center text-[8px] font-semibold text-white shrink-0",
99081
+ style: { backgroundColor: user.userColor },
99082
+ title: user.userName,
99083
+ children: user.userAvatar ? /* @__PURE__ */ jsxRuntime.jsx(
99084
+ "img",
99085
+ {
99086
+ src: user.userAvatar,
99087
+ alt: user.userName,
99088
+ className: "w-full h-full rounded-full object-cover"
99089
+ }
99090
+ ) : user.userName.slice(0, 2).toUpperCase()
99091
+ },
99092
+ user.clientId
99093
+ )),
99094
+ collab.remoteUsers.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-6 h-6 rounded-full border-2 border-background bg-muted flex items-center justify-center text-[8px] text-muted-foreground shrink-0", children: [
99095
+ "+",
99096
+ collab.remoteUsers.length - 4
99097
+ ] })
99098
+ ] }),
97866
99099
  /* @__PURE__ */ jsxRuntime.jsx(
97867
99100
  ModeSwitcher,
97868
99101
  {
@@ -97893,40 +99126,80 @@ function ToolbarPrimaryRow(p3) {
97893
99126
  }
97894
99127
  ),
97895
99128
  sep,
97896
- (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsx(
99129
+ (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsxs(
97897
99130
  "button",
97898
99131
  {
97899
99132
  type: "button",
97900
- onClick: onToggleInspector,
99133
+ onClick: p3.onOpenShareDialog ?? p3.onPackageForSharing,
97901
99134
  className: cn(
97902
- "p-1.5 max-md:p-2.5 max-md:min-h-[44px] max-md:min-w-[44px] rounded transition-colors",
97903
- isInspectorPaneOpen ? "bg-primary/80 text-primary-foreground" : "bg-muted hover:bg-accent"
99135
+ "relative inline-flex items-center gap-1 px-2.5 py-1 rounded-sm text-[11px] font-medium transition-colors",
99136
+ collab && collab.status === "connected" ? "bg-green-600 hover:bg-green-500 text-white" : "bg-primary hover:bg-primary/90 text-primary-foreground"
97904
99137
  ),
97905
- title: "Toggle inspector panel",
97906
- "aria-label": "Toggle inspector panel",
99138
+ title: collab && collab.status === "connected" ? t2("pptx.toolbar.sharingUsers", { count: collab.connectedCount }) : t2("pptx.toolbar.share"),
99139
+ "aria-label": t2("pptx.toolbar.share"),
99140
+ children: [
99141
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuShare2, { className: "w-3 h-3" }),
99142
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-md:hidden", children: collab && collab.status === "connected" ? t2("pptx.toolbar.sharingCount", { count: collab.connectedCount }) : t2("pptx.toolbar.share") })
99143
+ ]
99144
+ }
99145
+ ),
99146
+ (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsx(
99147
+ "button",
99148
+ {
99149
+ type: "button",
99150
+ onClick: onToggleInspector,
99151
+ className: cn(qab, isInspectorPaneOpen ? "text-foreground" : "text-muted-foreground"),
99152
+ title: t2("pptx.toolbar.toggleInspector"),
99153
+ "aria-label": t2("pptx.toolbar.toggleInspector"),
97907
99154
  children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuPanelRight, { className: ic2 })
97908
99155
  }
97909
99156
  ),
97910
- !canEdit && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center px-2 py-1 rounded bg-amber-600/90 text-[11px] text-amber-50", children: "Read-only" }),
97911
- /* @__PURE__ */ jsxRuntime.jsx(OverflowMenu, { ...p3 }),
97912
- isNarrowViewport && (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsx(
99157
+ /* @__PURE__ */ jsxRuntime.jsx(
97913
99158
  "button",
97914
99159
  {
97915
99160
  type: "button",
97916
- onClick: onToggleCompactToolbar,
97917
- className: cn(
97918
- "p-1.5 rounded text-[11px] transition-colors",
97919
- isCompactToolbarOpen ? "bg-primary/80 text-primary-foreground" : "bg-muted hover:bg-accent"
97920
- ),
97921
- title: "Toggle editing tools",
97922
- children: isCompactToolbarOpen ? "Less" : "Tools"
99161
+ onClick: p3.onOpenSettings ?? p3.onToggleShortcuts,
99162
+ className: cn(qab, "text-muted-foreground"),
99163
+ title: t2("pptx.toolbar.settingsShortcuts"),
99164
+ "aria-label": t2("pptx.toolbar.settings"),
99165
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuSettings, { className: ics })
97923
99166
  }
97924
- )
99167
+ ),
99168
+ !canEdit && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center px-2 py-0.5 rounded-sm bg-amber-600/90 text-[10px] text-amber-50", children: t2("pptx.toolbar.readOnly") }),
99169
+ /* @__PURE__ */ jsxRuntime.jsx(OverflowMenu, { ...p3 })
97925
99170
  ] });
97926
99171
  }
97927
99172
  function ViewSection(p3) {
97928
99173
  const { t: t2 } = reactI18next.useTranslation();
97929
99174
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
99175
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99176
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
99177
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: pill, title: "Normal view", children: "Normal" }),
99178
+ p3.onToggleSlideSorter ? /* @__PURE__ */ jsxRuntime.jsx("button", { className: pill, onClick: p3.onToggleSlideSorter, title: "Slide Sorter view", children: "Slide Sorter" }) : /* @__PURE__ */ jsxRuntime.jsx("button", { className: pill, title: "Slide Sorter view", children: "Slide Sorter" }),
99179
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: pill, title: "Reading View", children: "Reading View" })
99180
+ ] }),
99181
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Presentation Views" })
99182
+ ] }),
99183
+ sep,
99184
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99185
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
99186
+ "button",
99187
+ {
99188
+ onClick: p3.onEnterMasterView,
99189
+ disabled: !p3.canEdit,
99190
+ className: pill,
99191
+ title: "Edit slide masters and layouts",
99192
+ children: "Slide Master"
99193
+ }
99194
+ ) }),
99195
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Master Views" })
99196
+ ] }),
99197
+ sep,
99198
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
99199
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5", children: p3.onZoomToFit && /* @__PURE__ */ jsxRuntime.jsx("button", { className: pill, onClick: p3.onZoomToFit, title: "Zoom to fit slide in window", children: "Zoom to Fit" }) }),
99200
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: "Zoom" })
99201
+ ] }),
99202
+ sep,
97930
99203
  /* @__PURE__ */ jsxRuntime.jsx(
97931
99204
  "button",
97932
99205
  {
@@ -98019,60 +99292,105 @@ function ViewSection(p3) {
98019
99292
  title: "Toggle spell check",
98020
99293
  children: "Spell"
98021
99294
  }
98022
- ),
98023
- sep,
98024
- /* @__PURE__ */ jsxRuntime.jsx(
98025
- "button",
98026
- {
98027
- onClick: p3.onEnterMasterView,
98028
- disabled: !p3.canEdit,
98029
- className: pill,
98030
- title: "Edit slide masters and layouts",
98031
- children: "Slide Master"
98032
- }
98033
99295
  )
98034
99296
  ] });
98035
99297
  }
98036
99298
  function Toolbar(p3) {
98037
99299
  const { mode, isNarrowViewport, isCompactToolbarOpen, toolbarSection, onSetToolbarSection } = p3;
99300
+ const sFil = toolbarSection === "file";
98038
99301
  const sHome = toolbarSection === "home";
98039
- const sIns = sHome || toolbarSection === "insert";
99302
+ const sIns = toolbarSection === "insert";
98040
99303
  const sTxt = sHome || toolbarSection === "text";
98041
99304
  const sArr = toolbarSection === "arrange";
98042
99305
  const sDrw = toolbarSection === "draw";
98043
99306
  const sDes = toolbarSection === "design";
98044
99307
  const sTrn = toolbarSection === "transitions";
99308
+ const sAni = toolbarSection === "animations";
99309
+ const sSlw = toolbarSection === "slideShow";
98045
99310
  const sRev = toolbarSection === "review";
98046
99311
  const sViw = toolbarSection === "view";
99312
+ const sHlp = toolbarSection === "help";
99313
+ const showRibbon = mode === "edit" || mode === "master";
98047
99314
  return /* @__PURE__ */ jsxRuntime.jsxs(
98048
99315
  "div",
98049
99316
  {
98050
99317
  role: "toolbar",
98051
99318
  "aria-label": "Presentation toolbar",
98052
- className: "relative z-20 px-2 py-1.5 max-md:px-1 max-md:py-1 border-b border-border bg-background shadow-[0_4px_12px_rgba(0,0,0,0.3)] overflow-visible",
99319
+ className: "relative z-20 border-b border-border bg-secondary/50 overflow-visible",
98053
99320
  children: [
98054
99321
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarPrimaryRow, { ...p3 }),
98055
- (mode === "edit" || mode === "master") && /* @__PURE__ */ jsxRuntime.jsxs(
99322
+ showRibbon && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center border-b border-border/60 px-1 max-md:overflow-x-auto max-md:scrollbar-none", children: [
99323
+ TOOLBAR_SECTIONS.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
99324
+ "button",
99325
+ {
99326
+ type: "button",
99327
+ onClick: () => onSetToolbarSection(s.id),
99328
+ className: cn(
99329
+ "relative px-3.5 py-2 text-[12px] font-medium whitespace-nowrap transition-colors max-md:min-h-[36px] max-md:px-3",
99330
+ toolbarSection === s.id ? s.id === "file" ? "text-primary-foreground bg-primary/80 rounded-sm" : "text-foreground after:absolute after:-bottom-px after:left-0 after:right-0 after:h-[2.5px] after:bg-primary" : s.id === "file" ? "text-primary hover:bg-primary/15 rounded-sm" : "text-muted-foreground hover:text-foreground hover:bg-accent/30"
99331
+ ),
99332
+ children: s.label
99333
+ },
99334
+ s.id
99335
+ )),
99336
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
99337
+ isNarrowViewport && /* @__PURE__ */ jsxRuntime.jsx(
99338
+ "button",
99339
+ {
99340
+ type: "button",
99341
+ onClick: p3.onToggleCompactToolbar,
99342
+ className: cn(
99343
+ "px-2 py-1 rounded text-[11px] transition-colors mr-1",
99344
+ isCompactToolbarOpen ? "bg-primary/80 text-primary-foreground" : "text-muted-foreground hover:text-foreground"
99345
+ ),
99346
+ title: "Toggle ribbon",
99347
+ children: isCompactToolbarOpen ? "Collapse" : "Expand"
99348
+ }
99349
+ )
99350
+ ] }),
99351
+ showRibbon && /* @__PURE__ */ jsxRuntime.jsxs(
98056
99352
  "div",
98057
99353
  {
98058
99354
  className: cn(
98059
- "flex items-center gap-1.5 flex-wrap mt-1.5",
99355
+ "flex items-center gap-1.5 px-2 py-1 max-md:px-1 max-md:py-0.5 overflow-visible flex-nowrap",
98060
99356
  isNarrowViewport && !isCompactToolbarOpen && "hidden"
98061
99357
  ),
98062
99358
  children: [
98063
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center rounded-md border border-border/60 bg-muted/50 p-0.5 text-[11px] max-md:overflow-x-auto max-md:max-w-full max-md:scrollbar-none", children: TOOLBAR_SECTIONS.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
98064
- "button",
99359
+ sFil && /* @__PURE__ */ jsxRuntime.jsx(
99360
+ FileSection,
98065
99361
  {
98066
- type: "button",
98067
- onClick: () => onSetToolbarSection(s.id),
98068
- className: cn(
98069
- "rounded px-2 py-0.5 transition-colors whitespace-nowrap max-md:min-h-[36px] max-md:px-3",
98070
- toolbarSection === s.id ? "bg-primary text-primary-foreground" : "text-foreground hover:bg-accent"
98071
- ),
98072
- children: s.label
98073
- },
98074
- s.id
98075
- )) }),
99362
+ onExportPng: p3.onExportPng,
99363
+ onExportPdf: p3.onExportPdf,
99364
+ onExportVideo: p3.onExportVideo,
99365
+ onExportGif: p3.onExportGif,
99366
+ onPackageForSharing: p3.onPackageForSharing,
99367
+ onSaveAsPpsx: p3.onSaveAsPpsx,
99368
+ onSaveAsPptm: p3.onSaveAsPptm,
99369
+ hasMacros: p3.hasMacros,
99370
+ onCopySlideAsImage: p3.onCopySlideAsImage,
99371
+ onPrint: p3.onPrint,
99372
+ onOpenDocumentProperties: p3.onOpenDocumentProperties,
99373
+ onOpenPasswordProtection: p3.onOpenPasswordProtection,
99374
+ onOpenFontEmbedding: p3.onOpenFontEmbedding,
99375
+ onOpenDigitalSignatures: p3.onOpenDigitalSignatures
99376
+ }
99377
+ ),
99378
+ sHome && /* @__PURE__ */ jsxRuntime.jsx(
99379
+ HomeSection,
99380
+ {
99381
+ canEdit: p3.canEdit,
99382
+ clipboardPayload: p3.clipboardPayload,
99383
+ formatPainterActive: p3.formatPainterActive,
99384
+ onCopy: p3.onCopy,
99385
+ onCut: p3.onCut,
99386
+ onPaste: p3.onPaste,
99387
+ onToggleFormatPainter: p3.onToggleFormatPainter,
99388
+ layoutOptions: p3.layoutOptions,
99389
+ onInsertSlideFromLayout: p3.onInsertSlideFromLayout,
99390
+ selectedElement: p3.selectedElement,
99391
+ onUpdateTextStyle: p3.onUpdateTextStyle
99392
+ }
99393
+ ),
98076
99394
  sIns && /* @__PURE__ */ jsxRuntime.jsx(
98077
99395
  InsertSection,
98078
99396
  {
@@ -98135,7 +99453,10 @@ function Toolbar(p3) {
98135
99453
  onToggleThemeGallery: p3.onToggleThemeGallery,
98136
99454
  isThemeGalleryOpen: p3.isThemeGalleryOpen,
98137
99455
  onToggleThemeEditor: p3.onToggleThemeEditor,
98138
- isThemeEditorOpen: p3.isThemeEditorOpen
99456
+ isThemeEditorOpen: p3.isThemeEditorOpen,
99457
+ onOpenDocumentProperties: p3.onOpenDocumentProperties,
99458
+ onToggleInspector: p3.onToggleInspector,
99459
+ isInspectorPaneOpen: p3.isInspectorPaneOpen
98139
99460
  }
98140
99461
  ),
98141
99462
  sTrn && /* @__PURE__ */ jsxRuntime.jsx(
@@ -98145,6 +99466,33 @@ function Toolbar(p3) {
98145
99466
  onToggleInspector: p3.onToggleInspector
98146
99467
  }
98147
99468
  ),
99469
+ sAni && /* @__PURE__ */ jsxRuntime.jsx(
99470
+ AnimationsSection,
99471
+ {
99472
+ canEdit: p3.canEdit,
99473
+ selectedElement: p3.selectedElement,
99474
+ isInspectorPaneOpen: p3.isInspectorPaneOpen,
99475
+ onToggleInspector: p3.onToggleInspector
99476
+ }
99477
+ ),
99478
+ sSlw && /* @__PURE__ */ jsxRuntime.jsx(
99479
+ SlideShowSection,
99480
+ {
99481
+ onPresent: () => p3.onSetMode("present"),
99482
+ onEnterPresenterView: p3.onEnterPresenterView ?? (() => {
99483
+ }),
99484
+ onEnterRehearsalMode: p3.onEnterRehearsalMode ?? (() => {
99485
+ }),
99486
+ onOpenSetUpSlideShow: p3.onOpenSetUpSlideShow ?? (() => {
99487
+ }),
99488
+ onOpenBroadcastDialog: p3.onOpenBroadcastDialog ?? (() => {
99489
+ }),
99490
+ onToggleSubtitles: p3.onToggleSubtitles ?? (() => {
99491
+ }),
99492
+ showSubtitles: p3.showSubtitles ?? false,
99493
+ onSetMode: p3.onSetMode
99494
+ }
99495
+ ),
98148
99496
  sRev && /* @__PURE__ */ jsxRuntime.jsx(
98149
99497
  ReviewSection,
98150
99498
  {
@@ -98180,7 +99528,29 @@ function Toolbar(p3) {
98180
99528
  eyedropperActive: p3.eyedropperActive,
98181
99529
  onToggleEyedropper: p3.onToggleEyedropper
98182
99530
  }
98183
- )
99531
+ ),
99532
+ sHlp && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
99533
+ /* @__PURE__ */ jsxRuntime.jsx(
99534
+ "button",
99535
+ {
99536
+ type: "button",
99537
+ onClick: p3.onToggleShortcuts,
99538
+ className: pill,
99539
+ title: "Keyboard shortcuts",
99540
+ children: "Keyboard Shortcuts"
99541
+ }
99542
+ ),
99543
+ /* @__PURE__ */ jsxRuntime.jsx(
99544
+ "button",
99545
+ {
99546
+ type: "button",
99547
+ onClick: p3.onRunAccessibilityCheck,
99548
+ className: pill,
99549
+ title: "Accessibility check",
99550
+ children: "Accessibility"
99551
+ }
99552
+ )
99553
+ ] })
98184
99554
  ]
98185
99555
  }
98186
99556
  )
@@ -105601,6 +106971,333 @@ function BroadcastDialog({
105601
106971
  ] }) })
105602
106972
  ] });
105603
106973
  }
106974
+ function getInitials(name) {
106975
+ const parts = name.trim().split(/\s+/);
106976
+ if (parts.length >= 2) {
106977
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
106978
+ }
106979
+ return name.slice(0, 2).toUpperCase();
106980
+ }
106981
+ function ShareDialog({
106982
+ open,
106983
+ onClose,
106984
+ activeCollaboration,
106985
+ onStartCollaboration,
106986
+ onStopCollaboration,
106987
+ preconfigured,
106988
+ defaultRoomId,
106989
+ defaultUserName,
106990
+ defaultServerUrl
106991
+ }) {
106992
+ const collab = useCollaboration();
106993
+ const isActive = collab !== null && collab.status !== "disconnected" && collab.status !== "error";
106994
+ const { t: t2 } = reactI18next.useTranslation();
106995
+ const [roomId, setRoomId] = React10.useState(defaultRoomId ?? "");
106996
+ const [userName, setUserName] = React10.useState(defaultUserName ?? "");
106997
+ const [serverUrl, setServerUrl] = React10.useState(defaultServerUrl ?? "");
106998
+ const [copied, setCopied] = React10.useState(false);
106999
+ const dialogRef = React10.useRef(null);
107000
+ React10.useEffect(() => {
107001
+ if (activeCollaboration) {
107002
+ setRoomId(activeCollaboration.roomId);
107003
+ setUserName(activeCollaboration.userName);
107004
+ setServerUrl(activeCollaboration.serverUrl);
107005
+ }
107006
+ }, [activeCollaboration]);
107007
+ React10.useEffect(() => {
107008
+ if (!open) {
107009
+ return;
107010
+ }
107011
+ function handleKeyDown(e2) {
107012
+ if (e2.key === "Escape") {
107013
+ onClose();
107014
+ }
107015
+ }
107016
+ document.addEventListener("keydown", handleKeyDown);
107017
+ return () => document.removeEventListener("keydown", handleKeyDown);
107018
+ }, [open, onClose]);
107019
+ React10.useEffect(() => {
107020
+ if (open && dialogRef.current) {
107021
+ dialogRef.current.focus();
107022
+ }
107023
+ }, [open]);
107024
+ const handleCopyRoomId = React10.useCallback(() => {
107025
+ const config = activeCollaboration ?? { roomId, serverUrl };
107026
+ const shareUrl = typeof window !== "undefined" ? `${window.location.origin}${window.location.pathname}?room=${encodeURIComponent(config.roomId)}&server=${encodeURIComponent(config.serverUrl)}` : config.roomId;
107027
+ void navigator.clipboard.writeText(shareUrl).then(() => {
107028
+ setCopied(true);
107029
+ setTimeout(() => setCopied(false), 2e3);
107030
+ return void 0;
107031
+ });
107032
+ }, [activeCollaboration, roomId, serverUrl]);
107033
+ const handleStartSharing = React10.useCallback(() => {
107034
+ if (!roomId.trim() || !userName.trim()) {
107035
+ return;
107036
+ }
107037
+ onStartCollaboration?.({
107038
+ roomId: roomId.trim(),
107039
+ serverUrl: serverUrl.trim(),
107040
+ userName: userName.trim()
107041
+ });
107042
+ }, [roomId, userName, serverUrl, onStartCollaboration]);
107043
+ const canStart = roomId.trim().length > 0 && userName.trim().length > 0 && serverUrl.trim().length > 0;
107044
+ if (!open) {
107045
+ return null;
107046
+ }
107047
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107048
+ /* @__PURE__ */ jsxRuntime.jsx(
107049
+ "button",
107050
+ {
107051
+ type: "button",
107052
+ className: "fixed inset-0 z-[200] bg-black/50",
107053
+ "aria-label": t2("pptx.share.closeDialog"),
107054
+ onClick: onClose
107055
+ }
107056
+ ),
107057
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-[201] flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs(
107058
+ "div",
107059
+ {
107060
+ ref: dialogRef,
107061
+ role: "dialog",
107062
+ "aria-modal": "true",
107063
+ "aria-label": t2("pptx.share.title"),
107064
+ tabIndex: -1,
107065
+ className: "pointer-events-auto w-full max-w-md rounded-xl border border-border bg-popover text-foreground shadow-2xl outline-none",
107066
+ children: [
107067
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-3 border-b border-border", children: [
107068
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-foreground", children: isActive ? t2("pptx.share.collaborationActive") : t2("pptx.share.title") }),
107069
+ /* @__PURE__ */ jsxRuntime.jsx(
107070
+ "button",
107071
+ {
107072
+ type: "button",
107073
+ onClick: onClose,
107074
+ className: "text-muted-foreground hover:text-foreground text-lg leading-none",
107075
+ "aria-label": t2("pptx.share.close"),
107076
+ children: "\xD7"
107077
+ }
107078
+ )
107079
+ ] }),
107080
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4", children: isActive ? /* @__PURE__ */ jsxRuntime.jsx(
107081
+ ActiveSessionView,
107082
+ {
107083
+ collab,
107084
+ activeCollaboration,
107085
+ copied,
107086
+ onCopyRoomId: handleCopyRoomId,
107087
+ onStopCollaboration
107088
+ }
107089
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
107090
+ StartSessionForm,
107091
+ {
107092
+ roomId,
107093
+ userName,
107094
+ serverUrl,
107095
+ onRoomIdChange: setRoomId,
107096
+ onUserNameChange: setUserName,
107097
+ onServerUrlChange: setServerUrl,
107098
+ preconfigured
107099
+ }
107100
+ ) }),
107101
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 px-5 py-3 border-t border-border", children: [
107102
+ /* @__PURE__ */ jsxRuntime.jsx(
107103
+ "button",
107104
+ {
107105
+ type: "button",
107106
+ onClick: onClose,
107107
+ className: "px-3 py-1.5 rounded bg-muted hover:bg-accent text-[12px] text-foreground transition-colors",
107108
+ children: isActive ? t2("pptx.share.close") : t2("pptx.share.cancel")
107109
+ }
107110
+ ),
107111
+ !isActive && /* @__PURE__ */ jsxRuntime.jsx(
107112
+ "button",
107113
+ {
107114
+ type: "button",
107115
+ disabled: !canStart,
107116
+ onClick: handleStartSharing,
107117
+ className: "px-3 py-1.5 rounded bg-primary hover:bg-primary/90 text-[12px] text-primary-foreground transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
107118
+ children: t2("pptx.share.startSharing")
107119
+ }
107120
+ )
107121
+ ] })
107122
+ ]
107123
+ }
107124
+ ) })
107125
+ ] });
107126
+ }
107127
+ function StartSessionForm({
107128
+ roomId,
107129
+ userName,
107130
+ serverUrl,
107131
+ onRoomIdChange,
107132
+ onUserNameChange,
107133
+ onServerUrlChange,
107134
+ preconfigured
107135
+ }) {
107136
+ const { t: t2 } = reactI18next.useTranslation();
107137
+ const inputReadOnlyClass = preconfigured ? " opacity-70 cursor-not-allowed" : "";
107138
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
107139
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[13px] text-muted-foreground leading-relaxed", children: preconfigured ? t2("pptx.share.preconfiguredDescription") : t2("pptx.share.description") }),
107140
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
107141
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "share-room-id", className: "block text-[12px] font-medium text-foreground", children: t2("pptx.share.sessionName") }),
107142
+ /* @__PURE__ */ jsxRuntime.jsx(
107143
+ "input",
107144
+ {
107145
+ id: "share-room-id",
107146
+ type: "text",
107147
+ value: roomId,
107148
+ onChange: (e2) => onRoomIdChange(e2.target.value),
107149
+ readOnly: preconfigured,
107150
+ placeholder: t2("pptx.share.sessionPlaceholder"),
107151
+ className: `w-full px-3 py-1.5 rounded border border-border bg-background text-foreground text-[13px] placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary${inputReadOnlyClass}`
107152
+ }
107153
+ ),
107154
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("pptx.share.sessionHint") })
107155
+ ] }),
107156
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
107157
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "share-user-name", className: "block text-[12px] font-medium text-foreground", children: t2("pptx.share.displayName") }),
107158
+ /* @__PURE__ */ jsxRuntime.jsx(
107159
+ "input",
107160
+ {
107161
+ id: "share-user-name",
107162
+ type: "text",
107163
+ value: userName,
107164
+ onChange: (e2) => onUserNameChange(e2.target.value),
107165
+ readOnly: preconfigured,
107166
+ placeholder: t2("pptx.share.namePlaceholder"),
107167
+ className: `w-full px-3 py-1.5 rounded border border-border bg-background text-foreground text-[13px] placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary${inputReadOnlyClass}`
107168
+ }
107169
+ )
107170
+ ] }),
107171
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
107172
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "share-server-url", className: "block text-[12px] font-medium text-foreground", children: t2("pptx.share.serverLabel") }),
107173
+ /* @__PURE__ */ jsxRuntime.jsx(
107174
+ "input",
107175
+ {
107176
+ id: "share-server-url",
107177
+ type: "text",
107178
+ value: serverUrl,
107179
+ onChange: (e2) => onServerUrlChange(e2.target.value),
107180
+ readOnly: preconfigured,
107181
+ placeholder: t2("pptx.share.serverPlaceholder"),
107182
+ className: `w-full px-3 py-1.5 rounded border border-border bg-background text-foreground text-[13px] placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary${inputReadOnlyClass}`
107183
+ }
107184
+ )
107185
+ ] }),
107186
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[11px] text-muted-foreground/70 leading-relaxed", children: [
107187
+ "Run",
107188
+ " ",
107189
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "px-1 py-0.5 rounded bg-muted text-[10px] font-mono", children: "bun run collab" }),
107190
+ " ",
107191
+ "to start the server. Others can join at",
107192
+ " ",
107193
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "px-1 py-0.5 rounded bg-muted text-[10px] font-mono", children: "?room=SESSION_NAME" })
107194
+ ] })
107195
+ ] });
107196
+ }
107197
+ function ActiveSessionView({
107198
+ collab,
107199
+ activeCollaboration,
107200
+ copied,
107201
+ onCopyRoomId,
107202
+ onStopCollaboration
107203
+ }) {
107204
+ const { t: t2 } = reactI18next.useTranslation();
107205
+ const statusColor = collab.status === "connected" ? "text-green-400" : collab.status === "connecting" ? "text-yellow-400" : "text-red-400";
107206
+ const statusIcon = collab.status === "connected" || collab.status === "connecting" ? /* @__PURE__ */ jsxRuntime.jsx(lu.LuWifi, { className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lu.LuWifiOff, { className: "w-4 h-4" });
107207
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
107208
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
107209
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: statusColor, children: statusIcon }),
107210
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px] font-medium text-foreground capitalize", children: collab.status }),
107211
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[12px] text-muted-foreground ml-auto flex items-center gap-1", children: [
107212
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuUsers, { className: "w-3.5 h-3.5" }),
107213
+ t2("pptx.collaboration.userCount", { count: collab.connectedCount })
107214
+ ] })
107215
+ ] }),
107216
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
107217
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[12px] font-medium text-foreground", children: t2("pptx.share.shareLink") }),
107218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
107219
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 px-3 py-1.5 rounded border border-border bg-background text-[11px] text-foreground select-all font-mono truncate", children: typeof window !== "undefined" ? `${window.location.origin}${window.location.pathname}?room=${encodeURIComponent(collab.config.roomId)}&server=${encodeURIComponent(collab.config.serverUrl)}` : collab.config.roomId }),
107220
+ /* @__PURE__ */ jsxRuntime.jsx(
107221
+ "button",
107222
+ {
107223
+ type: "button",
107224
+ onClick: onCopyRoomId,
107225
+ className: "flex items-center gap-1 px-2.5 py-1.5 rounded border border-border bg-muted hover:bg-accent text-[12px] text-foreground transition-colors shrink-0",
107226
+ title: t2("pptx.share.copyLink"),
107227
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107228
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCheck, { className: "w-3.5 h-3.5 text-green-400" }),
107229
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t2("pptx.share.copied") })
107230
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107231
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { className: "w-3.5 h-3.5" }),
107232
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t2("pptx.share.copyUrl") })
107233
+ ] })
107234
+ }
107235
+ )
107236
+ ] }),
107237
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("pptx.share.shareHint") })
107238
+ ] }),
107239
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-[11px] text-muted-foreground", children: [
107240
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
107241
+ t2("pptx.share.room"),
107242
+ " ",
107243
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "font-mono text-foreground", children: collab.config.roomId })
107244
+ ] }),
107245
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
107246
+ t2("pptx.share.server"),
107247
+ " ",
107248
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "font-mono text-foreground", children: collab.config.serverUrl })
107249
+ ] })
107250
+ ] }),
107251
+ collab.remoteUsers.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
107252
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[12px] font-medium text-foreground", children: t2("pptx.share.connectedUsers") }),
107253
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded border border-border bg-background divide-y divide-border max-h-[140px] overflow-y-auto", children: [
107254
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [
107255
+ /* @__PURE__ */ jsxRuntime.jsx(
107256
+ "div",
107257
+ {
107258
+ className: "w-6 h-6 rounded-full flex items-center justify-center text-[9px] font-semibold text-white shrink-0",
107259
+ style: { backgroundColor: collab.config.userColor ?? "#6366f1" },
107260
+ children: getInitials(activeCollaboration?.userName ?? collab.config.userName)
107261
+ }
107262
+ ),
107263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] text-foreground truncate", children: activeCollaboration?.userName ?? collab.config.userName }),
107264
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground ml-auto", children: t2("pptx.share.you") })
107265
+ ] }),
107266
+ collab.remoteUsers.map((user) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [
107267
+ /* @__PURE__ */ jsxRuntime.jsx(
107268
+ "div",
107269
+ {
107270
+ className: "w-6 h-6 rounded-full flex items-center justify-center text-[9px] font-semibold text-white shrink-0",
107271
+ style: { backgroundColor: user.userColor },
107272
+ children: user.userAvatar ? /* @__PURE__ */ jsxRuntime.jsx(
107273
+ "img",
107274
+ {
107275
+ src: user.userAvatar,
107276
+ alt: "",
107277
+ className: "w-full h-full rounded-full object-cover"
107278
+ }
107279
+ ) : getInitials(user.userName)
107280
+ }
107281
+ ),
107282
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] text-foreground truncate", children: user.userName }),
107283
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground ml-auto", children: [
107284
+ "Slide ",
107285
+ user.activeSlideIndex + 1
107286
+ ] })
107287
+ ] }, user.clientId))
107288
+ ] })
107289
+ ] }),
107290
+ onStopCollaboration && /* @__PURE__ */ jsxRuntime.jsx(
107291
+ "button",
107292
+ {
107293
+ type: "button",
107294
+ onClick: onStopCollaboration,
107295
+ className: "w-full px-3 py-2 rounded border border-red-500/30 bg-red-500/10 hover:bg-red-500/20 text-[12px] text-red-400 font-medium transition-colors",
107296
+ children: t2("pptx.share.stopSharing")
107297
+ }
107298
+ )
107299
+ ] });
107300
+ }
105604
107301
 
105605
107302
  // src/viewer/components/hyperlink-edit-types.ts
105606
107303
  var ACTION_VERB_MAP = {
@@ -107501,7 +109198,14 @@ function ViewerBottomPanels({
107501
109198
  onUpdateNotes,
107502
109199
  collaborationSlot,
107503
109200
  notesPanelHeight,
107504
- onResizeBottom
109201
+ onResizeBottom,
109202
+ scale,
109203
+ onZoomIn,
109204
+ onZoomOut,
109205
+ onZoomToFit,
109206
+ mode,
109207
+ onSetMode,
109208
+ onToggleSlideSorter
107505
109209
  }) {
107506
109210
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107507
109211
  onResizeBottom && !isSlideNotesCollapsed && /* @__PURE__ */ jsxRuntime.jsx(ResizeHandle, { direction: "vertical", onResize: onResizeBottom }),
@@ -107517,18 +109221,25 @@ function ViewerBottomPanels({
107517
109221
  panelHeight: notesPanelHeight
107518
109222
  }
107519
109223
  ),
107520
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
107521
- /* @__PURE__ */ jsxRuntime.jsx(
107522
- StatusBar,
107523
- {
107524
- slideCount,
107525
- activeSlideIndex,
107526
- isDirty,
107527
- autosaveStatus
107528
- }
107529
- ),
107530
- collaborationSlot
107531
- ] })
109224
+ /* @__PURE__ */ jsxRuntime.jsx(
109225
+ StatusBar,
109226
+ {
109227
+ slideCount,
109228
+ activeSlideIndex,
109229
+ isDirty,
109230
+ autosaveStatus,
109231
+ scale,
109232
+ onZoomIn,
109233
+ onZoomOut,
109234
+ onZoomToFit,
109235
+ isNotesExpanded: !isSlideNotesCollapsed,
109236
+ onToggleNotes,
109237
+ mode,
109238
+ onSetMode,
109239
+ onToggleSlideSorter,
109240
+ collaborationSlot
109241
+ }
109242
+ )
107532
109243
  ] });
107533
109244
  }
107534
109245
  function ViewerInspector({
@@ -107687,7 +109398,9 @@ function ViewerToolbarSection(props) {
107687
109398
  ops,
107688
109399
  onSetMode,
107689
109400
  onEnterPresenterView,
107690
- onEnterRehearsalMode
109401
+ onEnterRehearsalMode,
109402
+ onOpenSettings,
109403
+ onOpenShareDialog
107691
109404
  } = props;
107692
109405
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
107693
109406
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -107766,12 +109479,14 @@ function ViewerToolbarSection(props) {
107766
109479
  onExportVideo: exportHandlers.handleExportVideo,
107767
109480
  onExportGif: exportHandlers.handleExportGif,
107768
109481
  onPackageForSharing: exportHandlers.handlePackageForSharing,
109482
+ onOpenShareDialog,
107769
109483
  onSaveAsPpsx: exportHandlers.handleSaveAsPpsx,
107770
109484
  onSaveAsPptm: exportHandlers.handleSaveAsPptm,
107771
109485
  hasMacros: s.hasMacros,
107772
109486
  onCopySlideAsImage: exportHandlers.handleCopySlideAsImage,
107773
109487
  onPrint: printHandlers.handlePrint,
107774
109488
  onToggleShortcuts: () => s.setIsShortcutHelpOpen((p3) => !p3),
109489
+ onOpenSettings,
107775
109490
  onRunAccessibilityCheck: dialogs.handleRunAccessibilityCheck,
107776
109491
  onToggleSlideSorter: () => s.setShowSlideSorter((p3) => !p3),
107777
109492
  onUpdateTextStyle: ops.updateSelectedTextStyle,
@@ -107801,7 +109516,12 @@ function ViewerToolbarSection(props) {
107801
109516
  onToggleThemeGallery: () => s.setIsThemeGalleryOpen((p3) => !p3),
107802
109517
  isThemeGalleryOpen: s.isThemeGalleryOpen,
107803
109518
  onCompare: propertyHandlers.handleCompare,
107804
- onToggleComments: () => s.setIsInspectorPaneOpen((p3) => !p3),
109519
+ onToggleComments: () => {
109520
+ s.setSidebarPanelMode("comments");
109521
+ if (!s.isInspectorPaneOpen) {
109522
+ s.setIsInspectorPaneOpen(true);
109523
+ }
109524
+ },
107805
109525
  isCommentsPanelOpen: s.isInspectorPaneOpen,
107806
109526
  slideCommentCount: activeSlide?.comments?.length ?? 0,
107807
109527
  formatPainterActive: s.formatPainterActive,
@@ -109548,7 +111268,7 @@ function ViewerCanvasArea(props) {
109548
111268
  const handleToolbarMouseLeave = React10.useCallback(() => {
109549
111269
  toolbarHoveringRef.current = false;
109550
111270
  }, []);
109551
- return /* @__PURE__ */ jsxRuntime.jsxs("main", { "aria-label": "Slide editor", className: "flex-1 min-w-0 relative flex flex-col", children: [
111271
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { "aria-label": "Slide editor", className: "flex-1 min-w-0 relative flex flex-col bg-background", children: [
109552
111272
  findReplace.findReplaceOpen && /* @__PURE__ */ jsxRuntime.jsx(
109553
111273
  FindReplacePanel,
109554
111274
  {
@@ -109918,7 +111638,7 @@ var PRESET_THEMES = [
109918
111638
  minorFont: "Gill Sans MT"
109919
111639
  }
109920
111640
  ];
109921
- var COMMON_FONTS = [
111641
+ var COMMON_FONTS2 = [
109922
111642
  "Arial",
109923
111643
  "Calibri",
109924
111644
  "Calibri Light",
@@ -110023,15 +111743,15 @@ function ThemeColorSchemeEditor({
110023
111743
  {
110024
111744
  className: THEME_EDITOR_INPUT,
110025
111745
  disabled: !canEdit,
110026
- value: COMMON_FONTS.includes(majorFont) ? majorFont : "__custom__",
111746
+ value: COMMON_FONTS2.includes(majorFont) ? majorFont : "__custom__",
110027
111747
  onChange: (e2) => {
110028
111748
  if (e2.target.value !== "__custom__") {
110029
111749
  onMajorFontChange(e2.target.value);
110030
111750
  }
110031
111751
  },
110032
111752
  children: [
110033
- COMMON_FONTS.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f, children: f }, f)),
110034
- !COMMON_FONTS.includes(majorFont) && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "__custom__", children: majorFont })
111753
+ COMMON_FONTS2.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f, children: f }, f)),
111754
+ !COMMON_FONTS2.includes(majorFont) && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "__custom__", children: majorFont })
110035
111755
  ]
110036
111756
  }
110037
111757
  )
@@ -110043,15 +111763,15 @@ function ThemeColorSchemeEditor({
110043
111763
  {
110044
111764
  className: THEME_EDITOR_INPUT,
110045
111765
  disabled: !canEdit,
110046
- value: COMMON_FONTS.includes(minorFont) ? minorFont : "__custom__",
111766
+ value: COMMON_FONTS2.includes(minorFont) ? minorFont : "__custom__",
110047
111767
  onChange: (e2) => {
110048
111768
  if (e2.target.value !== "__custom__") {
110049
111769
  onMinorFontChange(e2.target.value);
110050
111770
  }
110051
111771
  },
110052
111772
  children: [
110053
- COMMON_FONTS.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f, children: f }, f)),
110054
- !COMMON_FONTS.includes(minorFont) && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "__custom__", children: minorFont })
111773
+ COMMON_FONTS2.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f, children: f }, f)),
111774
+ !COMMON_FONTS2.includes(minorFont) && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "__custom__", children: minorFont })
110055
111775
  ]
110056
111776
  }
110057
111777
  )
@@ -110929,403 +112649,293 @@ function ViewerMainContent(props) {
110929
112649
  )
110930
112650
  ] });
110931
112651
  }
110932
-
110933
- // src/viewer/hooks/collaboration/sanitize.ts
110934
- var ROOM_ID_REGEX = /^[a-zA-Z0-9_-]{1,128}$/;
110935
- function validateRoomId(roomId) {
110936
- if (!ROOM_ID_REGEX.test(roomId)) {
110937
- throw new Error(
110938
- `Invalid collaboration room ID: "${roomId}". Must be 1-128 alphanumeric characters, hyphens, or underscores.`
110939
- );
110940
- }
110941
- return roomId;
110942
- }
110943
- function sanitizeUserName(name) {
110944
- if (typeof name !== "string") {
110945
- return "Anonymous";
110946
- }
110947
- const stripped = name.replace(/<[^>]*>/g, "");
110948
- const trimmed = stripped.trim().slice(0, 64);
110949
- return trimmed || "Anonymous";
110950
- }
110951
- function clampCursorPosition(value, min2, max2) {
110952
- if (typeof value !== "number" || !Number.isFinite(value)) {
110953
- return 0;
110954
- }
110955
- const margin = 20;
110956
- return Math.max(min2 - margin, Math.min(max2 + margin, value));
110957
- }
110958
- var HEX_COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
110959
- function sanitizeColor(color, fallback = "#6366f1") {
110960
- if (typeof color !== "string") {
110961
- return fallback;
110962
- }
110963
- return HEX_COLOR_REGEX.test(color) ? color : fallback;
110964
- }
110965
- function sanitizeAvatarUrl(url) {
110966
- if (typeof url !== "string") {
110967
- return void 0;
110968
- }
110969
- try {
110970
- const parsed = new URL(url);
110971
- if (parsed.protocol === "https:" || parsed.protocol === "http:" || parsed.protocol === "data:") {
110972
- return url;
112652
+ function ToggleSwitch({
112653
+ label,
112654
+ enabled,
112655
+ onToggle
112656
+ }) {
112657
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2.5 px-3", children: [
112658
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-foreground", children: label }),
112659
+ /* @__PURE__ */ jsxRuntime.jsx(
112660
+ "button",
112661
+ {
112662
+ type: "button",
112663
+ onClick: onToggle,
112664
+ className: cn(
112665
+ "relative inline-flex h-5 w-9 items-center rounded-full transition-colors",
112666
+ enabled ? "bg-primary" : "bg-muted-foreground/30"
112667
+ ),
112668
+ role: "switch",
112669
+ "aria-checked": enabled,
112670
+ "aria-label": label,
112671
+ children: /* @__PURE__ */ jsxRuntime.jsx(
112672
+ "span",
112673
+ {
112674
+ className: cn(
112675
+ "inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",
112676
+ enabled ? "translate-x-[18px]" : "translate-x-[3px]"
112677
+ )
112678
+ }
112679
+ )
112680
+ }
112681
+ )
112682
+ ] });
112683
+ }
112684
+ function SettingsDialog({
112685
+ isOpen,
112686
+ onClose,
112687
+ spellCheckEnabled = false,
112688
+ onSetSpellCheckEnabled,
112689
+ showGrid = false,
112690
+ onSetShowGrid,
112691
+ showRulers = false,
112692
+ onSetShowRulers,
112693
+ snapToGrid = false,
112694
+ onSetSnapToGrid,
112695
+ reducedMotion = false,
112696
+ onToggleReducedMotion
112697
+ }) {
112698
+ const [activeTab, setActiveTab] = React10.useState("general");
112699
+ const [autoSave, setAutoSave] = React10.useState(true);
112700
+ const { t: t2 } = reactI18next.useTranslation();
112701
+ const handleKeyDown = React10.useCallback(
112702
+ (e2) => {
112703
+ if (e2.key === "Escape") {
112704
+ onClose();
112705
+ }
112706
+ },
112707
+ [onClose]
112708
+ );
112709
+ React10.useEffect(() => {
112710
+ if (isOpen) {
112711
+ document.addEventListener("keydown", handleKeyDown);
112712
+ return () => document.removeEventListener("keydown", handleKeyDown);
110973
112713
  }
110974
- } catch {
110975
- }
110976
- return void 0;
110977
- }
110978
- function sanitizeSlideIndex(value) {
110979
- if (typeof value !== "number" || !Number.isFinite(value)) {
110980
- return 0;
110981
- }
110982
- return Math.max(0, Math.floor(value));
110983
- }
110984
- function sanitizePresence(raw, canvasWidth, canvasHeight) {
110985
- if (typeof raw.clientId !== "number") {
112714
+ }, [isOpen, handleKeyDown]);
112715
+ if (!isOpen) {
110986
112716
  return null;
110987
112717
  }
110988
- return {
110989
- clientId: raw.clientId,
110990
- userName: sanitizeUserName(raw.userName),
110991
- userAvatar: sanitizeAvatarUrl(raw.userAvatar),
110992
- userColor: sanitizeColor(raw.userColor),
110993
- activeSlideIndex: sanitizeSlideIndex(raw.activeSlideIndex),
110994
- cursorX: clampCursorPosition(raw.cursorX, 0, canvasWidth),
110995
- cursorY: clampCursorPosition(raw.cursorY, 0, canvasHeight),
110996
- lastUpdated: typeof raw.lastUpdated === "string" ? raw.lastUpdated : (/* @__PURE__ */ new Date()).toISOString(),
110997
- selectedElementId: typeof raw.selectedElementId === "string" ? raw.selectedElementId.slice(0, 128) : void 0
110998
- };
112718
+ const tabs = [
112719
+ { id: "general", label: t2("pptx.settings.general") },
112720
+ { id: "shortcuts", label: t2("pptx.settings.keyboardShortcuts") }
112721
+ ];
112722
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
112723
+ /* @__PURE__ */ jsxRuntime.jsx(
112724
+ "button",
112725
+ {
112726
+ type: "button",
112727
+ className: "fixed inset-0 z-50 bg-black/60",
112728
+ "aria-label": t2("pptx.settings.closeSettings"),
112729
+ onClick: onClose
112730
+ }
112731
+ ),
112732
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-auto w-[min(32rem,calc(100%-2rem))] rounded-xl border border-border bg-popover backdrop-blur-xl shadow-2xl", children: [
112733
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-border/60", children: [
112734
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
112735
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuSettings, { className: "w-5 h-5 text-primary" }),
112736
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-foreground", children: t2("pptx.settings.title") })
112737
+ ] }),
112738
+ /* @__PURE__ */ jsxRuntime.jsx(
112739
+ "button",
112740
+ {
112741
+ type: "button",
112742
+ onClick: onClose,
112743
+ className: "p-1 rounded hover:bg-accent transition-colors",
112744
+ "aria-label": t2("pptx.settings.close"),
112745
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuX, { className: "w-4 h-4 text-muted-foreground" })
112746
+ }
112747
+ )
112748
+ ] }),
112749
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex border-b border-border/60 px-5", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
112750
+ "button",
112751
+ {
112752
+ type: "button",
112753
+ onClick: () => setActiveTab(tab.id),
112754
+ className: cn(
112755
+ "px-3 py-2 text-xs font-medium transition-colors relative",
112756
+ activeTab === tab.id ? "text-primary" : "text-muted-foreground hover:text-foreground"
112757
+ ),
112758
+ children: [
112759
+ tab.label,
112760
+ activeTab === tab.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-0 left-0 right-0 h-0.5 bg-primary rounded-full" })
112761
+ ]
112762
+ },
112763
+ tab.id
112764
+ )) }),
112765
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-5 py-4 max-h-[60vh] overflow-y-auto", children: [
112766
+ activeTab === "general" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
112767
+ /* @__PURE__ */ jsxRuntime.jsx(
112768
+ ToggleSwitch,
112769
+ {
112770
+ label: t2("pptx.settings.autoSave"),
112771
+ enabled: autoSave,
112772
+ onToggle: () => setAutoSave(!autoSave)
112773
+ }
112774
+ ),
112775
+ /* @__PURE__ */ jsxRuntime.jsx(
112776
+ ToggleSwitch,
112777
+ {
112778
+ label: t2("pptx.settings.spellCheck"),
112779
+ enabled: spellCheckEnabled,
112780
+ onToggle: () => onSetSpellCheckEnabled?.(!spellCheckEnabled)
112781
+ }
112782
+ ),
112783
+ /* @__PURE__ */ jsxRuntime.jsx(
112784
+ ToggleSwitch,
112785
+ {
112786
+ label: t2("pptx.settings.showGrid"),
112787
+ enabled: showGrid,
112788
+ onToggle: () => onSetShowGrid?.(!showGrid)
112789
+ }
112790
+ ),
112791
+ /* @__PURE__ */ jsxRuntime.jsx(
112792
+ ToggleSwitch,
112793
+ {
112794
+ label: t2("pptx.settings.showRulers"),
112795
+ enabled: showRulers,
112796
+ onToggle: () => onSetShowRulers?.(!showRulers)
112797
+ }
112798
+ ),
112799
+ /* @__PURE__ */ jsxRuntime.jsx(
112800
+ ToggleSwitch,
112801
+ {
112802
+ label: t2("pptx.settings.snapToGrid"),
112803
+ enabled: snapToGrid,
112804
+ onToggle: () => onSetSnapToGrid?.(!snapToGrid)
112805
+ }
112806
+ ),
112807
+ /* @__PURE__ */ jsxRuntime.jsx(
112808
+ ToggleSwitch,
112809
+ {
112810
+ label: t2("pptx.settings.reducedMotion"),
112811
+ enabled: reducedMotion,
112812
+ onToggle: () => onToggleReducedMotion?.()
112813
+ }
112814
+ )
112815
+ ] }),
112816
+ activeTab === "shortcuts" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0.5", children: SHORTCUT_REFERENCE_ITEMS.map((shortcut, i3) => /* @__PURE__ */ jsxRuntime.jsxs(
112817
+ "div",
112818
+ {
112819
+ className: cn(
112820
+ "flex items-center justify-between gap-3 rounded px-3 py-2",
112821
+ i3 % 2 === 0 ? "bg-muted/60" : ""
112822
+ ),
112823
+ children: [
112824
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-foreground", children: shortcut.action }),
112825
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px] text-muted-foreground whitespace-nowrap", children: shortcut.shortcut })
112826
+ ]
112827
+ },
112828
+ shortcut.action
112829
+ )) })
112830
+ ] })
112831
+ ] }) })
112832
+ ] });
110999
112833
  }
111000
- var BROADCAST_THROTTLE_MS = 50;
111001
- var STALE_PRESENCE_MS = 3e4;
111002
- function usePresenceTracking({
111003
- awareness,
111004
- localClientId,
111005
- userName,
111006
- userColor,
111007
- userAvatar,
111008
- canvasWidth,
111009
- canvasHeight
112834
+ function useYjsDocumentSync({
112835
+ doc: doc2,
112836
+ slides,
112837
+ setSlides,
112838
+ isConnected
111010
112839
  }) {
111011
- const [remoteUsers, setRemoteUsers] = React10.useState([]);
111012
- const lastBroadcastRef = React10.useRef(0);
111013
- const pendingBroadcastRef = React10.useRef(null);
111014
- const latestLocalState = React10.useRef({});
111015
- const broadcastPresence = React10.useCallback(
111016
- (update2) => {
111017
- if (!awareness) {
111018
- return;
112840
+ const isApplyingRemoteRef = React10.useRef(false);
112841
+ const lastSyncedRef = React10.useRef("");
112842
+ const hasInitializedRef = React10.useRef(false);
112843
+ const getDocMap = React10.useCallback(() => {
112844
+ if (!doc2 || typeof doc2 !== "object") {
112845
+ return null;
112846
+ }
112847
+ const d = doc2;
112848
+ if (typeof d.getMap !== "function") {
112849
+ return null;
112850
+ }
112851
+ return d.getMap("slides-data");
112852
+ }, [doc2]);
112853
+ React10.useEffect(() => {
112854
+ if (!isConnected || !doc2) {
112855
+ return;
112856
+ }
112857
+ if (isApplyingRemoteRef.current) {
112858
+ return;
112859
+ }
112860
+ if (slides.length === 0) {
112861
+ return;
112862
+ }
112863
+ const map3 = getDocMap();
112864
+ if (!map3) {
112865
+ return;
112866
+ }
112867
+ const serialized = JSON.stringify(slides);
112868
+ if (serialized === lastSyncedRef.current) {
112869
+ return;
112870
+ }
112871
+ lastSyncedRef.current = serialized;
112872
+ const d = doc2;
112873
+ d.transact(() => {
112874
+ const currentCount = map3.get("count");
112875
+ if (currentCount && currentCount > slides.length) {
112876
+ for (let i3 = slides.length; i3 < currentCount; i3++) {
112877
+ map3.delete(`slide-${i3}`);
112878
+ }
111019
112879
  }
111020
- Object.assign(latestLocalState.current, update2);
111021
- const now = Date.now();
111022
- const elapsed = now - lastBroadcastRef.current;
111023
- const flush = () => {
111024
- const state2 = {
111025
- ...latestLocalState.current,
111026
- userName,
111027
- userColor,
111028
- userAvatar,
111029
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
111030
- };
111031
- awareness.setLocalStateField("presence", state2);
111032
- lastBroadcastRef.current = Date.now();
111033
- };
111034
- if (elapsed >= BROADCAST_THROTTLE_MS) {
111035
- if (pendingBroadcastRef.current) {
111036
- clearTimeout(pendingBroadcastRef.current);
111037
- pendingBroadcastRef.current = null;
112880
+ map3.set("count", slides.length);
112881
+ for (let i3 = 0; i3 < slides.length; i3++) {
112882
+ const slideJson = JSON.stringify(slides[i3]);
112883
+ const existing = map3.get(`slide-${i3}`);
112884
+ if (existing !== slideJson) {
112885
+ map3.set(`slide-${i3}`, slideJson);
111038
112886
  }
111039
- flush();
111040
- } else if (!pendingBroadcastRef.current) {
111041
- pendingBroadcastRef.current = setTimeout(() => {
111042
- pendingBroadcastRef.current = null;
111043
- flush();
111044
- }, BROADCAST_THROTTLE_MS - elapsed);
111045
112887
  }
111046
- },
111047
- [awareness, userName, userColor, userAvatar]
111048
- );
112888
+ });
112889
+ }, [doc2, slides, isConnected, getDocMap]);
111049
112890
  React10.useEffect(() => {
111050
- if (!awareness || localClientId === null) {
112891
+ if (!isConnected || !doc2) {
111051
112892
  return;
111052
112893
  }
111053
- const handleChange = () => {
111054
- const now = Date.now();
111055
- const states = awareness.getStates();
111056
- const users = [];
111057
- states.forEach((state2, cid) => {
111058
- if (cid === localClientId) {
111059
- return;
111060
- }
111061
- const raw = state2?.presence;
111062
- if (!raw || typeof raw !== "object") {
111063
- return;
111064
- }
111065
- const sanitized = sanitizePresence({ ...raw, clientId: cid }, canvasWidth, canvasHeight);
111066
- if (!sanitized) {
111067
- return;
111068
- }
111069
- const updatedAt = new Date(sanitized.lastUpdated).getTime();
111070
- if (Number.isNaN(updatedAt) || now - updatedAt > STALE_PRESENCE_MS) {
111071
- return;
112894
+ const map3 = getDocMap();
112895
+ if (!map3) {
112896
+ return;
112897
+ }
112898
+ const handleUpdate = () => {
112899
+ const count = map3.get("count");
112900
+ if (!count || count === 0) {
112901
+ return;
112902
+ }
112903
+ const remoteSlides = [];
112904
+ for (let i3 = 0; i3 < count; i3++) {
112905
+ const slideJson = map3.get(`slide-${i3}`);
112906
+ if (slideJson) {
112907
+ try {
112908
+ remoteSlides.push(JSON.parse(slideJson));
112909
+ } catch {
112910
+ }
111072
112911
  }
111073
- users.push(sanitized);
111074
- });
111075
- setRemoteUsers(users);
111076
- };
111077
- awareness.on("change", handleChange);
111078
- handleChange();
111079
- return () => {
111080
- awareness.off("change", handleChange);
111081
- };
111082
- }, [awareness, localClientId, canvasWidth, canvasHeight]);
111083
- React10.useEffect(() => {
111084
- return () => {
111085
- if (pendingBroadcastRef.current) {
111086
- clearTimeout(pendingBroadcastRef.current);
111087
112912
  }
112913
+ if (remoteSlides.length === 0) {
112914
+ return;
112915
+ }
112916
+ const serialized = JSON.stringify(remoteSlides);
112917
+ if (serialized === lastSyncedRef.current) {
112918
+ return;
112919
+ }
112920
+ lastSyncedRef.current = serialized;
112921
+ isApplyingRemoteRef.current = true;
112922
+ setSlides(remoteSlides);
112923
+ requestAnimationFrame(() => {
112924
+ isApplyingRemoteRef.current = false;
112925
+ });
111088
112926
  };
111089
- }, []);
111090
- return { remoteUsers, broadcastPresence };
111091
- }
111092
- function useYjsProvider({ config }) {
111093
- const [status, setStatus] = React10.useState("disconnected");
111094
- const [awareness, setAwareness] = React10.useState(null);
111095
- const [doc2, setDoc] = React10.useState(null);
111096
- const [clientId, setClientId] = React10.useState(null);
111097
- const cleanupRef = React10.useRef(null);
111098
- const init = React10.useCallback(async () => {
111099
- const roomId = validateRoomId(config.roomId);
111100
- setStatus("connecting");
111101
- try {
111102
- const [Y, { WebsocketProvider: WebsocketProvider2 }] = await Promise.all([Promise.resolve().then(() => (init_yjs(), yjs_exports)), Promise.resolve().then(() => (init_y_websocket(), y_websocket_exports))]);
111103
- const yDoc = new Y.Doc();
111104
- const provider = new WebsocketProvider2(
111105
- config.serverUrl,
111106
- roomId,
111107
- yDoc,
111108
- // eslint-disable-line @typescript-eslint/no-explicit-any
111109
- {
111110
- params: config.authToken ? { token: config.authToken } : void 0
111111
- }
111112
- );
111113
- const handleStatus = (event) => {
111114
- if (event.status === "connected") {
111115
- setStatus("connected");
111116
- } else if (event.status === "disconnected") {
111117
- setStatus("disconnected");
111118
- }
111119
- };
111120
- provider.on("status", handleStatus);
111121
- if (provider.wsconnected) {
111122
- setStatus("connected");
112927
+ map3.observe(handleUpdate);
112928
+ if (!hasInitializedRef.current) {
112929
+ hasInitializedRef.current = true;
112930
+ const count = map3.get("count");
112931
+ if (count && count > 0) {
112932
+ handleUpdate();
111123
112933
  }
111124
- setDoc(yDoc);
111125
- setAwareness(provider.awareness);
111126
- setClientId(provider.awareness.clientID);
111127
- cleanupRef.current = () => {
111128
- provider.off("status", handleStatus);
111129
- provider.destroy();
111130
- yDoc.destroy();
111131
- setDoc(null);
111132
- setAwareness(null);
111133
- setClientId(null);
111134
- setStatus("disconnected");
111135
- };
111136
- } catch (err) {
111137
- console.warn(
111138
- "[pptx-viewer] Collaboration packages not available:",
111139
- err instanceof Error ? err.message : err
111140
- );
111141
- setStatus("error");
111142
112934
  }
111143
- }, [config.roomId, config.serverUrl, config.authToken]);
111144
- React10.useEffect(() => {
111145
- init();
111146
112935
  return () => {
111147
- cleanupRef.current?.();
111148
- cleanupRef.current = null;
112936
+ map3.unobserve(handleUpdate);
111149
112937
  };
111150
- }, [init]);
111151
- return { status, awareness, doc: doc2, clientId };
111152
- }
111153
-
111154
- // src/viewer/hooks/collaboration/useCollaborativeState.ts
111155
- function useCollaborativeState({
111156
- config,
111157
- canvasWidth,
111158
- canvasHeight
111159
- }) {
111160
- const userColor = sanitizeColor(config.userColor, "#6366f1");
111161
- const { status, awareness, clientId } = useYjsProvider({ config });
111162
- const { remoteUsers, broadcastPresence } = usePresenceTracking({
111163
- awareness,
111164
- localClientId: clientId,
111165
- userName: config.userName,
111166
- userColor,
111167
- userAvatar: config.userAvatar,
111168
- canvasWidth,
111169
- canvasHeight
111170
- });
111171
- const connectedCount = status === "connected" ? remoteUsers.length + 1 : remoteUsers.length;
111172
- return {
111173
- status,
111174
- remoteUsers,
111175
- broadcastPresence,
111176
- connectedCount,
111177
- config
111178
- };
111179
- }
111180
- var CollaborationContext = React10.createContext(null);
111181
- function useCollaboration() {
111182
- return React10.useContext(CollaborationContext);
111183
- }
111184
- function CollaborationProvider({
111185
- config,
111186
- canvasWidth,
111187
- canvasHeight,
111188
- children
111189
- }) {
111190
- const value = useCollaborativeState({
111191
- config,
111192
- canvasWidth,
111193
- canvasHeight
111194
- });
111195
- return /* @__PURE__ */ jsxRuntime.jsx(CollaborationContext.Provider, { value, children });
111196
- }
111197
- function getInitials(name) {
111198
- const parts = name.trim().split(/\s+/);
111199
- if (parts.length >= 2) {
111200
- return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
111201
- }
111202
- return name.slice(0, 2).toUpperCase();
111203
- }
111204
- function AvatarCircle({
111205
- name,
111206
- color,
111207
- avatar,
111208
- isLocal
111209
- }) {
111210
- const initials = getInitials(name);
111211
- const title = isLocal ? `${name} (you)` : name;
111212
- return /* @__PURE__ */ jsxRuntime.jsx(
111213
- "div",
111214
- {
111215
- className: "relative w-7 h-7 rounded-full flex items-center justify-center text-[10px] font-semibold text-white border-2 -ml-1 first:ml-0",
111216
- style: {
111217
- backgroundColor: color,
111218
- borderColor: isLocal ? "#fff" : color
111219
- },
111220
- title,
111221
- "aria-label": title,
111222
- children: avatar ? /* @__PURE__ */ jsxRuntime.jsx(
111223
- "img",
111224
- {
111225
- src: avatar,
111226
- alt: "",
111227
- className: "w-full h-full rounded-full object-cover",
111228
- onError: (e2) => {
111229
- e2.target.style.display = "none";
111230
- }
111231
- }
111232
- ) : initials
111233
- }
111234
- );
111235
- }
111236
- function UserAvatarBar({
111237
- remoteUsers,
111238
- localUserName,
111239
- localUserColor,
111240
- localUserAvatar,
111241
- status,
111242
- maxVisible = 5
111243
- }) {
111244
- if (status === "disconnected" || status === "error") {
111245
- return null;
111246
- }
111247
- const allUsers = [
111248
- { name: localUserName, color: localUserColor, avatar: localUserAvatar, isLocal: true },
111249
- ...remoteUsers.map((u2) => ({
111250
- name: u2.userName,
111251
- color: u2.userColor,
111252
- avatar: u2.userAvatar,
111253
- isLocal: false
111254
- }))
111255
- ];
111256
- const visible = allUsers.slice(0, maxVisible);
111257
- const overflow = allUsers.length - maxVisible;
111258
- return /* @__PURE__ */ jsxRuntime.jsxs(
111259
- "div",
111260
- {
111261
- "data-testid": "user-avatar-bar",
111262
- className: "flex items-center px-2",
111263
- "aria-label": `${allUsers.length} user${allUsers.length !== 1 ? "s" : ""} connected`,
111264
- children: [
111265
- visible.map((user, i3) => /* @__PURE__ */ jsxRuntime.jsx(
111266
- AvatarCircle,
111267
- {
111268
- name: user.name,
111269
- color: user.color,
111270
- avatar: user.avatar,
111271
- isLocal: user.isLocal
111272
- },
111273
- user.isLocal ? "local" : `remote-${i3}`
111274
- )),
111275
- overflow > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
111276
- "div",
111277
- {
111278
- className: "w-7 h-7 rounded-full flex items-center justify-center text-[10px] font-semibold text-gray-300 bg-gray-700 border-2 border-gray-600 -ml-1",
111279
- title: `${overflow} more user${overflow !== 1 ? "s" : ""}`,
111280
- children: [
111281
- "+",
111282
- overflow
111283
- ]
111284
- }
111285
- )
111286
- ]
111287
- }
111288
- );
111289
- }
111290
- var STATUS_STYLES = {
111291
- connected: {
111292
- dot: "bg-green-400",
111293
- text: "text-green-400",
111294
- label: "Connected"
111295
- },
111296
- connecting: {
111297
- dot: "bg-yellow-400 animate-pulse",
111298
- text: "text-yellow-400",
111299
- label: "Connecting..."
111300
- },
111301
- disconnected: {
111302
- dot: "bg-gray-500",
111303
- text: "text-gray-500",
111304
- label: "Disconnected"
111305
- },
111306
- error: {
111307
- dot: "bg-red-400",
111308
- text: "text-red-400",
111309
- label: "Connection error"
111310
- }
111311
- };
111312
- function CollaborationStatusIndicator({
111313
- status,
111314
- connectedCount
111315
- }) {
111316
- const style = STATUS_STYLES[status];
111317
- return /* @__PURE__ */ jsxRuntime.jsxs(
111318
- "div",
111319
- {
111320
- "data-testid": "collaboration-status",
111321
- className: "flex items-center gap-1.5",
111322
- "aria-label": `Collaboration: ${style.label}. ${connectedCount} user${connectedCount !== 1 ? "s" : ""} connected.`,
111323
- children: [
111324
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-block w-2 h-2 rounded-full ${style.dot}`, "aria-hidden": "true" }),
111325
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-[10px] ${style.text}`, children: status === "connected" ? `${connectedCount} user${connectedCount !== 1 ? "s" : ""}` : style.label })
111326
- ]
111327
- }
111328
- );
112938
+ }, [doc2, isConnected, getDocMap, setSlides]);
111329
112939
  }
111330
112940
  function computeGridSpacingPx(presentationGridSpacing) {
111331
112941
  if (presentationGridSpacing) {
@@ -121000,7 +122610,7 @@ function useViewerUIState() {
121000
122610
  const [isCompactToolbarOpen, setIsCompactToolbarOpen] = React10.useState(false);
121001
122611
  const [toolbarSection, setToolbarSection] = React10.useState("home");
121002
122612
  const [isSlidesPaneOpen, setIsSlidesPaneOpen] = React10.useState(true);
121003
- const [isInspectorPaneOpen, setIsInspectorPaneOpen] = React10.useState(true);
122613
+ const [isInspectorPaneOpen, setIsInspectorPaneOpen] = React10.useState(false);
121004
122614
  const [isSlideNotesCollapsed, setIsSlideNotesCollapsed] = React10.useState(true);
121005
122615
  const [isOverflowMenuOpen, setIsOverflowMenuOpen] = React10.useState(false);
121006
122616
  const [isSidebarCollapsed, setIsSidebarCollapsed] = React10.useState(false);
@@ -121283,13 +122893,18 @@ var PowerPointViewer = React10.forwardRef(
121283
122893
  onDirtyChange,
121284
122894
  onActiveSlideChange,
121285
122895
  theme,
121286
- collaboration
122896
+ collaboration,
122897
+ onStartCollaboration,
122898
+ onStopCollaboration,
122899
+ shareDefaults
121287
122900
  } = props;
121288
122901
  const themeStyle = useThemeStyle(theme);
121289
122902
  const [content, setContent] = React10.useState(incomingContent);
121290
122903
  React10.useEffect(() => {
121291
122904
  setContent(incomingContent);
121292
122905
  }, [incomingContent]);
122906
+ const [isSettingsOpen, setIsSettingsOpen] = React10.useState(false);
122907
+ const [isShareDialogOpen, setIsShareDialogOpen] = React10.useState(false);
121293
122908
  const { reducedMotion, toggleReducedMotion } = useReducedMotion();
121294
122909
  const state2 = useViewerState();
121295
122910
  const {
@@ -121490,36 +123105,35 @@ var PowerPointViewer = React10.forwardRef(
121490
123105
  ref: containerRef,
121491
123106
  tabIndex: 0,
121492
123107
  style: themeStyle,
123108
+ "data-pptx-viewer": "",
121493
123109
  className: "h-full w-full bg-background text-foreground flex flex-col relative overflow-hidden outline-none",
121494
123110
  children: [
121495
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-b from-purple-500/3 to-transparent z-0" }),
121496
- mode !== "present" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
121497
- /* @__PURE__ */ jsxRuntime.jsx(
121498
- ViewerToolbarSection,
121499
- {
121500
- mode,
121501
- canEdit,
121502
- state: state2,
121503
- selectedElement,
121504
- activeSlide,
121505
- zoom,
121506
- history,
121507
- findReplace: editorOps.findReplace,
121508
- manipulation: editorOps.manipulation,
121509
- insertHandlers: editorOps.insertHandlers,
121510
- exportHandlers,
121511
- printHandlers,
121512
- propertyHandlers,
121513
- dialogs,
121514
- slideOps: editorOps.slideOps,
121515
- ops: editorOps.ops,
121516
- onSetMode: handleSetMode,
121517
- onEnterPresenterView: handleEnterPresenterView,
121518
- onEnterRehearsalMode: handleEnterRehearsalMode
121519
- }
121520
- ),
121521
- collaboration && /* @__PURE__ */ jsxRuntime.jsx(CollaborationToolbarStrip, { collaboration })
121522
- ] }),
123111
+ mode !== "present" && /* @__PURE__ */ jsxRuntime.jsx(
123112
+ ViewerToolbarSection,
123113
+ {
123114
+ mode,
123115
+ canEdit,
123116
+ state: state2,
123117
+ selectedElement,
123118
+ activeSlide,
123119
+ zoom,
123120
+ history,
123121
+ findReplace: editorOps.findReplace,
123122
+ manipulation: editorOps.manipulation,
123123
+ insertHandlers: editorOps.insertHandlers,
123124
+ exportHandlers,
123125
+ printHandlers,
123126
+ propertyHandlers,
123127
+ dialogs,
123128
+ slideOps: editorOps.slideOps,
123129
+ ops: editorOps.ops,
123130
+ onSetMode: handleSetMode,
123131
+ onEnterPresenterView: handleEnterPresenterView,
123132
+ onEnterRehearsalMode: handleEnterRehearsalMode,
123133
+ onOpenSettings: () => setIsSettingsOpen(true),
123134
+ onOpenShareDialog: () => setIsShareDialogOpen(true)
123135
+ }
123136
+ ),
121523
123137
  /* @__PURE__ */ jsxRuntime.jsx(
121524
123138
  ViewerMainContent,
121525
123139
  {
@@ -121569,7 +123183,14 @@ var PowerPointViewer = React10.forwardRef(
121569
123183
  onUpdateNotes: propertyHandlers.handleUpdateNotes,
121570
123184
  collaborationSlot: collaboration ? /* @__PURE__ */ jsxRuntime.jsx(CollaborationStatusStrip, {}) : void 0,
121571
123185
  notesPanelHeight: isMobile ? void 0 : resizablePanels.bottomHeight,
121572
- onResizeBottom: isMobile ? void 0 : resizablePanels.onResizeBottom
123186
+ onResizeBottom: isMobile ? void 0 : resizablePanels.onResizeBottom,
123187
+ scale: zoom.scale,
123188
+ onZoomIn: zoom.handleZoomIn,
123189
+ onZoomOut: zoom.handleZoomOut,
123190
+ onZoomToFit: zoom.handleZoomToFit,
123191
+ mode,
123192
+ onSetMode: handleSetMode,
123193
+ onToggleSlideSorter: () => state2.setShowSlideSorter((p3) => !p3)
121573
123194
  }
121574
123195
  ),
121575
123196
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -121601,6 +123222,37 @@ var PowerPointViewer = React10.forwardRef(
121601
123222
  onDiscardAnnotations: handleDiscardAnnotations
121602
123223
  }
121603
123224
  ),
123225
+ /* @__PURE__ */ jsxRuntime.jsx(
123226
+ SettingsDialog,
123227
+ {
123228
+ isOpen: isSettingsOpen,
123229
+ onClose: () => setIsSettingsOpen(false),
123230
+ spellCheckEnabled: state2.spellCheckEnabled,
123231
+ onSetSpellCheckEnabled: state2.setSpellCheckEnabled,
123232
+ showGrid: state2.showGrid,
123233
+ onSetShowGrid: state2.setShowGrid,
123234
+ showRulers: state2.showRulers,
123235
+ onSetShowRulers: state2.setShowRulers,
123236
+ snapToGrid: state2.snapToGrid,
123237
+ onSetSnapToGrid: state2.setSnapToGrid,
123238
+ reducedMotion,
123239
+ onToggleReducedMotion: toggleReducedMotion
123240
+ }
123241
+ ),
123242
+ /* @__PURE__ */ jsxRuntime.jsx(
123243
+ ShareDialog,
123244
+ {
123245
+ open: isShareDialogOpen,
123246
+ onClose: () => setIsShareDialogOpen(false),
123247
+ activeCollaboration: collaboration,
123248
+ onStartCollaboration,
123249
+ onStopCollaboration,
123250
+ preconfigured: Boolean(collaboration),
123251
+ defaultRoomId: shareDefaults?.roomId,
123252
+ defaultUserName: shareDefaults?.userName,
123253
+ defaultServerUrl: shareDefaults?.serverUrl
123254
+ }
123255
+ ),
121604
123256
  /* @__PURE__ */ jsxRuntime.jsx(
121605
123257
  ViewerOverlays,
121606
123258
  {
@@ -121642,36 +123294,21 @@ var PowerPointViewer = React10.forwardRef(
121642
123294
  ]
121643
123295
  }
121644
123296
  );
121645
- return /* @__PURE__ */ jsxRuntime.jsx(ViewerThemeProvider, { theme, children: collaboration ? /* @__PURE__ */ jsxRuntime.jsx(
123297
+ return /* @__PURE__ */ jsxRuntime.jsx(ViewerThemeProvider, { theme, children: collaboration ? /* @__PURE__ */ jsxRuntime.jsxs(
121646
123298
  CollaborationProvider,
121647
123299
  {
121648
123300
  config: collaboration,
121649
123301
  canvasWidth: canvasSize.width,
121650
123302
  canvasHeight: canvasSize.height,
121651
- children: viewerContent
123303
+ children: [
123304
+ /* @__PURE__ */ jsxRuntime.jsx(CollaborationDocumentSync, { slides, setSlides: state2.setSlides }),
123305
+ viewerContent
123306
+ ]
121652
123307
  }
121653
123308
  ) : viewerContent });
121654
123309
  }
121655
123310
  );
121656
123311
  PowerPointViewer.displayName = "PowerPointViewer";
121657
- function CollaborationToolbarStrip({
121658
- collaboration
121659
- }) {
121660
- const collab = useCollaboration();
121661
- if (!collab) {
121662
- return null;
121663
- }
121664
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end px-2 py-0.5 border-b border-border bg-background/30 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(
121665
- UserAvatarBar,
121666
- {
121667
- remoteUsers: collab.remoteUsers,
121668
- localUserName: collaboration.userName,
121669
- localUserColor: collab.config.userColor ?? "#6366f1",
121670
- localUserAvatar: collaboration.userAvatar,
121671
- status: collab.status
121672
- }
121673
- ) });
121674
- }
121675
123312
  function CollaborationStatusStrip() {
121676
123313
  const collab = useCollaboration();
121677
123314
  if (!collab) {
@@ -121679,6 +123316,19 @@ function CollaborationStatusStrip() {
121679
123316
  }
121680
123317
  return /* @__PURE__ */ jsxRuntime.jsx(CollaborationStatusIndicator, { status: collab.status, connectedCount: collab.connectedCount });
121681
123318
  }
123319
+ function CollaborationDocumentSync({
123320
+ slides,
123321
+ setSlides
123322
+ }) {
123323
+ const collab = useCollaboration();
123324
+ useYjsDocumentSync({
123325
+ doc: collab?.doc ?? null,
123326
+ slides,
123327
+ setSlides,
123328
+ isConnected: collab?.status === "connected"
123329
+ });
123330
+ return null;
123331
+ }
121682
123332
  /*! Bundled license information:
121683
123333
 
121684
123334
  three/build/three.core.js: