pptx-react-viewer 3.2.0 → 3.4.0

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 (166) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/{AiChatPanel-TKBVSYD2.js → AiChatPanel-3Q4STA5E.js} +29 -28
  3. package/dist/AiChatPanel-3Q4STA5E.js.br +0 -0
  4. package/dist/AiChatPanel-3Q4STA5E.js.gz +0 -0
  5. package/dist/{AiChatPanel-C3E7NLX3.mjs → AiChatPanel-NQZBZIRM.mjs} +9 -8
  6. package/dist/AiChatPanel-NQZBZIRM.mjs.br +0 -0
  7. package/dist/AiChatPanel-NQZBZIRM.mjs.gz +0 -0
  8. package/dist/{Area3DChartScene-7YCFWEGX.mjs → Area3DChartScene-7PARQDOZ.mjs} +1 -1
  9. package/dist/Area3DChartScene-7PARQDOZ.mjs.br +3 -0
  10. package/dist/Area3DChartScene-7PARQDOZ.mjs.gz +0 -0
  11. package/dist/{Area3DChartScene-3A3CXKN3.js → Area3DChartScene-THLG74CM.js} +2 -2
  12. package/dist/Area3DChartScene-THLG74CM.js.br +0 -0
  13. package/dist/Area3DChartScene-THLG74CM.js.gz +0 -0
  14. package/dist/{Bar3DChartScene-BDUENAWJ.js → Bar3DChartScene-OLZ2V56I.js} +2 -2
  15. package/dist/Bar3DChartScene-OLZ2V56I.js.br +2 -0
  16. package/dist/Bar3DChartScene-OLZ2V56I.js.gz +0 -0
  17. package/dist/{Bar3DChartScene-7CNSJ35X.mjs → Bar3DChartScene-QUU6RUXM.mjs} +1 -1
  18. package/dist/Bar3DChartScene-QUU6RUXM.mjs.br +0 -0
  19. package/dist/Bar3DChartScene-QUU6RUXM.mjs.gz +0 -0
  20. package/dist/{Line3DChartScene-R352CZ5U.js → Line3DChartScene-2VZS52IB.js} +2 -2
  21. package/dist/Line3DChartScene-2VZS52IB.js.br +0 -0
  22. package/dist/Line3DChartScene-2VZS52IB.js.gz +0 -0
  23. package/dist/{Line3DChartScene-SMTHK6MD.mjs → Line3DChartScene-D77ORA4K.mjs} +1 -1
  24. package/dist/Line3DChartScene-D77ORA4K.mjs.br +3 -0
  25. package/dist/Line3DChartScene-D77ORA4K.mjs.gz +0 -0
  26. package/dist/{Model3DScene-Y4SGFQYW.js → Model3DScene-3WCALTXO.js} +2 -2
  27. package/dist/Model3DScene-3WCALTXO.js.br +0 -0
  28. package/dist/Model3DScene-3WCALTXO.js.gz +0 -0
  29. package/dist/{Model3DScene-AUVJXXPV.mjs → Model3DScene-ZEGWNILH.mjs} +1 -1
  30. package/dist/Model3DScene-ZEGWNILH.mjs.br +0 -0
  31. package/dist/Model3DScene-ZEGWNILH.mjs.gz +0 -0
  32. package/dist/{PieChart3DScene-TK2VT4RS.js → PieChart3DScene-7C3ACGKN.js} +2 -2
  33. package/dist/PieChart3DScene-7C3ACGKN.js.br +0 -0
  34. package/dist/PieChart3DScene-7C3ACGKN.js.gz +0 -0
  35. package/dist/{PieChart3DScene-UD7GRH55.mjs → PieChart3DScene-DGD4PZVV.mjs} +1 -1
  36. package/dist/PieChart3DScene-DGD4PZVV.mjs.br +0 -0
  37. package/dist/PieChart3DScene-DGD4PZVV.mjs.gz +0 -0
  38. package/dist/PowerPointViewer-Cl6jBPNw.d.ts.map +1 -1
  39. package/dist/{SurfaceChart3DScene-TXY6UZSW.mjs → SurfaceChart3DScene-QGH3OLJV.mjs} +1 -1
  40. package/dist/SurfaceChart3DScene-QGH3OLJV.mjs.br +0 -0
  41. package/dist/SurfaceChart3DScene-QGH3OLJV.mjs.gz +0 -0
  42. package/dist/{SurfaceChart3DScene-ERFXZW2O.js → SurfaceChart3DScene-SJPQGGGE.js} +2 -2
  43. package/dist/SurfaceChart3DScene-SJPQGGGE.js.br +0 -0
  44. package/dist/SurfaceChart3DScene-SJPQGGGE.js.gz +0 -0
  45. package/dist/{chunk-RRAL4Y7V.js → chunk-7E6SM42A.js} +2604 -2024
  46. package/dist/chunk-7E6SM42A.js.br +0 -0
  47. package/dist/chunk-7E6SM42A.js.gz +0 -0
  48. package/dist/{chunk-G7UBIKAG.mjs → chunk-B7WAGQW4.mjs} +5383 -873
  49. package/dist/chunk-B7WAGQW4.mjs.br +0 -0
  50. package/dist/chunk-B7WAGQW4.mjs.gz +0 -0
  51. package/dist/{chunk-RX6FE24O.mjs → chunk-C4WTQIC6.mjs} +302 -61
  52. package/dist/chunk-C4WTQIC6.mjs.br +0 -0
  53. package/dist/chunk-C4WTQIC6.mjs.gz +0 -0
  54. package/dist/{chunk-Q52HT4EZ.js → chunk-KY6JQJXK.js} +307 -63
  55. package/dist/chunk-KY6JQJXK.js.br +0 -0
  56. package/dist/chunk-KY6JQJXK.js.gz +0 -0
  57. package/dist/{chunk-PPNXPEEZ.mjs → chunk-L5MRKDRM.mjs} +1514 -934
  58. package/dist/chunk-L5MRKDRM.mjs.br +0 -0
  59. package/dist/chunk-L5MRKDRM.mjs.gz +0 -0
  60. package/dist/{chunk-UHR7EYFM.mjs → chunk-MSUUPLQ7.mjs} +718 -435
  61. package/dist/chunk-MSUUPLQ7.mjs.br +0 -0
  62. package/dist/chunk-MSUUPLQ7.mjs.gz +0 -0
  63. package/dist/{chunk-FJDZ7AEE.js → chunk-PCW2KT7Z.js} +5446 -879
  64. package/dist/chunk-PCW2KT7Z.js.br +0 -0
  65. package/dist/chunk-PCW2KT7Z.js.gz +0 -0
  66. package/dist/{chunk-FMEEIS6L.mjs → chunk-SFMATKNJ.mjs} +13 -114
  67. package/dist/chunk-SFMATKNJ.mjs.br +0 -0
  68. package/dist/chunk-SFMATKNJ.mjs.gz +0 -0
  69. package/dist/{chunk-ARQGUUYU.js → chunk-TJNG5VEK.js} +1114 -826
  70. package/dist/chunk-TJNG5VEK.js.br +0 -0
  71. package/dist/chunk-TJNG5VEK.js.gz +0 -0
  72. package/dist/{chunk-ALO4ZOHQ.js → chunk-UNIJUD5C.js} +45 -1
  73. package/dist/chunk-UNIJUD5C.js.br +0 -0
  74. package/dist/chunk-UNIJUD5C.js.gz +0 -0
  75. package/dist/{chunk-B6UIVRFA.js → chunk-XBTHWXNO.js} +177 -279
  76. package/dist/chunk-XBTHWXNO.js.br +0 -0
  77. package/dist/chunk-XBTHWXNO.js.gz +0 -0
  78. package/dist/{chunk-RHSA5BW5.mjs → chunk-XWUMM2TX.mjs} +45 -1
  79. package/dist/chunk-XWUMM2TX.mjs.br +0 -0
  80. package/dist/chunk-XWUMM2TX.mjs.gz +0 -0
  81. package/dist/i18n.js +4 -4
  82. package/dist/i18n.js.br +0 -0
  83. package/dist/i18n.js.gz +0 -0
  84. package/dist/i18n.mjs +1 -1
  85. package/dist/i18n.mjs.br +0 -0
  86. package/dist/i18n.mjs.gz +0 -0
  87. package/dist/index.d.ts +7 -0
  88. package/dist/index.js +47 -47
  89. package/dist/index.js.br +0 -0
  90. package/dist/index.js.gz +0 -0
  91. package/dist/index.mjs +6 -6
  92. package/dist/index.mjs.br +0 -0
  93. package/dist/index.mjs.gz +0 -0
  94. package/dist/internals.d.ts +176 -5
  95. package/dist/internals.d.ts.map +1 -1
  96. package/dist/internals.js +79 -79
  97. package/dist/internals.js.br +0 -0
  98. package/dist/internals.js.gz +0 -0
  99. package/dist/internals.mjs +4 -4
  100. package/dist/internals.mjs.br +0 -0
  101. package/dist/internals.mjs.gz +0 -0
  102. package/dist/pptx-viewer.css +1 -1
  103. package/dist/pptx-viewer.css.br +0 -0
  104. package/dist/pptx-viewer.css.gz +0 -0
  105. package/dist/{useViewerBuildingBlocks-X1L8BjsQ.d.ts → useViewerBuildingBlocks-f6oNGKMX.d.ts} +8 -1
  106. package/dist/useViewerBuildingBlocks-f6oNGKMX.d.ts.map +1 -0
  107. package/dist/viewer/index.js +27 -27
  108. package/dist/viewer/index.js.br +0 -0
  109. package/dist/viewer/index.js.gz +0 -0
  110. package/dist/viewer/index.mjs +6 -6
  111. package/dist/viewer/index.mjs.br +0 -0
  112. package/dist/viewer/index.mjs.gz +0 -0
  113. package/package.json +1 -1
  114. package/dist/AiChatPanel-C3E7NLX3.mjs.br +0 -0
  115. package/dist/AiChatPanel-C3E7NLX3.mjs.gz +0 -0
  116. package/dist/AiChatPanel-TKBVSYD2.js.br +0 -0
  117. package/dist/AiChatPanel-TKBVSYD2.js.gz +0 -0
  118. package/dist/Area3DChartScene-3A3CXKN3.js.br +0 -0
  119. package/dist/Area3DChartScene-3A3CXKN3.js.gz +0 -0
  120. package/dist/Area3DChartScene-7YCFWEGX.mjs.br +0 -3
  121. package/dist/Area3DChartScene-7YCFWEGX.mjs.gz +0 -0
  122. package/dist/Bar3DChartScene-7CNSJ35X.mjs.br +0 -0
  123. package/dist/Bar3DChartScene-7CNSJ35X.mjs.gz +0 -0
  124. package/dist/Bar3DChartScene-BDUENAWJ.js.br +0 -0
  125. package/dist/Bar3DChartScene-BDUENAWJ.js.gz +0 -0
  126. package/dist/Line3DChartScene-R352CZ5U.js.br +0 -0
  127. package/dist/Line3DChartScene-R352CZ5U.js.gz +0 -0
  128. package/dist/Line3DChartScene-SMTHK6MD.mjs.br +0 -3
  129. package/dist/Line3DChartScene-SMTHK6MD.mjs.gz +0 -0
  130. package/dist/Model3DScene-AUVJXXPV.mjs.br +0 -0
  131. package/dist/Model3DScene-AUVJXXPV.mjs.gz +0 -0
  132. package/dist/Model3DScene-Y4SGFQYW.js.br +0 -0
  133. package/dist/Model3DScene-Y4SGFQYW.js.gz +0 -0
  134. package/dist/PieChart3DScene-TK2VT4RS.js.br +0 -0
  135. package/dist/PieChart3DScene-TK2VT4RS.js.gz +0 -0
  136. package/dist/PieChart3DScene-UD7GRH55.mjs.br +0 -0
  137. package/dist/PieChart3DScene-UD7GRH55.mjs.gz +0 -0
  138. package/dist/SurfaceChart3DScene-ERFXZW2O.js.br +0 -0
  139. package/dist/SurfaceChart3DScene-ERFXZW2O.js.gz +0 -0
  140. package/dist/SurfaceChart3DScene-TXY6UZSW.mjs.br +0 -0
  141. package/dist/SurfaceChart3DScene-TXY6UZSW.mjs.gz +0 -0
  142. package/dist/chunk-ALO4ZOHQ.js.br +0 -0
  143. package/dist/chunk-ALO4ZOHQ.js.gz +0 -0
  144. package/dist/chunk-ARQGUUYU.js.br +0 -0
  145. package/dist/chunk-ARQGUUYU.js.gz +0 -0
  146. package/dist/chunk-B6UIVRFA.js.br +0 -0
  147. package/dist/chunk-B6UIVRFA.js.gz +0 -0
  148. package/dist/chunk-FJDZ7AEE.js.br +0 -0
  149. package/dist/chunk-FJDZ7AEE.js.gz +0 -0
  150. package/dist/chunk-FMEEIS6L.mjs.br +0 -0
  151. package/dist/chunk-FMEEIS6L.mjs.gz +0 -0
  152. package/dist/chunk-G7UBIKAG.mjs.br +0 -0
  153. package/dist/chunk-G7UBIKAG.mjs.gz +0 -0
  154. package/dist/chunk-PPNXPEEZ.mjs.br +0 -0
  155. package/dist/chunk-PPNXPEEZ.mjs.gz +0 -0
  156. package/dist/chunk-Q52HT4EZ.js.br +0 -0
  157. package/dist/chunk-Q52HT4EZ.js.gz +0 -0
  158. package/dist/chunk-RHSA5BW5.mjs.br +0 -0
  159. package/dist/chunk-RHSA5BW5.mjs.gz +0 -0
  160. package/dist/chunk-RRAL4Y7V.js.br +0 -0
  161. package/dist/chunk-RRAL4Y7V.js.gz +0 -0
  162. package/dist/chunk-RX6FE24O.mjs.br +0 -0
  163. package/dist/chunk-RX6FE24O.mjs.gz +0 -0
  164. package/dist/chunk-UHR7EYFM.mjs.br +0 -0
  165. package/dist/chunk-UHR7EYFM.mjs.gz +0 -0
  166. package/dist/useViewerBuildingBlocks-X1L8BjsQ.d.ts.map +0 -1
@@ -1,6 +1,6 @@
1
- import { cloneHistorySnapshot, MIN_ELEMENT_SIZE, ZOOM_TO_SELECTION_PADDING, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, UNGROUPED_SECTION_ID, DEFAULT_SECTION_GROUP_ID, DEFAULT_TABLE_ROWS, DEFAULT_TABLE_COLUMNS, executeMediaCommand, buildPdfFromImageData, buildNotesPdf, GRID_SIZE, DEFAULT_TEXT_FONT_SIZE } from './chunk-FMEEIS6L.mjs';
2
- import { DEFAULT_VIEWER_OPTIONS, TOOLBAR_TABS, computeVirtualRange, DEFAULT_VIRTUAL_OVERSCAN, createPresenceProjector, BROADCAST_THROTTLE_MS, createSyncGate, clearLocalAwareness, validateRoomId, createDepartureChannel, resolveTransportForServerUrl, isMixedContentBlocked, CONNECTION_TIMEOUT_MS, registerCollaborationTeardown, sanitizeColor, cloneTemplateElementsBySlideId, cloneSlide, createWheelStepBuffer, createViewerZoomStore, clampZoomScale, mapEditingWheel, PresentationAnimationController, PRESENTATION_HASH, PRESENTATION_NONCE_KEY, PRESENTATION_CHANNEL_NAME, swapPresentationWindows, PRESENTATION_MESSAGE_ORIGIN, clearAudienceContent, buildPresentationAudienceUrl, resolveAudienceScreenPlacement, placeAudienceWindow, storeAudienceContent, isPresentationSessionMessage, endAudienceDisplay, createPresentationKeyBuffer, acceptsPresentationInput, resolveAutoAdvanceDelayMs, hasShowSlideAfter, nextShowSlideIndex, previousShowSlideIndex, createPresenterShowGuard, attachPresentationVisibilityPause, buildMorphTransitionPlan, morphOptionToMode, buildMorphAnimationRules, findInSlides, applyFindReplacements, createCollaborationLivePatcher, publishLiveInlineText, PptxHandler, collectMediaElements, collectImagePaths, collectTableCellImagePaths, applyTableCellImagePatches, collectTableStyleImagePaths, applyTableStyleImagePatches, partitionTemplateElements, resolveAuthoredCustomShowId, buildInitialGuides, EncryptedFileError, resolveAutosaveIntervalMs, shouldWriteAutosaveSnapshot, saveAutosaveSnapshot, autosaveSnapshotMark, updateMasterViewElement, isTemplateElementId, hasShapeProperties, hasTextProperties, getInlineEditorSelection, applyStyleToSelectedSegments, setPendingSelectionRestore, applyCaseTransformToSegments, transformTextCase, masterViewElements, replaceMasterViewElements, addSection, renameSection, deleteSection, moveSectionUp, moveSectionDown, moveSlidesToSection, normalizeColorsForCapture, preprocessCssForCapture, slideProgressPercent, slideStatusLabel, EXPORT_ASSEMBLING_PERCENT, EXPORT_DONE_PERCENT, isExportAbortError, recordProgressPercent, downloadBlob, resolveImageResolutionScale, applyThemeToData, THEME_PRESETS, themeColorSchemesEqual, comparePresentation, applyAcceptSlide, applyAcceptAllSlides, collectUsedFonts, describeFontEmbedding, isMobileViewport, mayLeaveSlideShow, stopAllPersistentAudio, remapTextToSegments, guidePxToEmu, buildSaveSlides, embeddedFontSaveOptions, resolveSlideSizeSelection, saveDeckWithPassword, shouldProbeAutosaveRecovery, probeAutosaveRecovery, acceptAutosaveRecovery, discardAutosaveRecovery, shouldLoopContinuously, applyRehearsalTimings, copyFormatFromElement, applyFormatToElement, collectReferencedFontFamilies, resolveGoogleWebfontHref, mapEditorKey, isEditorTextInputTarget, createViewerOptionsStore, readSlidesFromYDoc, shouldRoomSlidesReplaceLoad, reconcileSlidesInYDoc, LOCAL_SYNC_ORIGIN, observeYDocSlides, shouldAutoFollowBroadcaster, detectTouchDevice, detectOrientation, deriveViewportBreakpoints, createTouchGestureRecognizer, MAX_ZOOM_SCALE, MIN_ZOOM_SCALE, isKeyboardOpen, scopeLayoutOptionsToSlide, openPptxFile, readBackstageRecentFile, isActionHidden, resolveInlineEditAutoFitHeight, canInteractWithElement, filterInteractableIds, beginShapeAdjustment, buildElementClipboardPayload, savedPresentationFileName, exportDeckJson, groupElements, generateElementId, makeCloneId, ungroupElements, alignElements, distributeElements, bringForward, sendBackward, bringToFront, sendToBack, newTableElement, createDefaultChartElement, readViewportMetrics, computeKeyboardInset, mergeShapes, mapPresentationKey, lastShowSlideIndex, firstShowSlideIndex, mapPresentationWheel, openPendingPrintWindow, computeColorFilter, filterHiddenSlideIndices, computeSlideIndices, buildPrintDocument, finishPrintWindow, buildPrintHtmlDocument, buildOutlineHtml, buildSlidesHtml, buildNotesHtml, buildHandoutsHtml, createBlankSlide, makeSlideId, buildSlideTemplateSlide, templateSchemeFromTheme, buildThemeColorMap, reResolveSlideColors, computeEntranceAnimationDelay, createPresentationSessionId, resolveTransitionDurationMs, runPresentationAction, createPresenterTimer, createInitialPresentationSnapshot, mergePresentationSnapshot, resetPresenterTimer, presenterElapsed, stepPresenterZoom, togglePresenterTimer, addCommentToList, removeCommentFromList, editCommentInList, toggleCommentResolvedInList, replyToCommentInList, canvasToJpegData, downloadDataUrl, pickSupportedMimeType, WEBM_MIME_CANDIDATES, fpsToFrameIntervalMs, segmentFrameCount, encodeGif, computeGridSpacingPx, resolveShowSlideIndexes, groupSlidesBySection, masterViewPseudoSlide, resolveHistoryDepth, hasCopyableFormat, createBackstagePresentation, buildFieldSubstitutionContext, cloneElementForPaste, computeScrollDelta, getPresetShapeClipPath, svgPathToPolygons, polygonsToSvgPath, rerouteConnectorsForMovedElements, applyReroutedConnectors, SvgExporter, deleteTableColumn, rebuildTableStructureInRawXml, insertTableColumn, deleteTableRow, insertTableRow, updateCellTextStyleInRawXml, setCellText, updateCellTextInRawXml, splitCell, updateMergeAttrsInRawXml, mergeCells, isPpactionUrl, parsePpactionUrl, resolvePresentationAction, ACTION_BUTTON_PRESETS, elementActionToPptxAction, isUrlSafe, buildSmartArtPresetData, classifyMediaType, computeSnapToShape, publishLiveGeometry, getDraggedShapeAdjustments, computeMarqueeHitIds, mergeAdditiveSelection, shouldConfirmExternalHyperlink, safeOpenUrl, applyResize, snapBoxToGrid, applyMotionPathPreset } from './chunk-G7UBIKAG.mjs';
3
- import { translationsEn } from './chunk-RHSA5BW5.mjs';
1
+ import { cloneHistorySnapshot, MIN_ELEMENT_SIZE, ZOOM_TO_SELECTION_PADDING, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, UNGROUPED_SECTION_ID, DEFAULT_SECTION_GROUP_ID, DEFAULT_TABLE_ROWS, DEFAULT_TABLE_COLUMNS, buildPdfFromImageData, buildNotesPdf, GRID_SIZE, DEFAULT_TEXT_FONT_SIZE } from './chunk-SFMATKNJ.mjs';
2
+ import { DEFAULT_VIEWER_OPTIONS, TOOLBAR_TABS, computeVirtualRange, DEFAULT_VIRTUAL_OVERSCAN, createPresenceProjector, BROADCAST_THROTTLE_MS, createSyncGate, clearLocalAwareness, validateRoomId, createDepartureChannel, resolveTransportForServerUrl, isMixedContentBlocked, CONNECTION_TIMEOUT_MS, registerCollaborationTeardown, sanitizeColor, cloneTemplateElementsBySlideId, cloneSlide, createWheelStepBuffer, createViewerZoomStore, clampZoomScale, mapEditingWheel, cancelBuildReveal, PresentationAnimationController, scheduleAutoAdvanceChain, playGroup, PRESENTATION_HASH, PRESENTATION_NONCE_KEY, PRESENTATION_CHANNEL_NAME, swapPresentationWindows, PRESENTATION_MESSAGE_ORIGIN, clearAudienceContent, buildPresentationAudienceUrl, resolveAudienceScreenPlacement, placeAudienceWindow, storeAudienceContent, isPresentationSessionMessage, endAudienceDisplay, createPresentationKeyBuffer, acceptsPresentationInput, resolveAutoAdvanceDelayMs, hasShowSlideAfter, nextShowSlideIndex, previousShowSlideIndex, createPresenterShowGuard, firstShowSlideIndex, attachPresentationVisibilityPause, presentationEntrySlideIndex, buildMorphTransitionPlan, morphOptionToMode, buildMorphAnimationRules, findInSlides, applyFindReplacements, createCollaborationLivePatcher, publishLiveInlineText, PptxHandler, collectMediaElements, collectImagePaths, applyImagePathPatches, resolveTableCellImageUrls, resolveTableStyleImageUrls, partitionTemplateElements, seedRecentColors, resolveAuthoredCustomShowId, buildInitialGuides, readOnlyRecommendation, compatibilityWarningToasts, EncryptedFileError, resolveAutosaveIntervalMs, shouldWriteAutosaveSnapshot, saveAutosaveSnapshot, autosaveSnapshotMark, updateMasterViewElement, isTemplateElementId, updateElement, hasShapeProperties, hasTextProperties, getInlineEditorSelection, applyStyleToSelectedSegments, setPendingSelectionRestore, applyCaseTransformToSegments, transformTextCase, masterViewElements, replaceMasterViewElements, addSection, renameSection, deleteSection, moveSectionUp, moveSectionDown, moveSlidesToSection, normalizeColorsForCapture, preprocessCssForCapture, slideProgressPercent, slideStatusLabel, EXPORT_ASSEMBLING_PERCENT, EXPORT_DONE_PERCENT, isExportAbortError, recordProgressPercent, downloadBlob, resolveImageResolutionScale, applyThemeToData, THEME_PRESETS, themeColorSchemesEqual, comparePresentation, applyAcceptSlide, applyAcceptAllSlides, collectUsedFonts, describeFontEmbedding, isMobileViewport, resolveAuthoredSlideRange, mayLeaveSlideShow, stopAllPersistentAudio, remapTextToSegments, guidePxToEmu, buildSaveSlides, embeddedFontSaveOptions, resolveSlideSizeSelection, saveDeckWithPassword, shouldProbeAutosaveRecovery, probeAutosaveRecovery, acceptAutosaveRecovery, discardAutosaveRecovery, shouldLoopContinuously, applyRehearsalTimings, copyFormatFromElement, applyFormatToElement, collectReferencedFontFamilies, resolveGoogleWebfontHref, mapSlideShowStartKey, mapEditorKey, isEditorTextInputTarget, createViewerOptionsStore, readSlidesFromYDoc, shouldRoomSlidesReplaceLoad, reconcileSlidesInYDoc, LOCAL_SYNC_ORIGIN, observeYDocSlides, shouldAutoFollowBroadcaster, detectTouchDevice, detectOrientation, deriveViewportBreakpoints, createTouchGestureRecognizer, MAX_ZOOM_SCALE, MIN_ZOOM_SCALE, isKeyboardOpen, scopeLayoutOptionsToSlide, viewerPreferencesFromViewProperties, DEFAULT_VIEWER_PREFERENCES, viewPropertiesPatchFromPreferences, openPptxFile, readBackstageRecentFile, isActionHidden, resolveInlineEditAutoFitHeight, canInteractWithElement, filterInteractableIds, beginShapeAdjustment, buildElementClipboardPayload, savedPresentationFileName, exportDeckJson, groupElements, generateElementId, makeCloneId, ungroupElements, alignElements, distributeElements, bringForward, sendBackward, bringToFront, sendToBack, newTableElement, createDefaultChartElement, readViewportMetrics, computeKeyboardInset, mergeShapes, mapPresentationKey, lastShowSlideIndex, mapPresentationWheel, openPendingPrintWindow, computeColorFilter, filterHiddenSlideIndices, computeSlideIndices, buildPrintDocument, finishPrintWindow, buildPrintHtmlDocument, buildOutlineHtml, buildSlidesHtml, buildNotesHtml, buildHandoutsHtml, createBlankSlide, makeSlideId, buildSlideTemplateSlide, templateSchemeFromTheme, buildThemeColorMap, reResolveSlideColors, computeEntranceAnimationDelay, createPresentationSessionId, resolveTransitionDurationMs, runPresentationAction, safeOpenUrl, toggleStageElementMedia, resolveOleVerbTarget, openUrlInNewTab, createPresenterTimer, createInitialPresentationSnapshot, mergePresentationSnapshot, resetPresenterTimer, presenterElapsed, stepPresenterZoom, togglePresenterTimer, addCommentToList, removeCommentFromList, editCommentInList, toggleCommentResolvedInList, canvasToJpegData, downloadDataUrl, pickSupportedMimeType, WEBM_MIME_CANDIDATES, fpsToFrameIntervalMs, segmentFrameCount, encodeGif, computeGridSpacingPx, resolveShowSlideIndexes, groupSlidesBySection, masterViewPseudoSlide, resolveHistoryDepth, hasCopyableFormat, createBackstagePresentation, buildFieldSubstitutionContext, cloneElementForPaste, computeScrollDelta, getPresetShapeClipPath, svgPathToPolygons, polygonsToSvgPath, rerouteConnectorsForMovedElements, applyReroutedConnectors, SvgExporter, replyToCommentInList, deleteTableColumn, rebuildTableStructureInRawXml, insertTableColumn, deleteTableRow, insertTableRow, updateCellTextStyleInRawXml, setCellText, updateCellTextInRawXml, splitCell, updateMergeAttrsInRawXml, mergeCells, isPpactionUrl, parsePpactionUrl, resolvePresentationAction, ACTION_BUTTON_PRESETS, elementActionToPptxAction, isUrlSafe, buildSmartArtPresetData, classifyMediaType, computeSnapToShape, publishLiveGeometry, getDraggedShapeAdjustments, computeMarqueeHitIds, mergeAdditiveSelection, shouldConfirmExternalHyperlink, applyResize, lockResizeAspect, snapBoxToGrid, applyMotionPathPreset } from './chunk-B7WAGQW4.mjs';
3
+ import { translationsEn } from './chunk-XWUMM2TX.mjs';
4
4
  import { createContext, useRef, useState, useEffect, useCallback, useMemo, useLayoutEffect, useContext, useImperativeHandle, useSyncExternalStore } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
@@ -969,67 +969,6 @@ function stopAnimationSound() {
969
969
  }
970
970
 
971
971
  // src/viewer/hooks/presentation-mode/animation-helpers.ts
972
- function applyAnimationGroupSteps(group, onPlayActionSound, setPresentationElementStates, presentationTimersRef) {
973
- for (const step of group.steps) {
974
- if (step.command) {
975
- const command = step.command;
976
- const timer = window.setTimeout(
977
- () => {
978
- executeMediaCommand(command);
979
- },
980
- Math.max(0, step.delayMs)
981
- );
982
- presentationTimersRef.current.push(timer);
983
- continue;
984
- }
985
- if (step.stopSound) {
986
- stopAnimationSound();
987
- } else if (step.soundPath && onPlayActionSound) {
988
- onPlayActionSound(step.soundPath);
989
- }
990
- }
991
- setPresentationElementStates((previousStates) => {
992
- const nextStates = new Map(previousStates);
993
- for (const step of group.steps) {
994
- if (step.command) {
995
- continue;
996
- }
997
- const currentState = nextStates.get(step.elementId) ?? {
998
- visible: true};
999
- const shouldBeVisible = step.presetClass === "exit" ? currentState.visible : true;
1000
- const colorTargets = step.colorTargets;
1001
- nextStates.set(step.elementId, {
1002
- visible: shouldBeVisible,
1003
- cssAnimation: step.cssAnimation,
1004
- animatesFill: colorTargets?.includes("fill") ? true : void 0,
1005
- animatesStroke: colorTargets?.includes("stroke") ? true : void 0
1006
- });
1007
- }
1008
- return nextStates;
1009
- });
1010
- for (const step of group.steps) {
1011
- if (step.command) {
1012
- continue;
1013
- }
1014
- const timer = window.setTimeout(
1015
- () => {
1016
- setPresentationElementStates((previousStates) => {
1017
- const nextStates = new Map(previousStates);
1018
- const currentState = nextStates.get(step.elementId) ?? {
1019
- visible: true};
1020
- const visibleAfterStep = step.presetClass === "exit" || step.hideAfterEffect ? false : currentState.visible;
1021
- nextStates.set(step.elementId, {
1022
- visible: visibleAfterStep,
1023
- cssAnimation: step.holdEndState ? step.cssAnimation : void 0
1024
- });
1025
- return nextStates;
1026
- });
1027
- },
1028
- Math.max(0, step.delayMs + step.durationMs + 8)
1029
- );
1030
- presentationTimersRef.current.push(timer);
1031
- }
1032
- }
1033
972
  function shouldSeekAnimationGroup(group, endAtMs, nowMs) {
1034
973
  return group?.seqNextAction === "seek" && nowMs < endAtMs;
1035
974
  }
@@ -1102,49 +1041,8 @@ function finishAnimationGroupSteps(group, setPresentationElementStates, complete
1102
1041
  });
1103
1042
  }
1104
1043
 
1105
- // src/viewer/hooks/presentation-mode/build-playback.ts
1106
- function cancelBuildReveal(handle) {
1107
- if (handle.current !== null) {
1108
- cancelAnimationFrame(handle.current);
1109
- handle.current = null;
1110
- }
1111
- }
1112
- function driveBuildReveal(controller, buildIds, setStates, handle) {
1113
- cancelBuildReveal(handle);
1114
- if (buildIds.length === 0 || typeof requestAnimationFrame !== "function") {
1115
- return;
1116
- }
1117
- const start = performance.now();
1118
- const tick = () => {
1119
- const elapsedMs = performance.now() - start;
1120
- const states = controller.computeStatesFor(buildIds, { elapsedMs });
1121
- setStates((prev) => {
1122
- const next = new Map(prev);
1123
- for (const id of buildIds) {
1124
- const build = states.get(id)?.build;
1125
- if (!build) {
1126
- continue;
1127
- }
1128
- const existing = next.get(id) ?? { visible: true, cssAnimation: void 0 };
1129
- next.set(id, { ...existing, build });
1130
- }
1131
- return next;
1132
- });
1133
- let pending = false;
1134
- for (const id of buildIds) {
1135
- const build = states.get(id)?.build;
1136
- if (build && build.progress < 1) {
1137
- pending = true;
1138
- break;
1139
- }
1140
- }
1141
- handle.current = pending ? requestAnimationFrame(tick) : null;
1142
- };
1143
- tick();
1144
- }
1145
-
1146
1044
  // src/viewer/hooks/presentation-mode/entrance-animation-timers.ts
1147
- function scheduleOpeningAutoPlayGroup(controller, deps) {
1045
+ function scheduleOpeningAutoPlayGroup(controller, ctx) {
1148
1046
  if (!controller.hasMoreSteps()) {
1149
1047
  return;
1150
1048
  }
@@ -1157,17 +1055,10 @@ function scheduleOpeningAutoPlayGroup(controller, deps) {
1157
1055
  if (!group) {
1158
1056
  return;
1159
1057
  }
1160
- applyAnimationGroupSteps(
1161
- group,
1162
- deps.onPlayActionSound,
1163
- deps.setPresentationElementStates,
1164
- deps.presentationTimersRef
1165
- );
1166
- deps.markAnimationGroupActive(group);
1167
- deps.startBuildReveal(controller, group);
1168
- deps.scheduleAutoAdvanceChain(controller);
1058
+ playGroup(controller, group, ctx);
1059
+ scheduleAutoAdvanceChain(controller, ctx);
1169
1060
  }, firstGroup.autoAdvanceDelayMs ?? 0);
1170
- deps.presentationTimersRef.current.push(timer);
1061
+ ctx.timers.push(timer);
1171
1062
  }
1172
1063
  function scheduleEntranceAnimationTimers(slide, setPresentationAnimations, presentationTimersRef) {
1173
1064
  const entranceAnimations = [...slide.animations || []].filter((animation) => Boolean(animation.entrance)).sort(
@@ -1214,17 +1105,16 @@ function useAnimationPlayback(input) {
1214
1105
  const activeAnimationEndAtRef = useRef(0);
1215
1106
  const seededCompletedRef = useRef(false);
1216
1107
  const lastSeedCompletedRef = useRef(false);
1217
- const startBuildReveal = useCallback(
1218
- (controller, group) => {
1219
- driveBuildReveal(
1220
- controller,
1221
- PresentationAnimationController.collectBuildStepIds(group),
1222
- setPresentationElementStates,
1223
- buildRafRef
1224
- );
1225
- },
1226
- []
1227
- );
1108
+ const buildPlaybackContext = useCallback(() => {
1109
+ return {
1110
+ setStates: setPresentationElementStates,
1111
+ timers: presentationTimersRef.current,
1112
+ buildHandle: buildRafRef,
1113
+ onPlayActionSound,
1114
+ playSound: playAnimationSound,
1115
+ stopSound: stopAnimationSound
1116
+ };
1117
+ }, [onPlayActionSound]);
1228
1118
  useEffect(() => {
1229
1119
  return () => {
1230
1120
  cancelBuildReveal(buildRafRef);
@@ -1243,45 +1133,6 @@ function useAnimationPlayback(input) {
1243
1133
  activeAnimationGroupRef.current = group;
1244
1134
  activeAnimationEndAtRef.current = performance.now() + Math.max(0, group.totalDurationMs);
1245
1135
  }, []);
1246
- const scheduleAutoAdvanceChain = useCallback(
1247
- (controller) => {
1248
- if (!controller.shouldAutoAdvance()) {
1249
- return;
1250
- }
1251
- const delay = controller.getAutoAdvanceDelay();
1252
- const previousGroup = controller.peekNext();
1253
- if (!previousGroup) {
1254
- return;
1255
- }
1256
- const totalDelay = delay + (previousGroup.autoAdvanceDelayMs ?? 0);
1257
- const timer = window.setTimeout(
1258
- () => {
1259
- const group = controller.advance();
1260
- if (!group) {
1261
- return;
1262
- }
1263
- applyAnimationGroupSteps(
1264
- group,
1265
- onPlayActionSound,
1266
- setPresentationElementStates,
1267
- presentationTimersRef
1268
- );
1269
- markAnimationGroupActive(group);
1270
- startBuildReveal(controller, group);
1271
- scheduleAutoAdvanceChain(controller);
1272
- },
1273
- Math.max(0, totalDelay)
1274
- );
1275
- presentationTimersRef.current.push(timer);
1276
- },
1277
- // `scheduleAutoAdvanceChain` recursively calls itself (line 190) to walk the
1278
- // auto-advance chain; it can't list itself as its own dependency (the const
1279
- // isn't assigned yet when the array is evaluated). The recursive call still
1280
- // resolves correctly because the setTimeout callback only reads the binding
1281
- // once the outer const has been assigned.
1282
- // oxlint-disable-next-line react/memo-dependencies -- see comment above
1283
- [markAnimationGroupActive, onPlayActionSound, startBuildReveal]
1284
- );
1285
1136
  const resetSlideTimeline = useCallback(
1286
1137
  (slideIndex) => {
1287
1138
  cancelBuildReveal(buildRafRef);
@@ -1315,7 +1166,7 @@ function useAnimationPlayback(input) {
1315
1166
  const completedStates = controller.computeStatesFor(buildIds);
1316
1167
  clearPresentationTimers();
1317
1168
  finishAnimationGroupSteps(activeGroup, setPresentationElementStates, completedStates);
1318
- scheduleAutoAdvanceChain(controller);
1169
+ scheduleAutoAdvanceChain(controller, buildPlaybackContext());
1319
1170
  return true;
1320
1171
  }
1321
1172
  if (!controller || !controller.hasMoreSteps()) {
@@ -1325,24 +1176,12 @@ function useAnimationPlayback(input) {
1325
1176
  if (!group) {
1326
1177
  return false;
1327
1178
  }
1328
- applyAnimationGroupSteps(
1329
- group,
1330
- onPlayActionSound,
1331
- setPresentationElementStates,
1332
- presentationTimersRef
1333
- );
1179
+ const ctx = buildPlaybackContext();
1180
+ playGroup(controller, group, ctx);
1334
1181
  markAnimationGroupActive(group);
1335
- startBuildReveal(controller, group);
1336
- scheduleAutoAdvanceChain(controller);
1182
+ scheduleAutoAdvanceChain(controller, ctx);
1337
1183
  return true;
1338
- }, [
1339
- animationsEnabled,
1340
- clearPresentationTimers,
1341
- markAnimationGroupActive,
1342
- onPlayActionSound,
1343
- scheduleAutoAdvanceChain,
1344
- startBuildReveal
1345
- ]);
1184
+ }, [animationsEnabled, buildPlaybackContext, clearPresentationTimers, markAnimationGroupActive]);
1346
1185
  const handleInteractiveShapeClick = useCallback(
1347
1186
  (shapeId) => {
1348
1187
  const controller = controllerRef.current;
@@ -1353,16 +1192,10 @@ function useAnimationPlayback(input) {
1353
1192
  if (!group) {
1354
1193
  return false;
1355
1194
  }
1356
- applyAnimationGroupSteps(
1357
- group,
1358
- onPlayActionSound,
1359
- setPresentationElementStates,
1360
- presentationTimersRef
1361
- );
1362
- startBuildReveal(controller, group);
1195
+ playGroup(controller, group, buildPlaybackContext());
1363
1196
  return true;
1364
1197
  },
1365
- [onPlayActionSound, startBuildReveal]
1198
+ [buildPlaybackContext]
1366
1199
  );
1367
1200
  const handleHoverStart = useCallback(
1368
1201
  (shapeId) => {
@@ -1378,16 +1211,10 @@ function useAnimationPlayback(input) {
1378
1211
  if (!group) {
1379
1212
  return false;
1380
1213
  }
1381
- applyAnimationGroupSteps(
1382
- group,
1383
- onPlayActionSound,
1384
- setPresentationElementStates,
1385
- presentationTimersRef
1386
- );
1387
- startBuildReveal(controller, group);
1214
+ playGroup(controller, group, buildPlaybackContext());
1388
1215
  return true;
1389
1216
  },
1390
- [animationsEnabled, onPlayActionSound, startBuildReveal]
1217
+ [animationsEnabled, buildPlaybackContext]
1391
1218
  );
1392
1219
  const handleHoverEnd = useCallback((shapeId) => {
1393
1220
  const controller = controllerRef.current;
@@ -1433,25 +1260,12 @@ function useAnimationPlayback(input) {
1433
1260
  }
1434
1261
  const controller = controllerRef.current;
1435
1262
  if (controller) {
1436
- scheduleOpeningAutoPlayGroup(controller, {
1437
- onPlayActionSound,
1438
- setPresentationElementStates,
1439
- presentationTimersRef,
1440
- startBuildReveal,
1441
- scheduleAutoAdvanceChain,
1442
- markAnimationGroupActive
1443
- });
1263
+ const ctx = buildPlaybackContext();
1264
+ scheduleOpeningAutoPlayGroup(controller, ctx);
1444
1265
  }
1445
1266
  scheduleEntranceAnimationTimers(slide, setPresentationAnimations, presentationTimersRef);
1446
1267
  },
1447
- [
1448
- animationsEnabled,
1449
- slides,
1450
- onPlayActionSound,
1451
- markAnimationGroupActive,
1452
- scheduleAutoAdvanceChain,
1453
- startBuildReveal
1454
- ]
1268
+ [animationsEnabled, slides, buildPlaybackContext]
1455
1269
  );
1456
1270
  const runPresentationEntranceAnimations = useCallback(
1457
1271
  (slideIndex, options) => {
@@ -2019,7 +1833,7 @@ function useRehearsalTimings(input) {
2019
1833
  function handlePresentationActionImpl(action, deps) {
2020
1834
  runPresentationAction(
2021
1835
  action,
2022
- { slideCount: deps.slidesLength },
1836
+ { slideCount: deps.slidesLength, ...deps.elementId ? { elementId: deps.elementId } : {} },
2023
1837
  {
2024
1838
  goToSlide: (slideIndex) => {
2025
1839
  deps.navigateToSlide(slideIndex);
@@ -2030,7 +1844,13 @@ function handlePresentationActionImpl(action, deps) {
2030
1844
  endShow: () => {
2031
1845
  deps.onSetMode("edit");
2032
1846
  },
2033
- playSound: deps.onPlayActionSound ? (soundPath) => deps.onPlayActionSound?.(soundPath) : void 0
1847
+ playSound: deps.onPlayActionSound ? (soundPath) => deps.onPlayActionSound?.(soundPath) : void 0,
1848
+ lastViewed: deps.onLastViewed,
1849
+ customShow: deps.onCustomShow,
1850
+ openFile: deps.onOpenFile,
1851
+ openPresentation: deps.onOpenPresentation,
1852
+ playMedia: deps.onPlayMedia,
1853
+ oleVerb: deps.onOleVerb
2034
1854
  }
2035
1855
  );
2036
1856
  }
@@ -2038,7 +1858,7 @@ function handlePresentationActionImpl(action, deps) {
2038
1858
  // src/viewer/hooks/presentation-mode/slide-transition.ts
2039
1859
  function executeSlideTransition(nextSlideIndex, deps) {
2040
1860
  const incomingSlide = deps.slides[nextSlideIndex];
2041
- const transition = incomingSlide?.transition;
1861
+ const transition = deps.reverse ? deps.slides[deps.currentSlideIndex]?.transition : incomingSlide?.transition;
2042
1862
  const durationMs = deps.playTransition ? resolveTransitionDurationMs(transition) : 0;
2043
1863
  deps.clearPresentationTimers();
2044
1864
  deps.setPresentationSlideIndex(nextSlideIndex);
@@ -2092,7 +1912,13 @@ function useSlideNavigation(input) {
2092
1912
  presentationTimersRef,
2093
1913
  rehearsing,
2094
1914
  recordCurrentSlideTime,
2095
- setShowRehearsalSummary
1915
+ setShowRehearsalSummary,
1916
+ onLastViewed,
1917
+ onCustomShow,
1918
+ onOpenFile,
1919
+ onOpenPresentation,
1920
+ onPlayMedia,
1921
+ onOleVerb
2096
1922
  } = input;
2097
1923
  const movePresentationSlideRef = useRef(() => {
2098
1924
  });
@@ -2161,8 +1987,11 @@ function useSlideNavigation(input) {
2161
1987
  scheduleAutoAdvanceForSlide: rehearsing ? void 0 : scheduleAutoAdvanceForSlide,
2162
1988
  presentationTimersRef,
2163
1989
  setTransitionOverlay,
2164
- // PowerPoint plays a slide's transition only when advancing into it.
2165
- playTransition: direction === 1,
1990
+ // PowerPoint plays a slide's transition only when advancing into it,
1991
+ // and replays the LEAVING slide's transition in reverse when stepping
1992
+ // back (a morph glides its shapes back to where they came from).
1993
+ playTransition: true,
1994
+ reverse: direction === -1,
2166
1995
  // Stepping back onto a slide shows it with its builds already played.
2167
1996
  seedCompleted: direction === -1
2168
1997
  });
@@ -2238,16 +2067,35 @@ function useSlideNavigation(input) {
2238
2067
  ]
2239
2068
  );
2240
2069
  const handlePresentationAction = useCallback(
2241
- (action) => {
2070
+ (action, elementId) => {
2242
2071
  handlePresentationActionImpl(action, {
2072
+ elementId,
2243
2073
  movePresentationSlide,
2244
2074
  navigateToSlide,
2245
2075
  onPlayActionSound,
2246
2076
  onSetMode,
2247
- slidesLength: slides.length
2077
+ slidesLength: slides.length,
2078
+ onLastViewed,
2079
+ onCustomShow,
2080
+ onOpenFile,
2081
+ onOpenPresentation,
2082
+ onPlayMedia,
2083
+ onOleVerb
2248
2084
  });
2249
2085
  },
2250
- [movePresentationSlide, navigateToSlide, onPlayActionSound, onSetMode, slides.length]
2086
+ [
2087
+ movePresentationSlide,
2088
+ navigateToSlide,
2089
+ onPlayActionSound,
2090
+ onSetMode,
2091
+ slides.length,
2092
+ onLastViewed,
2093
+ onCustomShow,
2094
+ onOpenFile,
2095
+ onOpenPresentation,
2096
+ onPlayMedia,
2097
+ onOleVerb
2098
+ ]
2251
2099
  );
2252
2100
  return {
2253
2101
  movePresentationSlide,
@@ -2285,6 +2133,136 @@ function useZoomNavigation(input) {
2285
2133
  clearZoomReturn
2286
2134
  };
2287
2135
  }
2136
+ function createCustomShowRunner(deps) {
2137
+ let origin = null;
2138
+ return {
2139
+ runCustomShow(customShowId, returnAfter) {
2140
+ const target = deps.getCustomShows().find((show) => show.id === customShowId);
2141
+ if (!target) {
2142
+ return;
2143
+ }
2144
+ origin = returnAfter ? { showId: deps.getActiveCustomShowId(), slideIndex: deps.getPresentationSlideIndex() } : null;
2145
+ const first = firstShowSlideIndex(resolveShowSlideIndexes(deps.getSlides(), target));
2146
+ deps.setActiveCustomShowId(customShowId);
2147
+ if (first !== void 0) {
2148
+ deps.navigateToSlide(first);
2149
+ }
2150
+ },
2151
+ tryReturnFromCustomShow() {
2152
+ if (!origin) {
2153
+ return false;
2154
+ }
2155
+ const { showId, slideIndex } = origin;
2156
+ origin = null;
2157
+ deps.setActiveCustomShowId(showId);
2158
+ deps.navigateToSlide(slideIndex);
2159
+ return true;
2160
+ }
2161
+ };
2162
+ }
2163
+ function useCustomShowRunner(deps) {
2164
+ const depsRef = useRef(deps);
2165
+ depsRef.current = deps;
2166
+ const runnerRef = useRef(null);
2167
+ runnerRef.current ??= createCustomShowRunner({
2168
+ getSlides: () => depsRef.current.getSlides(),
2169
+ getCustomShows: () => depsRef.current.getCustomShows(),
2170
+ getActiveCustomShowId: () => depsRef.current.getActiveCustomShowId(),
2171
+ setActiveCustomShowId: (id) => depsRef.current.setActiveCustomShowId(id),
2172
+ navigateToSlide: (index) => depsRef.current.navigateToSlide(index),
2173
+ getPresentationSlideIndex: () => depsRef.current.getPresentationSlideIndex()
2174
+ });
2175
+ return runnerRef.current;
2176
+ }
2177
+
2178
+ // src/viewer/hooks/presentation-mode/usePresentationActionExtensions.ts
2179
+ function usePresentationActionExtensions(input) {
2180
+ const {
2181
+ slides,
2182
+ customShows,
2183
+ activeCustomShowId,
2184
+ onSetActiveCustomShowId,
2185
+ presentationSlideIndex,
2186
+ containerRef,
2187
+ endWithBlackSlide,
2188
+ onSetMode,
2189
+ setEndOfShowVisible
2190
+ } = input;
2191
+ const navigateToSlideRef = useRef(null);
2192
+ const navigateToSlideViaRef = useCallback((index) => {
2193
+ navigateToSlideRef.current?.(index);
2194
+ }, []);
2195
+ const customShowRunner = useCustomShowRunner({
2196
+ getSlides: () => slides,
2197
+ getCustomShows: () => customShows,
2198
+ getActiveCustomShowId: () => activeCustomShowId,
2199
+ setActiveCustomShowId: (id) => onSetActiveCustomShowId?.(id),
2200
+ navigateToSlide: navigateToSlideViaRef,
2201
+ getPresentationSlideIndex: () => presentationSlideIndex
2202
+ });
2203
+ const previousSlideRef = useRef(presentationSlideIndex);
2204
+ const lastViewedSlideRef = useRef(null);
2205
+ useEffect(() => {
2206
+ lastViewedSlideRef.current = previousSlideRef.current;
2207
+ previousSlideRef.current = presentationSlideIndex;
2208
+ }, [presentationSlideIndex]);
2209
+ const onCustomShow = useCallback(
2210
+ (customShowId, returnAfter) => {
2211
+ customShowRunner.runCustomShow(customShowId, returnAfter);
2212
+ },
2213
+ [customShowRunner]
2214
+ );
2215
+ const onLastViewed = useCallback(() => {
2216
+ const previous = lastViewedSlideRef.current;
2217
+ if (previous !== null) {
2218
+ navigateToSlideViaRef(previous);
2219
+ }
2220
+ }, [navigateToSlideViaRef]);
2221
+ const onOpenFile = useCallback((target) => {
2222
+ safeOpenUrl(target);
2223
+ }, []);
2224
+ const onOpenPresentation = useCallback((target) => {
2225
+ safeOpenUrl(target);
2226
+ }, []);
2227
+ const onPlayMedia = useCallback(
2228
+ (elementId) => {
2229
+ toggleStageElementMedia(containerRef.current, elementId);
2230
+ },
2231
+ [containerRef]
2232
+ );
2233
+ const onOleVerb = useCallback(
2234
+ (verb, elementId) => {
2235
+ const target = resolveOleVerbTarget(slides[presentationSlideIndex], elementId, verb);
2236
+ if (target) {
2237
+ openUrlInNewTab(target.url);
2238
+ }
2239
+ },
2240
+ [slides, presentationSlideIndex]
2241
+ );
2242
+ const handleAdvancePastLastSlide = useCallback(() => {
2243
+ if (customShowRunner.tryReturnFromCustomShow()) {
2244
+ return;
2245
+ }
2246
+ if (endWithBlackSlide) {
2247
+ setEndOfShowVisible(true);
2248
+ } else {
2249
+ onSetMode("edit");
2250
+ }
2251
+ }, [customShowRunner, endWithBlackSlide, onSetMode, setEndOfShowVisible]);
2252
+ const bindNavigateToSlide = useCallback((navigateToSlide) => {
2253
+ navigateToSlideRef.current = navigateToSlide;
2254
+ }, []);
2255
+ return {
2256
+ onLastViewed,
2257
+ onCustomShow,
2258
+ onOpenFile,
2259
+ onOpenPresentation,
2260
+ onPlayMedia,
2261
+ onOleVerb,
2262
+ handleAdvancePastLastSlide,
2263
+ bindNavigateToSlide
2264
+ };
2265
+ }
2288
2266
  function usePresenterConsole(slideIndex, active) {
2289
2267
  const timerRef = useRef(createPresenterTimer());
2290
2268
  const [snapshot, setSnapshot] = useState(() => createInitialPresentationSnapshot(slideIndex));
@@ -2341,6 +2319,7 @@ function usePresentationMode(input) {
2341
2319
  const {
2342
2320
  mode,
2343
2321
  slides,
2322
+ templateElementsBySlideId,
2344
2323
  visibleSlideIndexes,
2345
2324
  activeSlideIndex,
2346
2325
  containerRef,
@@ -2357,7 +2336,10 @@ function usePresentationMode(input) {
2357
2336
  loopContinuously,
2358
2337
  showWithAnimation,
2359
2338
  useTimings,
2360
- endWithBlackSlide = true
2339
+ endWithBlackSlide = true,
2340
+ customShows = [],
2341
+ activeCustomShowId = null,
2342
+ onSetActiveCustomShowId
2361
2343
  } = input;
2362
2344
  const [presentationSlideIndex, setPresentationSlideIndex] = useState(0);
2363
2345
  const [presentationSlideVisible, setPresentationSlideVisible] = useState(true);
@@ -2412,13 +2394,17 @@ function usePresentationMode(input) {
2412
2394
  setPresenterMode(false);
2413
2395
  }
2414
2396
  }, [mode]);
2415
- const handleAdvancePastLastSlide = useCallback(() => {
2416
- if (endWithBlackSlide) {
2417
- setEndOfShowVisible(true);
2418
- } else {
2419
- onSetMode("edit");
2420
- }
2421
- }, [endWithBlackSlide, onSetMode]);
2397
+ const actionExtensions = usePresentationActionExtensions({
2398
+ slides,
2399
+ customShows,
2400
+ activeCustomShowId,
2401
+ onSetActiveCustomShowId,
2402
+ presentationSlideIndex,
2403
+ containerRef,
2404
+ endWithBlackSlide,
2405
+ onSetMode,
2406
+ setEndOfShowVisible
2407
+ });
2422
2408
  const {
2423
2409
  movePresentationSlide: moveNavigationSlide,
2424
2410
  navigateToSlide,
@@ -2436,7 +2422,7 @@ function usePresentationMode(input) {
2436
2422
  onPlayActionSound,
2437
2423
  loopContinuously,
2438
2424
  useTimings,
2439
- onAdvancePastLastSlide: handleAdvancePastLastSlide,
2425
+ onAdvancePastLastSlide: actionExtensions.handleAdvancePastLastSlide,
2440
2426
  playNextAnimationGroup,
2441
2427
  clearPresentationTimers,
2442
2428
  seedSlideAnimations,
@@ -2444,8 +2430,15 @@ function usePresentationMode(input) {
2444
2430
  presentationTimersRef,
2445
2431
  rehearsing,
2446
2432
  recordCurrentSlideTime,
2447
- setShowRehearsalSummary
2433
+ setShowRehearsalSummary,
2434
+ onLastViewed: actionExtensions.onLastViewed,
2435
+ onCustomShow: actionExtensions.onCustomShow,
2436
+ onOpenFile: actionExtensions.onOpenFile,
2437
+ onOpenPresentation: actionExtensions.onOpenPresentation,
2438
+ onPlayMedia: actionExtensions.onPlayMedia,
2439
+ onOleVerb: actionExtensions.onOleVerb
2448
2440
  });
2441
+ actionExtensions.bindNavigateToSlide(navigateToSlide);
2449
2442
  const movePresentationSlide = useCallback(
2450
2443
  (direction, trigger = "explicit") => {
2451
2444
  if (!acceptsPresentationInput()) {
@@ -2516,6 +2509,13 @@ function usePresentationMode(input) {
2516
2509
  }
2517
2510
  onSetMode("present");
2518
2511
  }, [containerRef, onSetMode, setRehearsing]);
2512
+ const enterPresentModeFromBeginning = useCallback(() => {
2513
+ const first = firstShowSlideIndex(visibleSlideIndexes);
2514
+ if (first !== void 0) {
2515
+ onSetActiveSlideIndex(first);
2516
+ }
2517
+ enterPresentMode();
2518
+ }, [visibleSlideIndexes, onSetActiveSlideIndex, enterPresentMode]);
2519
2519
  const enterPresenterView = useCallback(() => {
2520
2520
  setPresenterMode(true);
2521
2521
  setRehearsing(false);
@@ -2578,6 +2578,10 @@ function usePresentationMode(input) {
2578
2578
  scheduleAutoAdvanceRef.current = scheduleAutoAdvanceForSlide;
2579
2579
  const activeSlideIndexRef = useRef(activeSlideIndex);
2580
2580
  activeSlideIndexRef.current = activeSlideIndex;
2581
+ const visibleSlideIndexesRef = useRef(visibleSlideIndexes);
2582
+ visibleSlideIndexesRef.current = visibleSlideIndexes;
2583
+ const onSetActiveSlideIndexRef = useRef(onSetActiveSlideIndex);
2584
+ onSetActiveSlideIndexRef.current = onSetActiveSlideIndex;
2581
2585
  const presentationSlideIndexRef = useRef(presentationSlideIndex);
2582
2586
  presentationSlideIndexRef.current = presentationSlideIndex;
2583
2587
  const clearPresentationTimersRef = useRef(clearPresentationTimers);
@@ -2594,8 +2598,14 @@ function usePresentationMode(input) {
2594
2598
  }, [mode, containerRef]);
2595
2599
  useLayoutEffect(() => {
2596
2600
  if (mode === "present") {
2597
- const idx = activeSlideIndexRef.current;
2601
+ const idx = presentationEntrySlideIndex(
2602
+ activeSlideIndexRef.current,
2603
+ visibleSlideIndexesRef.current
2604
+ );
2598
2605
  setPresentationSlideIndex(idx);
2606
+ if (idx !== activeSlideIndexRef.current) {
2607
+ onSetActiveSlideIndexRef.current(idx);
2608
+ }
2599
2609
  runEntranceAnimationsRef.current(idx);
2600
2610
  scheduleAutoAdvanceRef.current(idx);
2601
2611
  } else {
@@ -2666,13 +2676,18 @@ function usePresentationMode(input) {
2666
2676
  if (!transitionOverlay || transitionOverlay.transition.type !== "morph") {
2667
2677
  return void 0;
2668
2678
  }
2679
+ const incoming = slides[transitionOverlay.incomingSlideIndex];
2669
2680
  return buildMorphTransitionPlan(
2670
2681
  slides[transitionOverlay.outgoingSlideIndex],
2671
- slides[transitionOverlay.incomingSlideIndex],
2682
+ incoming,
2672
2683
  transitionOverlay.durationMs,
2673
- morphOptionToMode(transitionOverlay.transition.morphOption)
2684
+ morphOptionToMode(transitionOverlay.transition.morphOption),
2685
+ // `PresentationStage` stacks the incoming slide's elements above its
2686
+ // template shapes (`zIndex = templateElements.length + index`), so a
2687
+ // z-index journey has to start counting where the stage does.
2688
+ { zIndexBase: incoming ? templateElementsBySlideId?.[incoming.id]?.length ?? 0 : 0 }
2674
2689
  );
2675
- }, [transitionOverlay, slides]);
2690
+ }, [transitionOverlay, slides, templateElementsBySlideId]);
2676
2691
  const morphedElementStates = useMemo(() => {
2677
2692
  if (!morphPlan || morphPlan.incomingAnimations.size === 0) {
2678
2693
  return presentationElementStates;
@@ -2721,6 +2736,7 @@ ${imageRules}`;
2721
2736
  handleHoverStart,
2722
2737
  handleHoverEnd,
2723
2738
  enterPresentMode,
2739
+ enterPresentModeFromBeginning,
2724
2740
  presenterMode,
2725
2741
  enterPresenterView,
2726
2742
  togglePresenterView,
@@ -3112,6 +3128,38 @@ function useModalDismissDrag(onClose, threshold = 120) {
3112
3128
  handlers: { onPointerDown, onPointerMove, onPointerUp, onPointerCancel: onPointerUp }
3113
3129
  };
3114
3130
  }
3131
+ function withoutKey(map, key) {
3132
+ if (!(key in map)) {
3133
+ return map;
3134
+ }
3135
+ const next = { ...map };
3136
+ delete next[key];
3137
+ return next;
3138
+ }
3139
+ function useCommentMentionDrafts() {
3140
+ const [commentDraftMentionsBySlideId, setCommentDraftMentionsBySlideId] = useState({});
3141
+ const [replyDraftMentionsByCommentId, setReplyDraftMentionsByCommentId] = useState({});
3142
+ const setCommentDraftMentions = useCallback((slideId, mentions) => {
3143
+ setCommentDraftMentionsBySlideId((prev) => ({ ...prev, [slideId]: mentions }));
3144
+ }, []);
3145
+ const setReplyDraftMentions = useCallback((commentId, mentions) => {
3146
+ setReplyDraftMentionsByCommentId((prev) => ({ ...prev, [commentId]: mentions }));
3147
+ }, []);
3148
+ const clearCommentDraftMentions = useCallback((slideId) => {
3149
+ setCommentDraftMentionsBySlideId((prev) => withoutKey(prev, slideId));
3150
+ }, []);
3151
+ const clearReplyDraftMentions = useCallback((commentId) => {
3152
+ setReplyDraftMentionsByCommentId((prev) => withoutKey(prev, commentId));
3153
+ }, []);
3154
+ return {
3155
+ commentDraftMentionsBySlideId,
3156
+ replyDraftMentionsByCommentId,
3157
+ setCommentDraftMentions,
3158
+ setReplyDraftMentions,
3159
+ clearCommentDraftMentions,
3160
+ clearReplyDraftMentions
3161
+ };
3162
+ }
3115
3163
 
3116
3164
  // src/viewer/hooks/useComments-helpers.ts
3117
3165
  function replaceCommentsOnSlide(slides, slideIndex, next) {
@@ -3119,7 +3167,7 @@ function replaceCommentsOnSlide(slides, slideIndex, next) {
3119
3167
  (entry, index) => index === slideIndex ? { ...entry, comments: next } : entry
3120
3168
  );
3121
3169
  }
3122
- function addCommentToSlide(slides, slideIndex, text, authorName, elementId) {
3170
+ function addCommentToSlide(slides, slideIndex, text, authorName, elementId, mentions) {
3123
3171
  const slide = slides[slideIndex];
3124
3172
  if (!slide) {
3125
3173
  return { slides, didAdd: false };
@@ -3135,7 +3183,10 @@ function addCommentToSlide(slides, slideIndex, text, authorName, elementId) {
3135
3183
  if (!next) {
3136
3184
  return { slides, didAdd: false };
3137
3185
  }
3138
- return { slides: replaceCommentsOnSlide(slides, slideIndex, next), didAdd: true };
3186
+ const withMentions = mentions && mentions.length > 0 ? next.map(
3187
+ (comment, index) => index === next.length - 1 ? { ...comment, mentions } : comment
3188
+ ) : next;
3189
+ return { slides: replaceCommentsOnSlide(slides, slideIndex, withMentions), didAdd: true };
3139
3190
  }
3140
3191
  function removeCommentFromSlide(slides, slideIndex, commentId) {
3141
3192
  const slide = slides[slideIndex];
@@ -3170,7 +3221,7 @@ function toggleResolvedInSlide(slides, slideIndex, commentId) {
3170
3221
  }
3171
3222
  return { slides: replaceCommentsOnSlide(slides, slideIndex, next), didUpdate: true };
3172
3223
  }
3173
- function addReplyToSlide(slides, slideIndex, parentId, text, authorName) {
3224
+ function addReplyToSlide(slides, slideIndex, parentId, text, authorName, mentions) {
3174
3225
  const slide = slides[slideIndex];
3175
3226
  if (!slide) {
3176
3227
  return { slides, didAdd: false };
@@ -3179,7 +3230,15 @@ function addReplyToSlide(slides, slideIndex, parentId, text, authorName) {
3179
3230
  if (!next) {
3180
3231
  return { slides, didAdd: false };
3181
3232
  }
3182
- return { slides: replaceCommentsOnSlide(slides, slideIndex, next), didAdd: true };
3233
+ const withMentions = mentions && mentions.length > 0 ? next.map(
3234
+ (comment) => comment.id === parentId && comment.replies && comment.replies.length > 0 ? {
3235
+ ...comment,
3236
+ replies: comment.replies.map(
3237
+ (reply, index) => index === comment.replies.length - 1 ? { ...reply, mentions } : reply
3238
+ )
3239
+ } : comment
3240
+ ) : next;
3241
+ return { slides: replaceCommentsOnSlide(slides, slideIndex, withMentions), didAdd: true };
3183
3242
  }
3184
3243
  function pruneSlideDrafts(drafts, slideIds) {
3185
3244
  const next = {};
@@ -3194,6 +3253,95 @@ function pruneSlideDrafts(drafts, slideIds) {
3194
3253
  return changed ? next : null;
3195
3254
  }
3196
3255
 
3256
+ // src/viewer/hooks/useCommentReplyHandlers.ts
3257
+ function useCommentReplyHandlers(input) {
3258
+ const {
3259
+ slides,
3260
+ canEdit,
3261
+ userName,
3262
+ onUpdateSlides,
3263
+ onMarkDirty,
3264
+ replyDraftMentionsByCommentId,
3265
+ setReplyDraftMentions,
3266
+ clearReplyDraftMentions
3267
+ } = input;
3268
+ const [replyDraftByCommentId, setReplyDraftByCommentId] = useState({});
3269
+ const [replyingToCommentId, setReplyingToCommentId] = useState(null);
3270
+ const handleStartReply = useCallback((_slideIndex, commentId) => {
3271
+ setReplyingToCommentId(commentId);
3272
+ setReplyDraftByCommentId((prev) => ({ ...prev, [commentId]: "" }));
3273
+ }, []);
3274
+ const handleCancelReply = useCallback(() => {
3275
+ setReplyingToCommentId(null);
3276
+ }, []);
3277
+ const handleReplyDraftChange = useCallback(
3278
+ (commentId, draft, mentions) => {
3279
+ setReplyDraftByCommentId((prev) => ({ ...prev, [commentId]: draft }));
3280
+ if (mentions) {
3281
+ setReplyDraftMentions(commentId, mentions);
3282
+ }
3283
+ },
3284
+ [setReplyDraftMentions]
3285
+ );
3286
+ const handleSubmitReply = useCallback(
3287
+ (slideIndex, commentId) => {
3288
+ if (!canEdit) {
3289
+ return;
3290
+ }
3291
+ const slide = slides[slideIndex];
3292
+ if (!slide) {
3293
+ return;
3294
+ }
3295
+ const replyText = String(replyDraftByCommentId[commentId] || "").trim();
3296
+ if (replyText.length === 0) {
3297
+ return;
3298
+ }
3299
+ let didAdd = false;
3300
+ onUpdateSlides((prev) => {
3301
+ const result = addReplyToSlide(
3302
+ prev,
3303
+ slideIndex,
3304
+ commentId,
3305
+ replyText,
3306
+ userName,
3307
+ replyDraftMentionsByCommentId[commentId]
3308
+ );
3309
+ didAdd = result.didAdd;
3310
+ return result.slides;
3311
+ });
3312
+ if (!didAdd) {
3313
+ return;
3314
+ }
3315
+ setReplyingToCommentId(null);
3316
+ setReplyDraftByCommentId((prev) => {
3317
+ const next = { ...prev };
3318
+ delete next[commentId];
3319
+ return next;
3320
+ });
3321
+ clearReplyDraftMentions(commentId);
3322
+ onMarkDirty();
3323
+ },
3324
+ [
3325
+ canEdit,
3326
+ onMarkDirty,
3327
+ onUpdateSlides,
3328
+ replyDraftByCommentId,
3329
+ replyDraftMentionsByCommentId,
3330
+ clearReplyDraftMentions,
3331
+ slides,
3332
+ userName
3333
+ ]
3334
+ );
3335
+ return {
3336
+ replyingToCommentId,
3337
+ replyDraftByCommentId,
3338
+ handleStartReply,
3339
+ handleCancelReply,
3340
+ handleReplyDraftChange,
3341
+ handleSubmitReply
3342
+ };
3343
+ }
3344
+
3197
3345
  // src/viewer/hooks/useComments.ts
3198
3346
  function useComments({
3199
3347
  slides,
@@ -3206,11 +3354,33 @@ function useComments({
3206
3354
  const [commentDraftBySlideId, setCommentDraftBySlideId] = useState({});
3207
3355
  const [editingCommentIdBySlideId, setEditingCommentIdBySlideId] = useState({});
3208
3356
  const [commentEditDraftByCommentId, setCommentEditDraftByCommentId] = useState({});
3209
- const [replyDraftByCommentId, setReplyDraftByCommentId] = useState({});
3210
- const [replyingToCommentId, setReplyingToCommentId] = useState(null);
3211
- const handleCommentDraftChange = useCallback((slideId, draft) => {
3212
- setCommentDraftBySlideId((prev) => ({ ...prev, [slideId]: draft }));
3213
- }, []);
3357
+ const {
3358
+ commentDraftMentionsBySlideId,
3359
+ replyDraftMentionsByCommentId,
3360
+ setCommentDraftMentions,
3361
+ setReplyDraftMentions,
3362
+ clearCommentDraftMentions,
3363
+ clearReplyDraftMentions
3364
+ } = useCommentMentionDrafts();
3365
+ const reply = useCommentReplyHandlers({
3366
+ slides,
3367
+ canEdit,
3368
+ userName,
3369
+ onUpdateSlides,
3370
+ onMarkDirty,
3371
+ replyDraftMentionsByCommentId,
3372
+ setReplyDraftMentions,
3373
+ clearReplyDraftMentions
3374
+ });
3375
+ const handleCommentDraftChange = useCallback(
3376
+ (slideId, draft, mentions) => {
3377
+ setCommentDraftBySlideId((prev) => ({ ...prev, [slideId]: draft }));
3378
+ if (mentions) {
3379
+ setCommentDraftMentions(slideId, mentions);
3380
+ }
3381
+ },
3382
+ [setCommentDraftMentions]
3383
+ );
3214
3384
  const handleSetCommentEditDraft = useCallback((commentId, draft) => {
3215
3385
  setCommentEditDraftByCommentId((prev) => ({
3216
3386
  ...prev,
@@ -3237,7 +3407,8 @@ function useComments({
3237
3407
  slideIndex,
3238
3408
  draft,
3239
3409
  userName,
3240
- selectedElementId ?? void 0
3410
+ selectedElementId ?? void 0,
3411
+ commentDraftMentionsBySlideId[slide.id]
3241
3412
  );
3242
3413
  didAdd = result.didAdd;
3243
3414
  return result.slides;
@@ -3249,11 +3420,14 @@ function useComments({
3249
3420
  ...prev,
3250
3421
  [slide.id]: ""
3251
3422
  }));
3423
+ clearCommentDraftMentions(slide.id);
3252
3424
  onMarkDirty();
3253
3425
  },
3254
3426
  [
3255
3427
  canEdit,
3256
3428
  commentDraftBySlideId,
3429
+ commentDraftMentionsBySlideId,
3430
+ clearCommentDraftMentions,
3257
3431
  onMarkDirty,
3258
3432
  onUpdateSlides,
3259
3433
  selectedElementId,
@@ -3365,48 +3539,6 @@ function useComments({
3365
3539
  },
3366
3540
  [canEdit, onMarkDirty, onUpdateSlides, slides]
3367
3541
  );
3368
- const handleStartReply = useCallback((_slideIndex, commentId) => {
3369
- setReplyingToCommentId(commentId);
3370
- setReplyDraftByCommentId((prev) => ({ ...prev, [commentId]: "" }));
3371
- }, []);
3372
- const handleCancelReply = useCallback(() => {
3373
- setReplyingToCommentId(null);
3374
- }, []);
3375
- const handleReplyDraftChange = useCallback((commentId, draft) => {
3376
- setReplyDraftByCommentId((prev) => ({ ...prev, [commentId]: draft }));
3377
- }, []);
3378
- const handleSubmitReply = useCallback(
3379
- (slideIndex, commentId) => {
3380
- if (!canEdit) {
3381
- return;
3382
- }
3383
- const slide = slides[slideIndex];
3384
- if (!slide) {
3385
- return;
3386
- }
3387
- const replyText = String(replyDraftByCommentId[commentId] || "").trim();
3388
- if (replyText.length === 0) {
3389
- return;
3390
- }
3391
- let didAdd = false;
3392
- onUpdateSlides((prev) => {
3393
- const result = addReplyToSlide(prev, slideIndex, commentId, replyText, userName);
3394
- didAdd = result.didAdd;
3395
- return result.slides;
3396
- });
3397
- if (!didAdd) {
3398
- return;
3399
- }
3400
- setReplyingToCommentId(null);
3401
- setReplyDraftByCommentId((prev) => {
3402
- const next = { ...prev };
3403
- delete next[commentId];
3404
- return next;
3405
- });
3406
- onMarkDirty();
3407
- },
3408
- [canEdit, onMarkDirty, onUpdateSlides, replyDraftByCommentId, slides, userName]
3409
- );
3410
3542
  useEffect(() => {
3411
3543
  const slideIds = new Set(slides.map((slide) => slide.id));
3412
3544
  setCommentDraftBySlideId((prev) => {
@@ -3418,8 +3550,8 @@ function useComments({
3418
3550
  commentDraftBySlideId,
3419
3551
  editingCommentIdBySlideId,
3420
3552
  commentEditDraftByCommentId,
3421
- replyingToCommentId,
3422
- replyDraftByCommentId,
3553
+ commentDraftMentionsBySlideId,
3554
+ replyDraftMentionsByCommentId,
3423
3555
  handleCommentDraftChange,
3424
3556
  handleAddSlideComment,
3425
3557
  handleDeleteSlideComment,
@@ -3428,10 +3560,7 @@ function useComments({
3428
3560
  handleSaveCommentEdit,
3429
3561
  handleSetCommentEditDraft,
3430
3562
  handleToggleCommentResolved,
3431
- handleStartReply,
3432
- handleCancelReply,
3433
- handleReplyDraftChange,
3434
- handleSubmitReply
3563
+ ...reply
3435
3564
  };
3436
3565
  }
3437
3566
  function buildElementLookup(templateElements, slideElements) {
@@ -3596,6 +3725,8 @@ function useViewerCoreState(_input) {
3596
3725
  const [headerFooter, setHeaderFooter] = useState({});
3597
3726
  const [layoutOptions, setLayoutOptions] = useState([]);
3598
3727
  const [slideMasters, setSlideMasters] = useState([]);
3728
+ const [modernCommentAuthors, setModernCommentAuthors] = useState([]);
3729
+ const [recentColors, setRecentColors] = useState([]);
3599
3730
  const [theme, setTheme] = useState();
3600
3731
  const [tableStyleMap, setTableStyleMap] = useState();
3601
3732
  const [themeOptions, setThemeOptions] = useState([]);
@@ -3689,6 +3820,10 @@ function useViewerCoreState(_input) {
3689
3820
  setLayoutOptions,
3690
3821
  slideMasters,
3691
3822
  setSlideMasters,
3823
+ modernCommentAuthors,
3824
+ setModernCommentAuthors,
3825
+ recentColors,
3826
+ setRecentColors,
3692
3827
  theme,
3693
3828
  setTheme,
3694
3829
  tableStyleMap,
@@ -3873,6 +4008,8 @@ function useLoadContent({
3873
4008
  setHeaderFooter,
3874
4009
  setLayoutOptions,
3875
4010
  setSlideMasters,
4011
+ setModernCommentAuthors,
4012
+ setRecentColors,
3876
4013
  setTheme,
3877
4014
  setTableStyleMap,
3878
4015
  setThemeOptions,
@@ -3894,6 +4031,8 @@ function useLoadContent({
3894
4031
  setHasDigitalSignatures,
3895
4032
  setDigitalSignatureCount,
3896
4033
  setGuides,
4034
+ setReadOnlyRecommendation,
4035
+ setCompatToasts,
3897
4036
  setLoading,
3898
4037
  setError,
3899
4038
  setIsDirty,
@@ -3992,93 +4131,19 @@ function useLoadContent({
3992
4131
  }
3993
4132
  })
3994
4133
  );
3995
- const elementPatches = /* @__PURE__ */ new Map();
3996
- for (const ref of imageRefs) {
3997
- const url = resolvedMap.get(ref.path);
3998
- if (!url) {
3999
- continue;
4000
- }
4001
- const id = ref.element.id;
4002
- const existing = elementPatches.get(id) ?? {};
4003
- existing[ref.field] = url;
4004
- elementPatches.set(id, existing);
4005
- }
4006
- if (elementPatches.size > 0) {
4007
- const patchElements = (elements) => {
4008
- let mutated = false;
4009
- const next = elements.map((el) => {
4010
- let updated = el;
4011
- const patch = elementPatches.get(el.id);
4012
- if (patch) {
4013
- updated = { ...el, ...patch };
4014
- }
4015
- if (updated.type === "group" && updated.children?.length) {
4016
- const newChildren = patchElements(updated.children);
4017
- if (newChildren !== updated.children) {
4018
- updated = { ...updated, children: newChildren };
4019
- }
4020
- }
4021
- if (updated !== el) {
4022
- mutated = true;
4023
- }
4024
- return updated;
4025
- });
4026
- return mutated ? next : elements;
4027
- };
4028
- nextSlides = parsed.slides.map((s) => {
4029
- const newElements = patchElements(s.elements);
4030
- return newElements === s.elements ? s : { ...s, elements: newElements };
4031
- });
4032
- }
4033
- }
4034
- const { paths: tableImagePaths, refs: tableImageRefs } = collectTableCellImagePaths(nextSlides);
4035
- if (tableImagePaths.size > 0) {
4036
- const resolvedTableMap = /* @__PURE__ */ new Map();
4037
- await Promise.all(
4038
- Array.from(tableImagePaths).map(async (path) => {
4039
- try {
4040
- const url = await handler.getImageData(path);
4041
- if (url) {
4042
- resolvedTableMap.set(path, url);
4043
- }
4044
- } catch {
4045
- }
4046
- })
4047
- );
4048
- if (resolvedTableMap.size > 0) {
4049
- nextSlides = nextSlides.map((s) => {
4050
- const newElements = applyTableCellImagePatches(
4051
- s.elements,
4052
- resolvedTableMap,
4053
- tableImageRefs
4054
- );
4055
- return newElements === s.elements ? s : { ...s, elements: newElements };
4056
- });
4057
- }
4058
- }
4059
- let nextTableStyleMap = parsed.tableStyleMap;
4060
- const { paths: tableStyleImagePaths, refs: tableStyleImageRefs } = collectTableStyleImagePaths(nextTableStyleMap);
4061
- if (tableStyleImagePaths.size > 0) {
4062
- const resolvedStyleMap = /* @__PURE__ */ new Map();
4063
- await Promise.all(
4064
- Array.from(tableStyleImagePaths).map(async (path) => {
4065
- try {
4066
- const url = await handler.getImageData(path);
4067
- if (url) {
4068
- resolvedStyleMap.set(path, url);
4069
- }
4070
- } catch {
4071
- }
4072
- })
4073
- );
4074
- if (resolvedStyleMap.size > 0 && nextTableStyleMap) {
4075
- nextTableStyleMap = applyTableStyleImagePatches(
4076
- nextTableStyleMap,
4077
- resolvedStyleMap,
4078
- tableStyleImageRefs
4079
- );
4080
- }
4134
+ nextSlides = parsed.slides.map((s) => {
4135
+ const newElements = applyImagePathPatches(s.elements, resolvedMap, imageRefs);
4136
+ return newElements === s.elements ? s : { ...s, elements: newElements };
4137
+ });
4081
4138
  }
4139
+ nextSlides = await resolveTableCellImageUrls(
4140
+ nextSlides,
4141
+ (path) => handler.getImageData(path)
4142
+ );
4143
+ const nextTableStyleMap = await resolveTableStyleImageUrls(
4144
+ parsed.tableStyleMap,
4145
+ (path) => handler.getImageData(path)
4146
+ );
4082
4147
  handlerRef.current = handler;
4083
4148
  const partition = partitionTemplateElements(nextSlides);
4084
4149
  setSlides(partition.slides);
@@ -4097,6 +4162,8 @@ function useLoadContent({
4097
4162
  setHeaderFooter(parsed.headerFooter ?? {});
4098
4163
  setLayoutOptions(parsed.layoutOptions ?? []);
4099
4164
  setSlideMasters(parsed.slideMasters ?? []);
4165
+ setModernCommentAuthors(parsed.modernCommentAuthors ?? []);
4166
+ setRecentColors(seedRecentColors({ mruColors: parsed.mruColors }));
4100
4167
  setTheme(parsed.theme);
4101
4168
  setTableStyleMap(nextTableStyleMap);
4102
4169
  setThemeOptions(parsed.themeOptions ?? []);
@@ -4126,6 +4193,13 @@ function useLoadContent({
4126
4193
  setHasDigitalSignatures(parsed.hasDigitalSignatures === true);
4127
4194
  setDigitalSignatureCount(parsed.digitalSignatureCount ?? 0);
4128
4195
  setGuides(buildInitialGuides(parsed.presentationGuides, parsed.slides[0]?.guides));
4196
+ setReadOnlyRecommendation(readOnlyRecommendation(parsed));
4197
+ setCompatToasts(
4198
+ compatibilityWarningToasts([
4199
+ ...parsed.warnings ?? [],
4200
+ ...parsed.slides.flatMap((slide) => slide.warnings ?? [])
4201
+ ])
4202
+ );
4129
4203
  setActiveSlideIndex(0);
4130
4204
  clearSelection();
4131
4205
  setIsDirty(false);
@@ -4312,19 +4386,7 @@ function useElementOperations(input) {
4312
4386
  }));
4313
4387
  }
4314
4388
  } else {
4315
- setSlides(
4316
- (prev) => prev.map(
4317
- (s, i) => i !== activeSlideIndex ? s : {
4318
- ...s,
4319
- elements: s.elements.map(
4320
- (el) => el.id === elementId ? {
4321
- ...el,
4322
- ...updates
4323
- } : el
4324
- )
4325
- }
4326
- )
4327
- );
4389
+ setSlides((prev) => updateElement(prev, activeSlideIndex, elementId, updates));
4328
4390
  }
4329
4391
  history.markDirty();
4330
4392
  },
@@ -4554,16 +4616,49 @@ function useSectionOperations(input) {
4554
4616
  };
4555
4617
  }
4556
4618
 
4619
+ // src/viewer/utils/selection-handle-overlay.ts
4620
+ var VIEWPORT_SELECTOR = "[data-pptx-viewport]";
4621
+ function findSelectionHandleHost(elementNode, elementId) {
4622
+ const scope = elementNode.closest(VIEWPORT_SELECTOR) ?? elementNode.parentElement;
4623
+ if (!scope) {
4624
+ return null;
4625
+ }
4626
+ for (const candidate of scope.querySelectorAll("[data-pptx-handle-for]")) {
4627
+ if (candidate.getAttribute("data-pptx-handle-for") === elementId) {
4628
+ return candidate;
4629
+ }
4630
+ }
4631
+ return null;
4632
+ }
4633
+ function syncSelectionHandleOverlay(elementNode, elementId, geometry) {
4634
+ const host = findSelectionHandleHost(elementNode, elementId);
4635
+ if (!host) {
4636
+ return;
4637
+ }
4638
+ if (geometry.x !== void 0) {
4639
+ host.style.left = `${geometry.x}px`;
4640
+ }
4641
+ if (geometry.y !== void 0) {
4642
+ host.style.top = `${geometry.y}px`;
4643
+ }
4644
+ if (geometry.width !== void 0) {
4645
+ host.style.width = `${geometry.width}px`;
4646
+ }
4647
+ if (geometry.height !== void 0) {
4648
+ host.style.height = `${geometry.height}px`;
4649
+ }
4650
+ if (geometry.transform !== void 0) {
4651
+ host.style.transform = geometry.transform;
4652
+ }
4653
+ }
4654
+
4557
4655
  // src/viewer/hooks/pointer-move-handlers.ts
4558
- function computeResizeGeometry(handle, startX, startY, startWidth, startHeight, dx, dy, snapToGrid, gridSpacingPx) {
4559
- const resized = applyResize(
4560
- { x: startX, y: startY, width: startWidth, height: startHeight },
4561
- handle,
4562
- dx,
4563
- dy,
4564
- 1,
4565
- { minSize: MIN_ELEMENT_SIZE }
4566
- );
4656
+ function computeResizeGeometry(handle, startX, startY, startWidth, startHeight, dx, dy, snapToGrid, gridSpacingPx, lockAspect = false) {
4657
+ const startBox = { x: startX, y: startY, width: startWidth, height: startHeight };
4658
+ let resized = applyResize(startBox, handle, dx, dy, 1, { minSize: MIN_ELEMENT_SIZE });
4659
+ if (lockAspect) {
4660
+ resized = lockResizeAspect(resized, startBox, handle, MIN_ELEMENT_SIZE);
4661
+ }
4567
4662
  const box = { x: resized.x, y: resized.y, width: resized.width, height: resized.height };
4568
4663
  if (!snapToGrid) {
4569
4664
  return box;
@@ -4713,8 +4808,11 @@ function processDragMove(e, drag, editorScale, snapToGrid, snapToShape, gridSpac
4713
4808
  for (const [id, domEl] of drag.domEls) {
4714
4809
  const start = drag.startPositionsById[id];
4715
4810
  if (start) {
4716
- domEl.style.left = `${start.x + appliedDx}px`;
4717
- domEl.style.top = `${start.y + appliedDy}px`;
4811
+ const x = start.x + appliedDx;
4812
+ const y = start.y + appliedDy;
4813
+ domEl.style.left = `${x}px`;
4814
+ domEl.style.top = `${y}px`;
4815
+ syncSelectionHandleOverlay(domEl, id, { x, y });
4718
4816
  }
4719
4817
  }
4720
4818
  if (live) {
@@ -4745,24 +4843,28 @@ function processResizeMove(e, rs, editorScale, snapToGrid, gridSpacingPx, live)
4745
4843
  dx,
4746
4844
  dy,
4747
4845
  snapToGrid,
4748
- gridSpacingPx
4846
+ gridSpacingPx,
4847
+ e.shiftKey
4749
4848
  );
4750
4849
  rs.lastX = geo.x;
4751
4850
  rs.lastY = geo.y;
4752
4851
  rs.lastWidth = geo.width;
4753
4852
  rs.lastHeight = geo.height;
4853
+ const width = Math.max(geo.width, MIN_ELEMENT_SIZE);
4854
+ const height = Math.max(geo.height, MIN_ELEMENT_SIZE);
4754
4855
  if (rs.domEl) {
4755
4856
  rs.domEl.style.left = `${geo.x}px`;
4756
4857
  rs.domEl.style.top = `${geo.y}px`;
4757
- rs.domEl.style.width = `${Math.max(geo.width, MIN_ELEMENT_SIZE)}px`;
4758
- rs.domEl.style.height = `${Math.max(geo.height, MIN_ELEMENT_SIZE)}px`;
4858
+ rs.domEl.style.width = `${width}px`;
4859
+ rs.domEl.style.height = `${height}px`;
4860
+ syncSelectionHandleOverlay(rs.domEl, rs.elementId, { x: geo.x, y: geo.y, width, height });
4759
4861
  }
4760
4862
  if (live) {
4761
4863
  publishLiveGeometry(live.patcher, live.slideId, rs.elementId, {
4762
4864
  x: geo.x,
4763
4865
  y: geo.y,
4764
- width: Math.max(geo.width, MIN_ELEMENT_SIZE),
4765
- height: Math.max(geo.height, MIN_ELEMENT_SIZE)
4866
+ width,
4867
+ height
4766
4868
  });
4767
4869
  }
4768
4870
  }
@@ -7721,7 +7823,7 @@ function usePrintHandlers(input) {
7721
7823
  printWindow,
7722
7824
  buildPrintHtmlDocument({
7723
7825
  title: `Handout ${spp} per page`,
7724
- bodyHtml: buildHandoutsHtml(slideImages, slideIndices, spp),
7826
+ bodyHtml: buildHandoutsHtml(slideImages, slideIndices, spp, pptxData?.handoutMaster),
7725
7827
  orientation: "portrait",
7726
7828
  colorFilter,
7727
7829
  frameSlides: settings.frameSlides,
@@ -8378,9 +8480,10 @@ function useViewerDialogs(input) {
8378
8480
  function computeGridSpacingPx2(documentGridSpacing) {
8379
8481
  return computeGridSpacingPx(documentGridSpacing, GRID_SIZE);
8380
8482
  }
8381
- function computeVisibleSlideIndexes(slides, activeCustomShowId, customShows) {
8483
+ function computeVisibleSlideIndexes(slides, activeCustomShowId, customShows, presentationProperties) {
8382
8484
  const activeShow = activeCustomShowId ? customShows.find((show) => show.id === activeCustomShowId) : void 0;
8383
- return resolveShowSlideIndexes(slides, activeShow);
8485
+ const authoredRange = resolveAuthoredSlideRange(presentationProperties, slides.length);
8486
+ return resolveShowSlideIndexes(slides, activeShow, authoredRange);
8384
8487
  }
8385
8488
  function computeSlideSectionGroups(slides, sections) {
8386
8489
  return groupSlidesBySection(sections, slides).map((group) => ({
@@ -8409,6 +8512,7 @@ function useDerivedSlideState(input) {
8409
8512
  sections,
8410
8513
  customShows,
8411
8514
  activeCustomShowId,
8515
+ presentationProperties,
8412
8516
  mode,
8413
8517
  activeLayout,
8414
8518
  activeMaster,
@@ -8419,8 +8523,12 @@ function useDerivedSlideState(input) {
8419
8523
  [documentGridSpacing]
8420
8524
  );
8421
8525
  const visibleSlideIndexes = useMemo(
8422
- () => computeVisibleSlideIndexes(slides, activeCustomShowId, customShows),
8423
- [slides, activeCustomShowId, customShows]
8526
+ () => computeVisibleSlideIndexes(slides, activeCustomShowId, customShows, presentationProperties),
8527
+ [slides, activeCustomShowId, customShows, presentationProperties]
8528
+ );
8529
+ const authoredRange = useMemo(
8530
+ () => resolveAuthoredSlideRange(presentationProperties, slides.length),
8531
+ [presentationProperties, slides.length]
8424
8532
  );
8425
8533
  const slideSectionGroups = useMemo(
8426
8534
  () => computeSlideSectionGroups(slides, sections).map((group) => {
@@ -8442,7 +8550,8 @@ function useDerivedSlideState(input) {
8442
8550
  gridSpacingPx,
8443
8551
  visibleSlideIndexes,
8444
8552
  slideSectionGroups,
8445
- masterPseudoSlide
8553
+ masterPseudoSlide,
8554
+ authoredRange
8446
8555
  };
8447
8556
  }
8448
8557
  function useAnnotationHandlers(input) {
@@ -8580,6 +8689,7 @@ function useSerialize(input) {
8580
8689
  guides,
8581
8690
  headerFooter,
8582
8691
  presentationProperties,
8692
+ viewProperties,
8583
8693
  customShows,
8584
8694
  sections,
8585
8695
  coreProperties,
@@ -8641,6 +8751,7 @@ function useSerialize(input) {
8641
8751
  slideSize: resolveSlideSizeSelection({ current: slideSizeEmu, canvas: canvasSize }).size,
8642
8752
  headerFooter,
8643
8753
  presentationProperties,
8754
+ viewProperties,
8644
8755
  customShows: customShows.length > 0 ? customShows : void 0,
8645
8756
  sections: sections.length > 0 ? sections : void 0,
8646
8757
  coreProperties,
@@ -8660,6 +8771,7 @@ function useSerialize(input) {
8660
8771
  slideSizeEmu,
8661
8772
  headerFooter,
8662
8773
  presentationProperties,
8774
+ viewProperties,
8663
8775
  customShows,
8664
8776
  sections,
8665
8777
  coreProperties,
@@ -8730,6 +8842,7 @@ function usePresentationSetup(input) {
8730
8842
  const {
8731
8843
  mode,
8732
8844
  slides,
8845
+ templateElementsBySlideId,
8733
8846
  visibleSlideIndexes,
8734
8847
  activeSlideIndex,
8735
8848
  containerRef,
@@ -8743,7 +8856,10 @@ function usePresentationSetup(input) {
8743
8856
  endWithBlackSlide = true,
8744
8857
  promptKeepInkAnnotations = true,
8745
8858
  popupToolbarEnabled = true,
8746
- onToggleSubtitles
8859
+ onToggleSubtitles,
8860
+ customShows = [],
8861
+ activeCustomShowId = null,
8862
+ onSetActiveCustomShowId
8747
8863
  } = input;
8748
8864
  const actionSoundHandlerRef = useRef(null);
8749
8865
  const exitModeHandlerRef = useRef(null);
@@ -8755,6 +8871,7 @@ function usePresentationSetup(input) {
8755
8871
  const presentation = usePresentationMode({
8756
8872
  mode,
8757
8873
  slides,
8874
+ templateElementsBySlideId,
8758
8875
  visibleSlideIndexes,
8759
8876
  activeSlideIndex,
8760
8877
  containerRef,
@@ -8832,7 +8949,10 @@ function usePresentationSetup(input) {
8832
8949
  loopContinuously: shouldLoopContinuously(presentationProperties),
8833
8950
  showWithAnimation: presentationProperties.showWithAnimation,
8834
8951
  useTimings: presentationProperties.advanceMode !== "manual",
8835
- endWithBlackSlide
8952
+ endWithBlackSlide,
8953
+ customShows,
8954
+ activeCustomShowId,
8955
+ onSetActiveCustomShowId
8836
8956
  });
8837
8957
  return {
8838
8958
  presentation,
@@ -9316,7 +9436,9 @@ function useContentLifecycle(input) {
9316
9436
  setIsEncryptedDialogOpen,
9317
9437
  password,
9318
9438
  onContentApplied,
9319
- allowExternalImages
9439
+ allowExternalImages,
9440
+ setReadOnlyRecommendation,
9441
+ setCompatToasts
9320
9442
  } = input;
9321
9443
  const { handlerRef } = useLoadContent({
9322
9444
  content,
@@ -9330,6 +9452,8 @@ function useContentLifecycle(input) {
9330
9452
  setHeaderFooter: state.setHeaderFooter,
9331
9453
  setLayoutOptions: state.setLayoutOptions,
9332
9454
  setSlideMasters: state.setSlideMasters,
9455
+ setModernCommentAuthors: state.setModernCommentAuthors,
9456
+ setRecentColors: state.setRecentColors,
9333
9457
  setTheme: state.setTheme,
9334
9458
  setTableStyleMap: state.setTableStyleMap,
9335
9459
  setThemeOptions: state.setThemeOptions,
@@ -9351,6 +9475,8 @@ function useContentLifecycle(input) {
9351
9475
  setHasDigitalSignatures: state.setHasDigitalSignatures,
9352
9476
  setDigitalSignatureCount: state.setDigitalSignatureCount,
9353
9477
  setGuides: state.setGuides,
9478
+ setReadOnlyRecommendation,
9479
+ setCompatToasts,
9354
9480
  setLoading: state.setLoading,
9355
9481
  setError: state.setError,
9356
9482
  setIsDirty: state.setIsDirty,
@@ -9371,6 +9497,7 @@ function useContentLifecycle(input) {
9371
9497
  guides: state.guides,
9372
9498
  headerFooter: state.headerFooter,
9373
9499
  presentationProperties: state.presentationProperties,
9500
+ viewProperties: state.viewProperties,
9374
9501
  customShows: state.customShows,
9375
9502
  sections: state.sections,
9376
9503
  coreProperties: state.coreProperties,
@@ -9426,6 +9553,16 @@ function useKeyboardShortcuts(input) {
9426
9553
  inputRef.current = input;
9427
9554
  const handleKeyDown = useCallback((e) => {
9428
9555
  const current = inputRef.current;
9556
+ const startAction = mapSlideShowStartKey(e, { isPresenting: current.mode === "present" });
9557
+ if (startAction !== null) {
9558
+ e.preventDefault();
9559
+ if (startAction === "fromBeginning") {
9560
+ current.onStartShowFromBeginning();
9561
+ } else {
9562
+ current.onStartShowFromCurrent();
9563
+ }
9564
+ return;
9565
+ }
9429
9566
  const { action, dx, dy } = mapEditorKey(e, {
9430
9567
  canEdit: current.canEdit,
9431
9568
  // The Slide Master view is an editing surface, not a viewing one.
@@ -9506,7 +9643,18 @@ function useKeyboardShortcuts(input) {
9506
9643
 
9507
9644
  // src/viewer/hooks/useKeyboardShortcutWiring.ts
9508
9645
  function useKeyboardShortcutWiring(input) {
9509
- const { state, mode, canEdit, slides, activeSlide, ops, manipulation, history } = input;
9646
+ const {
9647
+ state,
9648
+ mode,
9649
+ canEdit,
9650
+ slides,
9651
+ activeSlide,
9652
+ ops,
9653
+ manipulation,
9654
+ history,
9655
+ onEnterPresentModeFromBeginning,
9656
+ onSetMode
9657
+ } = input;
9510
9658
  useKeyboardShortcuts({
9511
9659
  containerRef: state.containerRef,
9512
9660
  mode,
@@ -9576,7 +9724,9 @@ function useKeyboardShortcutWiring(input) {
9576
9724
  return;
9577
9725
  }
9578
9726
  state.setActiveSlideIndex((prev) => Math.min(slides.length - 1, prev + 1));
9579
- }
9727
+ },
9728
+ onStartShowFromBeginning: onEnterPresentModeFromBeginning,
9729
+ onStartShowFromCurrent: () => onSetMode("present")
9580
9730
  });
9581
9731
  }
9582
9732
  function useViewerIntegration(input) {
@@ -9596,6 +9746,8 @@ function useViewerIntegration(input) {
9596
9746
  autosaveAllowed = true,
9597
9747
  autosaveIntervalMs,
9598
9748
  allowExternalImages,
9749
+ setReadOnlyRecommendation,
9750
+ setCompatToasts,
9599
9751
  canEdit,
9600
9752
  promptKeepInkAnnotations,
9601
9753
  imageExportScale,
@@ -9664,7 +9816,9 @@ function useViewerIntegration(input) {
9664
9816
  setIsEncryptedDialogOpen,
9665
9817
  password: dialogs.presentationPassword ?? void 0,
9666
9818
  onContentApplied: () => setLoadVersion((v) => v + 1),
9667
- allowExternalImages
9819
+ allowExternalImages,
9820
+ setReadOnlyRecommendation,
9821
+ setCompatToasts
9668
9822
  }
9669
9823
  );
9670
9824
  const { exportHandlers, printHandlers, themeHandlers, propertyHandlers } = useIOHandlers({
@@ -9899,7 +10053,9 @@ function useViewerIntegration(input) {
9899
10053
  activeSlide,
9900
10054
  ops: editorOps.ops,
9901
10055
  manipulation: editorOps.manipulation,
9902
- history
10056
+ history,
10057
+ onEnterPresentModeFromBeginning: presentation.enterPresentModeFromBeginning,
10058
+ onSetMode: handleSetMode
9903
10059
  });
9904
10060
  return {
9905
10061
  exportHandlers,
@@ -10579,14 +10735,14 @@ function buildCanvasProps(input) {
10579
10735
  }
10580
10736
  safeOpenUrl(url);
10581
10737
  };
10582
- const handleActionClick = (_elementId, action) => {
10738
+ const handleActionClick = (elementId, action) => {
10583
10739
  if (mode === "present") {
10584
10740
  const resolved = resolvePresentationAction(action, { slideCount: slides.length });
10585
10741
  if (resolved.intent.kind === "openUrl") {
10586
10742
  openExternalUrl(resolved.intent.url);
10587
10743
  return;
10588
10744
  }
10589
- presentation.handlePresentationAction(action);
10745
+ presentation.handlePresentationAction(action, elementId);
10590
10746
  } else if (action.url) {
10591
10747
  openExternalUrl(action.url);
10592
10748
  }
@@ -10795,7 +10951,8 @@ function buildToolbarProps(input) {
10795
10951
  onOpenFile,
10796
10952
  onOpenRecentFile,
10797
10953
  fileName,
10798
- hiddenActions
10954
+ hiddenActions,
10955
+ viewPreferencesSync
10799
10956
  } = input;
10800
10957
  const {
10801
10958
  handleAddAnimation,
@@ -10879,9 +11036,9 @@ function buildToolbarProps(input) {
10879
11036
  onSetSpellCheckEnabled: s.setSpellCheckEnabled,
10880
11037
  onSetShowGrid: s.setShowGrid,
10881
11038
  onSetShowRulers: s.setShowRulers,
10882
- onSetShowGuides: s.setShowGuides,
10883
- onSetSnapToGrid: s.setSnapToGrid,
10884
- onSetSnapToShape: s.setSnapToShape,
11039
+ onSetShowGuides: viewPreferencesSync?.handleSetShowGuides ?? s.setShowGuides,
11040
+ onSetSnapToGrid: viewPreferencesSync?.handleSetSnapToGrid ?? s.setSnapToGrid,
11041
+ onSetSnapToShape: viewPreferencesSync?.handleSetSnapToShape ?? s.setSnapToShape,
10885
11042
  onAddGuide: dialogs.handleAddGuide,
10886
11043
  onAlignElements: manipulation.handleAlignElements,
10887
11044
  onDistributeElements: manipulation.handleDistributeElements,
@@ -11068,6 +11225,7 @@ function useViewerBuildingBlocksCore(input) {
11068
11225
  sections: state.sections,
11069
11226
  customShows: state.customShows,
11070
11227
  activeCustomShowId: state.activeCustomShowId,
11228
+ presentationProperties: state.presentationProperties,
11071
11229
  mode,
11072
11230
  activeLayout: state.activeLayout,
11073
11231
  activeMaster: state.activeMaster,
@@ -11076,6 +11234,7 @@ function useViewerBuildingBlocksCore(input) {
11076
11234
  const { presentation, annotations, actionSoundHandlerRef, setExitModeHandler } = usePresentationSetup({
11077
11235
  mode,
11078
11236
  slides,
11237
+ templateElementsBySlideId,
11079
11238
  visibleSlideIndexes,
11080
11239
  endWithBlackSlide: viewerOptions.advanced.slideShowEndWithBlackSlide,
11081
11240
  // File > Options > Advanced > "Prompt to keep ink annotations when
@@ -11120,6 +11279,111 @@ function useViewerBuildingBlocksCore(input) {
11120
11279
  viewerOptions
11121
11280
  };
11122
11281
  }
11282
+ function useCompatibilityToastsState() {
11283
+ const [toasts, setToasts] = useState([]);
11284
+ const dismiss = useCallback((id) => {
11285
+ setToasts((prev) => prev.filter((toast) => toast.id !== id));
11286
+ }, []);
11287
+ const dismissAll = useCallback(() => {
11288
+ setToasts([]);
11289
+ }, []);
11290
+ return { toasts, setToasts, dismiss, dismissAll };
11291
+ }
11292
+ function useReadOnlyRecommendationState(content) {
11293
+ const [recommendation, setRecommendation] = useState(
11294
+ () => readOnlyRecommendation(void 0)
11295
+ );
11296
+ const [bannerHidden, setBannerHidden] = useState(false);
11297
+ const [editAnywayActive, setEditAnywayActive] = useState(false);
11298
+ useEffect(() => {
11299
+ setBannerHidden(false);
11300
+ setEditAnywayActive(false);
11301
+ }, [content]);
11302
+ const editAnyway = useCallback(() => {
11303
+ setEditAnywayActive(true);
11304
+ setBannerHidden(true);
11305
+ }, []);
11306
+ const dismiss = useCallback(() => {
11307
+ setBannerHidden(true);
11308
+ }, []);
11309
+ return {
11310
+ recommendation,
11311
+ setRecommendation,
11312
+ /** Whether the viewer's `canEdit` should be forced off. */
11313
+ locked: recommendation.defaultReadOnly && !editAnywayActive,
11314
+ bannerVisible: recommendation.kind !== null && !bannerHidden,
11315
+ editAnyway,
11316
+ dismiss
11317
+ };
11318
+ }
11319
+ function useViewPreferencesSync(input) {
11320
+ const {
11321
+ loadVersion,
11322
+ viewProperties,
11323
+ setViewProperties,
11324
+ snapToGrid,
11325
+ setSnapToGrid,
11326
+ snapToShape,
11327
+ setSnapToShape,
11328
+ showGuides,
11329
+ setShowGuides
11330
+ } = input;
11331
+ useEffect(() => {
11332
+ const seeded = viewerPreferencesFromViewProperties(
11333
+ { viewProperties },
11334
+ { ...DEFAULT_VIEWER_PREFERENCES, snapToGrid, snapToObjects: snapToShape, showGuides }
11335
+ );
11336
+ setSnapToGrid(seeded.snapToGrid);
11337
+ setSnapToShape(seeded.snapToObjects ?? snapToShape);
11338
+ setShowGuides(seeded.showGuides ?? showGuides);
11339
+ }, [loadVersion]);
11340
+ const patchViewProperties = useCallback(
11341
+ (prefsOverride) => {
11342
+ setViewProperties((prev) => {
11343
+ const merged = {
11344
+ ...DEFAULT_VIEWER_PREFERENCES,
11345
+ snapToGrid,
11346
+ snapToObjects: snapToShape,
11347
+ showGuides,
11348
+ gridSpacingCx: prev?.gridSpacing?.cx,
11349
+ gridSpacingCy: prev?.gridSpacing?.cy,
11350
+ ...prefsOverride
11351
+ };
11352
+ const patch = viewPropertiesPatchFromPreferences(merged);
11353
+ return {
11354
+ ...prev,
11355
+ slideViewPr: { ...prev?.slideViewPr, ...patch.slideViewPr },
11356
+ ...patch.gridSpacing ? { gridSpacing: patch.gridSpacing } : {}
11357
+ };
11358
+ });
11359
+ },
11360
+ [setViewProperties, snapToGrid, snapToShape, showGuides]
11361
+ );
11362
+ const handleSetSnapToGrid = useCallback(
11363
+ (value) => {
11364
+ setSnapToGrid(value);
11365
+ patchViewProperties({ snapToGrid: value });
11366
+ },
11367
+ [setSnapToGrid, patchViewProperties]
11368
+ );
11369
+ const handleSetSnapToShape = useCallback(
11370
+ (value) => {
11371
+ setSnapToShape(value);
11372
+ patchViewProperties({ snapToObjects: value });
11373
+ },
11374
+ [setSnapToShape, patchViewProperties]
11375
+ );
11376
+ const handleSetShowGuides = useCallback(
11377
+ (value) => {
11378
+ setShowGuides(value);
11379
+ patchViewProperties({ showGuides: value });
11380
+ },
11381
+ [setShowGuides, patchViewProperties]
11382
+ );
11383
+ return { handleSetSnapToGrid, handleSetSnapToShape, handleSetShowGuides };
11384
+ }
11385
+
11386
+ // src/viewer/hooks/useViewerBuildingBlocksState.ts
11123
11387
  function useViewerBuildingBlocksState(input) {
11124
11388
  const { core, content, canEdit, filePath, autosaveEnabled, userName, handle, setContent } = input;
11125
11389
  const {
@@ -11175,6 +11439,8 @@ function useViewerBuildingBlocksState(input) {
11175
11439
  isDirty: state.isDirty,
11176
11440
  history
11177
11441
  });
11442
+ const readOnlyRec = useReadOnlyRecommendationState(content);
11443
+ const compatToastsState = useCompatibilityToastsState();
11178
11444
  useEffect(() => {
11179
11445
  state.setSelectedElementId(null);
11180
11446
  state.setSelectedElementIds([]);
@@ -11205,7 +11471,8 @@ function useViewerBuildingBlocksState(input) {
11205
11471
  handleSetMode,
11206
11472
  handleEnterPresenterView,
11207
11473
  handleEnterRehearsalMode,
11208
- autosaveStatus
11474
+ autosaveStatus,
11475
+ loadVersion
11209
11476
  } = useViewerIntegration({
11210
11477
  state,
11211
11478
  zoom,
@@ -11223,6 +11490,8 @@ function useViewerBuildingBlocksState(input) {
11223
11490
  // applies, so the headless building-blocks API loads http(s) images
11224
11491
  // under the same default-off, core-side SSRF/privacy guard.
11225
11492
  allowExternalImages: viewerOptions.trust.allowExternalContent,
11493
+ setReadOnlyRecommendation: readOnlyRec.setRecommendation,
11494
+ setCompatToasts: compatToastsState.setToasts,
11226
11495
  canEdit,
11227
11496
  promptKeepInkAnnotations: viewerOptions.advanced.slideShowPromptKeepInkAnnotations,
11228
11497
  imageExportScale,
@@ -11244,6 +11513,17 @@ function useViewerBuildingBlocksState(input) {
11244
11513
  onSlideCountChange
11245
11514
  });
11246
11515
  setExitModeHandler(handleSetMode);
11516
+ const viewPreferencesSync = useViewPreferencesSync({
11517
+ loadVersion,
11518
+ viewProperties: state.viewProperties,
11519
+ setViewProperties: state.setViewProperties,
11520
+ snapToGrid: state.snapToGrid,
11521
+ setSnapToGrid: state.setSnapToGrid,
11522
+ snapToShape: state.snapToShape,
11523
+ setSnapToShape: state.setSnapToShape,
11524
+ showGuides: state.showGuides,
11525
+ setShowGuides: state.setShowGuides
11526
+ });
11247
11527
  return {
11248
11528
  dialogs,
11249
11529
  editorOps,
@@ -11253,7 +11533,8 @@ function useViewerBuildingBlocksState(input) {
11253
11533
  handleSetMode,
11254
11534
  handleEnterPresenterView,
11255
11535
  handleEnterRehearsalMode,
11256
- autosaveStatus
11536
+ autosaveStatus,
11537
+ viewPreferencesSync
11257
11538
  };
11258
11539
  }
11259
11540
 
@@ -11326,7 +11607,8 @@ function useViewerBuildingBlocks(input) {
11326
11607
  handleSetMode,
11327
11608
  handleEnterPresenterView,
11328
11609
  handleEnterRehearsalMode,
11329
- autosaveStatus
11610
+ autosaveStatus,
11611
+ viewPreferencesSync
11330
11612
  } = useViewerBuildingBlocksState({
11331
11613
  core,
11332
11614
  content,
@@ -11371,7 +11653,8 @@ function useViewerBuildingBlocks(input) {
11371
11653
  onOpenFile,
11372
11654
  onOpenRecentFile,
11373
11655
  fileName,
11374
- hiddenActions
11656
+ hiddenActions,
11657
+ viewPreferencesSync
11375
11658
  });
11376
11659
  const canvasProps = buildCanvasProps({
11377
11660
  mode,
@@ -11469,4 +11752,4 @@ function useResizablePanels() {
11469
11752
  };
11470
11753
  }
11471
11754
 
11472
- export { ACTION_VERB_MAP, MIN_TOUCH_TARGET, ViewerOptionsContext, computeToolbarVisibility, detectTargetType, isAudienceTab, parseAudienceNonce, parseEmailUrl, parseSlideFromUrl, renderToCanvas, scopeLayoutOptionsToActiveSlide, useAnimationPlayback, useAnnotationHandlers, useAudienceMode, useAutosave, useBroadcastFollower, useCanvasInteractions, useClipboardHandlers, useCollaborationLivePatch, useCollaborativeHistory, useCollaborativeState, useComments, useContentLifecycle, useDerivedElementState, useDerivedSlideState, useDialogCustomShows, useEditorHistory, useEditorOperations, useElementManipulation, useElementOperations, useExportHandlers, useExportSaveAs, useFindReplace, useFollowMode, useFontInjection, useGroupAlignLayerHandlers, useIOHandlers, useInsertElements, useIsMobile, useKeyboardInsets, useKeyboardShortcutWiring, useKeyboardShortcuts, useLayoutSwitching, useLoadContent, useMergeShapesHandler, useModalDismissDrag, usePointerHandlers, usePresenceTracking, usePresentationAnnotations, usePresentationKeyboard, usePresentationMode, usePresentationSetup, usePresenterWindow, usePrintHandlers, usePropertyHandlers, useRecoveryDetection, useReducedMotion, useRehearsalTimings, useResizablePanels, useSectionOperations, useSerialize, useSheetDismissDrag, useSlideManagement, useSlideNavigation, useSwipeNavigation, useTableOperations, useThemeHandlers, useThemeSwitching, useToolbarVisibility, useTouchGestures, useViewerBuildingBlocks, useViewerCoreState, useViewerDialogs, useViewerIntegration, useViewerOptions, useViewerOptionsContext, useViewerState, useViewerUIState, useVirtualizedSlides, useYjsDocumentSync, useYjsProvider, useZoomNavigation, useZoomViewport };
11755
+ export { ACTION_VERB_MAP, MIN_TOUCH_TARGET, ViewerOptionsContext, computeToolbarVisibility, detectTargetType, isAudienceTab, parseAudienceNonce, parseEmailUrl, parseSlideFromUrl, renderToCanvas, safePrompt, scopeLayoutOptionsToActiveSlide, syncSelectionHandleOverlay, useAnimationPlayback, useAnnotationHandlers, useAudienceMode, useAutosave, useBroadcastFollower, useCanvasInteractions, useClipboardHandlers, useCollaborationLivePatch, useCollaborativeHistory, useCollaborativeState, useComments, useCompatibilityToastsState, useContentLifecycle, useDerivedElementState, useDerivedSlideState, useDialogCustomShows, useEditorHistory, useEditorOperations, useElementManipulation, useElementOperations, useExportHandlers, useExportSaveAs, useFindReplace, useFollowMode, useFontInjection, useGroupAlignLayerHandlers, useIOHandlers, useInsertElements, useIsMobile, useKeyboardInsets, useKeyboardShortcutWiring, useKeyboardShortcuts, useLayoutSwitching, useLoadContent, useMergeShapesHandler, useModalDismissDrag, usePointerHandlers, usePresenceTracking, usePresentationAnnotations, usePresentationKeyboard, usePresentationMode, usePresentationSetup, usePresenterWindow, usePrintHandlers, usePropertyHandlers, useReadOnlyRecommendationState, useRecoveryDetection, useReducedMotion, useRehearsalTimings, useResizablePanels, useSectionOperations, useSerialize, useSheetDismissDrag, useSlideManagement, useSlideNavigation, useSwipeNavigation, useTableOperations, useThemeHandlers, useThemeSwitching, useToolbarVisibility, useTouchGestures, useViewPreferencesSync, useViewerBuildingBlocks, useViewerCoreState, useViewerDialogs, useViewerIntegration, useViewerOptions, useViewerOptionsContext, useViewerState, useViewerUIState, useVirtualizedSlides, useYjsDocumentSync, useYjsProvider, useZoomNavigation, useZoomViewport };