pptx-react-viewer 2.16.10 → 2.18.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 (136) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +14 -14
  3. package/dist/{AiChatPanel-XHKYPYPH.mjs → AiChatPanel-SSSBERWJ.mjs} +4 -4
  4. package/dist/AiChatPanel-SSSBERWJ.mjs.br +0 -0
  5. package/dist/AiChatPanel-SSSBERWJ.mjs.gz +0 -0
  6. package/dist/{AiChatPanel-EPMBL6EA.js → AiChatPanel-UN5DHPZY.js} +24 -24
  7. package/dist/AiChatPanel-UN5DHPZY.js.br +0 -0
  8. package/dist/AiChatPanel-UN5DHPZY.js.gz +0 -0
  9. package/dist/{Model3DScene-DUJOZA2B.js → Model3DScene-4RPYKC2I.js} +2 -2
  10. package/dist/Model3DScene-4RPYKC2I.js.br +0 -0
  11. package/dist/Model3DScene-4RPYKC2I.js.gz +0 -0
  12. package/dist/{Model3DScene-YMIAGRPG.mjs → Model3DScene-LYEESYAI.mjs} +1 -1
  13. package/dist/Model3DScene-LYEESYAI.mjs.br +0 -0
  14. package/dist/Model3DScene-LYEESYAI.mjs.gz +0 -0
  15. package/dist/{PowerPointViewer-Du7u4INi.d.ts → PowerPointViewer-Cf4YHjdF.d.ts} +2 -2
  16. package/dist/PowerPointViewer-Cf4YHjdF.d.ts.map +1 -0
  17. package/dist/{SurfaceChart3DScene-TNCHWQAK.js → SurfaceChart3DScene-ACPKCWLJ.js} +2 -2
  18. package/dist/SurfaceChart3DScene-ACPKCWLJ.js.br +0 -0
  19. package/dist/SurfaceChart3DScene-ACPKCWLJ.js.gz +0 -0
  20. package/dist/{SurfaceChart3DScene-Q47OEJXS.mjs → SurfaceChart3DScene-JR62SOYO.mjs} +1 -1
  21. package/dist/SurfaceChart3DScene-JR62SOYO.mjs.br +0 -0
  22. package/dist/SurfaceChart3DScene-JR62SOYO.mjs.gz +0 -0
  23. package/dist/{audience-content-store-BvqKy7Hf.d.ts → audience-content-store-CyS0oP6t.d.ts} +2 -2
  24. package/dist/{audience-content-store-BvqKy7Hf.d.ts.map → audience-content-store-CyS0oP6t.d.ts.map} +1 -1
  25. package/dist/{chunk-WK22Z7PK.js → chunk-4LWGAVAP.js} +526 -928
  26. package/dist/chunk-4LWGAVAP.js.br +0 -0
  27. package/dist/chunk-4LWGAVAP.js.gz +0 -0
  28. package/dist/{chunk-EL2YF26P.mjs → chunk-7XF6S4AM.mjs} +1 -1
  29. package/dist/chunk-7XF6S4AM.mjs.br +0 -0
  30. package/dist/chunk-7XF6S4AM.mjs.gz +0 -0
  31. package/dist/{chunk-FSVE6WUY.js → chunk-ASKYZ5ZY.js} +728 -523
  32. package/dist/chunk-ASKYZ5ZY.js.br +0 -0
  33. package/dist/chunk-ASKYZ5ZY.js.gz +0 -0
  34. package/dist/{chunk-VHME5BE6.mjs → chunk-BZOL6FXF.mjs} +326 -728
  35. package/dist/chunk-BZOL6FXF.mjs.br +0 -0
  36. package/dist/chunk-BZOL6FXF.mjs.gz +0 -0
  37. package/dist/{chunk-YWKUIUQX.mjs → chunk-GODW6RNR.mjs} +87648 -78804
  38. package/dist/chunk-GODW6RNR.mjs.br +0 -0
  39. package/dist/chunk-GODW6RNR.mjs.gz +0 -0
  40. package/dist/{chunk-ESC6IB2F.js → chunk-ICZDMFF2.js} +2147 -3399
  41. package/dist/chunk-ICZDMFF2.js.br +0 -0
  42. package/dist/chunk-ICZDMFF2.js.gz +0 -0
  43. package/dist/{chunk-PI5SFTDC.js → chunk-MAVLV56S.js} +3 -3
  44. package/dist/chunk-MAVLV56S.js.br +0 -0
  45. package/dist/chunk-MAVLV56S.js.gz +0 -0
  46. package/dist/{chunk-G3VAE7ZF.mjs → chunk-O7FTUUXP.mjs} +1212 -2464
  47. package/dist/chunk-O7FTUUXP.mjs.br +0 -0
  48. package/dist/chunk-O7FTUUXP.mjs.gz +0 -0
  49. package/dist/{chunk-J7EFC3GS.mjs → chunk-QUB5OSKY.mjs} +394 -189
  50. package/dist/chunk-QUB5OSKY.mjs.br +0 -0
  51. package/dist/chunk-QUB5OSKY.mjs.gz +0 -0
  52. package/dist/{chunk-DVZF4ARG.js → chunk-RTUGI4HQ.js} +49 -4
  53. package/dist/chunk-RTUGI4HQ.js.br +0 -0
  54. package/dist/chunk-RTUGI4HQ.js.gz +0 -0
  55. package/dist/{chunk-5IUEPQTK.mjs → chunk-SNL6LBRX.mjs} +49 -4
  56. package/dist/chunk-SNL6LBRX.mjs.br +0 -0
  57. package/dist/chunk-SNL6LBRX.mjs.gz +0 -0
  58. package/dist/{chunk-Y2KIB3QH.js → chunk-WVDM36ZM.js} +87730 -78843
  59. package/dist/chunk-WVDM36ZM.js.br +0 -0
  60. package/dist/chunk-WVDM36ZM.js.gz +0 -0
  61. package/dist/i18n.js +4 -4
  62. package/dist/i18n.js.br +0 -0
  63. package/dist/i18n.js.gz +0 -0
  64. package/dist/i18n.mjs +1 -1
  65. package/dist/i18n.mjs.br +0 -0
  66. package/dist/i18n.mjs.gz +0 -0
  67. package/dist/index.d.ts +702 -37
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +65 -41
  70. package/dist/index.js.br +0 -0
  71. package/dist/index.js.gz +0 -0
  72. package/dist/index.mjs +6 -6
  73. package/dist/index.mjs.br +0 -0
  74. package/dist/index.mjs.gz +0 -0
  75. package/dist/internals.d.ts +923 -54
  76. package/dist/internals.d.ts.map +1 -1
  77. package/dist/internals.js +79 -79
  78. package/dist/internals.js.br +0 -0
  79. package/dist/internals.js.gz +0 -0
  80. package/dist/internals.mjs +4 -4
  81. package/dist/internals.mjs.br +0 -0
  82. package/dist/internals.mjs.gz +0 -0
  83. package/dist/pptx-viewer.css +1 -1
  84. package/dist/pptx-viewer.css.br +0 -0
  85. package/dist/pptx-viewer.css.gz +0 -0
  86. package/dist/{types-DU35aAJK.d.ts → types-C_CTI-QE.d.ts} +3 -31
  87. package/dist/types-C_CTI-QE.d.ts.map +1 -0
  88. package/dist/{useViewerBuildingBlocks-Xddn6SFj.d.ts → useViewerBuildingBlocks-DItilDbP.d.ts} +47 -8
  89. package/dist/useViewerBuildingBlocks-DItilDbP.d.ts.map +1 -0
  90. package/dist/viewer/index.d.ts +593 -56
  91. package/dist/viewer/index.js +27 -27
  92. package/dist/viewer/index.js.br +0 -0
  93. package/dist/viewer/index.js.gz +0 -0
  94. package/dist/viewer/index.mjs +6 -6
  95. package/dist/viewer/index.mjs.br +0 -0
  96. package/dist/viewer/index.mjs.gz +0 -0
  97. package/package.json +2 -2
  98. package/dist/AiChatPanel-EPMBL6EA.js.br +0 -0
  99. package/dist/AiChatPanel-EPMBL6EA.js.gz +0 -0
  100. package/dist/AiChatPanel-XHKYPYPH.mjs.br +0 -0
  101. package/dist/AiChatPanel-XHKYPYPH.mjs.gz +0 -0
  102. package/dist/Model3DScene-DUJOZA2B.js.br +0 -0
  103. package/dist/Model3DScene-DUJOZA2B.js.gz +0 -0
  104. package/dist/Model3DScene-YMIAGRPG.mjs.br +0 -0
  105. package/dist/Model3DScene-YMIAGRPG.mjs.gz +0 -0
  106. package/dist/PowerPointViewer-Du7u4INi.d.ts.map +0 -1
  107. package/dist/SurfaceChart3DScene-Q47OEJXS.mjs.br +0 -0
  108. package/dist/SurfaceChart3DScene-Q47OEJXS.mjs.gz +0 -0
  109. package/dist/SurfaceChart3DScene-TNCHWQAK.js.br +0 -0
  110. package/dist/SurfaceChart3DScene-TNCHWQAK.js.gz +0 -0
  111. package/dist/chunk-5IUEPQTK.mjs.br +0 -0
  112. package/dist/chunk-5IUEPQTK.mjs.gz +0 -0
  113. package/dist/chunk-DVZF4ARG.js.br +0 -0
  114. package/dist/chunk-DVZF4ARG.js.gz +0 -0
  115. package/dist/chunk-EL2YF26P.mjs.br +0 -0
  116. package/dist/chunk-EL2YF26P.mjs.gz +0 -0
  117. package/dist/chunk-ESC6IB2F.js.br +0 -0
  118. package/dist/chunk-ESC6IB2F.js.gz +0 -0
  119. package/dist/chunk-FSVE6WUY.js.br +0 -0
  120. package/dist/chunk-FSVE6WUY.js.gz +0 -0
  121. package/dist/chunk-G3VAE7ZF.mjs.br +0 -0
  122. package/dist/chunk-G3VAE7ZF.mjs.gz +0 -0
  123. package/dist/chunk-J7EFC3GS.mjs.br +0 -0
  124. package/dist/chunk-J7EFC3GS.mjs.gz +0 -0
  125. package/dist/chunk-PI5SFTDC.js.br +0 -0
  126. package/dist/chunk-PI5SFTDC.js.gz +0 -0
  127. package/dist/chunk-VHME5BE6.mjs.br +0 -0
  128. package/dist/chunk-VHME5BE6.mjs.gz +0 -0
  129. package/dist/chunk-WK22Z7PK.js.br +0 -0
  130. package/dist/chunk-WK22Z7PK.js.gz +0 -0
  131. package/dist/chunk-Y2KIB3QH.js.br +0 -0
  132. package/dist/chunk-Y2KIB3QH.js.gz +0 -0
  133. package/dist/chunk-YWKUIUQX.mjs.br +0 -0
  134. package/dist/chunk-YWKUIUQX.mjs.gz +0 -0
  135. package/dist/types-DU35aAJK.d.ts.map +0 -1
  136. package/dist/useViewerBuildingBlocks-Xddn6SFj.d.ts.map +0 -1
@@ -1,12 +1,12 @@
1
- import { computeFocusTargets, aiChangeAnimationCss, changeGhostStyle, createChatHistoryStore, toRenderableParts } from './chunk-EL2YF26P.mjs';
2
- import { useReducedMotion, useViewerState, useViewerOptions, useIsMobile, useResizablePanels, useDerivedSlideState, useZoomViewport, useEditorHistory, usePresentationSetup, useTouchGestures, useViewerDialogs, useEditorOperations, useViewerIntegration, useLayoutSwitching, useYjsDocumentSync, useCollaborationLivePatch, useFollowMode, useBroadcastFollower, scopeLayoutOptionsToActiveSlide, useToolbarVisibility, useKeyboardInsets, useModalDismissDrag, useCollaborativeState, useVirtualizedSlides, useSwipeNavigation, useSheetDismissDrag, detectTargetType, parseEmailUrl, parseSlideFromUrl, ACTION_VERB_MAP, downloadBlob } from './chunk-J7EFC3GS.mjs';
3
- import { MIN_ELEMENT_SIZE, DEFAULT_STROKE_COLOR, getConnectorPathGeometry, buildLineShadowCss, buildLineGlowFilter, renderConnectorMarker, getShapeVisualStyle, getTextStyleForElement, DEFAULT_TEXT_COLOR, renderVectorShape, isConnectorOrLineElement, build3DExtrusionData, getImageRenderStyle, cn, getTextLayoutStyle, renderTableElement, renderMediaElement, shouldRenderFallbackLabel, SHAPE_PRESETS, ANIMATION_PRESET_OPTIONS, DEFAULT_FILL_COLOR, hasDagDuotoneEffect, renderDagDuotoneSvgFilter, getCropShapeClipPath, isImageTiled, getImageTilingStyle, getDuotoneColors, getTextWarpStyle, renderTextSegments, buildReactChartViewModel, renderChartElement, buildTextBody3DSceneStyle, SLIDE_NAV_THUMBNAIL_WIDTH, DEFAULT_TEXT_FONT_SIZE, DEFAULT_FONT_FAMILY, TOOLBAR_SECTIONS, SHORTCUT_REFERENCE_ITEMS as SHORTCUT_REFERENCE_ITEMS$1, HYPERLINK_COLOR, GRID_SIZE, CONNECTOR_ARROW_OPTIONS, ARROW_SIZE_OPTIONS, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation, THEME_COLOR_SWATCHES, STROKE_DASH_OPTIONS } from './chunk-VHME5BE6.mjs';
4
- import { LOCALE_CATALOG, translationsEn } from './chunk-5IUEPQTK.mjs';
5
- import { hasShapeProperties, normalizeHexColor, colorWithOpacity, normalizeStrokeDashType, getSvgStrokeDasharray, getCompoundLineOffsets, getCompoundLineWidths, svgLineCap, hasTextProperties, getElementTransform, isEditableTextElement, isElementRendered, isElementActionable, getAriaRole, getAriaLabel, getAriaRoleDescription, getImageEffectsOpacity, getImageEffectsFilter, getElementTransformWithoutRotation, isHollowShapeElement, LINK_TOOLTIP_HOST_CLASS, ACTION_BUTTON_PRESETS, mediaTransportVisible, isInkElement, getElementLabel, SWITCHABLE_LAYOUT_TYPES, DEFAULT_VIEWER_OPTIONS, compileEquationTemplateMathMl, buildUserFontFaceStyles, readStoredViewerPrefs, resolveThemeCatalogEntry, THEME_CATALOG, writeStoredViewerPrefs, openPptxFile, readBackstageRecentFile, viewerOptionsToPreferences, applyPreferenceToOptions, listAutosaveSnapshots, deleteAutosaveSnapshot, paintedStrokeWidth, buildCssGradientFromShapeStyle, ensureActionAffordanceStyles, resolveElementActionAffordance, actionAffordanceLabels, getComputedEffectStyle, getSoftEdgeSvgFilter, buildStrokeOutline, buildHollowHitOutline, strokeOutlineViewBox, getGroupChildParentFill, revealedSmartArtNodeCount, buildSmartArtA11y, buildSummaryZoomView, getImageColorWashStyle, isImageLikeElement, placeCaretAtEnd, getPendingSelectionRestore, restoreSegmentSelection, getTextCompensationTransform, applyChartBuildReveal, formatAxisValue, hasPressureVariation, getInkReplayStyles, INK_REPLAY_KEYFRAMES, resolveInkColor, resolveInkWidth, resolveInkOpacity, pressuresToWidths, getContentPartReplayStyles, resolveOleType, getOleTypeColor, getOleTypeLabel, filterRenderedElements, resolveGroupChildFill, isLinkedTextBox, getOverflowSegments, shouldUseSvgWarp, RULER_THICKNESS, RULER_FONT_SIZE, buildPreviewElements, deriveSlideFieldContext, SLIDE_TRANSITION_OPTIONS, hiddenSlideCue, HIDDEN_SLIDE_SLASH_GRADIENT, HIDDEN_SLIDE_LABEL_KEY, themeToCssVars, applyMotionPathPreset, DEFAULT_INSERT_CHART_KIND, hasCopyableFormat, templateSchemeFromTheme, createBackstagePresentation, printPropertiesFrameSlides, printPropertiesSlidesPerPage, VIEWER_OPTIONS_TABS, DEFAULT_QUICK_ACCESS_COMMAND_IDS, buildJoinCollaborationConfig, buildCreateCollaborationConfig, generateBroadcastRoomId, DEFAULT_BROADCAST_SERVER_URL, buildBroadcastViewerUrl, resolveTransportForServerUrl, ACTION_INDICATOR_CLASS, ACTION_INDICATOR_ICON_PATH, LINK_TOOLTIP_CLASS, LINK_TOOLTIP_PANEL_CLASS, LINK_TOOLTIP_LABEL_CLASS, LINK_TOOLTIP_HINT_CLASS, DEFAULT_PALETTE2, resolvePalette, computeDrawingViewBox, projectDrawingShapes, styleShadowFilter, resolveDrawingShapeNodeId, getImageSvgFilters, buildDuotoneCacheKey, getDuotoneCachedResult, applyDuotone, setDuotoneCachedResult, buildCacheKey, DEFAULT_COLOR_CHANGE_TOLERANCE, getCachedResult, applyColorChange, setCachedResult, findChartPartTarget, dragValueForPart, withChartPointValue, dragAnchorViewY, withChartTitle, computeSmartArtLayout, buildSmartArt3DModel, extractPathPoints, generatePressureCircles, isBrowserOpenableMime, formatBytes, getOleBadgeLabel, openUrlInNewTab, getSubstituteFontFamily, getWarpPath, getSlideBackgroundStyle, filterCommands, resolveTitleBarStatusKey, TITLE_BAR_CLASSES, TITLE_BAR_DEFAULT_FILE_KEY, buildFieldSubstitutionContext, shouldConfirmExternalHyperlink, safeOpenUrl, isPpactionUrl, parsePpactionUrl, isPresentationAdvanceClick, toggleBlackboard, PRESENT_TOOLBAR_CLASSES, buildContextMenuEntries, themeColorSchemesEqual, EXPANDED_MAX_HEIGHT, STATUS_BAR_CLASSES, formatVersionTimestamp, formatRelativeTime, scanAvailableFontFamilies, PRESETS, CATEGORIES, convertOmmlToLatex, compileLatexEquation, convertLatexToOmml, EQUATION_TEMPLATES, clampPercent, HANDOUT_OPTIONS, TOOLBAR_TABS, SHORTCUT_REFERENCE_ITEMS, resolveViewerAddinRows, availableQuickAccessCommands, addQuickAccessCommand, removeQuickAccessCommand, moveQuickAccessCommand, activateModalFocus, buildCollaborationShareUrl, canGoPrevious, formatSlideCounter, READING_VIEW_COUNTER_ATTR, canGoNext, READING_VIEW_ATTR, OUTLINE_LEVEL_ATTR, OUTLINE_SLIDE_ATTR, OUTLINE_ROW_ATTR, OUTLINE_VIEW_ATTR, mobileElapsedSince, nextPresentedSlide, presenterPrevDisabled, presenterNextDisabled, formatMobileElapsed, mobileSlideCounter, presenterPaneAdvancesOnClick, presenterTimerProgress, PRESENTER_CONSOLE_CLASSES, formatElapsed, centeredSvgTextLines, buildTree, treeWidth, treeDepth, fitFontSize, computeInlineEditorRect, findSmartArtNodeText, setSmartArtNodeStyle, rebuildDrawingShapesIfCleared, shouldCommitSmartArtNodeText, updateSmartArtNodeText, groupIntoParagraphs, substituteFieldText, extraQuickAccessCommands, TAB_ROW_ACTION_CLASSES, listBackstageRecentFiles, BACKSTAGE_NAV, INSERT_CHART_TYPES, CHARACTER_SPACING_OPTIONS, DEFAULT_MOTION_PATH_PRESET_ID, SLIDE_VIRTUALIZATION_THRESHOLD, PRESENTATION_STAGE_ATTRIBUTE, getSlideTransitionAnimations, SLIDE_TRANSITION_KEYFRAMES, annotationOverlayZIndex, presentationInkPath, motionPathFor, setMotionPath, getShapeAdjustmentHandleDescriptor, PRESENTER_NAVIGATOR_LABEL_KEYS, PEN_COLORS, HIGHLIGHTER_COLORS, isBlackboardActive, PRESET_THEMES, normalizeSegments, createPlainNotesSegments, segmentsToEditorHtml, DEBOUNCE_MS, parseSegmentsFromRichEditor, segmentsToPlainText, getCurrentParagraphIndex, segmentsToParagraphs, paragraphsToSegments, MAX_INDENT_LEVEL, INDENT_PX, isUrlSafe, escapeHtml2, sanitizeMathMl, computeHandoutLayout, getPrintableArea, generateNoteLineCount, computeAllNotesPages, getNotesPrintableArea, QUICK_ACCESS_COMMAND_CATALOG, openReadingView, applyReadingViewCommand, createPresentationKeyBuffer, readingViewFitScale, READING_VIEW_STAGE_ATTR, buildOutline, applyOutlineEdit, mapOutlineKey, notesSegmentsToSpans, erasePresentationInkAt, movePresenterPointer, appendPresentationInkPoint, PRESENTER_CONSOLE_CONTROLS, NOTES_FONT_SIZE_DEFAULT, formatTime, NOTES_FONT_SIZE_MIN, clampNotesFontSize, NOTES_FONT_SIZE_STEP, NOTES_FONT_SIZE_MAX, styleShadow, styleStroke, nodeOpacity, truncate, paletteColour, BACKSTAGE_TEMPLATES, formatBackstageDate, formatBackstageSize, backstageCardsFor, DEFAULT_VIEWER_PROFILE, resolveProfileInitial, AVATAR_COLOR_SWATCHES, ENTRANCE_PRESET_VALUES, EMPHASIS_PRESET_VALUES, EXIT_PRESET_VALUES, MOTION_PATH_FAMILIES, motionPathFamilyLabelKey, motionPathPresetsByFamily, motionPathPresetLabelKey, getConnectionSites, generateTicks, rulerDragToGuidePosition, getCommentMarkerPosition, motionPathToSvgD, motionPathEndPixel, isEditableMotionPath, setMotionPathEnd, themeColorLabel, buildThemeColorGrid, THEME_COLOR_SCHEME_KEYS, COMMON_FONTS, buildSmartArtPresetData, handleReadingViewKey, getLocalStorageUsageSummary, saveViewerProfile, clearAllLocalViewerData, SLIDE_TEMPLATES, mergeSlideTransition, buildSlideTemplateContent, buildTableDataGrid, appendTableElementRow, removeLastTableElementRow, appendTableElementColumn, removeLastTableElementColumn, removeTableElementColumn, removeTableElementRow, setTableElementCellText, TABLE_STYLE_PRESETS, chartDataChangeType, setChartCategoryLabel, setChartCellValue, addChartCategory, removeChartCategory, addChartSeries, removeChartSeries, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, setChartDataPointMarker, setChartDataPointLabel, getOleObjectTypeLabel, sanitizeGradientStops, pptxActionToElementAction, withFrameSlides, withSlidesPerPage, applyThemeOverrideToSlide, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, schemaLabel, THEME_COLOR_SLOT_LABEL_KEYS, TRANSITION_VALID_DIRECTIONS, TRANSITION_ORIENTATION_TYPES, clampTransitionNumber, flattenTagCollections, formatCommentTimestamp, clearMotionPath, DIRECTIONAL_PRESETS, motionPathPresetIdForPath, computeMergeCellRight, computeMergeCellDown, computeSplitCell, SMARTART_COLOR_SCHEME_LABEL_KEYS, SMARTART_STYLE_LABEL_KEYS, addSmartArtNodeAsChild, removeSmartArtNode, clampCropValue, DUOTONE_PRESETS, ARTISTIC_EFFECTS, resolveActionType, ELEMENT_ACTION_TYPE_OPTIONS, toSlideIndex, elementActionToPptxAction, canCommitActionType, TRANSITION_DIR_ARROWS, buildDirectionGrid, updateTagInCollections, deleteTagFromCollections, addTagToCollections, animationEffectLabel, LBL, SEL, FILL_MODE_OPTIONS, SECTION_HEADING, NUM, switchSmartArtLayout, addSmartArtNode, demoteSmartArtNode, promoteSmartArtNode, reorderSmartArtNode, SHAPE_QUICK_STYLES, quickStyleSwatchCss, getPatternSvg, TEXT_WARP_PRESETS, warpPreviewPath, text3dPtToEmu, text3dEmuToPt, MATERIAL_PRESETS, GRADIENT_TYPE_OPTIONS, PATTERN_OPTIONS, FILL_PATTERN_LABEL_KEYS, openNativeEyeDropper, toggleText3dExtrusion, BEVEL_PRESETS } from './chunk-YWKUIUQX.mjs';
1
+ import { computeFocusTargets, aiChangeAnimationCss, changeGhostStyle, createChatHistoryStore, toRenderableParts } from './chunk-7XF6S4AM.mjs';
2
+ import { useReducedMotion, useViewerState, useViewerOptions, useIsMobile, useResizablePanels, useDerivedSlideState, useZoomViewport, useEditorHistory, usePresentationSetup, useTouchGestures, useViewerDialogs, useEditorOperations, useViewerIntegration, useLayoutSwitching, useYjsDocumentSync, useCollaborationLivePatch, useFollowMode, useBroadcastFollower, scopeLayoutOptionsToActiveSlide, useToolbarVisibility, useKeyboardInsets, useModalDismissDrag, useCollaborativeState, useVirtualizedSlides, useSwipeNavigation, useSheetDismissDrag, detectTargetType, parseEmailUrl, parseSlideFromUrl, ACTION_VERB_MAP, downloadBlob } from './chunk-QUB5OSKY.mjs';
3
+ import { MIN_ELEMENT_SIZE, DEFAULT_STROKE_COLOR, getConnectorPathGeometry, buildLineShadowCss, buildLineGlowFilter, renderConnectorMarker, getShapeVisualStyle, getTextStyleForElement, DEFAULT_TEXT_COLOR, renderVectorShape, isConnectorOrLineElement, build3DExtrusionData, getImageRenderStyle, cn, getTextLayoutStyle, renderTableElement, renderMediaElement, shouldRenderFallbackLabel, SHAPE_PRESETS, ANIMATION_PRESET_OPTIONS, DEFAULT_FILL_COLOR, hasDagDuotoneEffect, renderDagDuotoneSvgFilter, getCropShapeClipPath, getImageTilingStyle, getDuotoneColors, getTextWarpStyle, renderTextSegments, buildReactChartViewModel, renderChartElement, buildTextBody3DSceneStyle, SLIDE_NAV_THUMBNAIL_WIDTH, DEFAULT_TEXT_FONT_SIZE, DEFAULT_FONT_FAMILY, TOOLBAR_SECTIONS, DEFAULT_SECTION_GROUP_ID, SHORTCUT_REFERENCE_ITEMS as SHORTCUT_REFERENCE_ITEMS$1, HYPERLINK_COLOR, GRID_SIZE, CONNECTOR_ARROW_OPTIONS, ARROW_SIZE_OPTIONS, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation, THEME_COLOR_SWATCHES, STROKE_DASH_OPTIONS } from './chunk-BZOL6FXF.mjs';
4
+ import { LOCALE_CATALOG, translationsEn } from './chunk-SNL6LBRX.mjs';
5
+ import { hasShapeProperties, normalizeHexColor, colorWithOpacity, normalizeStrokeDashType, getSvgStrokeDasharray, getCompoundLineOffsets, getCompoundLineWidths, svgLineCap, hasTextProperties, getElementTransform, getAriaRoleDescription, getAriaLabel, getAriaRole, resolveElementInteractivity, isEditableTextElement, isElementRendered, isElementActionable, getImageEffectsOpacity, getImageEffectsFilter, getElementTransformWithoutRotation, inlineElementPointerEvents, isHollowShapeElement, LINK_TOOLTIP_HOST_CLASS, ACTION_BUTTON_PRESETS, mediaTransportVisible, isInkElement, getGroupChildParentFill, getElementLabel, SWITCHABLE_LAYOUT_TYPES, DEFAULT_VIEWER_OPTIONS, compileEquationTemplateMathMl, buildUserFontFaceStyles, readStoredViewerPrefs, resolveThemeCatalogEntry, THEME_CATALOG, writeStoredViewerPrefs, openPptxFile, readBackstageRecentFile, viewerOptionsToPreferences, applyPreferenceToOptions, listAutosaveSnapshots, deleteAutosaveSnapshot, resolveAutosaveIntervalSeconds, paintedStrokeWidth, buildCssGradientFromShapeStyle, ensureActionAffordanceStyles, resolveElementActionAffordance, actionAffordanceLabels, getComputedEffectStyle, getSoftEdgeSvgFilter, buildStrokeOutline, buildHollowHitOutline, strokeOutlineViewBox, revealedSmartArtNodeCount, buildSmartArtA11y, computeSmartArtLayout, flattenNodes, buildSummaryZoomView, isImageTiled, getImageColorWashStyle, isImageLikeElement, placeCaretAtEnd, getPendingSelectionRestore, restoreSegmentSelection, getTextCompensationTransform, applyChartBuildReveal, formatAxisValue, hasPressureVariation, getInkReplayStyles, INK_REPLAY_KEYFRAMES, resolveInkColor, resolveInkWidth, resolveInkOpacity, pressuresToWidths, getContentPartReplayStyles, resolveOleType, getOleTypeColor, getOleTypeLabel, filterRenderedElements, resolveGroupChildFill, isLinkedTextBox, getOverflowSegments, shouldUseSvgWarp, getTextBodyRotationTransform, RULER_THICKNESS, RULER_FONT_SIZE, buildPreviewElements, deriveSlideFieldContext, SLIDE_TRANSITION_OPTIONS, hiddenSlideCue, HIDDEN_SLIDE_SLASH_GRADIENT, HIDDEN_SLIDE_LABEL_KEY, themeToCssVars, applyMotionPathPreset, resetSlideLayoutPath, DEFAULT_INSERT_CHART_KIND, hasCopyableFormat, templateSchemeFromTheme, createBackstagePresentation, setMasterViewBackgroundColor, printPropertiesFrameSlides, printPropertiesSlidesPerPage, VIEWER_OPTIONS_TABS, DEFAULT_QUICK_ACCESS_COMMAND_IDS, buildJoinCollaborationConfig, buildCreateCollaborationConfig, generateBroadcastRoomId, DEFAULT_BROADCAST_SERVER_URL, buildBroadcastViewerUrl, resolveTransportForServerUrl, ACTION_INDICATOR_CLASS, ACTION_INDICATOR_ICON_PATH, LINK_TOOLTIP_CLASS, LINK_TOOLTIP_PANEL_CLASS, LINK_TOOLTIP_LABEL_CLASS, LINK_TOOLTIP_HINT_CLASS, DEFAULT_PALETTE2, resolvePalette, computeDrawingViewBox, projectDrawingShapes, styleShadowFilter, resolveDrawingShapeNodeId, getImageSvgFilters, buildDuotoneCacheKey, getDuotoneCachedResult, applyDuotone, setDuotoneCachedResult, buildCacheKey, DEFAULT_COLOR_CHANGE_TOLERANCE, getCachedResult, applyColorChange, setCachedResult, findChartPartTarget, dragValueForPart, withChartPointValue, dragAnchorViewY, withChartTitle, buildSmartArt3DModel, extractPathPoints, generatePressureCircles, isBrowserOpenableMime, formatBytes, getOleBadgeLabel, openUrlInNewTab, getSubstituteFontFamily, getWarpPath, getSlideBackgroundStyle, filterCommands, resolveTitleBarStatusKey, TITLE_BAR_CLASSES, TITLE_BAR_DEFAULT_FILE_KEY, masterViewBackgroundColor, buildFieldSubstitutionContext, shouldConfirmExternalHyperlink, safeOpenUrl, isPpactionUrl, parsePpactionUrl, isPresentationAdvanceClick, toggleBlackboard, PRESENT_TOOLBAR_CLASSES, buildContextMenuEntries, themeColorSchemesEqual, EXPANDED_MAX_HEIGHT, STATUS_BAR_CLASSES, formatVersionTimestamp, formatRelativeTime, scanAvailableFontFamilies, PRESETS, CATEGORIES, convertOmmlToLatex, compileLatexEquation, convertLatexToOmml, EQUATION_TEMPLATES, clampPercent, HANDOUT_OPTIONS, TOOLBAR_TABS, SHORTCUT_REFERENCE_ITEMS, resolveViewerAddinRows, availableQuickAccessCommands, addQuickAccessCommand, removeQuickAccessCommand, moveQuickAccessCommand, CUSTOM_FONT_ACCEPT, activateModalFocus, buildCollaborationShareUrl, SORTER_MIN_ZOOM, SORTER_ZOOM_STEP, SORTER_MAX_ZOOM, canGoPrevious, formatSlideCounter, READING_VIEW_COUNTER_ATTR, canGoNext, READING_VIEW_ATTR, OUTLINE_LEVEL_ATTR, OUTLINE_SLIDE_ATTR, OUTLINE_ROW_ATTR, OUTLINE_VIEW_ATTR, mobileElapsedSince, nextPresentedSlide, presenterPrevDisabled, presenterNextDisabled, formatMobileElapsed, mobileSlideCounter, presenterPaneAdvancesOnClick, presenterTimerProgress, PRESENTER_CONSOLE_CLASSES, formatElapsed, centeredSvgTextLines, computeInlineEditorRect, findSmartArtNodeText, setSmartArtNodeStyle, rebuildDrawingShapesIfCleared, shouldCommitSmartArtNodeText, updateSmartArtNodeText, groupIntoParagraphs, substituteFieldText, extraQuickAccessCommands, TAB_ROW_ACTION_CLASSES, listBackstageRecentFiles, BACKSTAGE_NAV, INSERT_CHART_TYPES, CHARACTER_SPACING_OPTIONS, RIBBON_SHAPE_SWATCHES, shapeFillChange, shapeOutlineChange, readRibbonTransitionDraft, ribbonTransitionUpdates, playSlideTransitionPreview, RIBBON_TRANSITION_PRESETS, DEFAULT_MOTION_PATH_PRESET_ID, SLIDE_SHOW_OPTIONS, slideShowOptionChange, readSlideShowOption, SLIDE_VIRTUALIZATION_THRESHOLD, PRESENTATION_HIT_TEST_CSS, PRESENTATION_STAGE_ATTRIBUTE, getSlideTransitionAnimations, SLIDE_TRANSITION_KEYFRAMES, annotationOverlayZIndex, presentationInkPath, motionPathFor, setMotionPath, getShapeAdjustmentHandleDescriptors, PRESENTER_NAVIGATOR_LABEL_KEYS, PEN_COLORS, HIGHLIGHTER_COLORS, isBlackboardActive, slideSizeToCanvasPx, resolveSelectionPaneRename, PRESET_THEMES, normalizeSegments, createPlainNotesSegments, segmentsToEditorHtml, DEBOUNCE_MS, parseSegmentsFromRichEditor, segmentsToPlainText, getCurrentParagraphIndex, segmentsToParagraphs, paragraphsToSegments, MAX_INDENT_LEVEL, INDENT_PX, isUrlSafe, escapeHtml2, sanitizeMathMl, computeHandoutLayout, getPrintableArea, generateNoteLineCount, computeAllNotesPages, getNotesPrintableArea, QUICK_ACCESS_COMMAND_CATALOG, registerCustomFont, openReadingView, applyReadingViewCommand, createPresentationKeyBuffer, readingViewFitScale, READING_VIEW_STAGE_ATTR, buildOutline, applyOutlineEdit, mapOutlineKey, notesSegmentsToSpans, erasePresentationInkAt, movePresenterPointer, appendPresentationInkPoint, PRESENTER_CONSOLE_CONTROLS, NOTES_FONT_SIZE_DEFAULT, formatTime, NOTES_FONT_SIZE_MIN, clampNotesFontSize, NOTES_FONT_SIZE_STEP, NOTES_FONT_SIZE_MAX, BACKSTAGE_TEMPLATES, formatBackstageDate, formatBackstageSize, backstageCardsFor, DEFAULT_VIEWER_PROFILE, resolveProfileInitial, AVATAR_COLOR_SWATCHES, resolveDefaultFontFamily, buildFontCatalog, ENTRANCE_PRESET_VALUES, EMPHASIS_PRESET_VALUES, EXIT_PRESET_VALUES, MOTION_PATH_FAMILIES, motionPathFamilyLabelKey, motionPathPresetsByFamily, motionPathPresetLabelKey, masterViewPseudoSlide, authorConnectorBetweenSites, generateTicks, rulerDragToGuidePosition, getCommentMarkerPosition, collectConnectorSiteCandidates, getConnectorEndpointHandles, findConnectorSiteNear, motionPathToSvgD, motionPathEndPixel, isEditableMotionPath, setMotionPathEnd, themeColorLabel, buildThemeColorGrid, THEME_COLOR_SCHEME_KEYS, COMMON_FONTS, buildSmartArtPresetData, handleReadingViewKey, getLocalStorageUsageSummary, saveViewerProfile, clearAllLocalViewerData, isCurrentLayout, SLIDE_TEMPLATES, CONNECTOR_SITE_SNAP_PX, resolveConnectorEndpointUpdate, withConnectorEndpointUpdate, getShapeConnectionSites, isElementLocked, mergeSlideTransition, mapSlideSorterKey, isEditorTextInputTarget, clampSorterZoom, buildLayoutPreviewGeometry, buildSlideTemplateContent, elementLockTogglePatch, buildTableDataGrid, appendTableElementRow, removeLastTableElementRow, appendTableElementColumn, removeLastTableElementColumn, removeTableElementColumn, removeTableElementRow, setTableElementCellText, TABLE_STYLE_PRESETS, chartDataChangeType, setChartCategoryLabel, setChartCellValue, addChartCategory, removeChartCategory, addChartSeries, removeChartSeries, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, setChartDataPointMarker, setChartDataPointLabel, getOleObjectTypeLabel, sanitizeGradientStops, pptxActionToElementAction, withFrameSlides, withSlidesPerPage, applyThemeOverrideToSlide, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, schemaLabel, THEME_COLOR_SLOT_LABEL_KEYS, resolveSlideSizeSelection, SLIDE_SIZE_PRESETS, TRANSITION_VALID_DIRECTIONS, TRANSITION_ORIENTATION_TYPES, clampTransitionNumber, flattenTagCollections, formatCommentTimestamp, clearMotionPath, DIRECTIONAL_PRESETS, motionPathPresetIdForPath, computeMergeCellRight, computeMergeCellDown, computeSplitCell, SMARTART_COLOR_SCHEME_LABEL_KEYS, SMARTART_STYLE_LABEL_KEYS, addSmartArtNodeAsChild, removeSmartArtNode, clampCropValue, DUOTONE_PRESETS, ARTISTIC_EFFECTS, resolveActionType, ELEMENT_ACTION_TYPE_OPTIONS, toSlideIndex, elementActionToPptxAction, canCommitActionType, slideSizeFromPreset, withSlideSizeOrientation, TRANSITION_DIR_ARROWS, buildDirectionGrid, updateTagInCollections, deleteTagFromCollections, addTagToCollections, commentTextSegments, COMMENT_MENTION_CLASS, COMMENT_MENTION_ATTRIBUTE, animationEffectLabel, LBL, SEL, FILL_MODE_OPTIONS, SECTION_HEADING, NUM, switchSmartArtLayout, addSmartArtNode, demoteSmartArtNode, promoteSmartArtNode, reorderSmartArtNode, SHAPE_QUICK_STYLES, quickStyleSwatchCss, getPatternSvg, TEXT_WARP_PRESETS, warpPreviewPath, text3dPtToEmu, text3dEmuToPt, MATERIAL_PRESETS, GRADIENT_TYPE_OPTIONS, PATTERN_OPTIONS, FILL_PATTERN_LABEL_KEYS, openNativeEyeDropper, toggleText3dExtrusion, BEVEL_PRESETS } from './chunk-GODW6RNR.mjs';
6
6
  import React14, { createContext, useMemo, useState, useCallback, lazy, forwardRef, useEffect, useRef, Suspense, useLayoutEffect, useContext, Fragment as Fragment$1, useDeferredValue } from 'react';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
8
  import { useTranslation } from 'react-i18next';
9
- import { LuRotateCw, LuMessageSquare, LuEyeOff, LuSettings, LuX, LuCalendarDays, LuHash, LuText, LuCast, LuSave, LuUndo, LuRedo, LuSearch, LuShieldCheck, LuPlus, LuPanelLeftClose, LuStickyNote, LuMonitor, LuColumns2, LuPresentation, LuMinus, LuLayers, LuSettings2, LuClock, LuDownload, LuTrash2, LuCheck, LuLock, LuEye, LuFileText, LuType, LuLoader, LuShieldAlert, LuSignature, LuInfo, LuTriangleAlert, LuPrinter, LuPenTool, LuBug, LuChevronUp, LuChevronDown, LuWifi, LuWifiOff, LuUsers, LuCopy, LuChevronLeft, LuChevronRight, LuPlay, LuPause, LuZoomOut, LuZoomIn, LuSpellCheck, LuFileDown, LuMenu, LuSparkles, LuShare2, LuPanelLeft, LuPanelRight, LuArrowLeft, LuFolderOpen, LuClipboardPaste, LuScissors, LuPaintbrush, LuSquare, LuImage, LuVideo, LuDatabase, LuAArrowUp, LuAArrowDown, LuRemoveFormatting, LuHighlighter, LuList, LuListOrdered, LuIndentDecrease, LuIndentIncrease, LuReplace, LuMousePointer2, LuShapes, LuPaintBucket, LuPenLine, LuPalette, LuPencil, LuStar, LuMoveRight, LuMousePointerClick, LuClock3, LuMonitorPlay, LuListVideo, LuCaptions, LuCamera, LuEraser, LuRotateCcw, LuCircleHelp, LuBookOpen, LuLanguages, LuGlobe, LuGitCompare, LuMessageSquarePlus, LuLockKeyhole, LuPanelTop, LuLayoutGrid, LuGrid3X3, LuPipette, LuRuler, LuMaximize2, LuCode, LuCaseSensitive, LuTimer, LuGripVertical, LuLoaderCircle, LuUpload, LuBold, LuItalic, LuUnderline, LuStrikethrough, LuLink, LuGrid2X2, LuCopyPlus, LuScan, LuMonitorOff, LuCirclePlay, LuCirclePause, LuArrowLeftRight, LuClipboardCopy, LuWand, LuTextCursorInput, LuFile, LuEllipsis, LuUserRound, LuFilePlus2, LuHouse, LuPackage, LuFileJson, LuBox, LuFileImage, LuUser, LuHardDrive, LuLogIn, LuLayoutTemplate, LuFolderPlus, LuAlignLeft, LuAlignCenter, LuAlignRight, LuAlignJustify, LuColumns3, LuSpline, LuAlignHorizontalSpaceAround, LuAlignVerticalSpaceAround, LuGroup, LuUngroup, LuMove, LuRadio, LuLockOpen, LuArrowDown, LuArrowUp, LuArrowRight, LuReply, LuBookmark } from 'react-icons/lu';
9
+ import { LuRotateCw, LuMessageSquare, LuEyeOff, LuSettings, LuX, LuCalendarDays, LuHash, LuText, LuCast, LuSave, LuUndo, LuRedo, LuSearch, LuShieldCheck, LuPlus, LuPanelLeftClose, LuStickyNote, LuMonitor, LuColumns2, LuPresentation, LuMinus, LuLayers, LuSettings2, LuClock, LuDownload, LuTrash2, LuCheck, LuLock, LuEye, LuFileText, LuType, LuLoader, LuShieldAlert, LuSignature, LuInfo, LuTriangleAlert, LuPrinter, LuPenTool, LuBug, LuChevronUp, LuChevronDown, LuUpload, LuWifi, LuWifiOff, LuUsers, LuCopy, LuChevronLeft, LuChevronRight, LuPlay, LuPause, LuZoomOut, LuZoomIn, LuSpellCheck, LuFileDown, LuMenu, LuSparkles, LuShare2, LuPanelLeft, LuPanelRight, LuArrowLeft, LuFolderOpen, LuClipboardPaste, LuScissors, LuPaintbrush, LuSquare, LuImage, LuVideo, LuDatabase, LuAArrowUp, LuAArrowDown, LuRemoveFormatting, LuHighlighter, LuList, LuListOrdered, LuIndentDecrease, LuIndentIncrease, LuReplace, LuMousePointer2, LuShapes, LuPaintBucket, LuPenLine, LuPalette, LuPencil, LuStar, LuMoveRight, LuMousePointerClick, LuClock3, LuMonitorPlay, LuListVideo, LuCaptions, LuCamera, LuEraser, LuRotateCcw, LuCircleHelp, LuBookOpen, LuLanguages, LuGlobe, LuGitCompare, LuMessageSquarePlus, LuLockKeyhole, LuPanelTop, LuLayoutGrid, LuGrid3X3, LuPipette, LuRuler, LuMaximize2, LuCode, LuCaseSensitive, LuTimer, LuGripVertical, LuLoaderCircle, LuBold, LuItalic, LuUnderline, LuStrikethrough, LuLink, LuGrid2X2, LuCopyPlus, LuScan, LuMonitorOff, LuCirclePlay, LuCirclePause, LuArrowLeftRight, LuClipboardCopy, LuWand, LuTextCursorInput, LuFile, LuEllipsis, LuUserRound, LuFilePlus2, LuHouse, LuPackage, LuFileJson, LuBox, LuFileImage, LuUser, LuHardDrive, LuLogIn, LuLayoutTemplate, LuFolderPlus, LuAlignLeft, LuAlignCenter, LuAlignRight, LuAlignJustify, LuColumns3, LuSpline, LuAlignHorizontalSpaceAround, LuAlignVerticalSpaceAround, LuGroup, LuUngroup, LuMove, LuRadio, LuLockOpen, LuArrowDown, LuArrowUp, LuArrowRight, LuReply, LuBookmark } from 'react-icons/lu';
10
10
 
11
11
  var ViewerThemeContext = createContext(void 0);
12
12
  function ViewerThemeProvider({ theme, children }) {
@@ -1060,7 +1060,7 @@ var EDGE_HANDLES = [
1060
1060
  ];
1061
1061
  function ResizeHandles({
1062
1062
  elementId,
1063
- adjustmentHandleDescriptor: adjH,
1063
+ adjustmentHandles,
1064
1064
  onResizePointerDown,
1065
1065
  onAdjustmentPointerDown,
1066
1066
  forcePointerEvents,
@@ -1178,16 +1178,17 @@ function ResizeHandles({
1178
1178
  ]
1179
1179
  }
1180
1180
  ) : null,
1181
- adjH ? /* @__PURE__ */ jsx(
1181
+ adjustmentHandles.map((adjH) => /* @__PURE__ */ jsx(
1182
1182
  "button",
1183
1183
  {
1184
1184
  type: "button",
1185
1185
  "aria-label": t("pptx.canvas.adjustShape"),
1186
+ "data-pptx-adjust-key": adjH.key,
1186
1187
  "data-pptx-compact": true,
1187
1188
  className: "absolute h-2.5 w-2.5 max-md:h-4 max-md:w-4 rotate-45 border border-amber-700 bg-amber-300 shadow z-10",
1188
1189
  style: {
1189
1190
  left: adjH.left - 5,
1190
- top: adjH.top,
1191
+ top: adjH.top - 5,
1191
1192
  cursor: adjH.cursor,
1192
1193
  ...HANDLE_TOUCH_ACTION,
1193
1194
  ...forcePointerEvents ? { pointerEvents: "auto" } : {}
@@ -1198,14 +1199,15 @@ function ResizeHandles({
1198
1199
  }
1199
1200
  e.stopPropagation();
1200
1201
  e.currentTarget.setPointerCapture?.(e.pointerId);
1201
- onAdjustmentPointerDown(elementId, e);
1202
+ onAdjustmentPointerDown(elementId, e, adjH);
1202
1203
  },
1203
1204
  onMouseDown: (e) => {
1204
1205
  e.stopPropagation();
1205
- onAdjustmentPointerDown(elementId, e);
1206
+ onAdjustmentPointerDown(elementId, e, adjH);
1206
1207
  }
1207
- }
1208
- ) : null
1208
+ },
1209
+ adjH.key
1210
+ ))
1209
1211
  ] });
1210
1212
  }
1211
1213
  var ConnectorElementRenderer = React14.memo(
@@ -1219,7 +1221,7 @@ var ConnectorElementRenderer = React14.memo(
1219
1221
  selectionColorClass: selClr,
1220
1222
  opacity,
1221
1223
  zIndex,
1222
- adjustmentHandleDescriptor: adjH,
1224
+ adjustmentHandles: adjH,
1223
1225
  onResizePointerDown,
1224
1226
  onAdjustmentPointerDown,
1225
1227
  animationState
@@ -1268,6 +1270,10 @@ var ConnectorElementRenderer = React14.memo(
1268
1270
  {
1269
1271
  "data-pptx-element": "true",
1270
1272
  "data-element-id": el.id,
1273
+ role: getAriaRole(el, { actionable: false }),
1274
+ "aria-label": getAriaLabel(el),
1275
+ "aria-roledescription": getAriaRoleDescription(el),
1276
+ "aria-selected": isSelected ? true : void 0,
1271
1277
  className: "absolute",
1272
1278
  style: {
1273
1279
  left: el.x,
@@ -1428,7 +1434,7 @@ var ConnectorElementRenderer = React14.memo(
1428
1434
  ResizeHandles,
1429
1435
  {
1430
1436
  elementId: el.id,
1431
- adjustmentHandleDescriptor: adjH,
1437
+ adjustmentHandles: adjH,
1432
1438
  onResizePointerDown,
1433
1439
  onAdjustmentPointerDown,
1434
1440
  forcePointerEvents: true
@@ -1545,13 +1551,18 @@ function getContainerStyle({
1545
1551
  overflow: overflowValue,
1546
1552
  clipPath: isImg && !has3DExtrusion ? getCropShapeClipPath(el) : void 0,
1547
1553
  zIndex: isFullscreenMedia ? 20 : zIndex,
1548
- opacity,
1549
1554
  visibility: animationState?.visible === false ? "hidden" : "visible",
1550
1555
  animation: animationState?.cssAnimation,
1551
1556
  background: isFullscreenMedia ? "#000" : void 0,
1552
1557
  transition: isFullscreenMedia ? "left 0.3s ease, top 0.3s ease, width 0.3s ease, height 0.3s ease" : void 0,
1553
1558
  borderColor: isFullscreenMedia ? "transparent" : void 0,
1554
1559
  ...shapeVisualStyle,
1560
+ // COMPOSE the effect alpha (`a:alphaModFix` on the effect DAG) with the
1561
+ // element opacity instead of letting the spread clobber it. The other four
1562
+ // bindings multiply the two; React spread the shape style over its own
1563
+ // `opacity` key, so a half-transparent element carrying a DAG alpha
1564
+ // rendered at the DAG's alpha alone.
1565
+ opacity: typeof shapeVisualStyle.opacity === "number" ? (opacity ?? 1) * shapeVisualStyle.opacity : opacity,
1555
1566
  // Editable-template affordance: a distinct amber dashed ring + slight
1556
1567
  // transparency so inherited master/layout shapes read as "template" while
1557
1568
  // edit-template mode is on. Applied after the shape style so it wins; never
@@ -1748,6 +1759,7 @@ var ElementRenderer = React14.memo(
1748
1759
  isInlineEditing,
1749
1760
  inlineEditingText,
1750
1761
  canInteract,
1762
+ presenting,
1751
1763
  spellCheckEnabled,
1752
1764
  mediaDataUrls,
1753
1765
  tableEditorState,
@@ -1760,7 +1772,7 @@ var ElementRenderer = React14.memo(
1760
1772
  showResizeHandles,
1761
1773
  renderInk: doInk,
1762
1774
  renderGroups: doGrp,
1763
- adjustmentHandleDescriptor: adjH,
1775
+ adjustmentHandles: adjH,
1764
1776
  onResizePointerDown,
1765
1777
  onAdjustmentPointerDown,
1766
1778
  onRotate,
@@ -1799,8 +1811,8 @@ var ElementRenderer = React14.memo(
1799
1811
  });
1800
1812
  const chartUpdateHandler = smartArtUpdateHandler;
1801
1813
  const { hf, fc, sw, sc } = shapeParams(el);
1802
- const elementLocks = el.locks;
1803
- const isTxt = isEditableTextElement(el) && !elementLocks?.noTextEdit;
1814
+ const allow = resolveElementInteractivity(el);
1815
+ const isTxt = isEditableTextElement(el) && allow.textEditable;
1804
1816
  const txtSE = hasTextProperties(el) ? el.textStyle : void 0;
1805
1817
  const ss = getShapeVisualStyle(
1806
1818
  el,
@@ -1844,22 +1856,22 @@ var ElementRenderer = React14.memo(
1844
1856
  el,
1845
1857
  isSelected,
1846
1858
  canInteract,
1847
- showResizeHandles: showResizeHandles && !elementLocks?.noResize,
1859
+ showResizeHandles: showResizeHandles && allow.resizable,
1848
1860
  showHoverBorder,
1849
1861
  selectionColorClass: selClr,
1850
1862
  opacity,
1851
1863
  zIndex,
1852
- adjustmentHandleDescriptor: adjH,
1864
+ adjustmentHandles: adjH,
1853
1865
  onResizePointerDown,
1854
1866
  onAdjustmentPointerDown,
1855
1867
  animationState
1856
1868
  }
1857
1869
  );
1858
1870
  }
1859
- const effectiveCanInteract = canInteract && !elementLocks?.noSelect;
1860
- const effectiveShowResizeHandles = showResizeHandles && !elementLocks?.noResize;
1861
- const effectiveIsInlineEditing = isInlineEditing && !elementLocks?.noTextEdit;
1862
- const canEditSmartArt = effectiveCanInteract && !elementLocks?.noTextEdit;
1871
+ const effectiveCanInteract = canInteract && allow.selectable;
1872
+ const effectiveShowResizeHandles = showResizeHandles && allow.resizable;
1873
+ const effectiveIsInlineEditing = isInlineEditing && allow.textEditable;
1874
+ const canEditSmartArt = effectiveCanInteract && allow.textEditable;
1863
1875
  const canEditChart = effectiveCanInteract;
1864
1876
  const hasAction = Boolean(el.actionClick && onActionClick);
1865
1877
  const isZoom = el.type === "zoom" && Boolean(onZoomClick);
@@ -1869,7 +1881,7 @@ var ElementRenderer = React14.memo(
1869
1881
  hasZoomHandler: Boolean(onZoomClick)
1870
1882
  });
1871
1883
  const selB = isSelected ? `outline-1 -outline-offset-1 outline-${selClr} ring-2 ring-${selClr}/50` : showHoverBorder ? "outline-1 -outline-offset-1 outline-transparent hover:outline-primary/40" : "";
1872
- const cur = effectiveIsInlineEditing ? "cursor-text" : effectiveCanInteract ? elementLocks?.noMove ? "cursor-default" : "cursor-move" : hasAction || isZoom ? "cursor-pointer" : "";
1884
+ const cur = effectiveIsInlineEditing ? "cursor-text" : effectiveCanInteract ? !allow.movable ? "cursor-default" : "cursor-move" : hasAction || isZoom ? "cursor-pointer" : "";
1873
1885
  const isPresentationPassive = !effectiveCanInteract;
1874
1886
  const isFullscreenMedia = el.type === "media" && Boolean(el.fullScreen) && isPresentationPassive && isMediaPlaying;
1875
1887
  const ariaRole = getAriaRole(el, { actionable: isActionable });
@@ -1901,7 +1913,16 @@ var ElementRenderer = React14.memo(
1901
1913
  "absolute",
1902
1914
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500",
1903
1915
  cur,
1904
- effectiveCanInteract || isActionable ? "" : "pointer-events-none",
1916
+ // During a show this must stay EMPTY: `PRESENTATION_HIT_TEST_CSS`
1917
+ // (injected by `PresentationStage`) owns hit-testing there, because
1918
+ // it is the only form that can re-enable an action shape nested
1919
+ // inside inert scenery - an inline `pointer-events: none` on the
1920
+ // group could never do that, and React was the one binding that
1921
+ // wrote it. Off the show stage the inline rule still applies.
1922
+ inlineElementPointerEvents({
1923
+ interactive: effectiveCanInteract || isActionable,
1924
+ presenting: presenting === true
1925
+ }) === "none" ? "pointer-events-none" : "",
1905
1926
  // An unfilled, textless shape is a FRAME: PowerPoint hit-tests it on its
1906
1927
  // outline only, so its interior must not swallow clicks meant for what it
1907
1928
  // is drawn over. ShapeEffectOverlay paints a transparent
@@ -1995,12 +2016,12 @@ var ElementRenderer = React14.memo(
1995
2016
  ResizeHandles,
1996
2017
  {
1997
2018
  elementId: el.id,
1998
- adjustmentHandleDescriptor: adjH,
2019
+ adjustmentHandles: adjH,
1999
2020
  onResizePointerDown,
2000
2021
  onAdjustmentPointerDown,
2001
2022
  rotation: el.rotation,
2002
2023
  nonRotationTransform: getElementTransformWithoutRotation(el),
2003
- onRotate: elementLocks?.noRotation ? void 0 : onRotate
2024
+ onRotate: allow.rotatable ? onRotate : void 0
2004
2025
  }
2005
2026
  )
2006
2027
  ]
@@ -2112,7 +2133,11 @@ function renderGroup(children, parentGroupFill) {
2112
2133
  },
2113
2134
  children: [
2114
2135
  /* @__PURE__ */ jsx(ShapeEffectOverlay, { element: c }),
2115
- isI && ("svgData" in c && c.svgData || "imageData" in c && c.imageData) ? isImageTiled(c) ? /* @__PURE__ */ jsx(
2136
+ c.type === "group" ? (
2137
+ // A nested group: recurse, chaining the inherited fill so a
2138
+ // `grpFill` shape under a fill-less sub-group still paints.
2139
+ renderGroup(c.children, getGroupChildParentFill(c, parentGroupFill))
2140
+ ) : isI && ("svgData" in c && c.svgData || "imageData" in c && c.imageData) ? isImageTiled(c) ? /* @__PURE__ */ jsx(
2116
2141
  "div",
2117
2142
  {
2118
2143
  className: "pointer-events-none select-none w-full h-full",
@@ -2563,7 +2588,7 @@ function FailedToLoad() {
2563
2588
  var LazyModel3DScene = React14.lazy(
2564
2589
  async () => {
2565
2590
  try {
2566
- return await import('./Model3DScene-YMIAGRPG.mjs');
2591
+ return await import('./Model3DScene-LYEESYAI.mjs');
2567
2592
  } catch {
2568
2593
  return { default: FailedToLoad };
2569
2594
  }
@@ -2827,24 +2852,10 @@ function OleRenderer({ element: el }) {
2827
2852
  );
2828
2853
  }
2829
2854
  var SmartArt3DContext = createContext(false);
2830
- var DEFAULT_PALETTE = DEFAULT_PALETTE2;
2831
- function colour(index, palette = DEFAULT_PALETTE) {
2832
- return paletteColour(index, palette);
2833
- }
2834
- function nodeOpacity2(index, total, style) {
2835
- return nodeOpacity(index, total, style ?? "flat");
2836
- }
2837
- function styleShadow2(style) {
2838
- return styleShadow(style ?? "flat");
2839
- }
2840
- function styleStroke2(style) {
2841
- return styleStroke(style ?? "flat");
2842
- }
2843
- function truncate2(text, max) {
2844
- return truncate(text, max);
2845
- }
2855
+
2856
+ // src/viewer/utils/smartart-helpers.tsx
2846
2857
  function resolvePalette2(el) {
2847
- return el.type === "smartArt" ? resolvePalette(el.smartArtData) : DEFAULT_PALETTE;
2858
+ return el.type === "smartArt" ? resolvePalette(el.smartArtData) : DEFAULT_PALETTE2;
2848
2859
  }
2849
2860
  function resolveStyle(el) {
2850
2861
  if (el.type !== "smartArt" || !el.smartArtData) {
@@ -2855,49 +2866,6 @@ function resolveStyle(el) {
2855
2866
  function resolveSmartArtDataPalette(data) {
2856
2867
  return resolvePalette(data);
2857
2868
  }
2858
- function layoutToCategory(layout) {
2859
- if (!layout) {
2860
- return "list";
2861
- }
2862
- const map = {
2863
- basicBlockList: "list",
2864
- alternatingHexagons: "list",
2865
- basicChevronProcess: "process",
2866
- basicCycle: "cycle",
2867
- basicPie: "cycle",
2868
- basicRadial: "radial",
2869
- basicVenn: "venn",
2870
- continuousBlockProcess: "process",
2871
- convergingRadial: "radial",
2872
- hierarchy: "hierarchy",
2873
- horizontalBulletList: "list",
2874
- linearVenn: "venn",
2875
- segmentedProcess: "process",
2876
- stackedList: "list",
2877
- tableList: "list",
2878
- trapezoidList: "list",
2879
- upwardArrow: "process",
2880
- basicFunnel: "funnel",
2881
- basicTarget: "radial",
2882
- interlockingGears: "radial",
2883
- basicTimeline: "process",
2884
- basicMatrix: "matrix",
2885
- basicPyramid: "pyramid",
2886
- invertedPyramid: "pyramid",
2887
- bendingProcess: "process",
2888
- stepDownProcess: "stepdown",
2889
- alternatingFlow: "alternatingflow",
2890
- descendingProcess: "descending",
2891
- pictureAccentList: "pictureaccent",
2892
- verticalBlockList: "verticalblock",
2893
- groupedList: "grouped",
2894
- pyramidList: "pyramidlist",
2895
- horizontalPictureList: "horizontalpicture",
2896
- accentProcess: "accentprocess",
2897
- verticalChevronList: "verticalchevron"
2898
- };
2899
- return map[layout] ?? "list";
2900
- }
2901
2869
  var LINE_HEIGHT_RATIO = 1.2;
2902
2870
  var DEFAULT_FONT_SIZE = 12;
2903
2871
  function SmartArtInlineNodeEditor({
@@ -3179,19 +3147,6 @@ function smartArtNodeGroupProps(nodeId, shadow, label) {
3179
3147
  }
3180
3148
  return props;
3181
3149
  }
3182
- function gearPath(cx, cy, outerR, innerR, teeth) {
3183
- const segments = [];
3184
- const step = Math.PI * 2 / (teeth * 2);
3185
- for (let i = 0; i < teeth * 2; i++) {
3186
- const angle = i * step - Math.PI / 2;
3187
- const r = i % 2 === 0 ? outerR : innerR;
3188
- const x = cx + r * Math.cos(angle);
3189
- const y = cy + r * Math.sin(angle);
3190
- segments.push(i === 0 ? `M${x},${y}` : `L${x},${y}`);
3191
- }
3192
- segments.push("Z");
3193
- return segments.join(" ");
3194
- }
3195
3150
  function SmartArtGradient({ gradient }) {
3196
3151
  const stops = gradient.stops.map((stop, i) => /* @__PURE__ */ jsx(
3197
3152
  "stop",
@@ -3224,6 +3179,7 @@ function SmartArtNodeText({
3224
3179
  fontWeight,
3225
3180
  fontStyle,
3226
3181
  className,
3182
+ textAnchor = "middle",
3227
3183
  anchor = "middle",
3228
3184
  maxWidth
3229
3185
  }) {
@@ -3232,7 +3188,7 @@ function SmartArtNodeText({
3232
3188
  "text",
3233
3189
  {
3234
3190
  x,
3235
- textAnchor: "middle",
3191
+ textAnchor,
3236
3192
  dominantBaseline: "central",
3237
3193
  fill,
3238
3194
  fontSize,
@@ -3265,7 +3221,7 @@ function SmartArtNodeText({
3265
3221
  "text",
3266
3222
  {
3267
3223
  x,
3268
- textAnchor: "middle",
3224
+ textAnchor,
3269
3225
  dominantBaseline,
3270
3226
  fill,
3271
3227
  fontSize,
@@ -3295,795 +3251,6 @@ function wrapChrome(chrome, content, className, a11y) {
3295
3251
  }
3296
3252
  );
3297
3253
  }
3298
- function renderStepDownProcess(element, nodes, palette, style) {
3299
- const w = element.width;
3300
- const h = element.height;
3301
- const pad = 8;
3302
- const usableW = w - pad * 2;
3303
- const usableH = h - pad * 2;
3304
- const stepW = usableW / nodes.length;
3305
- const stepH = usableH / nodes.length;
3306
- const boxW = stepW * 0.85;
3307
- const boxH = stepH * 0.6;
3308
- const shadow = styleShadow2(style);
3309
- const sw = styleStroke2(style);
3310
- return /* @__PURE__ */ jsx(
3311
- "svg",
3312
- {
3313
- className: "w-full h-full pointer-events-none",
3314
- viewBox: `0 0 ${w} ${h}`,
3315
- preserveAspectRatio: "xMidYMid meet",
3316
- style: { filter: shadow },
3317
- children: nodes.map((node, i) => {
3318
- const x = pad + i * stepW;
3319
- const y = pad + i * stepH;
3320
- const fontSize = Math.max(7, Math.min(10, boxW * 0.1));
3321
- return /* @__PURE__ */ jsxs("g", { children: [
3322
- i < nodes.length - 1 && /* @__PURE__ */ jsx(
3323
- "line",
3324
- {
3325
- x1: x + boxW,
3326
- y1: y + boxH / 2,
3327
- x2: x + stepW,
3328
- y2: y + stepH + boxH / 2,
3329
- stroke: "#94a3b8",
3330
- strokeWidth: 1.5,
3331
- opacity: 0.5
3332
- }
3333
- ),
3334
- /* @__PURE__ */ jsx(
3335
- "rect",
3336
- {
3337
- x,
3338
- y,
3339
- width: boxW,
3340
- height: boxH,
3341
- rx: 5,
3342
- fill: colour(i, palette),
3343
- opacity: nodeOpacity2(i, nodes.length, style),
3344
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3345
- strokeWidth: sw
3346
- }
3347
- ),
3348
- /* @__PURE__ */ jsx(
3349
- SmartArtNodeText,
3350
- {
3351
- x: x + boxW / 2,
3352
- y: y + boxH / 2,
3353
- text: truncate2(node.text, 20),
3354
- fill: "white",
3355
- fontSize,
3356
- className: "pointer-events-none"
3357
- }
3358
- )
3359
- ] }, `${element.id}-stepdown-${node.id}-${i}`);
3360
- })
3361
- }
3362
- );
3363
- }
3364
- function renderAlternatingFlow(element, nodes, palette, style) {
3365
- const w = element.width;
3366
- const h = element.height;
3367
- const pad = 8;
3368
- const usableH = h - pad * 2;
3369
- const rowH = usableH / nodes.length;
3370
- const halfW = (w - pad * 2) / 2;
3371
- const boxW = halfW * 0.8;
3372
- const boxH = Math.min(rowH * 0.7, 40);
3373
- const centerX = w / 2;
3374
- const shadow = styleShadow2(style);
3375
- const sw = styleStroke2(style);
3376
- return /* @__PURE__ */ jsxs(
3377
- "svg",
3378
- {
3379
- className: "w-full h-full pointer-events-none",
3380
- viewBox: `0 0 ${w} ${h}`,
3381
- preserveAspectRatio: "xMidYMid meet",
3382
- style: { filter: shadow },
3383
- children: [
3384
- /* @__PURE__ */ jsx(
3385
- "line",
3386
- {
3387
- x1: centerX,
3388
- y1: pad,
3389
- x2: centerX,
3390
- y2: h - pad,
3391
- stroke: "#94a3b8",
3392
- strokeWidth: 2,
3393
- opacity: 0.3
3394
- }
3395
- ),
3396
- nodes.map((node, i) => {
3397
- const isLeft = i % 2 === 0;
3398
- const y = pad + i * rowH + (rowH - boxH) / 2;
3399
- const x = isLeft ? centerX - boxW - 12 : centerX + 12;
3400
- const fontSize = Math.max(7, Math.min(10, boxW * 0.1));
3401
- return /* @__PURE__ */ jsxs("g", { children: [
3402
- /* @__PURE__ */ jsx(
3403
- "line",
3404
- {
3405
- x1: isLeft ? x + boxW : x,
3406
- y1: y + boxH / 2,
3407
- x2: centerX,
3408
- y2: y + boxH / 2,
3409
- stroke: colour(i, palette),
3410
- strokeWidth: 1.5,
3411
- opacity: 0.5
3412
- }
3413
- ),
3414
- /* @__PURE__ */ jsx("circle", { cx: centerX, cy: y + boxH / 2, r: 3, fill: colour(i, palette), opacity: 0.8 }),
3415
- /* @__PURE__ */ jsx(
3416
- "rect",
3417
- {
3418
- x,
3419
- y,
3420
- width: boxW,
3421
- height: boxH,
3422
- rx: 5,
3423
- fill: colour(i, palette),
3424
- opacity: nodeOpacity2(i, nodes.length, style),
3425
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3426
- strokeWidth: sw
3427
- }
3428
- ),
3429
- /* @__PURE__ */ jsx(
3430
- SmartArtNodeText,
3431
- {
3432
- x: x + boxW / 2,
3433
- y: y + boxH / 2,
3434
- text: truncate2(node.text, 20),
3435
- fill: "white",
3436
- fontSize,
3437
- className: "pointer-events-none"
3438
- }
3439
- )
3440
- ] }, `${element.id}-altflow-${node.id}-${i}`);
3441
- })
3442
- ]
3443
- }
3444
- );
3445
- }
3446
- function renderDescendingProcess(element, nodes, palette, style) {
3447
- const w = element.width;
3448
- const h = element.height;
3449
- const pad = 8;
3450
- const gap = 4;
3451
- const usableH = h - pad * 2;
3452
- const maxW = w - pad * 2;
3453
- const itemH = (usableH - gap * (nodes.length - 1)) / nodes.length;
3454
- const shadow = styleShadow2(style);
3455
- const sw = styleStroke2(style);
3456
- return /* @__PURE__ */ jsx(
3457
- "svg",
3458
- {
3459
- className: "w-full h-full pointer-events-none",
3460
- viewBox: `0 0 ${w} ${h}`,
3461
- preserveAspectRatio: "xMidYMid meet",
3462
- style: { filter: shadow },
3463
- children: nodes.map((node, i) => {
3464
- const widthFraction = 1 - i / Math.max(nodes.length, 1) * 0.5;
3465
- const bw = maxW * widthFraction;
3466
- const x = (w - bw) / 2;
3467
- const y = pad + i * (itemH + gap);
3468
- const fontSize = Math.max(7, Math.min(11, itemH * 0.4));
3469
- return /* @__PURE__ */ jsxs("g", { children: [
3470
- i < nodes.length - 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
3471
- /* @__PURE__ */ jsx(
3472
- "line",
3473
- {
3474
- x1: w / 2,
3475
- y1: y + itemH,
3476
- x2: w / 2,
3477
- y2: y + itemH + gap,
3478
- stroke: "#94a3b8",
3479
- strokeWidth: 1.5,
3480
- opacity: 0.5
3481
- }
3482
- ),
3483
- /* @__PURE__ */ jsx(
3484
- "polygon",
3485
- {
3486
- points: `${w / 2 - 3},${y + itemH + gap - 4} ${w / 2},${y + itemH + gap} ${w / 2 + 3},${y + itemH + gap - 4}`,
3487
- fill: "#94a3b8",
3488
- opacity: 0.5
3489
- }
3490
- )
3491
- ] }),
3492
- /* @__PURE__ */ jsx(
3493
- "rect",
3494
- {
3495
- x,
3496
- y,
3497
- width: bw,
3498
- height: itemH,
3499
- rx: 5,
3500
- fill: colour(i, palette),
3501
- opacity: nodeOpacity2(i, nodes.length, style),
3502
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3503
- strokeWidth: sw
3504
- }
3505
- ),
3506
- /* @__PURE__ */ jsx(
3507
- SmartArtNodeText,
3508
- {
3509
- x: w / 2,
3510
- y: y + itemH / 2,
3511
- text: truncate2(node.text, 30),
3512
- fill: "white",
3513
- fontSize,
3514
- className: "pointer-events-none"
3515
- }
3516
- )
3517
- ] }, `${element.id}-desc-${node.id}-${i}`);
3518
- })
3519
- }
3520
- );
3521
- }
3522
- function renderPictureAccentList(element, nodes, palette, style) {
3523
- const w = element.width;
3524
- const h = element.height;
3525
- const pad = 8;
3526
- const gap = 6;
3527
- const usableH = h - pad * 2;
3528
- const usableW = w - pad * 2;
3529
- const itemH = (usableH - gap * (nodes.length - 1)) / nodes.length;
3530
- const circleR = Math.min(itemH * 0.38, 18);
3531
- const textX = pad + circleR * 2 + 12;
3532
- const textW = usableW - circleR * 2 - 16;
3533
- const shadow = styleShadow2(style);
3534
- const sw = styleStroke2(style);
3535
- return /* @__PURE__ */ jsx(
3536
- "svg",
3537
- {
3538
- className: "w-full h-full pointer-events-none",
3539
- viewBox: `0 0 ${w} ${h}`,
3540
- preserveAspectRatio: "xMidYMid meet",
3541
- style: { filter: shadow },
3542
- children: nodes.map((node, i) => {
3543
- const y = pad + i * (itemH + gap);
3544
- const cy = y + itemH / 2;
3545
- const col = colour(i, palette);
3546
- const fontSize = Math.max(7, Math.min(11, itemH * 0.35));
3547
- return /* @__PURE__ */ jsxs("g", { children: [
3548
- /* @__PURE__ */ jsx(
3549
- "circle",
3550
- {
3551
- cx: pad + circleR,
3552
- cy,
3553
- r: circleR,
3554
- fill: col,
3555
- opacity: nodeOpacity2(i, nodes.length, style)
3556
- }
3557
- ),
3558
- /* @__PURE__ */ jsx(
3559
- "text",
3560
- {
3561
- x: pad + circleR,
3562
- y: cy,
3563
- textAnchor: "middle",
3564
- dominantBaseline: "central",
3565
- fill: "white",
3566
- fontSize: Math.max(7, circleR * 0.7),
3567
- fontWeight: "bold",
3568
- className: "pointer-events-none",
3569
- children: i + 1
3570
- }
3571
- ),
3572
- /* @__PURE__ */ jsx(
3573
- "rect",
3574
- {
3575
- x: textX,
3576
- y: y + 2,
3577
- width: textW,
3578
- height: itemH - 4,
3579
- rx: 4,
3580
- fill: col,
3581
- opacity: 0.12,
3582
- stroke: col,
3583
- strokeWidth: sw > 0 ? sw : 1,
3584
- strokeOpacity: 0.3
3585
- }
3586
- ),
3587
- /* @__PURE__ */ jsx(
3588
- "text",
3589
- {
3590
- x: textX + 8,
3591
- y: cy,
3592
- textAnchor: "start",
3593
- dominantBaseline: "central",
3594
- fill: col,
3595
- fontSize,
3596
- className: "pointer-events-none",
3597
- children: truncate2(node.text, 40)
3598
- }
3599
- )
3600
- ] }, `${element.id}-picacc-${node.id}-${i}`);
3601
- })
3602
- }
3603
- );
3604
- }
3605
- function renderVerticalBlockList(element, nodes, palette, style) {
3606
- const w = element.width;
3607
- const h = element.height;
3608
- const pad = 8;
3609
- const gap = 4;
3610
- const usableH = h - pad * 2;
3611
- const usableW = w - pad * 2;
3612
- const itemH = (usableH - gap * (nodes.length - 1)) / nodes.length;
3613
- const headerW = usableW * 0.22;
3614
- const bodyW = usableW - headerW - 4;
3615
- const shadow = styleShadow2(style);
3616
- const sw = styleStroke2(style);
3617
- return /* @__PURE__ */ jsx(
3618
- "svg",
3619
- {
3620
- className: "w-full h-full pointer-events-none",
3621
- viewBox: `0 0 ${w} ${h}`,
3622
- preserveAspectRatio: "xMidYMid meet",
3623
- style: { filter: shadow },
3624
- children: nodes.map((node, i) => {
3625
- const y = pad + i * (itemH + gap);
3626
- const col = colour(i, palette);
3627
- const fontSize = Math.max(7, Math.min(11, itemH * 0.35));
3628
- return /* @__PURE__ */ jsxs("g", { children: [
3629
- /* @__PURE__ */ jsx(
3630
- "rect",
3631
- {
3632
- x: pad,
3633
- y,
3634
- width: headerW,
3635
- height: itemH,
3636
- rx: 3,
3637
- fill: col,
3638
- opacity: nodeOpacity2(i, nodes.length, style),
3639
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3640
- strokeWidth: sw
3641
- }
3642
- ),
3643
- /* @__PURE__ */ jsx(
3644
- "text",
3645
- {
3646
- x: pad + headerW / 2,
3647
- y: y + itemH / 2,
3648
- textAnchor: "middle",
3649
- dominantBaseline: "central",
3650
- fill: "white",
3651
- fontSize: Math.max(8, itemH * 0.4),
3652
- fontWeight: "bold",
3653
- className: "pointer-events-none",
3654
- children: i + 1
3655
- }
3656
- ),
3657
- /* @__PURE__ */ jsx(
3658
- "rect",
3659
- {
3660
- x: pad + headerW + 4,
3661
- y,
3662
- width: bodyW,
3663
- height: itemH,
3664
- rx: 3,
3665
- fill: col,
3666
- opacity: 0.1,
3667
- stroke: col,
3668
- strokeWidth: 1,
3669
- strokeOpacity: 0.3
3670
- }
3671
- ),
3672
- /* @__PURE__ */ jsx(
3673
- "text",
3674
- {
3675
- x: pad + headerW + 12,
3676
- y: y + itemH / 2,
3677
- textAnchor: "start",
3678
- dominantBaseline: "central",
3679
- fill: col,
3680
- fontSize,
3681
- className: "pointer-events-none",
3682
- children: truncate2(node.text, 40)
3683
- }
3684
- )
3685
- ] }, `${element.id}-vblk-${node.id}-${i}`);
3686
- })
3687
- }
3688
- );
3689
- }
3690
- function renderGroupedList(element, nodes, palette, style) {
3691
- const w = element.width;
3692
- const h = element.height;
3693
- const pad = 8;
3694
- const gap = 8;
3695
- const shadow = styleShadow2(style);
3696
- const sw = styleStroke2(style);
3697
- const groupSize = nodes.length <= 4 ? 2 : 3;
3698
- const groups = [];
3699
- for (let i = 0; i < nodes.length; i += groupSize) {
3700
- groups.push(nodes.slice(i, i + groupSize));
3701
- }
3702
- const usableW = w - pad * 2;
3703
- const groupW = (usableW - gap * (groups.length - 1)) / groups.length;
3704
- const usableH = h - pad * 2;
3705
- const headerH = usableH * 0.18;
3706
- return /* @__PURE__ */ jsx(
3707
- "svg",
3708
- {
3709
- className: "w-full h-full pointer-events-none",
3710
- viewBox: `0 0 ${w} ${h}`,
3711
- preserveAspectRatio: "xMidYMid meet",
3712
- style: { filter: shadow },
3713
- children: groups.map((group, gi) => {
3714
- const gx = pad + gi * (groupW + gap);
3715
- const col = colour(gi, palette);
3716
- const subItemGap = 3;
3717
- const subItemH = (usableH - headerH - 4 - subItemGap * (group.length - 1)) / group.length;
3718
- return /* @__PURE__ */ jsxs("g", { children: [
3719
- /* @__PURE__ */ jsx(
3720
- "rect",
3721
- {
3722
- x: gx,
3723
- y: pad,
3724
- width: groupW,
3725
- height: headerH,
3726
- rx: 4,
3727
- fill: col,
3728
- opacity: nodeOpacity2(gi, groups.length, style),
3729
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3730
- strokeWidth: sw
3731
- }
3732
- ),
3733
- /* @__PURE__ */ jsx(
3734
- "text",
3735
- {
3736
- x: gx + groupW / 2,
3737
- y: pad + headerH / 2,
3738
- textAnchor: "middle",
3739
- dominantBaseline: "central",
3740
- fill: "white",
3741
- fontSize: Math.max(7, Math.min(10, groupW * 0.08)),
3742
- fontWeight: "bold",
3743
- className: "pointer-events-none",
3744
- children: `Group ${gi + 1}`
3745
- }
3746
- ),
3747
- group.map((node, si) => {
3748
- const sy = pad + headerH + 4 + si * (subItemH + subItemGap);
3749
- const fontSize = Math.max(7, Math.min(10, subItemH * 0.35));
3750
- return /* @__PURE__ */ jsxs("g", { children: [
3751
- /* @__PURE__ */ jsx(
3752
- "rect",
3753
- {
3754
- x: gx + 4,
3755
- y: sy,
3756
- width: groupW - 8,
3757
- height: subItemH,
3758
- rx: 3,
3759
- fill: col,
3760
- opacity: 0.15,
3761
- stroke: col,
3762
- strokeWidth: 1,
3763
- strokeOpacity: 0.3
3764
- }
3765
- ),
3766
- /* @__PURE__ */ jsx(
3767
- SmartArtNodeText,
3768
- {
3769
- x: gx + groupW / 2,
3770
- y: sy + subItemH / 2,
3771
- text: truncate2(node.text, 20),
3772
- fill: col,
3773
- fontSize,
3774
- className: "pointer-events-none"
3775
- }
3776
- )
3777
- ] }, `${element.id}-grp-${gi}-item-${node.id}-${si}`);
3778
- })
3779
- ] }, `${element.id}-grp-${gi}`);
3780
- })
3781
- }
3782
- );
3783
- }
3784
- function renderPyramidList(element, nodes, palette, style) {
3785
- const w = element.width;
3786
- const h = element.height;
3787
- const pad = 8;
3788
- const gap = 4;
3789
- const usableH = h - pad * 2;
3790
- const pyramidW = w * 0.35;
3791
- const labelStartX = pyramidW + pad + 16;
3792
- const labelW = w - labelStartX - pad;
3793
- const bandH = (usableH - gap * (nodes.length - 1)) / nodes.length;
3794
- const shadow = styleShadow2(style);
3795
- return /* @__PURE__ */ jsx(
3796
- "svg",
3797
- {
3798
- className: "w-full h-full pointer-events-none",
3799
- viewBox: `0 0 ${w} ${h}`,
3800
- preserveAspectRatio: "xMidYMid meet",
3801
- style: { filter: shadow },
3802
- children: nodes.map((node, i) => {
3803
- const y = pad + i * (bandH + gap);
3804
- const col = colour(i, palette);
3805
- const topFrac = 0.3 + i / Math.max(nodes.length - 1, 1) * 0.7;
3806
- const segW = pyramidW * topFrac;
3807
- const segX = pad + (pyramidW - segW) / 2;
3808
- const fontSize = Math.max(7, Math.min(11, bandH * 0.35));
3809
- return /* @__PURE__ */ jsxs("g", { children: [
3810
- /* @__PURE__ */ jsx(
3811
- "rect",
3812
- {
3813
- x: segX,
3814
- y,
3815
- width: segW,
3816
- height: bandH,
3817
- rx: 2,
3818
- fill: col,
3819
- opacity: nodeOpacity2(i, nodes.length, style)
3820
- }
3821
- ),
3822
- /* @__PURE__ */ jsx(
3823
- "line",
3824
- {
3825
- x1: segX + segW,
3826
- y1: y + bandH / 2,
3827
- x2: labelStartX - 4,
3828
- y2: y + bandH / 2,
3829
- stroke: col,
3830
- strokeWidth: 1,
3831
- opacity: 0.5,
3832
- strokeDasharray: "3,2"
3833
- }
3834
- ),
3835
- /* @__PURE__ */ jsx(
3836
- "rect",
3837
- {
3838
- x: labelStartX,
3839
- y: y + 2,
3840
- width: labelW,
3841
- height: bandH - 4,
3842
- rx: 3,
3843
- fill: col,
3844
- opacity: 0.1,
3845
- stroke: col,
3846
- strokeWidth: 1,
3847
- strokeOpacity: 0.25
3848
- }
3849
- ),
3850
- /* @__PURE__ */ jsx(
3851
- "text",
3852
- {
3853
- x: labelStartX + 8,
3854
- y: y + bandH / 2,
3855
- textAnchor: "start",
3856
- dominantBaseline: "central",
3857
- fill: col,
3858
- fontSize,
3859
- className: "pointer-events-none",
3860
- children: truncate2(node.text, 30)
3861
- }
3862
- )
3863
- ] }, `${element.id}-pyrlist-${node.id}-${i}`);
3864
- })
3865
- }
3866
- );
3867
- }
3868
- function renderHorizontalPictureList(element, nodes, palette, style) {
3869
- const w = element.width;
3870
- const h = element.height;
3871
- const pad = 8;
3872
- const gap = 8;
3873
- const usableW = w - pad * 2;
3874
- const colW = (usableW - gap * (nodes.length - 1)) / nodes.length;
3875
- const usableH = h - pad * 2;
3876
- const circleR = Math.min(colW * 0.38, usableH * 0.28);
3877
- const shadow = styleShadow2(style);
3878
- const sw = styleStroke2(style);
3879
- return /* @__PURE__ */ jsx(
3880
- "svg",
3881
- {
3882
- className: "w-full h-full pointer-events-none",
3883
- viewBox: `0 0 ${w} ${h}`,
3884
- preserveAspectRatio: "xMidYMid meet",
3885
- style: { filter: shadow },
3886
- children: nodes.map((node, i) => {
3887
- const cx = pad + i * (colW + gap) + colW / 2;
3888
- const cy = pad + circleR + 4;
3889
- const col = colour(i, palette);
3890
- const labelY = cy + circleR + 10;
3891
- const fontSize = Math.max(7, Math.min(10, colW * 0.1));
3892
- return /* @__PURE__ */ jsxs("g", { children: [
3893
- /* @__PURE__ */ jsx(
3894
- "circle",
3895
- {
3896
- cx,
3897
- cy,
3898
- r: circleR,
3899
- fill: col,
3900
- opacity: nodeOpacity2(i, nodes.length, style),
3901
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3902
- strokeWidth: sw
3903
- }
3904
- ),
3905
- /* @__PURE__ */ jsx(
3906
- "text",
3907
- {
3908
- x: cx,
3909
- y: cy,
3910
- textAnchor: "middle",
3911
- dominantBaseline: "central",
3912
- fill: "white",
3913
- fontSize: Math.max(8, circleR * 0.5),
3914
- fontWeight: "bold",
3915
- className: "pointer-events-none",
3916
- children: i + 1
3917
- }
3918
- ),
3919
- /* @__PURE__ */ jsx(
3920
- "text",
3921
- {
3922
- x: cx,
3923
- y: labelY,
3924
- textAnchor: "middle",
3925
- dominantBaseline: "hanging",
3926
- fill: col,
3927
- fontSize,
3928
- className: "pointer-events-none",
3929
- children: truncate2(node.text, 15)
3930
- }
3931
- )
3932
- ] }, `${element.id}-hpic-${node.id}-${i}`);
3933
- })
3934
- }
3935
- );
3936
- }
3937
- function renderAccentProcess(element, nodes, palette, style) {
3938
- const w = element.width;
3939
- const h = element.height;
3940
- const pad = 8;
3941
- const arrowGap = 16;
3942
- const usableW = w - pad * 2;
3943
- const nodeW = (usableW - arrowGap * (nodes.length - 1)) / nodes.length;
3944
- const nodeH = h * 0.45;
3945
- const yMid = h / 2;
3946
- const circleR = Math.min(nodeW, nodeH) * 0.55;
3947
- const shadow = styleShadow2(style);
3948
- const sw = styleStroke2(style);
3949
- return /* @__PURE__ */ jsx(
3950
- "svg",
3951
- {
3952
- className: "w-full h-full pointer-events-none",
3953
- viewBox: `0 0 ${w} ${h}`,
3954
- preserveAspectRatio: "xMidYMid meet",
3955
- style: { filter: shadow },
3956
- children: nodes.map((node, i) => {
3957
- const x = pad + i * (nodeW + arrowGap);
3958
- const cx = x + nodeW / 2;
3959
- const col = colour(i, palette);
3960
- const fontSize = Math.max(7, Math.min(11, nodeW * 0.1));
3961
- return /* @__PURE__ */ jsxs("g", { children: [
3962
- /* @__PURE__ */ jsx("circle", { cx, cy: yMid, r: circleR, fill: col, opacity: 0.15 }),
3963
- /* @__PURE__ */ jsx(
3964
- "rect",
3965
- {
3966
- x,
3967
- y: yMid - nodeH / 2,
3968
- width: nodeW,
3969
- height: nodeH,
3970
- rx: 5,
3971
- fill: col,
3972
- opacity: nodeOpacity2(i, nodes.length, style),
3973
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
3974
- strokeWidth: sw
3975
- }
3976
- ),
3977
- /* @__PURE__ */ jsx(
3978
- SmartArtNodeText,
3979
- {
3980
- x: cx,
3981
- y: yMid,
3982
- text: truncate2(node.text, 20),
3983
- fill: "white",
3984
- fontSize,
3985
- className: "pointer-events-none"
3986
- }
3987
- ),
3988
- i < nodes.length - 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
3989
- /* @__PURE__ */ jsx(
3990
- "line",
3991
- {
3992
- x1: x + nodeW + 2,
3993
- y1: yMid,
3994
- x2: x + nodeW + arrowGap - 2,
3995
- y2: yMid,
3996
- stroke: "#94a3b8",
3997
- strokeWidth: 1.5,
3998
- opacity: 0.5
3999
- }
4000
- ),
4001
- /* @__PURE__ */ jsx(
4002
- "polygon",
4003
- {
4004
- points: `${x + nodeW + arrowGap - 6},${yMid - 3} ${x + nodeW + arrowGap - 2},${yMid} ${x + nodeW + arrowGap - 6},${yMid + 3}`,
4005
- fill: "#94a3b8",
4006
- opacity: 0.5
4007
- }
4008
- )
4009
- ] })
4010
- ] }, `${element.id}-accproc-${node.id}-${i}`);
4011
- })
4012
- }
4013
- );
4014
- }
4015
- function renderVerticalChevronList(element, nodes, palette, style) {
4016
- const w = element.width;
4017
- const h = element.height;
4018
- const pad = 8;
4019
- const gap = 2;
4020
- const usableW = w - pad * 2;
4021
- const usableH = h - pad * 2;
4022
- const itemH = (usableH - gap * (nodes.length - 1)) / nodes.length;
4023
- const chevDepth = Math.min(12, itemH * 0.3);
4024
- const shadow = styleShadow2(style);
4025
- const sw = styleStroke2(style);
4026
- return /* @__PURE__ */ jsx(
4027
- "svg",
4028
- {
4029
- className: "w-full h-full pointer-events-none",
4030
- viewBox: `0 0 ${w} ${h}`,
4031
- preserveAspectRatio: "xMidYMid meet",
4032
- style: { filter: shadow },
4033
- children: nodes.map((node, i) => {
4034
- const x = pad;
4035
- const y = pad + i * (itemH + gap);
4036
- const col = colour(i, palette);
4037
- const fontSize = Math.max(7, Math.min(11, itemH * 0.4));
4038
- const isFirst = i === 0;
4039
- const isLast = i === nodes.length - 1;
4040
- const points = isFirst ? [
4041
- `${x},${y}`,
4042
- `${x + usableW},${y}`,
4043
- `${x + usableW},${y + itemH - chevDepth}`,
4044
- `${x + usableW / 2},${y + itemH}`,
4045
- `${x},${y + itemH - chevDepth}`
4046
- ].join(" ") : isLast ? [
4047
- `${x},${y + chevDepth}`,
4048
- `${x + usableW / 2},${y}`,
4049
- `${x + usableW},${y + chevDepth}`,
4050
- `${x + usableW},${y + itemH}`,
4051
- `${x},${y + itemH}`
4052
- ].join(" ") : [
4053
- `${x},${y + chevDepth}`,
4054
- `${x + usableW / 2},${y}`,
4055
- `${x + usableW},${y + chevDepth}`,
4056
- `${x + usableW},${y + itemH - chevDepth}`,
4057
- `${x + usableW / 2},${y + itemH}`,
4058
- `${x},${y + itemH - chevDepth}`
4059
- ].join(" ");
4060
- return /* @__PURE__ */ jsxs("g", { children: [
4061
- /* @__PURE__ */ jsx(
4062
- "polygon",
4063
- {
4064
- points,
4065
- fill: col,
4066
- opacity: nodeOpacity2(i, nodes.length, style),
4067
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
4068
- strokeWidth: sw
4069
- }
4070
- ),
4071
- /* @__PURE__ */ jsx(
4072
- SmartArtNodeText,
4073
- {
4074
- x: x + usableW / 2,
4075
- y: y + itemH / 2,
4076
- text: truncate2(node.text, 30),
4077
- fill: "white",
4078
- fontSize,
4079
- className: "pointer-events-none"
4080
- }
4081
- )
4082
- ] }, `${element.id}-vchev-${node.id}-${i}`);
4083
- })
4084
- }
4085
- );
4086
- }
4087
3254
  function shapeBody(shape) {
4088
3255
  switch (shape.kind) {
4089
3256
  case "image":
@@ -4183,1056 +3350,126 @@ function DrawingShapeRenderer({
4183
3350
  }
4184
3351
  );
4185
3352
  }
4186
-
4187
- // src/viewer/components/elements/smartart-node-style.tsx
4188
- function resolveNodeStyle(node, index, palette) {
4189
- const style = node.style;
4190
- return {
4191
- fill: style?.fillColor ?? colour(index, palette),
4192
- fontColor: style?.fontColor ?? "white",
4193
- fontWeight: style?.bold ? 700 : void 0,
4194
- fontStyle: style?.italic ? "italic" : void 0,
4195
- strokeColor: style?.lineColor
4196
- };
4197
- }
4198
- function ListRenderer({
4199
- element,
4200
- nodes,
4201
- palette,
4202
- style,
4203
- nodeLabels
4204
- }) {
4205
- const w = element.width;
4206
- const h = element.height;
4207
- const pad = 8;
4208
- const gap = 4;
4209
- const usableH = h - pad * 2;
4210
- const itemH = (usableH - gap * (nodes.length - 1)) / nodes.length;
4211
- const itemW = w - pad * 2;
4212
- const rx = Math.min(6, itemH * 0.15);
4213
- const shadow = styleShadow2(style);
4214
- const sw = styleStroke2(style);
4215
- return /* @__PURE__ */ jsx(
4216
- "svg",
4217
- {
4218
- className: "w-full h-full pointer-events-none",
4219
- viewBox: `0 0 ${w} ${h}`,
4220
- preserveAspectRatio: "xMidYMid meet",
4221
- "data-testid": "smartart-list",
4222
- children: nodes.map((node, i) => {
4223
- const y = pad + i * (itemH + gap);
4224
- const fontSize = fitFontSize(node.text, itemW * 0.9, itemH, 12);
4225
- const ns = resolveNodeStyle(node, i, palette);
4226
- const label = nodeLabels?.get(node.id);
4227
- return /* @__PURE__ */ jsxs(
4228
- "g",
4229
- {
4230
- ...smartArtNodeGroupProps(node.id, shadow, label),
4231
- children: [
4232
- label ? /* @__PURE__ */ jsx("title", { children: label }) : null,
4233
- /* @__PURE__ */ jsx(
4234
- "rect",
4235
- {
4236
- x: pad,
4237
- y,
4238
- width: itemW,
4239
- height: itemH,
4240
- rx,
4241
- fill: ns.fill,
4242
- opacity: nodeOpacity2(i, nodes.length, style),
4243
- stroke: ns.strokeColor ?? (sw > 0 ? "rgba(255,255,255,0.3)" : "none"),
4244
- strokeWidth: sw
4245
- }
4246
- ),
4247
- /* @__PURE__ */ jsx(
4248
- SmartArtNodeText,
4249
- {
4250
- x: pad + itemW / 2,
4251
- y: y + itemH / 2,
4252
- text: truncate2(node.text, 40),
4253
- fill: ns.fontColor,
4254
- fontWeight: ns.fontWeight,
4255
- fontStyle: ns.fontStyle,
4256
- fontSize,
4257
- className: "pointer-events-none"
4258
- }
4259
- )
4260
- ]
4261
- },
4262
- `${element.id}-list-${node.id}-${i}`
4263
- );
4264
- })
4265
- }
4266
- );
4267
- }
4268
- function ProcessRenderer({
4269
- element,
4270
- nodes,
4271
- palette,
4272
- style,
4273
- nodeLabels
4274
- }) {
4275
- const w = element.width;
4276
- const h = element.height;
4277
- const pad = 8;
4278
- const gap = 4;
4279
- const chevronDepth = Math.min(16, w * 0.04);
4280
- const usableW = w - pad * 2;
4281
- const itemW = (usableW - gap * (nodes.length - 1)) / nodes.length;
4282
- const itemH = Math.min(h - pad * 2, h * 0.6);
4283
- const yMid = h / 2;
4284
- const shadow = styleShadow2(style);
4285
- const sw = styleStroke2(style);
4286
- return /* @__PURE__ */ jsx(
4287
- "svg",
4288
- {
4289
- className: "w-full h-full pointer-events-none",
4290
- viewBox: `0 0 ${w} ${h}`,
4291
- preserveAspectRatio: "xMidYMid meet",
4292
- "data-testid": "smartart-process",
4293
- children: nodes.map((node, i) => {
4294
- const x = pad + i * (itemW + gap);
4295
- const halfH = itemH / 2;
4296
- const isFirst = i === 0;
4297
- const isLast = i === nodes.length - 1;
4298
- const points = isFirst ? `${x},${yMid - halfH} ${x + itemW - chevronDepth},${yMid - halfH} ${x + itemW},${yMid} ${x + itemW - chevronDepth},${yMid + halfH} ${x},${yMid + halfH}` : isLast ? `${x},${yMid - halfH} ${x + itemW},${yMid - halfH} ${x + itemW},${yMid + halfH} ${x},${yMid + halfH} ${x + chevronDepth},${yMid}` : `${x},${yMid - halfH} ${x + itemW - chevronDepth},${yMid - halfH} ${x + itemW},${yMid} ${x + itemW - chevronDepth},${yMid + halfH} ${x},${yMid + halfH} ${x + chevronDepth},${yMid}`;
4299
- const fontSize = fitFontSize(node.text, itemW * 0.7, itemH, 12);
4300
- const ns = resolveNodeStyle(node, i, palette);
4301
- const label = nodeLabels?.get(node.id);
4302
- return /* @__PURE__ */ jsxs(
4303
- "g",
4304
- {
4305
- ...smartArtNodeGroupProps(node.id, shadow, label),
4306
- children: [
4307
- label ? /* @__PURE__ */ jsx("title", { children: label }) : null,
4308
- /* @__PURE__ */ jsx(
4309
- "polygon",
4310
- {
4311
- points,
4312
- fill: ns.fill,
4313
- opacity: nodeOpacity2(i, nodes.length, style),
4314
- stroke: ns.strokeColor ?? (sw > 0 ? "rgba(255,255,255,0.3)" : "none"),
4315
- strokeWidth: sw
4316
- }
4317
- ),
4318
- /* @__PURE__ */ jsx(
4319
- SmartArtNodeText,
4320
- {
4321
- x: x + itemW / 2,
4322
- y: yMid,
4323
- text: truncate2(node.text, 25),
4324
- fill: ns.fontColor,
4325
- fontWeight: ns.fontWeight,
4326
- fontStyle: ns.fontStyle,
4327
- fontSize,
4328
- className: "pointer-events-none"
4329
- }
4330
- )
4331
- ]
4332
- },
4333
- `${element.id}-process-${node.id}-${i}`
4334
- );
4335
- })
4336
- }
4337
- );
4338
- }
4339
- function CycleRenderer({
4340
- element,
4341
- nodes,
4342
- palette,
4343
- style,
4344
- nodeLabels
4345
- }) {
4346
- const w = element.width;
4347
- const h = element.height;
4348
- const size = Math.min(w, h);
4349
- const cx = w / 2;
4350
- const cy = h / 2;
4351
- const radius = size * 0.35;
4352
- const nodeR = Math.max(size * 0.06, Math.min(size * 0.12, 200 / nodes.length));
4353
- const shadow = styleShadow2(style);
4354
- const sw = styleStroke2(style);
4355
- return /* @__PURE__ */ jsxs(
4356
- "svg",
4357
- {
4358
- className: "w-full h-full pointer-events-none",
4359
- viewBox: `0 0 ${w} ${h}`,
4360
- preserveAspectRatio: "xMidYMid meet",
4361
- "data-testid": "smartart-cycle",
4362
- children: [
4363
- nodes.map((_node, i) => {
4364
- const angle = i / nodes.length * Math.PI * 2 - Math.PI / 2;
4365
- const nx = cx + radius * Math.cos(angle);
4366
- const ny = cy + radius * Math.sin(angle);
4367
- const nextI = (i + 1) % nodes.length;
4368
- const nextAngle = nextI / nodes.length * Math.PI * 2 - Math.PI / 2;
4369
- const nextX = cx + radius * Math.cos(nextAngle);
4370
- const nextY = cy + radius * Math.sin(nextAngle);
4371
- const midAngle = (angle + nextAngle) / 2;
4372
- const adjustedMidAngle = i === nodes.length - 1 ? (angle + nextAngle + Math.PI * 2) / 2 : midAngle;
4373
- const arcBulge = radius * 0.15;
4374
- const controlX = cx + (radius + arcBulge) * Math.cos(adjustedMidAngle);
4375
- const controlY = cy + (radius + arcBulge) * Math.sin(adjustedMidAngle);
4376
- return /* @__PURE__ */ jsx(
4377
- "path",
4378
- {
4379
- d: `M${nx},${ny} Q${controlX},${controlY} ${nextX},${nextY}`,
4380
- fill: "none",
4381
- stroke: "#94a3b8",
4382
- strokeWidth: 1.5,
4383
- opacity: 0.5,
4384
- markerEnd: void 0
4385
- },
4386
- `${element.id}-cycle-conn-${i}`
4387
- );
4388
- }),
4389
- nodes.map((node, i) => {
4390
- const angle = i / nodes.length * Math.PI * 2 - Math.PI / 2;
4391
- const nx = cx + radius * Math.cos(angle);
4392
- const ny = cy + radius * Math.sin(angle);
4393
- const fontSize = fitFontSize(node.text, nodeR * 1.4, nodeR * 2, 11);
4394
- const ns = resolveNodeStyle(node, i, palette);
4395
- const label = nodeLabels?.get(node.id);
4396
- return /* @__PURE__ */ jsxs(
4397
- "g",
4398
- {
4399
- ...smartArtNodeGroupProps(node.id, shadow, label),
4400
- children: [
4401
- label ? /* @__PURE__ */ jsx("title", { children: label }) : null,
4402
- /* @__PURE__ */ jsx(
4403
- "circle",
4404
- {
4405
- cx: nx,
4406
- cy: ny,
4407
- r: nodeR,
4408
- fill: ns.fill,
4409
- opacity: nodeOpacity2(i, nodes.length, style),
4410
- stroke: ns.strokeColor ?? (sw > 0 ? "rgba(255,255,255,0.3)" : "none"),
4411
- strokeWidth: sw
4412
- }
4413
- ),
4414
- /* @__PURE__ */ jsx(
4415
- SmartArtNodeText,
4416
- {
4417
- x: nx,
4418
- y: ny,
4419
- text: truncate2(node.text, 20),
4420
- fill: ns.fontColor,
4421
- fontWeight: ns.fontWeight,
4422
- fontStyle: ns.fontStyle,
4423
- fontSize,
4424
- className: "pointer-events-none"
4425
- }
4426
- )
4427
- ]
4428
- },
4429
- `${element.id}-cycle-${node.id}-${i}`
4430
- );
4431
- })
4432
- ]
4433
- }
4434
- );
4435
- }
4436
- function MatrixRenderer({
4437
- element,
4438
- nodes,
4439
- palette,
4440
- style,
4441
- nodeLabels
4442
- }) {
4443
- const w = element.width;
4444
- const h = element.height;
4445
- const cols = Math.ceil(Math.sqrt(nodes.length));
4446
- const rows = Math.ceil(nodes.length / cols);
4447
- const pad = 8;
4448
- const gap = 6;
4449
- const usableW = w - pad * 2;
4450
- const usableH = h - pad * 2;
4451
- const cellW = (usableW - gap * (cols - 1)) / cols;
4452
- const cellH = (usableH - gap * (rows - 1)) / rows;
4453
- const rx = Math.min(6, Math.min(cellW, cellH) * 0.1);
4454
- const shadow = styleShadow2(style);
4455
- const sw = styleStroke2(style);
3353
+ var CONNECTOR_STROKE = "#94a3b8";
3354
+ var CONNECTOR_WIDTH = 1.5;
3355
+ var CONNECTOR_OPACITY = 0.5;
3356
+ var DEFAULT_LABEL_COLOR = "white";
3357
+ function ConnectorPath({ connector }) {
4456
3358
  return /* @__PURE__ */ jsx(
4457
- "svg",
3359
+ "path",
4458
3360
  {
4459
- className: "w-full h-full pointer-events-none",
4460
- viewBox: `0 0 ${w} ${h}`,
4461
- preserveAspectRatio: "xMidYMid meet",
4462
- "data-testid": "smartart-matrix",
4463
- children: nodes.map((node, i) => {
4464
- const col = i % cols;
4465
- const row = Math.floor(i / cols);
4466
- const x = pad + col * (cellW + gap);
4467
- const y = pad + row * (cellH + gap);
4468
- const fontSize = fitFontSize(node.text, cellW * 0.85, cellH, 12);
4469
- const ns = resolveNodeStyle(node, i, palette);
4470
- const label = nodeLabels?.get(node.id);
4471
- return /* @__PURE__ */ jsxs(
4472
- "g",
4473
- {
4474
- ...smartArtNodeGroupProps(node.id, shadow, label),
4475
- children: [
4476
- label ? /* @__PURE__ */ jsx("title", { children: label }) : null,
4477
- /* @__PURE__ */ jsx(
4478
- "rect",
4479
- {
4480
- x,
4481
- y,
4482
- width: cellW,
4483
- height: cellH,
4484
- rx,
4485
- fill: ns.fill,
4486
- opacity: nodeOpacity2(i, nodes.length, style),
4487
- stroke: ns.strokeColor ?? (sw > 0 ? "rgba(255,255,255,0.3)" : "none"),
4488
- strokeWidth: sw
4489
- }
4490
- ),
4491
- /* @__PURE__ */ jsx(
4492
- SmartArtNodeText,
4493
- {
4494
- x: x + cellW / 2,
4495
- y: y + cellH / 2,
4496
- text: truncate2(node.text, 30),
4497
- fill: ns.fontColor,
4498
- fontWeight: ns.fontWeight ?? 500,
4499
- fontStyle: ns.fontStyle,
4500
- fontSize,
4501
- className: "pointer-events-none"
4502
- }
4503
- )
4504
- ]
4505
- },
4506
- `${element.id}-matrix-${node.id}-${i}`
4507
- );
4508
- })
4509
- }
4510
- );
4511
- }
4512
- function PyramidRenderer({
4513
- element,
4514
- nodes,
4515
- palette,
4516
- style
4517
- }) {
4518
- const w = element.width;
4519
- const h = element.height;
4520
- const pad = 8;
4521
- const gap = 3;
4522
- const usableH = h - pad * 2;
4523
- const bandH = (usableH - gap * (nodes.length - 1)) / nodes.length;
4524
- const maxW = w - pad * 2;
4525
- const shadow = styleShadow2(style);
4526
- const sw = styleStroke2(style);
4527
- return /* @__PURE__ */ jsx(
4528
- "svg",
4529
- {
4530
- className: "w-full h-full pointer-events-none",
4531
- viewBox: `0 0 ${w} ${h}`,
4532
- preserveAspectRatio: "xMidYMid meet",
4533
- "data-testid": "smartart-pyramid",
4534
- children: nodes.map((node, i) => {
4535
- const topWidthFrac = 0.3 + i / Math.max(nodes.length - 1, 1) * 0.7;
4536
- const bottomWidthFrac = i < nodes.length - 1 ? 0.3 + (i + 1) / Math.max(nodes.length - 1, 1) * 0.7 : 1;
4537
- const topW = maxW * topWidthFrac;
4538
- const bottomW = maxW * bottomWidthFrac;
4539
- const y = pad + i * (bandH + gap);
4540
- const topLeft = (w - topW) / 2;
4541
- const topRight = topLeft + topW;
4542
- const bottomLeft = (w - bottomW) / 2;
4543
- const bottomRight = bottomLeft + bottomW;
4544
- const points = [
4545
- `${topLeft},${y}`,
4546
- `${topRight},${y}`,
4547
- `${bottomRight},${y + bandH}`,
4548
- `${bottomLeft},${y + bandH}`
4549
- ].join(" ");
4550
- const fontSize = fitFontSize(node.text, topW * 0.85, bandH, 12);
4551
- return /* @__PURE__ */ jsxs(
4552
- "g",
4553
- {
4554
- ...smartArtNodeGroupProps(node.id, shadow),
4555
- children: [
4556
- /* @__PURE__ */ jsx(
4557
- "polygon",
4558
- {
4559
- points,
4560
- fill: colour(i, palette),
4561
- opacity: nodeOpacity2(i, nodes.length, style),
4562
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
4563
- strokeWidth: sw
4564
- }
4565
- ),
4566
- /* @__PURE__ */ jsx(
4567
- SmartArtNodeText,
4568
- {
4569
- x: w / 2,
4570
- y: y + bandH / 2,
4571
- text: truncate2(node.text, 30),
4572
- fill: "white",
4573
- fontSize,
4574
- className: "pointer-events-none"
4575
- }
4576
- )
4577
- ]
4578
- },
4579
- `${element.id}-pyramid-${node.id}-${i}`
4580
- );
4581
- })
3361
+ d: connector.d,
3362
+ fill: "none",
3363
+ stroke: connector.stroke ?? CONNECTOR_STROKE,
3364
+ strokeWidth: connector.strokeWidth ?? CONNECTOR_WIDTH,
3365
+ opacity: connector.opacity ?? CONNECTOR_OPACITY,
3366
+ strokeDasharray: connector.dash
4582
3367
  }
4583
3368
  );
4584
3369
  }
4585
- function VennRenderer({
4586
- element,
4587
- nodes,
4588
- palette,
4589
- style
4590
- }) {
4591
- const w = element.width;
4592
- const h = element.height;
4593
- const shadow = styleShadow2(style);
4594
- if (nodes.length <= 4) {
4595
- const cx = w / 2;
4596
- const cy2 = h / 2;
4597
- const r2 = Math.min(w, h) * 0.28;
4598
- const spread = r2 * 0.55;
3370
+ function NodeShape({ node }) {
3371
+ if (node.kind === "circle") {
4599
3372
  return /* @__PURE__ */ jsx(
4600
- "svg",
3373
+ "circle",
4601
3374
  {
4602
- className: "w-full h-full pointer-events-none",
4603
- viewBox: `0 0 ${w} ${h}`,
4604
- preserveAspectRatio: "xMidYMid meet",
4605
- "data-testid": "smartart-venn",
4606
- children: nodes.map((node, i) => {
4607
- const angle = i / nodes.length * Math.PI * 2 - Math.PI / 2;
4608
- const nx = cx + spread * Math.cos(angle);
4609
- const ny = cy2 + spread * Math.sin(angle);
4610
- const fontSize = fitFontSize(node.text, r2 * 1.2, r2 * 2, 11);
4611
- return /* @__PURE__ */ jsxs(
4612
- "g",
4613
- {
4614
- ...smartArtNodeGroupProps(node.id, shadow),
4615
- children: [
4616
- /* @__PURE__ */ jsx("circle", { cx: nx, cy: ny, r: r2, fill: colour(i, palette), opacity: 0.35 }),
4617
- /* @__PURE__ */ jsx(
4618
- SmartArtNodeText,
4619
- {
4620
- x: nx,
4621
- y: ny,
4622
- text: truncate2(node.text, 20),
4623
- fill: "white",
4624
- fontSize,
4625
- fontWeight: "bold",
4626
- className: "pointer-events-none"
4627
- }
4628
- )
4629
- ]
4630
- },
4631
- `${element.id}-venn-${node.id}-${i}`
4632
- );
4633
- })
3375
+ cx: node.cx,
3376
+ cy: node.cy,
3377
+ r: node.r,
3378
+ fill: node.fill,
3379
+ stroke: node.stroke,
3380
+ strokeWidth: node.strokeWidth,
3381
+ opacity: node.opacity
4634
3382
  }
4635
3383
  );
4636
3384
  }
4637
- const r = Math.min(h * 0.38, w / (nodes.length * 0.9));
4638
- const overlap = r * 0.5;
4639
- const totalW = nodes.length * (r * 2 - overlap) + overlap;
4640
- const offsetX = (w - totalW) / 2 + r;
4641
- const cy = h / 2;
4642
- return /* @__PURE__ */ jsx(
4643
- "svg",
4644
- {
4645
- className: "w-full h-full pointer-events-none",
4646
- viewBox: `0 0 ${w} ${h}`,
4647
- preserveAspectRatio: "xMidYMid meet",
4648
- "data-testid": "smartart-venn",
4649
- children: nodes.map((node, i) => {
4650
- const nx = offsetX + i * (r * 2 - overlap);
4651
- const fontSize = fitFontSize(node.text, r * 1.2, r * 2, 10);
4652
- return /* @__PURE__ */ jsxs(
4653
- "g",
4654
- {
4655
- ...smartArtNodeGroupProps(node.id, shadow),
4656
- children: [
4657
- /* @__PURE__ */ jsx("circle", { cx: nx, cy, r, fill: colour(i, palette), opacity: 0.35 }),
4658
- /* @__PURE__ */ jsx(
4659
- SmartArtNodeText,
4660
- {
4661
- x: nx,
4662
- y: cy,
4663
- text: truncate2(node.text, 20),
4664
- fill: "white",
4665
- fontSize,
4666
- fontWeight: "bold",
4667
- className: "pointer-events-none"
4668
- }
4669
- )
4670
- ]
4671
- },
4672
- `${element.id}-venn-${node.id}-${i}`
4673
- );
4674
- })
4675
- }
4676
- );
4677
- }
4678
- function FunnelRenderer({
4679
- element,
4680
- nodes,
4681
- palette,
4682
- style
4683
- }) {
4684
- const w = element.width;
4685
- const h = element.height;
4686
- const pad = 8;
4687
- const usableW = w - pad * 2;
4688
- const stageH = (h - pad * 2) / nodes.length;
4689
- const shadow = styleShadow2(style);
4690
- return /* @__PURE__ */ jsx(
4691
- "svg",
4692
- {
4693
- className: "w-full h-full pointer-events-none",
4694
- viewBox: `0 0 ${w} ${h}`,
4695
- preserveAspectRatio: "xMidYMid meet",
4696
- "data-testid": "smartart-funnel",
4697
- children: nodes.map((node, i) => {
4698
- const topWidth = usableW * (1 - i / nodes.length);
4699
- const bottomWidth = usableW * (1 - (i + 1) / nodes.length);
4700
- const y = pad + i * stageH;
4701
- const topLeft = (w - topWidth) / 2;
4702
- const topRight = topLeft + topWidth;
4703
- const bottomLeft = (w - bottomWidth) / 2;
4704
- const bottomRight = bottomLeft + bottomWidth;
4705
- const points = [
4706
- `${topLeft},${y}`,
4707
- `${topRight},${y}`,
4708
- `${bottomRight},${y + stageH}`,
4709
- `${bottomLeft},${y + stageH}`
4710
- ].join(" ");
4711
- const fontSize = fitFontSize(node.text, topWidth * 0.85, stageH, 11);
4712
- return /* @__PURE__ */ jsxs(
4713
- "g",
4714
- {
4715
- ...smartArtNodeGroupProps(node.id, shadow),
4716
- children: [
4717
- /* @__PURE__ */ jsx(
4718
- "polygon",
4719
- {
4720
- points,
4721
- fill: colour(i, palette),
4722
- opacity: nodeOpacity2(i, nodes.length, style)
4723
- }
4724
- ),
4725
- /* @__PURE__ */ jsx(
4726
- SmartArtNodeText,
4727
- {
4728
- x: w / 2,
4729
- y: y + stageH / 2,
4730
- text: truncate2(node.text, 30),
4731
- fill: "white",
4732
- fontSize,
4733
- className: "pointer-events-none"
4734
- }
4735
- )
4736
- ]
4737
- },
4738
- `${element.id}-funnel-${node.id}-${i}`
4739
- );
4740
- })
4741
- }
4742
- );
4743
- }
4744
- function TargetRenderer({
4745
- element,
4746
- nodes,
4747
- palette,
4748
- style
4749
- }) {
4750
- const w = element.width;
4751
- const h = element.height;
4752
- const cx = w * 0.4;
4753
- const cy = h / 2;
4754
- const maxR = Math.min(cx - 8, cy - 8);
4755
- const fontSize = Math.max(7, Math.min(10, maxR / (nodes.length + 1)));
4756
- const labelX = cx + maxR + 8;
4757
- const shadow = styleShadow2(style);
4758
- return /* @__PURE__ */ jsx(
4759
- "svg",
4760
- {
4761
- className: "w-full h-full pointer-events-none",
4762
- viewBox: `0 0 ${w} ${h}`,
4763
- preserveAspectRatio: "xMidYMid meet",
4764
- "data-testid": "smartart-target",
4765
- children: nodes.map((node, i) => {
4766
- const r = maxR * ((nodes.length - i) / nodes.length);
4767
- return /* @__PURE__ */ jsxs(
4768
- "g",
4769
- {
4770
- ...smartArtNodeGroupProps(node.id, shadow),
4771
- children: [
4772
- /* @__PURE__ */ jsx(
4773
- "circle",
4774
- {
4775
- cx,
4776
- cy,
4777
- r: Math.max(r, 4),
4778
- fill: colour(i, palette),
4779
- opacity: nodeOpacity2(i, nodes.length, style)
4780
- }
4781
- ),
4782
- /* @__PURE__ */ jsx(
4783
- "line",
4784
- {
4785
- x1: cx + Math.max(r, 4),
4786
- y1: cy,
4787
- x2: labelX - 2,
4788
- y2: 8 + i * (fontSize + 6),
4789
- stroke: colour(i, palette),
4790
- strokeWidth: 1,
4791
- opacity: 0.6
4792
- }
4793
- ),
4794
- /* @__PURE__ */ jsx(
4795
- "text",
4796
- {
4797
- x: labelX,
4798
- y: 8 + i * (fontSize + 6) + fontSize / 2,
4799
- textAnchor: "start",
4800
- dominantBaseline: "central",
4801
- fill: colour(i, palette),
4802
- fontSize,
4803
- className: "pointer-events-none",
4804
- children: truncate2(node.text, 30)
4805
- }
4806
- )
4807
- ]
4808
- },
4809
- `${element.id}-target-${node.id}-${i}`
4810
- );
4811
- })
4812
- }
4813
- );
4814
- }
4815
- function HierarchyRenderer({
4816
- element,
4817
- nodes,
4818
- palette,
4819
- style
4820
- }) {
4821
- const roots = buildTree(nodes);
4822
- if (roots.length === 0) {
4823
- return /* @__PURE__ */ jsx(ListRenderer, { element, nodes, palette, style });
4824
- }
4825
- const totalLeaves = roots.reduce((s, r) => s + treeWidth(r), 0);
4826
- const depth = Math.max(...roots.map(treeDepth));
4827
- const svgW = element.width;
4828
- const svgH = element.height;
4829
- const cellW = svgW / totalLeaves;
4830
- const cellH = svgH / Math.max(depth, 1);
4831
- const boxW = Math.min(cellW * 0.8, 140);
4832
- const boxH = Math.min(cellH * 0.4, 36);
4833
- const rx = Math.min(6, boxH * 0.15);
4834
- const shadow = styleShadow2(style);
4835
- const sw = styleStroke2(style);
4836
- const elements = [];
4837
- let colourIdx = 0;
4838
- function renderTreeNode(t, xOffset, level) {
4839
- const w = treeWidth(t);
4840
- const nodeCx = (xOffset + w / 2) * cellW;
4841
- const nodeCy = level * cellH + cellH / 2;
4842
- const ci = colourIdx++;
4843
- const fontSize = fitFontSize(t.node.text, boxW * 0.9, boxH, 11);
4844
- for (const child of t.children) {
4845
- const childW = treeWidth(child);
4846
- let childOffset2 = xOffset;
4847
- for (const c of t.children) {
4848
- if (c === child) {
4849
- break;
4850
- }
4851
- childOffset2 += treeWidth(c);
3385
+ if (node.kind === "polygon") {
3386
+ return /* @__PURE__ */ jsx(
3387
+ "polygon",
3388
+ {
3389
+ points: node.points,
3390
+ fill: node.fill,
3391
+ stroke: node.stroke,
3392
+ strokeWidth: node.strokeWidth,
3393
+ opacity: node.opacity
4852
3394
  }
4853
- const childCx = (childOffset2 + childW / 2) * cellW;
4854
- const childCy = (level + 1) * cellH + cellH / 2;
4855
- const midY = nodeCy + boxH / 2 + (childCy - boxH / 2 - (nodeCy + boxH / 2)) / 2;
4856
- elements.push(
4857
- /* @__PURE__ */ jsx(
4858
- "path",
4859
- {
4860
- d: `M${nodeCx},${nodeCy + boxH / 2} L${nodeCx},${midY} L${childCx},${midY} L${childCx},${childCy - boxH / 2}`,
4861
- fill: "none",
4862
- stroke: "#94a3b8",
4863
- strokeWidth: 1.5,
4864
- opacity: 0.5
4865
- },
4866
- `${element.id}-hier-conn-${t.node.id}-${child.node.id}`
4867
- )
4868
- );
4869
- }
4870
- elements.push(
4871
- /* @__PURE__ */ jsxs(
4872
- "g",
4873
- {
4874
- ...smartArtNodeGroupProps(t.node.id, shadow),
4875
- children: [
4876
- /* @__PURE__ */ jsx(
4877
- "rect",
4878
- {
4879
- x: nodeCx - boxW / 2,
4880
- y: nodeCy - boxH / 2,
4881
- width: boxW,
4882
- height: boxH,
4883
- rx,
4884
- fill: colour(ci, palette),
4885
- opacity: nodeOpacity2(ci, nodes.length, style),
4886
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
4887
- strokeWidth: sw
4888
- }
4889
- ),
4890
- /* @__PURE__ */ jsx(
4891
- SmartArtNodeText,
4892
- {
4893
- x: nodeCx,
4894
- y: nodeCy,
4895
- text: truncate2(t.node.text, 40),
4896
- fill: "white",
4897
- fontSize,
4898
- className: "pointer-events-none"
4899
- }
4900
- )
4901
- ]
4902
- },
4903
- `${element.id}-hier-group-${t.node.id}`
4904
- )
4905
3395
  );
4906
- let childOffset = xOffset;
4907
- for (const child of t.children) {
4908
- renderTreeNode(child, childOffset, level + 1);
4909
- childOffset += treeWidth(child);
4910
- }
4911
- return w;
4912
- }
4913
- let offset = 0;
4914
- for (const root of roots) {
4915
- offset += renderTreeNode(root, offset, 0);
4916
3396
  }
4917
3397
  return /* @__PURE__ */ jsx(
4918
- "svg",
3398
+ "rect",
4919
3399
  {
4920
- className: "w-full h-full pointer-events-none",
4921
- viewBox: `0 0 ${svgW} ${svgH}`,
4922
- preserveAspectRatio: "xMidYMid meet",
4923
- "data-testid": "smartart-hierarchy",
4924
- children: elements
3400
+ x: node.x,
3401
+ y: node.y,
3402
+ width: node.width,
3403
+ height: node.height,
3404
+ rx: node.rx,
3405
+ fill: node.fill,
3406
+ stroke: node.stroke,
3407
+ strokeWidth: node.strokeWidth,
3408
+ opacity: node.opacity
4925
3409
  }
4926
3410
  );
4927
3411
  }
4928
- function GearRenderer({
4929
- element,
4930
- nodes,
4931
- palette,
4932
- style
4933
- }) {
4934
- const w = element.width;
4935
- const h = element.height;
4936
- const gearNodes = nodes.slice(0, 3);
4937
- const extraNodes = nodes.slice(3);
4938
- const gearCount = gearNodes.length;
4939
- const gearAreaW = extraNodes.length > 0 ? w * 0.7 : w;
4940
- const spacing = gearAreaW / (gearCount + 1);
4941
- const gearR = Math.min(spacing * 0.4, h * 0.35);
4942
- const innerR = gearR * 0.7;
4943
- const teethCount = 8;
4944
- const shadow = styleShadow2(style);
4945
- return /* @__PURE__ */ jsxs(
4946
- "svg",
4947
- {
4948
- className: "w-full h-full pointer-events-none",
4949
- viewBox: `0 0 ${w} ${h}`,
4950
- preserveAspectRatio: "xMidYMid meet",
4951
- "data-testid": "smartart-gear",
4952
- children: [
4953
- gearNodes.map((node, i) => {
4954
- const gx = spacing * (i + 1);
4955
- const gy = h / 2 + (i % 2 === 0 ? 0 : gearR * 0.35);
4956
- const fontSize = fitFontSize(node.text, innerR * 1.2, innerR * 2, 11);
4957
- return /* @__PURE__ */ jsxs(
4958
- "g",
4959
- {
4960
- ...smartArtNodeGroupProps(node.id, shadow),
4961
- children: [
4962
- /* @__PURE__ */ jsx(
4963
- "path",
4964
- {
4965
- d: gearPath(gx, gy, gearR, innerR, teethCount),
4966
- fill: colour(i, palette),
4967
- opacity: nodeOpacity2(i, nodes.length, style)
4968
- }
4969
- ),
4970
- /* @__PURE__ */ jsx("circle", { cx: gx, cy: gy, r: innerR * 0.5, fill: "white", opacity: 0.25 }),
4971
- /* @__PURE__ */ jsx(
4972
- SmartArtNodeText,
4973
- {
4974
- x: gx,
4975
- y: gy,
4976
- text: truncate2(node.text, 20),
4977
- fill: "white",
4978
- fontSize,
4979
- fontWeight: "bold",
4980
- className: "pointer-events-none"
4981
- }
4982
- )
4983
- ]
4984
- },
4985
- `${element.id}-gear-${node.id}-${i}`
4986
- );
4987
- }),
4988
- extraNodes.map((node, i) => {
4989
- const lx = gearAreaW + 10;
4990
- const ly = 14 + i * 18;
4991
- return /* @__PURE__ */ jsxs("g", { children: [
4992
- /* @__PURE__ */ jsx(
4993
- "circle",
4994
- {
4995
- cx: lx,
4996
- cy: ly,
4997
- r: 3,
4998
- fill: colour(gearCount + i, palette),
4999
- opacity: nodeOpacity2(gearCount + i, nodes.length, style)
5000
- }
5001
- ),
5002
- /* @__PURE__ */ jsx(
5003
- "text",
5004
- {
5005
- x: lx + 8,
5006
- y: ly,
5007
- textAnchor: "start",
5008
- dominantBaseline: "central",
5009
- fill: colour(gearCount + i, palette),
5010
- fontSize: 10,
5011
- className: "pointer-events-none",
5012
- children: truncate2(node.text, 30)
5013
- }
5014
- )
5015
- ] }, `${element.id}-gear-extra-${node.id}-${i}`);
5016
- })
5017
- ]
5018
- }
5019
- );
3412
+ function labelAnchor(node) {
3413
+ if (node.kind === "circle") {
3414
+ return {
3415
+ x: node.textX ?? node.cx,
3416
+ y: node.textY ?? node.cy,
3417
+ textAnchor: node.textAnchor ?? "middle",
3418
+ baseline: node.textBaseline ?? "middle"
3419
+ };
3420
+ }
3421
+ return { x: node.textX, y: node.textY, textAnchor: "middle", baseline: "middle" };
5020
3422
  }
5021
- function TimelineRenderer({
5022
- element,
5023
- nodes,
5024
- palette,
5025
- style
3423
+ function SmartArtLayoutSvg({
3424
+ layout,
3425
+ nodeIds,
3426
+ nodeLabels
5026
3427
  }) {
5027
- const w = element.width;
5028
- const h = element.height;
5029
- const padX = 24;
5030
- const lineY = h / 2;
5031
- const lineStartX = padX;
5032
- const lineEndX = w - padX;
5033
- const lineLen = lineEndX - lineStartX;
5034
- const dotR = Math.max(4, Math.min(8, lineLen / (nodes.length * 4)));
5035
- const labelOffset = Math.min(h * 0.28, 40);
5036
- const shadow = styleShadow2(style);
5037
3428
  return /* @__PURE__ */ jsxs(
5038
3429
  "svg",
5039
3430
  {
5040
3431
  className: "w-full h-full pointer-events-none",
5041
- viewBox: `0 0 ${w} ${h}`,
3432
+ viewBox: layout.viewBox,
5042
3433
  preserveAspectRatio: "xMidYMid meet",
5043
- "data-testid": "smartart-timeline",
3434
+ "data-testid": `smartart-${layout.family}`,
3435
+ "data-layout-family": layout.family,
5044
3436
  children: [
5045
- /* @__PURE__ */ jsx("line", { x1: lineStartX, y1: lineY, x2: lineEndX, y2: lineY, stroke: "#94a3b8", strokeWidth: 2 }),
5046
- /* @__PURE__ */ jsx(
5047
- "polygon",
5048
- {
5049
- points: `${lineEndX - 6},${lineY - 4} ${lineEndX},${lineY} ${lineEndX - 6},${lineY + 4}`,
5050
- fill: "#94a3b8"
5051
- }
5052
- ),
5053
- nodes.map((node, i) => {
5054
- const x = nodes.length === 1 ? (lineStartX + lineEndX) / 2 : lineStartX + i / (nodes.length - 1) * lineLen;
5055
- const above = i % 2 === 0;
5056
- const textY = above ? lineY - labelOffset : lineY + labelOffset;
5057
- const stemEndY = above ? lineY - dotR - 2 : lineY + dotR + 2;
5058
- const fontSize = fitFontSize(node.text, lineLen / nodes.length * 0.9, labelOffset, 10);
3437
+ layout.connectors.map((connector) => /* @__PURE__ */ jsx(ConnectorPath, { connector }, connector.key)),
3438
+ layout.nodes.map((node, i) => {
3439
+ const nodeId = nodeIds[i];
3440
+ const label = nodeId ? nodeLabels?.get(nodeId) : void 0;
3441
+ const anchor = labelAnchor(node);
5059
3442
  return /* @__PURE__ */ jsxs(
5060
3443
  "g",
5061
3444
  {
5062
- ...smartArtNodeGroupProps(node.id, shadow),
3445
+ ...smartArtNodeGroupProps(nodeId ?? node.key, layout.shadowFilter, label),
5063
3446
  children: [
5064
- /* @__PURE__ */ jsx(
5065
- "line",
5066
- {
5067
- x1: x,
5068
- y1: stemEndY,
5069
- x2: x,
5070
- y2: textY + (above ? fontSize : -fontSize),
5071
- stroke: colour(i, palette),
5072
- strokeWidth: 1,
5073
- opacity: 0.5
5074
- }
5075
- ),
5076
- /* @__PURE__ */ jsx(
5077
- "circle",
5078
- {
5079
- cx: x,
5080
- cy: lineY,
5081
- r: dotR,
5082
- fill: colour(i, palette),
5083
- opacity: nodeOpacity2(i, nodes.length, style)
5084
- }
5085
- ),
5086
- /* @__PURE__ */ jsx(
3447
+ label ? /* @__PURE__ */ jsx("title", { children: label }) : null,
3448
+ /* @__PURE__ */ jsx(NodeShape, { node }),
3449
+ node.text.length > 0 ? /* @__PURE__ */ jsx(
5087
3450
  SmartArtNodeText,
5088
3451
  {
5089
- text: truncate2(node.text, 20) ?? "",
5090
- x,
5091
- y: textY,
5092
- fill: colour(i, palette),
5093
- fontSize,
5094
- anchor: above ? "bottom" : "top",
3452
+ x: anchor.x,
3453
+ y: anchor.y,
3454
+ text: node.text,
3455
+ fill: node.fontColor ?? DEFAULT_LABEL_COLOR,
3456
+ fontSize: node.fontSize,
3457
+ fontWeight: node.fontWeight,
3458
+ fontStyle: node.fontStyle,
3459
+ textAnchor: anchor.textAnchor,
3460
+ anchor: anchor.baseline,
5095
3461
  className: "pointer-events-none"
5096
3462
  }
5097
- )
3463
+ ) : null
5098
3464
  ]
5099
3465
  },
5100
- `${element.id}-timeline-${node.id}-${i}`
3466
+ node.key
5101
3467
  );
5102
3468
  })
5103
3469
  ]
5104
3470
  }
5105
3471
  );
5106
3472
  }
5107
- function BendingProcessRenderer({
5108
- element,
5109
- nodes,
5110
- palette,
5111
- style
5112
- }) {
5113
- const COLS = 4;
5114
- const w = element.width;
5115
- const h = element.height;
5116
- const rowsCount = Math.ceil(nodes.length / COLS);
5117
- const padX = 8;
5118
- const padY = 8;
5119
- const cellW = (w - padX * 2) / COLS;
5120
- const cellH = (h - padY * 2) / Math.max(rowsCount, 1);
5121
- const boxW = cellW * 0.8;
5122
- const boxH = Math.min(cellH * 0.6, 32);
5123
- const rx = Math.min(5, boxH * 0.15);
5124
- const arrowSize = 6;
5125
- const shadow = styleShadow2(style);
5126
- const sw = styleStroke2(style);
5127
- return /* @__PURE__ */ jsx(
5128
- "svg",
5129
- {
5130
- className: "w-full h-full pointer-events-none",
5131
- viewBox: `0 0 ${w} ${h}`,
5132
- preserveAspectRatio: "xMidYMid meet",
5133
- "data-testid": "smartart-bending",
5134
- children: nodes.map((node, i) => {
5135
- const row = Math.floor(i / COLS);
5136
- const colInRow = i % COLS;
5137
- const col = row % 2 === 0 ? colInRow : COLS - 1 - colInRow;
5138
- const nodeCx = padX + col * cellW + cellW / 2;
5139
- const nodeCy = padY + row * cellH + cellH / 2;
5140
- const fontSize = fitFontSize(node.text, boxW * 0.85, boxH, 10);
5141
- let arrow = null;
5142
- if (i < nodes.length - 1) {
5143
- const nextRow = Math.floor((i + 1) / COLS);
5144
- const nextColInRow = (i + 1) % COLS;
5145
- const nextCol = nextRow % 2 === 0 ? nextColInRow : COLS - 1 - nextColInRow;
5146
- const nextCx = padX + nextCol * cellW + cellW / 2;
5147
- const nextCy = padY + nextRow * cellH + cellH / 2;
5148
- if (nextRow === row) {
5149
- const dir = nextCx > nodeCx ? 1 : -1;
5150
- const startX = nodeCx + dir * (boxW / 2 + 2);
5151
- const endX = nextCx - dir * (boxW / 2 + 2);
5152
- arrow = /* @__PURE__ */ jsxs("g", { children: [
5153
- /* @__PURE__ */ jsx(
5154
- "line",
5155
- {
5156
- x1: startX,
5157
- y1: nodeCy,
5158
- x2: endX,
5159
- y2: nodeCy,
5160
- stroke: "#94a3b8",
5161
- strokeWidth: 1.5
5162
- }
5163
- ),
5164
- /* @__PURE__ */ jsx(
5165
- "polygon",
5166
- {
5167
- points: `${endX},${nodeCy - arrowSize / 2} ${endX + dir * arrowSize},${nodeCy} ${endX},${nodeCy + arrowSize / 2}`,
5168
- fill: "#94a3b8"
5169
- }
5170
- )
5171
- ] }, `${element.id}-snake-arrow-${i}`);
5172
- } else {
5173
- const startY = nodeCy + boxH / 2 + 2;
5174
- const endY = nextCy - boxH / 2 - 2;
5175
- arrow = /* @__PURE__ */ jsxs("g", { children: [
5176
- /* @__PURE__ */ jsx(
5177
- "line",
5178
- {
5179
- x1: nodeCx,
5180
- y1: startY,
5181
- x2: nextCx,
5182
- y2: endY,
5183
- stroke: "#94a3b8",
5184
- strokeWidth: 1.5
5185
- }
5186
- ),
5187
- /* @__PURE__ */ jsx(
5188
- "polygon",
5189
- {
5190
- points: `${nextCx - arrowSize / 2},${endY} ${nextCx},${endY + arrowSize} ${nextCx + arrowSize / 2},${endY}`,
5191
- fill: "#94a3b8"
5192
- }
5193
- )
5194
- ] }, `${element.id}-snake-arrow-${i}`);
5195
- }
5196
- }
5197
- return /* @__PURE__ */ jsxs(
5198
- "g",
5199
- {
5200
- ...smartArtNodeGroupProps(node.id, shadow),
5201
- children: [
5202
- arrow,
5203
- /* @__PURE__ */ jsx(
5204
- "rect",
5205
- {
5206
- x: nodeCx - boxW / 2,
5207
- y: nodeCy - boxH / 2,
5208
- width: boxW,
5209
- height: boxH,
5210
- rx,
5211
- fill: colour(i, palette),
5212
- opacity: nodeOpacity2(i, nodes.length, style),
5213
- stroke: sw > 0 ? "rgba(255,255,255,0.3)" : "none",
5214
- strokeWidth: sw
5215
- }
5216
- ),
5217
- /* @__PURE__ */ jsx(
5218
- SmartArtNodeText,
5219
- {
5220
- x: nodeCx,
5221
- y: nodeCy,
5222
- text: truncate2(node.text, 20),
5223
- fill: "white",
5224
- fontSize,
5225
- className: "pointer-events-none"
5226
- }
5227
- )
5228
- ]
5229
- },
5230
- `${element.id}-snake-${node.id}-${i}`
5231
- );
5232
- })
5233
- }
5234
- );
5235
- }
5236
3473
  function SmartArtRendererImpl({
5237
3474
  element,
5238
3475
  className = "",
@@ -5322,9 +3559,20 @@ function SmartArtRendererImpl({
5322
3559
  }
5323
3560
  );
5324
3561
  } else {
5325
- const namedLayout = smartArtData.layout;
5326
- const layoutType = namedLayout ? layoutToCategory(namedLayout) : (smartArtData.resolvedLayoutType ?? smartArtData.layoutType ?? "list").toLowerCase();
5327
- content = renderLayout(layoutType, element, revealedNodes, palette, style, nodeLabels);
3562
+ const layout = computeSmartArtLayout(
3563
+ revealedNodes,
3564
+ { width: element.width, height: element.height },
3565
+ palette,
3566
+ style,
3567
+ element.id,
3568
+ smartArtData.resolvedLayoutType,
3569
+ smartArtData.layout,
3570
+ void 0,
3571
+ smartArtData.layoutDefinition,
3572
+ smartArtData.presLayoutVars
3573
+ );
3574
+ const nodeIds = flattenNodes(revealedNodes).map((n) => n.id);
3575
+ content = /* @__PURE__ */ jsx(SmartArtLayoutSvg, { layout, nodeIds, nodeLabels });
5328
3576
  }
5329
3577
  const body = editable ? /* @__PURE__ */ jsx(
5330
3578
  SmartArtEditableLayer,
@@ -5339,108 +3587,6 @@ function SmartArtRendererImpl({
5339
3587
  ) : content;
5340
3588
  return wrapChrome(chrome, body, className, { role: a11y.role, label: a11y.label });
5341
3589
  }
5342
- function renderLayout(layoutType, element, nodes, palette, style, nodeLabels) {
5343
- if (layoutType.includes("hierarchy") || layoutType.includes("org")) {
5344
- return /* @__PURE__ */ jsx(HierarchyRenderer, { element, nodes, palette, style });
5345
- }
5346
- if (layoutType.includes("process") || layoutType.includes("chevron") || layoutType.includes("arrow")) {
5347
- return /* @__PURE__ */ jsx(
5348
- ProcessRenderer,
5349
- {
5350
- element,
5351
- nodes,
5352
- palette,
5353
- style,
5354
- nodeLabels
5355
- }
5356
- );
5357
- }
5358
- if (layoutType.includes("cycle") || layoutType.includes("radial")) {
5359
- return /* @__PURE__ */ jsx(
5360
- CycleRenderer,
5361
- {
5362
- element,
5363
- nodes,
5364
- palette,
5365
- style,
5366
- nodeLabels
5367
- }
5368
- );
5369
- }
5370
- if (layoutType.includes("matrix")) {
5371
- return /* @__PURE__ */ jsx(
5372
- MatrixRenderer,
5373
- {
5374
- element,
5375
- nodes,
5376
- palette,
5377
- style,
5378
- nodeLabels
5379
- }
5380
- );
5381
- }
5382
- if (layoutType.includes("pyramid")) {
5383
- return /* @__PURE__ */ jsx(PyramidRenderer, { element, nodes, palette, style });
5384
- }
5385
- if (layoutType.includes("venn")) {
5386
- return /* @__PURE__ */ jsx(VennRenderer, { element, nodes, palette, style });
5387
- }
5388
- if (layoutType.includes("funnel")) {
5389
- return /* @__PURE__ */ jsx(FunnelRenderer, { element, nodes, palette, style });
5390
- }
5391
- if (layoutType.includes("target") || layoutType.includes("bullseye")) {
5392
- return /* @__PURE__ */ jsx(TargetRenderer, { element, nodes, palette, style });
5393
- }
5394
- if (layoutType.includes("gear")) {
5395
- return /* @__PURE__ */ jsx(GearRenderer, { element, nodes, palette, style });
5396
- }
5397
- if (layoutType.includes("timeline") || layoutType.includes("linear")) {
5398
- return /* @__PURE__ */ jsx(TimelineRenderer, { element, nodes, palette, style });
5399
- }
5400
- if (layoutType.includes("bending") || layoutType.includes("snake")) {
5401
- return /* @__PURE__ */ jsx(BendingProcessRenderer, { element, nodes, palette, style });
5402
- }
5403
- if (layoutType.includes("stepdown")) {
5404
- return /* @__PURE__ */ jsx(Fragment, { children: renderStepDownProcess(element, nodes, palette, style) });
5405
- }
5406
- if (layoutType.includes("alternatingflow") || layoutType.includes("alternating")) {
5407
- return /* @__PURE__ */ jsx(Fragment, { children: renderAlternatingFlow(element, nodes, palette, style) });
5408
- }
5409
- if (layoutType.includes("descending")) {
5410
- return /* @__PURE__ */ jsx(Fragment, { children: renderDescendingProcess(element, nodes, palette, style) });
5411
- }
5412
- if (layoutType.includes("pictureaccent")) {
5413
- return /* @__PURE__ */ jsx(Fragment, { children: renderPictureAccentList(element, nodes, palette, style) });
5414
- }
5415
- if (layoutType.includes("verticalblock")) {
5416
- return /* @__PURE__ */ jsx(Fragment, { children: renderVerticalBlockList(element, nodes, palette, style) });
5417
- }
5418
- if (layoutType.includes("grouped")) {
5419
- return /* @__PURE__ */ jsx(Fragment, { children: renderGroupedList(element, nodes, palette, style) });
5420
- }
5421
- if (layoutType.includes("pyramidlist")) {
5422
- return /* @__PURE__ */ jsx(Fragment, { children: renderPyramidList(element, nodes, palette, style) });
5423
- }
5424
- if (layoutType.includes("horizontalpicture")) {
5425
- return /* @__PURE__ */ jsx(Fragment, { children: renderHorizontalPictureList(element, nodes, palette, style) });
5426
- }
5427
- if (layoutType.includes("accentprocess")) {
5428
- return /* @__PURE__ */ jsx(Fragment, { children: renderAccentProcess(element, nodes, palette, style) });
5429
- }
5430
- if (layoutType.includes("verticalchevron")) {
5431
- return /* @__PURE__ */ jsx(Fragment, { children: renderVerticalChevronList(element, nodes, palette, style) });
5432
- }
5433
- return /* @__PURE__ */ jsx(
5434
- ListRenderer,
5435
- {
5436
- element,
5437
- nodes,
5438
- palette,
5439
- style,
5440
- nodeLabels
5441
- }
5442
- );
5443
- }
5444
3590
  function arePropsEqual(prev, next) {
5445
3591
  if (prev.className !== next.className) {
5446
3592
  return false;
@@ -5821,8 +3967,7 @@ function renderTextElementBody(options) {
5821
3967
  hasTextProperties(el) ? el.textStyle?.textWarpPreset : void 0
5822
3968
  );
5823
3969
  const scene3dStyle = hasTextProperties(el) ? buildTextBody3DSceneStyle(el.textStyle) : void 0;
5824
- const bodyRotation = hasTextProperties(el) ? el.textStyle?.textBodyRotation : void 0;
5825
- const rotationTransform = typeof bodyRotation === "number" && Number.isFinite(bodyRotation) && bodyRotation !== 0 ? `rotate(${bodyRotation}deg)` : void 0;
3970
+ const rotationTransform = getTextBodyRotationTransform(el);
5826
3971
  const composedTransform = [rotationTransform, getTextCompensationTransform(el), scene3dStyle?.transform].filter(Boolean).join(" ") || void 0;
5827
3972
  const transformStyle = {
5828
3973
  transform: composedTransform,
@@ -6257,7 +4402,7 @@ function renderBody(options) {
6257
4402
  if (renderGroupChild) {
6258
4403
  return /* @__PURE__ */ jsx("div", { className: "relative w-full h-full pointer-events-none", children: el.children.map(renderGroupChild) });
6259
4404
  }
6260
- return renderGroup(el.children, el.groupFill);
4405
+ return renderGroup(el.children, getGroupChildParentFill(el));
6261
4406
  }
6262
4407
  if (shouldRenderFallbackLabel(el, isTxtEl)) {
6263
4408
  return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: getElementLabel(el) });
@@ -6363,7 +4508,7 @@ function StaticElementRendererImpl({
6363
4508
  zIndex: index,
6364
4509
  fieldContext,
6365
4510
  tableStyleContext,
6366
- parentGroupFill: getGroupChildParentFill(element),
4511
+ parentGroupFill: getGroupChildParentFill(element, parentGroupFill),
6367
4512
  onActionClick,
6368
4513
  actionRequiresModifier,
6369
4514
  exposeElementId
@@ -7328,6 +5473,165 @@ function ArrangeSection(p) {
7328
5473
  /* @__PURE__ */ jsx("span", { className: "text-[9px] text-muted-foreground leading-none", children: t("pptx.arrange.groupLabel") })
7329
5474
  ] });
7330
5475
  }
5476
+ function TransitionsSection(p) {
5477
+ const { t } = useTranslation();
5478
+ const canEdit = p.canEdit !== false;
5479
+ const draft = React14.useMemo(() => readRibbonTransitionDraft(p.activeSlide), [p.activeSlide]);
5480
+ const [durationBuffer, setDurationBuffer] = React14.useState(null);
5481
+ const [advanceBuffer, setAdvanceBuffer] = React14.useState(null);
5482
+ const commit = React14.useCallback(
5483
+ (changes) => {
5484
+ p.onTransitionChange(ribbonTransitionUpdates({ ...draft, ...changes }));
5485
+ },
5486
+ [draft, p]
5487
+ );
5488
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5489
+ /* @__PURE__ */ jsxs(
5490
+ "button",
5491
+ {
5492
+ type: "button",
5493
+ className: pill,
5494
+ title: t("pptx.ribbon.previewTransition"),
5495
+ onClick: () => playSlideTransitionPreview(p.activeSlide?.transition, document),
5496
+ children: [
5497
+ /* @__PURE__ */ jsx(LuPlay, { className: ics }),
5498
+ t("pptx.ribbon.preview")
5499
+ ]
5500
+ }
5501
+ ),
5502
+ sep,
5503
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-0.5 overflow-x-auto max-w-[420px]", children: RIBBON_TRANSITION_PRESETS.map((preset) => /* @__PURE__ */ jsx(
5504
+ "button",
5505
+ {
5506
+ type: "button",
5507
+ disabled: !canEdit,
5508
+ onClick: () => commit({ type: preset.type }),
5509
+ className: cn(
5510
+ "flex-shrink-0 px-2 py-1 max-md:min-h-[44px] rounded border text-[11px] leading-tight transition-colors",
5511
+ draft.type === preset.type ? "border-primary bg-primary/10 text-primary font-medium" : "border-border bg-muted hover:bg-accent text-foreground"
5512
+ ),
5513
+ title: t("pptx.ribbon.transitionTitle", { name: t(preset.labelKey) }),
5514
+ children: t(preset.labelKey)
5515
+ },
5516
+ preset.type
5517
+ )) }),
5518
+ sep,
5519
+ /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [
5520
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.duration") }),
5521
+ /* @__PURE__ */ jsx(
5522
+ "input",
5523
+ {
5524
+ type: "number",
5525
+ min: 0,
5526
+ max: 20,
5527
+ step: 0.25,
5528
+ disabled: !canEdit,
5529
+ value: durationBuffer ?? String(draft.durationSec),
5530
+ onChange: (e) => {
5531
+ setDurationBuffer(e.target.value);
5532
+ const seconds = Number(e.target.value);
5533
+ if (Number.isFinite(seconds) && e.target.value !== "") {
5534
+ commit({ durationSec: seconds });
5535
+ }
5536
+ },
5537
+ onBlur: () => setDurationBuffer(null),
5538
+ className: "w-16 px-1.5 py-1 rounded border border-border bg-muted text-xs text-foreground text-center",
5539
+ title: t("pptx.ribbon.transitionDurationTitle")
5540
+ }
5541
+ )
5542
+ ] }),
5543
+ sep,
5544
+ /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [
5545
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.sound") }),
5546
+ /* @__PURE__ */ jsx(
5547
+ "select",
5548
+ {
5549
+ className: "w-24 px-1.5 py-1 rounded border border-border bg-muted text-xs text-foreground disabled:opacity-50",
5550
+ defaultValue: "none",
5551
+ disabled: true,
5552
+ children: /* @__PURE__ */ jsx("option", { value: "none", children: t("pptx.ribbon.soundNone") })
5553
+ }
5554
+ )
5555
+ ] }),
5556
+ sep,
5557
+ /* @__PURE__ */ jsxs(
5558
+ "button",
5559
+ {
5560
+ type: "button",
5561
+ disabled: !canEdit,
5562
+ className: pill,
5563
+ title: t("pptx.ribbon.applyTransitionToAll"),
5564
+ onClick: p.onApplyTransitionToAll,
5565
+ children: [
5566
+ /* @__PURE__ */ jsx(LuCopy, { className: ics }),
5567
+ t("pptx.headerFooter.applyToAll")
5568
+ ]
5569
+ }
5570
+ ),
5571
+ sep,
5572
+ /* @__PURE__ */ jsxs("div", { className: "inline-flex flex-col gap-1 text-xs text-muted-foreground", children: [
5573
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-foreground", children: t("pptx.ribbon.advanceSlide") }),
5574
+ /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 cursor-pointer", children: [
5575
+ /* @__PURE__ */ jsx(
5576
+ "input",
5577
+ {
5578
+ type: "checkbox",
5579
+ disabled: !canEdit,
5580
+ checked: draft.advanceOnClick,
5581
+ onChange: (e) => commit({ advanceOnClick: e.target.checked }),
5582
+ className: "accent-primary h-3 w-3"
5583
+ }
5584
+ ),
5585
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.onMouseClick") })
5586
+ ] }),
5587
+ /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 cursor-pointer", children: [
5588
+ /* @__PURE__ */ jsx(
5589
+ "input",
5590
+ {
5591
+ type: "checkbox",
5592
+ disabled: !canEdit,
5593
+ checked: draft.advanceAfter,
5594
+ onChange: (e) => commit({ advanceAfter: e.target.checked }),
5595
+ className: "accent-primary h-3 w-3"
5596
+ }
5597
+ ),
5598
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.afterDuration") }),
5599
+ /* @__PURE__ */ jsx(
5600
+ "input",
5601
+ {
5602
+ type: "text",
5603
+ value: advanceBuffer ?? draft.advanceAfterText,
5604
+ onChange: (e) => {
5605
+ setAdvanceBuffer(e.target.value);
5606
+ commit({ advanceAfter: true, advanceAfterText: e.target.value });
5607
+ },
5608
+ onBlur: () => setAdvanceBuffer(null),
5609
+ disabled: !canEdit || !draft.advanceAfter,
5610
+ className: "w-16 px-1 py-0.5 rounded border border-border bg-muted text-xs text-foreground text-center disabled:opacity-50",
5611
+ title: t("pptx.ribbon.advanceAfterSeconds")
5612
+ }
5613
+ )
5614
+ ] })
5615
+ ] }),
5616
+ sep,
5617
+ /* @__PURE__ */ jsxs(
5618
+ "button",
5619
+ {
5620
+ type: "button",
5621
+ onClick: p.onToggleInspector,
5622
+ className: cn(
5623
+ pill,
5624
+ p.isInspectorPaneOpen ? "bg-primary hover:bg-primary/80 text-white" : ""
5625
+ ),
5626
+ title: t("pptx.ribbon.openInspectorTransitions"),
5627
+ children: [
5628
+ /* @__PURE__ */ jsx(LuPanelRight, { className: ic }),
5629
+ t("pptx.ribbon.inspector")
5630
+ ]
5631
+ }
5632
+ )
5633
+ ] });
5634
+ }
7331
5635
  function DesignSection(p) {
7332
5636
  const { t } = useTranslation();
7333
5637
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -7361,10 +5665,10 @@ function DesignSection(p) {
7361
5665
  }
7362
5666
  ),
7363
5667
  sep,
7364
- p.onOpenDocumentProperties && /* @__PURE__ */ jsxs(
5668
+ (p.onOpenSlideSize ?? p.onOpenDocumentProperties) && /* @__PURE__ */ jsxs(
7365
5669
  "button",
7366
5670
  {
7367
- onClick: p.onOpenDocumentProperties,
5671
+ onClick: p.onOpenSlideSize ?? p.onOpenDocumentProperties,
7368
5672
  className: pill,
7369
5673
  title: t("pptx.ribbon.slideSizeTitle"),
7370
5674
  children: [
@@ -7390,133 +5694,6 @@ function DesignSection(p) {
7390
5694
  )
7391
5695
  ] });
7392
5696
  }
7393
- var TRANSITION_PRESETS = [
7394
- { value: "none", labelKey: "pptx.ribbon.transition.none" },
7395
- { value: "fade", labelKey: "pptx.ribbon.transition.fade" },
7396
- { value: "push", labelKey: "pptx.ribbon.transition.push" },
7397
- { value: "wipe", labelKey: "pptx.ribbon.transition.wipe" },
7398
- { value: "split", labelKey: "pptx.ribbon.transition.split" },
7399
- { value: "reveal", labelKey: "pptx.ribbon.transition.reveal" },
7400
- { value: "cut", labelKey: "pptx.ribbon.transition.cut" },
7401
- { value: "cover", labelKey: "pptx.ribbon.transition.cover" },
7402
- { value: "uncover", labelKey: "pptx.ribbon.transition.uncover" }
7403
- ];
7404
- function TransitionsSection(p) {
7405
- const { t } = useTranslation();
7406
- const [selected, setSelected] = React14.useState("none");
7407
- const [duration, setDuration] = React14.useState("00.50");
7408
- const [advanceOnClick, setAdvanceOnClick] = React14.useState(true);
7409
- const [advanceAfter, setAdvanceAfter] = React14.useState(false);
7410
- const [advanceAfterSeconds, setAdvanceAfterSeconds] = React14.useState("00:00.00");
7411
- return /* @__PURE__ */ jsxs(Fragment, { children: [
7412
- /* @__PURE__ */ jsxs("button", { type: "button", className: pill, title: t("pptx.ribbon.previewTransition"), children: [
7413
- /* @__PURE__ */ jsx(LuPlay, { className: ics }),
7414
- t("pptx.ribbon.preview")
7415
- ] }),
7416
- sep,
7417
- /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-0.5 overflow-x-auto max-w-[420px]", children: TRANSITION_PRESETS.map((preset) => /* @__PURE__ */ jsx(
7418
- "button",
7419
- {
7420
- type: "button",
7421
- onClick: () => setSelected(preset.value),
7422
- className: cn(
7423
- "flex-shrink-0 px-2 py-1 max-md:min-h-[44px] rounded border text-[11px] leading-tight transition-colors",
7424
- selected === preset.value ? "border-primary bg-primary/10 text-primary font-medium" : "border-border bg-muted hover:bg-accent text-foreground"
7425
- ),
7426
- title: t("pptx.ribbon.transitionTitle", { name: t(preset.labelKey) }),
7427
- children: t(preset.labelKey)
7428
- },
7429
- preset.value
7430
- )) }),
7431
- sep,
7432
- /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [
7433
- /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.duration") }),
7434
- /* @__PURE__ */ jsx(
7435
- "input",
7436
- {
7437
- type: "text",
7438
- value: duration,
7439
- onChange: (e) => setDuration(e.target.value),
7440
- className: "w-14 px-1.5 py-1 rounded border border-border bg-muted text-xs text-foreground text-center",
7441
- title: t("pptx.ribbon.transitionDurationTitle")
7442
- }
7443
- )
7444
- ] }),
7445
- sep,
7446
- /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [
7447
- /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.sound") }),
7448
- /* @__PURE__ */ jsx(
7449
- "select",
7450
- {
7451
- className: "w-24 px-1.5 py-1 rounded border border-border bg-muted text-xs text-foreground",
7452
- defaultValue: "none",
7453
- children: /* @__PURE__ */ jsx("option", { value: "none", children: t("pptx.ribbon.soundNone") })
7454
- }
7455
- )
7456
- ] }),
7457
- sep,
7458
- /* @__PURE__ */ jsxs("button", { type: "button", className: pill, title: t("pptx.ribbon.applyTransitionToAll"), children: [
7459
- /* @__PURE__ */ jsx(LuCopy, { className: ics }),
7460
- t("pptx.headerFooter.applyToAll")
7461
- ] }),
7462
- sep,
7463
- /* @__PURE__ */ jsxs("div", { className: "inline-flex flex-col gap-1 text-xs text-muted-foreground", children: [
7464
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-foreground", children: t("pptx.ribbon.advanceSlide") }),
7465
- /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 cursor-pointer", children: [
7466
- /* @__PURE__ */ jsx(
7467
- "input",
7468
- {
7469
- type: "checkbox",
7470
- checked: advanceOnClick,
7471
- onChange: (e) => setAdvanceOnClick(e.target.checked),
7472
- className: "accent-primary h-3 w-3"
7473
- }
7474
- ),
7475
- /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.onMouseClick") })
7476
- ] }),
7477
- /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-1.5 cursor-pointer", children: [
7478
- /* @__PURE__ */ jsx(
7479
- "input",
7480
- {
7481
- type: "checkbox",
7482
- checked: advanceAfter,
7483
- onChange: (e) => setAdvanceAfter(e.target.checked),
7484
- className: "accent-primary h-3 w-3"
7485
- }
7486
- ),
7487
- /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("pptx.ribbon.afterDuration") }),
7488
- /* @__PURE__ */ jsx(
7489
- "input",
7490
- {
7491
- type: "text",
7492
- value: advanceAfterSeconds,
7493
- onChange: (e) => setAdvanceAfterSeconds(e.target.value),
7494
- disabled: !advanceAfter,
7495
- className: "w-16 px-1 py-0.5 rounded border border-border bg-muted text-xs text-foreground text-center disabled:opacity-50",
7496
- title: t("pptx.ribbon.advanceAfterSeconds")
7497
- }
7498
- )
7499
- ] })
7500
- ] }),
7501
- sep,
7502
- /* @__PURE__ */ jsxs(
7503
- "button",
7504
- {
7505
- type: "button",
7506
- onClick: p.onToggleInspector,
7507
- className: cn(
7508
- pill,
7509
- p.isInspectorPaneOpen ? "bg-primary hover:bg-primary/80 text-white" : ""
7510
- ),
7511
- title: t("pptx.ribbon.openInspectorTransitions"),
7512
- children: [
7513
- /* @__PURE__ */ jsx(LuPanelRight, { className: ic }),
7514
- t("pptx.ribbon.inspector")
7515
- ]
7516
- }
7517
- )
7518
- ] });
7519
- }
7520
5697
  function DrawSection(p) {
7521
5698
  const { t } = useTranslation();
7522
5699
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -7609,7 +5786,7 @@ function AccountPage() {
7609
5786
  return void 0;
7610
5787
  });
7611
5788
  };
7612
- const version = "2.16.9" ;
5789
+ const version = "2.17.0" ;
7613
5790
  return /* @__PURE__ */ jsxs("div", { className: "mt-8 max-w-[700px] space-y-6", children: [
7614
5791
  /* @__PURE__ */ jsxs("section", { className: cardClass, children: [
7615
5792
  /* @__PURE__ */ jsxs("h2", { className: "flex items-center gap-2 text-sm font-semibold", children: [
@@ -8042,6 +6219,160 @@ function RibbonMenu({
8042
6219
  }
8043
6220
  );
8044
6221
  }
6222
+ function FontFamilyMenu(p) {
6223
+ const { t } = useTranslation();
6224
+ const groups = buildFontCatalog({
6225
+ themeFonts: p.themeFonts,
6226
+ embeddedFonts: p.embeddedFonts,
6227
+ customFonts: p.customFonts
6228
+ });
6229
+ return /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: p.anchorRef, className: "flex flex-col w-64 pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-80 overflow-y-auto", children: groups.map((group, groupIndex) => /* @__PURE__ */ jsxs(React14.Fragment, { children: [
6230
+ /* @__PURE__ */ jsx(
6231
+ "div",
6232
+ {
6233
+ className: `px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground${groupIndex > 0 ? " border-t border-border/60 mt-1" : ""}`,
6234
+ children: t(group.labelKey)
6235
+ }
6236
+ ),
6237
+ group.entries.map((entry) => /* @__PURE__ */ jsx(FontRow, { entry, onSelect: p.onSelect }, `${group.id}-${entry.family}`))
6238
+ ] }, group.id)) }) });
6239
+ }
6240
+ function FontRow({
6241
+ entry,
6242
+ onSelect
6243
+ }) {
6244
+ const { t } = useTranslation();
6245
+ return /* @__PURE__ */ jsxs(
6246
+ "button",
6247
+ {
6248
+ type: "button",
6249
+ className: "flex w-full items-center justify-between gap-3 px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
6250
+ style: { fontFamily: entry.family },
6251
+ onClick: () => onSelect(entry.family),
6252
+ children: [
6253
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: entry.family }),
6254
+ entry.themeRole && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: t(`pptx.font.role.${entry.themeRole}`) })
6255
+ ]
6256
+ }
6257
+ );
6258
+ }
6259
+ function useLayoutPreviews(loadPreviews, enabled) {
6260
+ const [previews, setPreviews] = useState(/* @__PURE__ */ new Map());
6261
+ useEffect(() => {
6262
+ if (!enabled || !loadPreviews) {
6263
+ return;
6264
+ }
6265
+ let cancelled = false;
6266
+ void loadPreviews().then((loaded) => {
6267
+ if (!cancelled) {
6268
+ setPreviews(new Map(loaded.map((preview) => [preview.path, preview])));
6269
+ }
6270
+ return void 0;
6271
+ }).catch(() => {
6272
+ });
6273
+ return () => {
6274
+ cancelled = true;
6275
+ };
6276
+ }, [enabled, loadPreviews]);
6277
+ return previews;
6278
+ }
6279
+ var THUMB_WIDTH = 128;
6280
+ var THUMB_HEIGHT = 72;
6281
+ var MAX_PREVIEW_ELEMENTS = 100;
6282
+ function LayoutGalleryMenu(p) {
6283
+ const { t } = useTranslation();
6284
+ return /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: p.anchorRef, className: "flex flex-col w-[620px] pt-1", children: /* @__PURE__ */ jsxs(
6285
+ "div",
6286
+ {
6287
+ "data-testid": "layout-gallery-menu",
6288
+ className: "grid grid-cols-4 gap-2 rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-3 max-h-[520px] overflow-y-auto",
6289
+ children: [
6290
+ p.layoutOptions.length === 0 && /* @__PURE__ */ jsx("p", { className: "col-span-4 px-2 py-3 text-xs text-muted-foreground", children: t("pptx.layoutGallery.empty") }),
6291
+ p.layoutOptions.map((layout) => {
6292
+ const isCurrent = isCurrentLayout(layout, p.currentLayoutPath);
6293
+ return /* @__PURE__ */ jsxs(
6294
+ "button",
6295
+ {
6296
+ type: "button",
6297
+ "aria-current": isCurrent ? "true" : void 0,
6298
+ title: isCurrent ? `${layout.name} (${t("pptx.layoutGallery.current")})` : layout.name,
6299
+ className: cn(
6300
+ "relative flex min-w-0 flex-col items-center gap-1 rounded border-2 p-1 text-xs text-foreground transition-colors hover:bg-muted",
6301
+ isCurrent ? "border-primary bg-primary/10" : "border-transparent"
6302
+ ),
6303
+ onClick: () => p.onSelect(layout),
6304
+ children: [
6305
+ /* @__PURE__ */ jsx(LayoutThumbnail, { preview: p.previews.get(layout.path), name: layout.name }),
6306
+ /* @__PURE__ */ jsx("span", { className: "w-full truncate text-center", children: layout.name })
6307
+ ]
6308
+ },
6309
+ layout.path
6310
+ );
6311
+ })
6312
+ ]
6313
+ }
6314
+ ) });
6315
+ }
6316
+ function LayoutThumbnail({ preview, name }) {
6317
+ const geometry = buildLayoutPreviewGeometry(preview, THUMB_WIDTH, THUMB_HEIGHT);
6318
+ const slide = {
6319
+ id: `layout-preview-${preview?.path ?? name}`,
6320
+ rId: "",
6321
+ slideNumber: 0,
6322
+ elements: preview?.elements ?? [],
6323
+ backgroundColor: geometry.backgroundColor
6324
+ };
6325
+ return /* @__PURE__ */ jsx(
6326
+ "div",
6327
+ {
6328
+ className: "relative shrink-0 overflow-hidden rounded-sm border border-border/70 shadow-sm",
6329
+ style: {
6330
+ width: geometry.boxWidth,
6331
+ height: geometry.boxHeight,
6332
+ backgroundColor: geometry.backgroundColor
6333
+ },
6334
+ children: /* @__PURE__ */ jsxs(
6335
+ "div",
6336
+ {
6337
+ className: "absolute left-0 top-0 origin-top-left overflow-hidden",
6338
+ style: {
6339
+ width: geometry.surfaceWidth,
6340
+ height: geometry.surfaceHeight,
6341
+ transform: `scale(${geometry.scale})`,
6342
+ backgroundColor: geometry.backgroundColor
6343
+ },
6344
+ children: [
6345
+ (preview?.elements ?? []).slice(0, MAX_PREVIEW_ELEMENTS).map((element, index) => /* @__PURE__ */ jsx(
6346
+ StaticElementRenderer,
6347
+ {
6348
+ element,
6349
+ activeSlide: slide,
6350
+ allSlides: [slide],
6351
+ zIndex: index
6352
+ },
6353
+ element.id
6354
+ )),
6355
+ geometry.frames.map((frame) => /* @__PURE__ */ jsx(
6356
+ "div",
6357
+ {
6358
+ className: "absolute border-dashed border-muted-foreground/70 bg-background/20",
6359
+ style: {
6360
+ left: frame.left,
6361
+ top: frame.top,
6362
+ width: frame.width,
6363
+ height: frame.height,
6364
+ borderWidth: geometry.frameBorderWidth,
6365
+ borderStyle: "dashed"
6366
+ }
6367
+ },
6368
+ frame.key
6369
+ ))
6370
+ ]
6371
+ }
6372
+ )
6373
+ }
6374
+ );
6375
+ }
8045
6376
  function SlidesGroup(p) {
8046
6377
  const { t } = useTranslation();
8047
6378
  const [newSlideMenuOpen, setNewSlideMenuOpen] = useState(false);
@@ -8049,6 +6380,7 @@ function SlidesGroup(p) {
8049
6380
  const [templateGalleryOpen, setTemplateGalleryOpen] = useState(false);
8050
6381
  const newSlideMenuRef = useRef(null);
8051
6382
  const layoutMenuRef = useRef(null);
6383
+ const previews = useLayoutPreviews(p.loadLayoutPreviews, newSlideMenuOpen || layoutMenuOpen);
8052
6384
  const handleNewSlide = useCallback(() => {
8053
6385
  if (p.layoutOptions.length > 0) {
8054
6386
  const first = p.layoutOptions[0];
@@ -8112,19 +6444,18 @@ function SlidesGroup(p) {
8112
6444
  children: /* @__PURE__ */ jsx(LuChevronDown, { className: "w-3 h-3" })
8113
6445
  }
8114
6446
  ),
8115
- newSlideMenuOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: newSlideMenuRef, className: "flex flex-col w-48 pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-60 overflow-y-auto", children: p.layoutOptions.map((lo) => /* @__PURE__ */ jsx(
8116
- "button",
8117
- {
8118
- type: "button",
8119
- className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
8120
- onClick: () => {
8121
- p.onInsertSlideFromLayout(lo.path, lo.name);
6447
+ newSlideMenuOpen && /* @__PURE__ */ jsx(
6448
+ LayoutGalleryMenu,
6449
+ {
6450
+ anchorRef: newSlideMenuRef,
6451
+ layoutOptions: p.layoutOptions,
6452
+ previews,
6453
+ onSelect: (layout) => {
6454
+ p.onInsertSlideFromLayout(layout.path, layout.name);
8122
6455
  setNewSlideMenuOpen(false);
8123
- },
8124
- children: lo.name
8125
- },
8126
- lo.path
8127
- )) }) })
6456
+ }
6457
+ }
6458
+ )
8128
6459
  ] }),
8129
6460
  p.onInsertSlideFromTemplate && /* @__PURE__ */ jsxs(
8130
6461
  "button",
@@ -8155,19 +6486,19 @@ function SlidesGroup(p) {
8155
6486
  ]
8156
6487
  }
8157
6488
  ),
8158
- layoutMenuOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: layoutMenuRef, className: "flex flex-col w-48 pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-60 overflow-y-auto", children: p.layoutOptions.map((lo) => /* @__PURE__ */ jsx(
8159
- "button",
8160
- {
8161
- type: "button",
8162
- className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
8163
- onClick: () => {
8164
- p.onApplyLayout?.(lo.path);
6489
+ layoutMenuOpen && /* @__PURE__ */ jsx(
6490
+ LayoutGalleryMenu,
6491
+ {
6492
+ anchorRef: layoutMenuRef,
6493
+ layoutOptions: p.layoutOptions,
6494
+ previews,
6495
+ currentLayoutPath: p.currentLayoutPath,
6496
+ onSelect: (layout) => {
6497
+ p.onApplyLayout?.(layout.path);
8165
6498
  setLayoutMenuOpen(false);
8166
- },
8167
- children: lo.name
8168
- },
8169
- lo.path
8170
- )) }) })
6499
+ }
6500
+ }
6501
+ )
8171
6502
  ] }),
8172
6503
  /* @__PURE__ */ jsxs(
8173
6504
  "button",
@@ -8212,12 +6543,13 @@ function SlidesGroup(p) {
8212
6543
  )
8213
6544
  ] });
8214
6545
  }
8215
- function extractFontInfo(element) {
8216
- const defaults = { fontFamily: "Segoe UI", fontSize: "24" };
8217
- if (!element) {
8218
- return defaults;
8219
- }
8220
- if (!hasTextProperties(element)) {
6546
+ function extractFontInfo(element, themeFonts) {
6547
+ const placeholderType = element?.placeholderType;
6548
+ const defaults = {
6549
+ fontFamily: resolveDefaultFontFamily(placeholderType, themeFonts),
6550
+ fontSize: "24"
6551
+ };
6552
+ if (!element || !hasTextProperties(element)) {
8221
6553
  return defaults;
8222
6554
  }
8223
6555
  const segStyle = element.textSegments?.[0]?.style;
@@ -8229,21 +6561,6 @@ function extractFontInfo(element) {
8229
6561
  fontSize: fontSize !== void 0 && fontSize !== null ? String(fontSize) : defaults.fontSize
8230
6562
  };
8231
6563
  }
8232
- var COMMON_FONTS2 = [
8233
- "Arial",
8234
- "Calibri",
8235
- "Cambria",
8236
- "Comic Sans MS",
8237
- "Courier New",
8238
- "Georgia",
8239
- "Helvetica",
8240
- "Impact",
8241
- "Segoe UI",
8242
- "Tahoma",
8243
- "Times New Roman",
8244
- "Trebuchet MS",
8245
- "Verdana"
8246
- ];
8247
6564
  var COMMON_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 44, 48, 54, 60, 72, 96];
8248
6565
  function HomeSection(p) {
8249
6566
  const { t } = useTranslation();
@@ -8253,7 +6570,7 @@ function HomeSection(p) {
8253
6570
  const [cutFeedback, setCutFeedback] = useState(false);
8254
6571
  const fontMenuRef = useRef(null);
8255
6572
  const sizeMenuRef = useRef(null);
8256
- const { fontFamily, fontSize } = extractFontInfo(p.selectedElement);
6573
+ const { fontFamily, fontSize } = extractFontInfo(p.selectedElement, p.themeFonts);
8257
6574
  const hasSelection = Boolean(p.selectedElement);
8258
6575
  useEffect(() => {
8259
6576
  if (!fontMenuOpen) {
@@ -8348,6 +6665,8 @@ function HomeSection(p) {
8348
6665
  {
8349
6666
  canEdit: p.canEdit,
8350
6667
  layoutOptions: p.layoutOptions,
6668
+ currentLayoutPath: p.currentLayoutPath,
6669
+ loadLayoutPreviews: p.loadLayoutPreviews,
8351
6670
  onInsertSlideFromLayout: p.onInsertSlideFromLayout,
8352
6671
  onInsertSlideFromTemplate: p.onInsertSlideFromTemplate,
8353
6672
  templateScheme: p.templateScheme,
@@ -8372,20 +6691,19 @@ function HomeSection(p) {
8372
6691
  ]
8373
6692
  }
8374
6693
  ),
8375
- fontMenuOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: fontMenuRef, className: "flex flex-col w-48 pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl py-1 max-h-60 overflow-y-auto", children: COMMON_FONTS2.map((f) => /* @__PURE__ */ jsx(
8376
- "button",
8377
- {
8378
- type: "button",
8379
- className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-foreground hover:bg-muted transition-colors",
8380
- style: { fontFamily: f },
8381
- onClick: () => {
8382
- p.onUpdateTextStyle?.({ fontFamily: f });
6694
+ fontMenuOpen && /* @__PURE__ */ jsx(
6695
+ FontFamilyMenu,
6696
+ {
6697
+ anchorRef: fontMenuRef,
6698
+ themeFonts: p.themeFonts,
6699
+ embeddedFonts: p.embeddedFontFamilies,
6700
+ customFonts: p.customFontFamilies,
6701
+ onSelect: (family) => {
6702
+ p.onUpdateTextStyle?.({ fontFamily: family });
8383
6703
  setFontMenuOpen(false);
8384
- },
8385
- children: f
8386
- },
8387
- f
8388
- )) }) })
6704
+ }
6705
+ }
6706
+ )
8389
6707
  ] }),
8390
6708
  /* @__PURE__ */ jsxs("div", { className: "relative", ref: sizeMenuRef, children: [
8391
6709
  /* @__PURE__ */ jsxs(
@@ -9267,36 +7585,28 @@ function SlideShowSection(p) {
9267
7585
  )
9268
7586
  ] }),
9269
7587
  /* @__PURE__ */ jsxs(RibbonGroup, { label: t("pptx.slideShow.options", { defaultValue: "Options" }), children: [
9270
- /* @__PURE__ */ jsxs(RibbonCommandStack, { children: [
9271
- /* @__PURE__ */ jsx(
9272
- RibbonToggle,
9273
- {
9274
- label: t("pptx.slideShow.keepUpdated", { defaultValue: "Keep Slides Updated" }),
9275
- checked: false,
9276
- disabled: true
9277
- }
9278
- ),
9279
- /* @__PURE__ */ jsx(
9280
- RibbonToggle,
9281
- {
9282
- label: t("pptx.slideShow.useTimings", { defaultValue: "Use Timings" }),
9283
- checked: true
9284
- }
9285
- ),
9286
- /* @__PURE__ */ jsx(
9287
- RibbonToggle,
9288
- {
9289
- label: t("pptx.slideShow.playNarrations", { defaultValue: "Play Narrations" }),
9290
- checked: true
7588
+ /* @__PURE__ */ jsx(RibbonCommandStack, { children: SLIDE_SHOW_OPTIONS.slice(0, 3).map((option) => /* @__PURE__ */ jsx(
7589
+ RibbonToggle,
7590
+ {
7591
+ label: t(option.labelKey),
7592
+ checked: readSlideShowOption(p.presentationProperties, option.id),
7593
+ disabled: option.unsupported,
7594
+ onChange: option.unsupported ? void 0 : (next) => {
7595
+ const change = slideShowOptionChange(option.id, next);
7596
+ if (change) {
7597
+ p.onPresentationPropertiesChange?.(change);
7598
+ }
9291
7599
  }
9292
- )
9293
- ] }),
7600
+ },
7601
+ option.id
7602
+ )) }),
9294
7603
  /* @__PURE__ */ jsxs(RibbonCommandStack, { children: [
9295
7604
  /* @__PURE__ */ jsx(
9296
7605
  RibbonToggle,
9297
7606
  {
9298
- label: t("pptx.slideShow.mediaControls", { defaultValue: "Show Media Controls" }),
9299
- checked: true
7607
+ label: t(SLIDE_SHOW_OPTIONS[3].labelKey),
7608
+ checked: readSlideShowOption(p.presentationProperties, SLIDE_SHOW_OPTIONS[3].id),
7609
+ disabled: SLIDE_SHOW_OPTIONS[3].unsupported
9300
7610
  }
9301
7611
  ),
9302
7612
  /* @__PURE__ */ jsx(
@@ -10510,6 +8820,7 @@ function MobileSectionBody({
10510
8820
  onToggleThemeEditor: p.onToggleThemeEditor,
10511
8821
  isThemeEditorOpen: p.isThemeEditorOpen,
10512
8822
  onOpenDocumentProperties: p.onOpenDocumentProperties,
8823
+ onOpenSlideSize: p.onOpenSlideSize,
10513
8824
  onToggleInspector: p.onToggleInspector,
10514
8825
  isInspectorPaneOpen: p.isInspectorPaneOpen
10515
8826
  }
@@ -10519,7 +8830,11 @@ function MobileSectionBody({
10519
8830
  TransitionsSection,
10520
8831
  {
10521
8832
  isInspectorPaneOpen: p.isInspectorPaneOpen,
10522
- onToggleInspector: p.onToggleInspector
8833
+ onToggleInspector: p.onToggleInspector,
8834
+ canEdit: p.canEdit,
8835
+ activeSlide: p.activeSlide,
8836
+ onTransitionChange: p.onTransitionChange,
8837
+ onApplyTransitionToAll: p.onApplyTransitionToAll
10523
8838
  }
10524
8839
  ) });
10525
8840
  case "animations":
@@ -10738,20 +9053,6 @@ function MobileToolbar(props) {
10738
9053
  }
10739
9054
  );
10740
9055
  }
10741
- var FILL_COLORS = [
10742
- "#ffffff",
10743
- "#000000",
10744
- "#ff0000",
10745
- "#00ff00",
10746
- "#0000ff",
10747
- "#ffff00",
10748
- "#ff00ff",
10749
- "#00ffff",
10750
- "#ff8800",
10751
- "#8800ff",
10752
- "#008888",
10753
- "#888888"
10754
- ];
10755
9056
  var TOP_SHAPES = SHAPE_PRESETS.slice(0, 12);
10756
9057
  function DrawingGroup(p) {
10757
9058
  const { t } = useTranslation();
@@ -10928,7 +9229,7 @@ function DrawingGroup(p) {
10928
9229
  children: /* @__PURE__ */ jsx(LuPaintBucket, { className: ic })
10929
9230
  }
10930
9231
  ),
10931
- fillOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: fillRef, className: "pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 grid grid-cols-6 gap-1", children: FILL_COLORS.map((c) => /* @__PURE__ */ jsx(
9232
+ fillOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: fillRef, className: "pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 grid grid-cols-6 gap-1", children: RIBBON_SHAPE_SWATCHES.map((c) => /* @__PURE__ */ jsx(
10932
9233
  "button",
10933
9234
  {
10934
9235
  type: "button",
@@ -10938,7 +9239,7 @@ function DrawingGroup(p) {
10938
9239
  style: { backgroundColor: c },
10939
9240
  title: c,
10940
9241
  onClick: () => {
10941
- p.onUpdateElementStyle?.({ fill: c });
9242
+ p.onUpdateElementStyle?.(shapeFillChange(c));
10942
9243
  setFillOpen(false);
10943
9244
  }
10944
9245
  },
@@ -10957,7 +9258,7 @@ function DrawingGroup(p) {
10957
9258
  children: /* @__PURE__ */ jsx(LuPenLine, { className: ic })
10958
9259
  }
10959
9260
  ),
10960
- outlineOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: outlineRef, className: "pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 grid grid-cols-6 gap-1", children: FILL_COLORS.map((c) => /* @__PURE__ */ jsx(
9261
+ outlineOpen && /* @__PURE__ */ jsx(RibbonMenu, { anchorRef: outlineRef, className: "pt-1", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-popover backdrop-blur-lg shadow-2xl p-2 grid grid-cols-6 gap-1", children: RIBBON_SHAPE_SWATCHES.map((c) => /* @__PURE__ */ jsx(
10961
9262
  "button",
10962
9263
  {
10963
9264
  type: "button",
@@ -10967,7 +9268,7 @@ function DrawingGroup(p) {
10967
9268
  style: { backgroundColor: c },
10968
9269
  title: c,
10969
9270
  onClick: () => {
10970
- p.onUpdateElementStyle?.({ outlineColor: c });
9271
+ p.onUpdateElementStyle?.(shapeOutlineChange(c));
10971
9272
  setOutlineOpen(false);
10972
9273
  }
10973
9274
  },
@@ -11671,12 +9972,19 @@ function Toolbar(p) {
11671
9972
  onPaste: p.onPaste,
11672
9973
  onToggleFormatPainter: p.onToggleFormatPainter,
11673
9974
  layoutOptions: p.layoutOptions,
9975
+ currentLayoutPath: p.currentLayoutPath,
9976
+ loadLayoutPreviews: p.loadLayoutPreviews,
9977
+ themeFonts: p.themeFonts,
9978
+ embeddedFontFamilies: p.embeddedFontFamilies,
9979
+ customFontFamilies: p.customFontFamilies,
11674
9980
  onInsertSlideFromLayout: p.onInsertSlideFromLayout,
11675
9981
  onApplyLayout: p.onApplyLayout,
11676
9982
  onInsertSlideFromTemplate: p.onInsertSlideFromTemplate,
11677
9983
  templateScheme: p.templateScheme,
11678
9984
  selectedElement: p.selectedElement,
11679
- onUpdateTextStyle: p.onUpdateTextStyle
9985
+ onUpdateTextStyle: p.onUpdateTextStyle,
9986
+ onResetSlide: p.onResetSlide,
9987
+ onAddSection: p.onAddSection
11680
9988
  }
11681
9989
  ),
11682
9990
  sIns && /* @__PURE__ */ jsx(
@@ -11710,7 +10018,13 @@ function Toolbar(p) {
11710
10018
  onTransformTextCase: p.onTransformTextCase
11711
10019
  }
11712
10020
  ),
11713
- sHome && /* @__PURE__ */ jsx(EditingSection, { onToggleFindReplace: p.onToggleFindReplace }),
10021
+ sHome && /* @__PURE__ */ jsx(
10022
+ EditingSection,
10023
+ {
10024
+ onToggleFindReplace: p.onToggleFindReplace,
10025
+ onSelectAll: p.onSelectAll
10026
+ }
10027
+ ),
11714
10028
  sHome && /* @__PURE__ */ jsx(
11715
10029
  DrawingGroup,
11716
10030
  {
@@ -11720,7 +10034,8 @@ function Toolbar(p) {
11720
10034
  onSetNewShapeType: p.onSetNewShapeType,
11721
10035
  onAddShape: p.onAddShape,
11722
10036
  onMoveLayer: p.onMoveLayer,
11723
- onMoveLayerToEdge: p.onMoveLayerToEdge
10037
+ onMoveLayerToEdge: p.onMoveLayerToEdge,
10038
+ onUpdateElementStyle: p.onUpdateElementStyle
11724
10039
  }
11725
10040
  ),
11726
10041
  sDrw && /* @__PURE__ */ jsx(
@@ -11765,6 +10080,7 @@ function Toolbar(p) {
11765
10080
  onToggleThemeEditor: p.onToggleThemeEditor,
11766
10081
  isThemeEditorOpen: p.isThemeEditorOpen,
11767
10082
  onOpenDocumentProperties: p.onOpenDocumentProperties,
10083
+ onOpenSlideSize: p.onOpenSlideSize,
11768
10084
  onToggleInspector: p.onToggleInspector,
11769
10085
  isInspectorPaneOpen: p.isInspectorPaneOpen
11770
10086
  }
@@ -11773,7 +10089,11 @@ function Toolbar(p) {
11773
10089
  TransitionsSection,
11774
10090
  {
11775
10091
  isInspectorPaneOpen: p.isInspectorPaneOpen,
11776
- onToggleInspector: p.onToggleInspector
10092
+ onToggleInspector: p.onToggleInspector,
10093
+ canEdit: p.canEdit,
10094
+ activeSlide: p.activeSlide,
10095
+ onTransitionChange: p.onTransitionChange,
10096
+ onApplyTransitionToAll: p.onApplyTransitionToAll
11777
10097
  }
11778
10098
  ),
11779
10099
  sAni && /* @__PURE__ */ jsx(
@@ -11808,7 +10128,9 @@ function Toolbar(p) {
11808
10128
  showSubtitles: p.showSubtitles ?? false,
11809
10129
  onSetMode: p.onSetMode,
11810
10130
  customShowControls: p,
11811
- hiddenActions: p.hiddenActions
10131
+ hiddenActions: p.hiddenActions,
10132
+ presentationProperties: p.presentationProperties,
10133
+ onPresentationPropertiesChange: p.onPresentationPropertiesChange
11812
10134
  }
11813
10135
  ),
11814
10136
  sRec && /* @__PURE__ */ jsx(
@@ -12005,6 +10327,118 @@ function CommentMarkersOverlay({
12005
10327
  );
12006
10328
  }) });
12007
10329
  }
10330
+ function ConnectorEndpointOverlay({
10331
+ connector,
10332
+ elements,
10333
+ editorScale,
10334
+ canvasStageRef,
10335
+ onUpdateElement
10336
+ }) {
10337
+ const { t } = useTranslation();
10338
+ const [drag, setDrag] = useState(null);
10339
+ const candidates = useMemo(
10340
+ () => collectConnectorSiteCandidates(elements.filter((el) => el.id !== connector.id)),
10341
+ [elements, connector.id]
10342
+ );
10343
+ const handles = getConnectorEndpointHandles(connector);
10344
+ const snap = drag ? findConnectorSiteNear(candidates, drag.x, drag.y) : null;
10345
+ const toSlidePoint = useCallback(
10346
+ (clientX, clientY) => {
10347
+ const rect = canvasStageRef.current?.getBoundingClientRect();
10348
+ const scale = editorScale || 1;
10349
+ return {
10350
+ x: (clientX - (rect?.left ?? 0)) / scale,
10351
+ y: (clientY - (rect?.top ?? 0)) / scale
10352
+ };
10353
+ },
10354
+ [canvasStageRef, editorScale]
10355
+ );
10356
+ useEffect(() => {
10357
+ if (!drag) {
10358
+ return;
10359
+ }
10360
+ const onMove = (event) => {
10361
+ const point = toSlidePoint(event.clientX, event.clientY);
10362
+ setDrag((prev) => prev ? { ...prev, ...point } : prev);
10363
+ };
10364
+ const onUp = (event) => {
10365
+ const point = toSlidePoint(event.clientX, event.clientY);
10366
+ const target = findConnectorSiteNear(candidates, point.x, point.y, CONNECTOR_SITE_SNAP_PX);
10367
+ const update = resolveConnectorEndpointUpdate(connector, elements, drag.kind, point, target);
10368
+ const next = withConnectorEndpointUpdate(connector, update);
10369
+ onUpdateElement(connector.id, {
10370
+ x: next.x,
10371
+ y: next.y,
10372
+ width: next.width,
10373
+ height: next.height,
10374
+ flipHorizontal: next.flipHorizontal,
10375
+ flipVertical: next.flipVertical,
10376
+ // Sent whole: a DETACHED end has had its binding key deleted, so a
10377
+ // merge of only the surviving keys would leave the stale one behind.
10378
+ shapeStyle: next.shapeStyle
10379
+ });
10380
+ setDrag(null);
10381
+ };
10382
+ window.addEventListener("pointermove", onMove);
10383
+ window.addEventListener("pointerup", onUp);
10384
+ window.addEventListener("pointercancel", onUp);
10385
+ return () => {
10386
+ window.removeEventListener("pointermove", onMove);
10387
+ window.removeEventListener("pointerup", onUp);
10388
+ window.removeEventListener("pointercancel", onUp);
10389
+ };
10390
+ }, [drag, candidates, connector, elements, onUpdateElement, toSlidePoint]);
10391
+ return /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-[56] pointer-events-none", "data-pptx-connector-endpoints": true, children: [
10392
+ drag ? candidates.map((site) => /* @__PURE__ */ jsx(
10393
+ "div",
10394
+ {
10395
+ "aria-hidden": "true",
10396
+ "data-pptx-connection-site": true,
10397
+ className: "absolute rounded-full border-2 border-blue-500",
10398
+ style: {
10399
+ left: site.x - 4,
10400
+ top: site.y - 4,
10401
+ width: 8,
10402
+ height: 8,
10403
+ background: snap?.elementId === site.elementId && snap.siteIndex === site.siteIndex ? "#3b82f6" : "rgba(96,165,250,0.6)"
10404
+ }
10405
+ },
10406
+ `${site.elementId}-${site.siteIndex}`
10407
+ )) : null,
10408
+ handles.map((handle) => /* @__PURE__ */ jsx(
10409
+ "button",
10410
+ {
10411
+ type: "button",
10412
+ "data-pptx-connector-endpoint": handle.kind,
10413
+ "data-pptx-connector-attached": String(handle.attached),
10414
+ "aria-label": t(
10415
+ handle.kind === "start" ? "pptx.canvas.connectorEndpointStart" : "pptx.canvas.connectorEndpointEnd"
10416
+ ),
10417
+ className: "absolute rounded-full border-2 border-white shadow",
10418
+ style: {
10419
+ left: (drag?.kind === handle.kind ? drag.x : handle.x) - 5,
10420
+ top: (drag?.kind === handle.kind ? drag.y : handle.y) - 5,
10421
+ width: 10,
10422
+ height: 10,
10423
+ // A bound end is filled, a loose one hollow, so "is this
10424
+ // connector actually attached?" is answerable at a glance.
10425
+ background: handle.attached ? "#16a34a" : "#ffffff",
10426
+ boxShadow: "0 0 0 1px #16a34a",
10427
+ cursor: "crosshair",
10428
+ pointerEvents: "auto",
10429
+ touchAction: "none"
10430
+ },
10431
+ onPointerDown: (event) => {
10432
+ event.stopPropagation();
10433
+ event.preventDefault();
10434
+ const point = toSlidePoint(event.clientX, event.clientY);
10435
+ setDrag({ kind: handle.kind, ...point });
10436
+ }
10437
+ },
10438
+ handle.kind
10439
+ ))
10440
+ ] });
10441
+ }
12008
10442
  function ConnectorOverlay({
12009
10443
  activeSlide,
12010
10444
  canvasSize,
@@ -12023,26 +10457,24 @@ function ConnectorOverlay({
12023
10457
  onMouseUp: onConnectorDragEnd,
12024
10458
  style: { pointerEvents: connectorDragState ? "auto" : "none" },
12025
10459
  children: [
12026
- activeSlide.elements.filter((el) => el.type !== "connector").map((el) => {
12027
- const sites = getConnectionSites(el.width, el.height);
12028
- return sites.map((site) => /* @__PURE__ */ jsx(
12029
- "div",
12030
- {
12031
- className: "absolute rounded-full border-2 border-blue-500 bg-blue-400/60 hover:bg-blue-500 hover:scale-125 transition-transform cursor-crosshair",
12032
- style: {
12033
- left: el.x + site.x - 5,
12034
- top: el.y + site.y - 5,
12035
- width: 10,
12036
- height: 10,
12037
- pointerEvents: "auto",
12038
- zIndex: 56
12039
- },
12040
- onMouseDown: (e) => onConnectionSiteDown(el.id, site.index, e),
12041
- onMouseUp: () => connectorDragState ? onConnectionSiteDrop(el.id, site.index) : void 0
10460
+ collectConnectorSiteCandidates(activeSlide.elements).map((site) => /* @__PURE__ */ jsx(
10461
+ "div",
10462
+ {
10463
+ "data-pptx-connection-site": true,
10464
+ className: "absolute rounded-full border-2 border-blue-500 bg-blue-400/60 hover:bg-blue-500 hover:scale-125 transition-transform cursor-crosshair",
10465
+ style: {
10466
+ left: site.x - 5,
10467
+ top: site.y - 5,
10468
+ width: 10,
10469
+ height: 10,
10470
+ pointerEvents: "auto",
10471
+ zIndex: 56
12042
10472
  },
12043
- `${el.id}-site-${site.index}`
12044
- ));
12045
- }),
10473
+ onMouseDown: (e) => onConnectionSiteDown(site.elementId, site.siteIndex, e),
10474
+ onMouseUp: () => connectorDragState ? onConnectionSiteDrop(site.elementId, site.siteIndex) : void 0
10475
+ },
10476
+ `${site.elementId}-site-${site.siteIndex}`
10477
+ )),
12046
10478
  connectorDragState && /* @__PURE__ */ jsx(
12047
10479
  ConnectorDragPreview,
12048
10480
  {
@@ -12066,7 +10498,7 @@ function ConnectorDragPreview({
12066
10498
  if (!startEl) {
12067
10499
  return null;
12068
10500
  }
12069
- const startSites = getConnectionSites(startEl.width, startEl.height);
10501
+ const startSites = getShapeConnectionSites(startEl);
12070
10502
  const startSite = startSites[connectorDragState.startSiteIndex] ?? startSites[0];
12071
10503
  const sx = startEl.x + startSite.x;
12072
10504
  const sy = startEl.y + startSite.y;
@@ -12786,10 +11218,6 @@ function useConnectorCreation({
12786
11218
  if (!connectorDragState || !onCreateConnector) {
12787
11219
  return;
12788
11220
  }
12789
- if (connectorDragState.startElementId === targetElementId) {
12790
- setConnectorDragState(null);
12791
- return;
12792
- }
12793
11221
  const startEl = activeSlide?.elements.find(
12794
11222
  (el) => el.id === connectorDragState.startElementId
12795
11223
  );
@@ -12798,37 +11226,27 @@ function useConnectorCreation({
12798
11226
  setConnectorDragState(null);
12799
11227
  return;
12800
11228
  }
12801
- const startSites = getConnectionSites(startEl.width, startEl.height);
12802
- const endSites = getConnectionSites(endEl.width, endEl.height);
12803
- const startSite = startSites[connectorDragState.startSiteIndex] ?? startSites[0];
12804
- const endSite = endSites[targetSiteIndex] ?? endSites[0];
12805
- const sx = startEl.x + startSite.x;
12806
- const sy = startEl.y + startSite.y;
12807
- const ex = endEl.x + endSite.x;
12808
- const ey = endEl.y + endSite.y;
12809
- const dx = Math.abs(ex - sx);
12810
- const dy = Math.abs(ey - sy);
12811
- const dist = Math.sqrt(dx * dx + dy * dy);
12812
- const shapeType = dist < 100 ? "straightConnector1" : dist < 300 ? "bentConnector3" : "curvedConnector3";
11229
+ const authored = authorConnectorBetweenSites(
11230
+ { element: startEl, siteIndex: connectorDragState.startSiteIndex },
11231
+ { element: endEl, siteIndex: targetSiteIndex }
11232
+ );
11233
+ if (!authored) {
11234
+ setConnectorDragState(null);
11235
+ return;
11236
+ }
12813
11237
  const newConnector = {
12814
11238
  id: `conn-new-${Date.now()}`,
12815
11239
  type: "connector",
12816
- x: Math.min(sx, ex),
12817
- y: Math.min(sy, ey),
12818
- width: Math.abs(ex - sx) || 1,
12819
- height: Math.abs(ey - sy) || 1,
12820
- shapeType,
11240
+ x: authored.x,
11241
+ y: authored.y,
11242
+ width: authored.width,
11243
+ height: authored.height,
11244
+ shapeType: authored.shapeType,
12821
11245
  shapeStyle: {
12822
11246
  strokeColor: "#4472C4",
12823
11247
  strokeWidth: 2,
12824
- connectorStartConnection: {
12825
- shapeId: connectorDragState.startElementId,
12826
- connectionSiteIndex: connectorDragState.startSiteIndex
12827
- },
12828
- connectorEndConnection: {
12829
- shapeId: targetElementId,
12830
- connectionSiteIndex: targetSiteIndex
12831
- }
11248
+ connectorStartConnection: authored.startConnection,
11249
+ connectorEndConnection: authored.endConnection
12832
11250
  }
12833
11251
  };
12834
11252
  onCreateConnector(newConnector);
@@ -13071,7 +11489,7 @@ function useStableCallbacks(callbacks) {
13071
11489
  []
13072
11490
  );
13073
11491
  const stableAdjustmentPointerDown = useCallback(
13074
- (elementId, e) => cbRef.current.onAdjustmentPointerDown(elementId, e),
11492
+ (elementId, e, descriptor) => cbRef.current.onAdjustmentPointerDown(elementId, e, descriptor),
13075
11493
  []
13076
11494
  );
13077
11495
  const stableRotate = useCallback(
@@ -13192,6 +11610,7 @@ function ActiveXControlOverlay({
13192
11610
  );
13193
11611
  }) });
13194
11612
  }
11613
+ var EMPTY_ADJUSTMENT_HANDLES = [];
13195
11614
  function SlideCanvas({
13196
11615
  activeSlide,
13197
11616
  templateElements,
@@ -13448,7 +11867,7 @@ function SlideCanvas({
13448
11867
  showResizeHandles: isEditableCanvas && selectedElementIdSet.has(element.id) && selectedElementIdSet.size <= 1 && !inlineEditingElementId,
13449
11868
  renderInk: false,
13450
11869
  renderGroups: true,
13451
- adjustmentHandleDescriptor: isEditableCanvas && selectedElement?.id === element.id ? getShapeAdjustmentHandleDescriptor(element) : null,
11870
+ adjustmentHandles: isEditableCanvas && selectedElement?.id === element.id ? getShapeAdjustmentHandleDescriptors(element) : EMPTY_ADJUSTMENT_HANDLES,
13452
11871
  onResizePointerDown: stableResizePointerDown,
13453
11872
  onAdjustmentPointerDown: stableAdjustmentPointerDown,
13454
11873
  onRotate: stableRotate,
@@ -13488,7 +11907,7 @@ function SlideCanvas({
13488
11907
  showResizeHandles: isEditableCanvas && selectedElementIdSet.has(element.id) && selectedElementIdSet.size <= 1 && !inlineEditingElementId,
13489
11908
  renderInk: true,
13490
11909
  renderGroups: true,
13491
- adjustmentHandleDescriptor: isEditableCanvas && selectedElement?.id === element.id ? getShapeAdjustmentHandleDescriptor(element) : null,
11910
+ adjustmentHandles: isEditableCanvas && selectedElement?.id === element.id ? getShapeAdjustmentHandleDescriptors(element) : EMPTY_ADJUSTMENT_HANDLES,
13492
11911
  onResizePointerDown: stableResizePointerDown,
13493
11912
  onAdjustmentPointerDown: stableAdjustmentPointerDown,
13494
11913
  onRotate: stableRotate,
@@ -13525,6 +11944,16 @@ function SlideCanvas({
13525
11944
  }
13526
11945
  ),
13527
11946
  /* @__PURE__ */ jsx(SnapLinesOverlay, { snapLines }),
11947
+ isEditableCanvas && selectedElement?.type === "connector" && activeSlide && /* @__PURE__ */ jsx(
11948
+ ConnectorEndpointOverlay,
11949
+ {
11950
+ connector: selectedElement,
11951
+ elements: activeSlide.elements,
11952
+ editorScale: zoom.editorScale,
11953
+ canvasStageRef: zoom.canvasStageRef,
11954
+ onUpdateElement: stableUpdateSmartArtElement
11955
+ }
11956
+ ),
13528
11957
  connectorCreationMode && activeSlide && /* @__PURE__ */ jsx(
13529
11958
  ConnectorOverlay,
13530
11959
  {
@@ -14539,7 +12968,7 @@ function SectionHeader({
14539
12968
  {
14540
12969
  type: "button",
14541
12970
  className: "flex w-full items-center gap-1 rounded px-1.5 py-1 text-[11px] uppercase tracking-wide text-muted-foreground hover:bg-muted/60 hover:text-foreground",
14542
- onClick: () => onToggle(sectionId),
12971
+ onClick: () => onToggle(sectionId, isCollapsed),
14543
12972
  onContextMenu: (e) => canEdit ? onContextMenu(e, sectionId, sectionIndex, totalSections) : void 0,
14544
12973
  onDoubleClick: (e) => {
14545
12974
  if (!canEdit) {
@@ -14825,7 +13254,7 @@ function SlideItemInner({
14825
13254
  );
14826
13255
  }
14827
13256
  var SlideItem = React14.memo(SlideItemInner);
14828
- function useSlidePaneCallbacks(onMoveSlide, onRenameSection) {
13257
+ function useSlidePaneCallbacks(onMoveSlide, onRenameSection, onToggleSectionCollapse) {
14829
13258
  const [collapsedSections, setCollapsedSections] = useState({});
14830
13259
  const [renamingSectionId, setRenamingSectionId] = useState(null);
14831
13260
  const [renameValue, setRenameValue] = useState("");
@@ -14863,12 +13292,16 @@ function useSlidePaneCallbacks(onMoveSlide, onRenameSection) {
14863
13292
  },
14864
13293
  [onMoveSlide]
14865
13294
  );
14866
- const toggleSection = useCallback((sectionId) => {
14867
- setCollapsedSections((prev) => ({
14868
- ...prev,
14869
- [sectionId]: !prev[sectionId]
14870
- }));
14871
- }, []);
13295
+ const toggleSection = useCallback(
13296
+ (sectionId, isCollapsed) => {
13297
+ setCollapsedSections((prev) => ({
13298
+ ...prev,
13299
+ [sectionId]: !isCollapsed
13300
+ }));
13301
+ onToggleSectionCollapse?.(sectionId);
13302
+ },
13303
+ [onToggleSectionCollapse]
13304
+ );
14872
13305
  const startRename = useCallback((sectionId, currentLabel) => {
14873
13306
  setRenamingSectionId(sectionId);
14874
13307
  setRenameValue(currentLabel);
@@ -14948,6 +13381,7 @@ function SlidesPaneSidebar({
14948
13381
  onDeleteSection,
14949
13382
  onMoveSectionUp,
14950
13383
  onMoveSectionDown,
13384
+ onToggleSectionCollapse,
14951
13385
  rehearsalTimings,
14952
13386
  panelWidth,
14953
13387
  fieldContext,
@@ -14978,7 +13412,7 @@ function SlidesPaneSidebar({
14978
13412
  () => estimateSlideItemHeight(canvasSize.width, canvasSize.height),
14979
13413
  [canvasSize.width, canvasSize.height]
14980
13414
  );
14981
- const showSectionHeaders = sectionGroups.length > 1;
13415
+ const showSectionHeaders = sectionGroups.some((group) => group.id !== DEFAULT_SECTION_GROUP_ID);
14982
13416
  const {
14983
13417
  collapsedSections,
14984
13418
  renamingSectionId,
@@ -14997,10 +13431,17 @@ function SlidesPaneSidebar({
14997
13431
  handleOpenSlideCtxMenu,
14998
13432
  closeSectionContextMenu,
14999
13433
  closeSlideCtxMenu
15000
- } = useSlidePaneCallbacks(onMoveSlide, onRenameSection);
13434
+ } = useSlidePaneCallbacks(onMoveSlide, onRenameSection, onToggleSectionCollapse);
13435
+ const effectiveCollapsed = useMemo(() => {
13436
+ const map = {};
13437
+ for (const group of sectionGroups) {
13438
+ map[group.id] = collapsedSections[group.id] ?? group.defaultCollapsed ?? false;
13439
+ }
13440
+ return map;
13441
+ }, [sectionGroups, collapsedSections]);
15001
13442
  const flatItems = useMemo(
15002
- () => buildFlatPaneItems(sectionGroups, showSectionHeaders, collapsedSections),
15003
- [sectionGroups, showSectionHeaders, collapsedSections]
13443
+ () => buildFlatPaneItems(sectionGroups, showSectionHeaders, effectiveCollapsed),
13444
+ [sectionGroups, showSectionHeaders, effectiveCollapsed]
15004
13445
  );
15005
13446
  const shouldVirtualize = slides.length >= VIRTUALIZATION_THRESHOLD;
15006
13447
  const { startIndex, endIndex, totalHeight, offsetY, scrollContainerRef, scrollToIndex } = useVirtualizedSlides({
@@ -15058,7 +13499,7 @@ function SlidesPaneSidebar({
15058
13499
  if (!section) {
15059
13500
  return null;
15060
13501
  }
15061
- const isCollapsed = collapsedSections[section.id] ?? false;
13502
+ const isCollapsed = effectiveCollapsed[section.id] ?? false;
15062
13503
  return /* @__PURE__ */ jsx(
15063
13504
  SectionHeader,
15064
13505
  {
@@ -15115,7 +13556,7 @@ function SlidesPaneSidebar({
15115
13556
  ) }) });
15116
13557
  };
15117
13558
  const renderNonVirtualized = () => /* @__PURE__ */ jsx("div", { className: "flex-1 space-y-1 overflow-y-auto px-1.5 pb-2", children: sectionGroups.map((section, sectionIndex) => {
15118
- const isCollapsed = collapsedSections[section.id] ?? false;
13559
+ const isCollapsed = effectiveCollapsed[section.id] ?? false;
15119
13560
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15120
13561
  showSectionHeaders && /* @__PURE__ */ jsx(
15121
13562
  SectionHeader,
@@ -15323,26 +13764,13 @@ function NotesMasterPanel({
15323
13764
  ] })
15324
13765
  ] });
15325
13766
  }
15326
- function masterToSlide(master) {
15327
- return {
15328
- id: master.path,
15329
- rId: "",
15330
- slideNumber: 0,
15331
- elements: master.elements ?? [],
15332
- backgroundColor: master.backgroundColor,
15333
- backgroundImage: master.backgroundImage
15334
- };
15335
- }
15336
- function layoutToSlide(layout) {
15337
- return {
15338
- id: layout.path,
15339
- rId: "",
15340
- slideNumber: 0,
15341
- elements: layout.elements ?? [],
15342
- backgroundColor: layout.backgroundColor,
15343
- backgroundImage: layout.backgroundImage
15344
- };
13767
+ function partToSlide(master, layoutIndex) {
13768
+ return masterViewPseudoSlide(
13769
+ { slideMasters: [master] },
13770
+ { tab: "slides", masterIndex: 0, layoutIndex }
13771
+ );
15345
13772
  }
13773
+ var EMPTY_SLIDE = { id: "", rId: "", slideNumber: 0, elements: [] };
15346
13774
  function SlideMastersList({
15347
13775
  slideMasters,
15348
13776
  activeMasterIndex,
@@ -15362,11 +13790,14 @@ function SlideMastersList({
15362
13790
  const layouts = master.layouts ?? [];
15363
13791
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15364
13792
  /* @__PURE__ */ jsxs(
15365
- "div",
13793
+ "button",
15366
13794
  {
13795
+ type: "button",
15367
13796
  ref: isMasterActive ? activeRef : void 0,
13797
+ "aria-pressed": isMasterActive,
13798
+ "aria-label": master.name || t("pptx.master.master"),
15368
13799
  className: cn(
15369
- "group relative cursor-pointer rounded-lg border-2 p-1 transition-all",
13800
+ "group relative block w-full cursor-pointer rounded-lg border-2 p-1 text-left transition-all",
15370
13801
  isMasterActive ? "border-amber-500 bg-amber-500/10" : "border-border bg-background/40 hover:border-border"
15371
13802
  ),
15372
13803
  onClick: () => onSelectMaster(masterIdx),
@@ -15374,7 +13805,7 @@ function SlideMastersList({
15374
13805
  /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden rounded bg-white", children: /* @__PURE__ */ jsx(
15375
13806
  SlideThumbnail,
15376
13807
  {
15377
- slide: masterToSlide(master),
13808
+ slide: partToSlide(master, null) ?? EMPTY_SLIDE,
15378
13809
  templateElements: [],
15379
13810
  canvasSize
15380
13811
  }
@@ -15395,11 +13826,14 @@ function SlideMastersList({
15395
13826
  layouts.length > 0 && /* @__PURE__ */ jsx("div", { className: "ml-3 space-y-1 border-l border-border/40 pl-2", children: layouts.map((layout, layoutIdx) => {
15396
13827
  const isLayoutActive = masterIdx === activeMasterIndex && layoutIdx === activeLayoutIndex;
15397
13828
  return /* @__PURE__ */ jsxs(
15398
- "div",
13829
+ "button",
15399
13830
  {
13831
+ type: "button",
15400
13832
  ref: isLayoutActive ? activeRef : void 0,
13833
+ "aria-pressed": isLayoutActive,
13834
+ "aria-label": layout.name || t("pptx.master.layout"),
15401
13835
  className: cn(
15402
- "group relative cursor-pointer rounded-md border-2 p-0.5 transition-all",
13836
+ "group relative block w-full cursor-pointer rounded-md border-2 p-0.5 text-left transition-all",
15403
13837
  isLayoutActive ? "border-primary bg-primary/10" : "border-border bg-background/40 hover:border-border"
15404
13838
  ),
15405
13839
  onClick: () => onSelectLayout(masterIdx, layoutIdx),
@@ -15407,8 +13841,8 @@ function SlideMastersList({
15407
13841
  /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden rounded bg-white", children: /* @__PURE__ */ jsx(
15408
13842
  SlideThumbnail,
15409
13843
  {
15410
- slide: layoutToSlide(layout),
15411
- templateElements: master.elements ?? [],
13844
+ slide: partToSlide(master, layoutIdx) ?? EMPTY_SLIDE,
13845
+ templateElements: [],
15412
13846
  canvasSize
15413
13847
  }
15414
13848
  ) }),
@@ -15446,16 +13880,22 @@ function MasterViewSidebar({
15446
13880
  notesMaster,
15447
13881
  handoutMaster,
15448
13882
  handoutSlidesPerPage,
13883
+ canEdit,
15449
13884
  onSelectMaster,
15450
13885
  onSelectLayout,
15451
13886
  onCollapse,
15452
13887
  onTabChange,
15453
13888
  onHandoutSlidesPerPageChange,
15454
13889
  onNotesMasterBackgroundChange,
15455
- onHandoutMasterBackgroundChange
13890
+ onHandoutMasterBackgroundChange,
13891
+ onSlidesBackgroundChange
15456
13892
  }) {
15457
13893
  const scrollRef = useRef(null);
15458
13894
  const { t } = useTranslation();
13895
+ const slidesBackground = masterViewBackgroundColor(
13896
+ { slideMasters },
13897
+ { tab: "slides", masterIndex: activeMasterIndex, layoutIndex: activeLayoutIndex }
13898
+ );
15459
13899
  const handleMasterClick = useCallback(
15460
13900
  (index) => {
15461
13901
  onSelectMaster(index);
@@ -15511,6 +13951,19 @@ function MasterViewSidebar({
15511
13951
  }
15512
13952
  ),
15513
13953
  /* @__PURE__ */ jsxs("div", { ref: scrollRef, className: "flex-1 space-y-2 overflow-y-auto px-1.5 pb-2 pt-1", children: [
13954
+ masterViewTab === "slides" && canEdit && /* @__PURE__ */ jsxs("section", { className: "space-y-1.5 rounded-md border border-border/60 p-2", children: [
13955
+ /* @__PURE__ */ jsx("div", { className: "text-[11px] text-muted-foreground", children: t("pptx.master.notesMasterBackground") }),
13956
+ /* @__PURE__ */ jsx(
13957
+ "input",
13958
+ {
13959
+ type: "color",
13960
+ "aria-label": t("pptx.master.backgroundColorLabel"),
13961
+ className: "h-7 w-full cursor-pointer rounded border border-border/60 bg-transparent",
13962
+ value: slidesBackground ?? "#ffffff",
13963
+ onChange: (event) => onSlidesBackgroundChange(event.target.value)
13964
+ }
13965
+ )
13966
+ ] }),
15514
13967
  masterViewTab === "slides" && /* @__PURE__ */ jsx(
15515
13968
  SlideMastersList,
15516
13969
  {
@@ -16225,10 +14678,10 @@ function SorterContextMenu({
16225
14678
  }
16226
14679
 
16227
14680
  // src/viewer/components/slide-sorter/types.ts
16228
- var MIN_ZOOM = 50;
16229
- var MAX_ZOOM = 200;
14681
+ var MIN_ZOOM = SORTER_MIN_ZOOM;
14682
+ var MAX_ZOOM = SORTER_MAX_ZOOM;
16230
14683
  var DEFAULT_ZOOM = 100;
16231
- var ZOOM_STEP = 10;
14684
+ var ZOOM_STEP = SORTER_ZOOM_STEP;
16232
14685
  function useKeyboardShortcuts(params) {
16233
14686
  const {
16234
14687
  slides,
@@ -16252,52 +14705,55 @@ function useKeyboardShortcuts(params) {
16252
14705
  if (contextMenu) {
16253
14706
  setContextMenu(null);
16254
14707
  }
16255
- const isCtrl = e.ctrlKey || e.metaKey;
16256
- if (e.key === "Escape") {
16257
- e.stopPropagation();
16258
- if (selectedSlideIds.length > 1) {
16259
- const activeSlide = slides[activeSlideIndex];
16260
- setSelectedSlideIds(activeSlide?.id ? [activeSlide.id] : []);
16261
- } else {
16262
- onClose();
16263
- }
14708
+ const { action } = mapSlideSorterKey(e, {
14709
+ canEdit,
14710
+ hasMultiSelection: selectedSlideIds.length > 1,
14711
+ isTextInputTarget: isEditorTextInputTarget(e.target)
14712
+ });
14713
+ if (action === null) {
16264
14714
  return;
16265
14715
  }
16266
- if ((e.key === "Delete" || e.key === "Backspace") && canEdit) {
16267
- e.preventDefault();
16268
- if (selectedIndexes.length > 0) {
16269
- handleDeleteSelected();
14716
+ switch (action) {
14717
+ case "collapseSelection": {
14718
+ e.stopPropagation();
14719
+ const activeSlide = slides[activeSlideIndex];
14720
+ setSelectedSlideIds(activeSlide?.id ? [activeSlide.id] : []);
14721
+ return;
16270
14722
  }
16271
- return;
16272
- }
16273
- if (isCtrl && e.key === "c") {
16274
- e.preventDefault();
16275
- handleCopySelected();
16276
- return;
16277
- }
16278
- if (isCtrl && e.key === "v" && canEdit) {
16279
- e.preventDefault();
16280
- handlePaste();
16281
- return;
16282
- }
16283
- if (isCtrl && e.key === "d" && canEdit) {
16284
- e.preventDefault();
16285
- handleDuplicateSelected();
16286
- return;
16287
- }
16288
- if (isCtrl && e.key === "a") {
16289
- e.preventDefault();
16290
- handleSelectAll();
16291
- return;
16292
- }
16293
- if (isCtrl && (e.key === "=" || e.key === "+")) {
16294
- e.preventDefault();
16295
- setZoom((z) => Math.min(MAX_ZOOM, z + ZOOM_STEP));
16296
- return;
16297
- }
16298
- if (isCtrl && e.key === "-") {
16299
- e.preventDefault();
16300
- setZoom((z) => Math.max(MIN_ZOOM, z - ZOOM_STEP));
14723
+ case "close":
14724
+ e.stopPropagation();
14725
+ onClose();
14726
+ return;
14727
+ case "delete":
14728
+ e.preventDefault();
14729
+ if (selectedIndexes.length > 0) {
14730
+ handleDeleteSelected();
14731
+ }
14732
+ return;
14733
+ case "copy":
14734
+ e.preventDefault();
14735
+ handleCopySelected();
14736
+ return;
14737
+ case "paste":
14738
+ e.preventDefault();
14739
+ handlePaste();
14740
+ return;
14741
+ case "duplicate":
14742
+ e.preventDefault();
14743
+ handleDuplicateSelected();
14744
+ return;
14745
+ case "selectAll":
14746
+ e.preventDefault();
14747
+ handleSelectAll();
14748
+ return;
14749
+ case "zoomIn":
14750
+ e.preventDefault();
14751
+ setZoom((z) => clampSorterZoom(z + ZOOM_STEP));
14752
+ return;
14753
+ case "zoomOut":
14754
+ e.preventDefault();
14755
+ setZoom((z) => clampSorterZoom(z - ZOOM_STEP));
14756
+ break;
16301
14757
  }
16302
14758
  };
16303
14759
  window.addEventListener("keydown", handleKeyDown);
@@ -19030,6 +17486,8 @@ function FontEmbeddingPanel({
19030
17486
  embedFontsEnabled,
19031
17487
  usedFontFamilies,
19032
17488
  embeddedFonts,
17489
+ canEmbedFonts = true,
17490
+ embedUnavailableKey,
19033
17491
  onClose,
19034
17492
  onToggleEmbedFonts
19035
17493
  }) {
@@ -19092,32 +17550,40 @@ function FontEmbeddingPanel({
19092
17550
  ] }),
19093
17551
  /* @__PURE__ */ jsxs("div", { className: "px-5 py-4 space-y-4 overflow-y-auto flex-1", children: [
19094
17552
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("pptx.fonts.embedDescription") }),
19095
- /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-3 cursor-pointer", children: [
19096
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
19097
- /* @__PURE__ */ jsx(
19098
- "input",
19099
- {
19100
- type: "checkbox",
19101
- className: "sr-only",
19102
- checked: embedFontsEnabled,
19103
- onChange: (e) => onToggleEmbedFonts(e.target.checked)
19104
- }
19105
- ),
19106
- /* @__PURE__ */ jsx(
19107
- "div",
19108
- {
19109
- className: `w-9 h-5 rounded-full transition-colors ${embedFontsEnabled ? "bg-primary" : "bg-muted-foreground"}`
19110
- }
19111
- ),
19112
- /* @__PURE__ */ jsx(
19113
- "div",
19114
- {
19115
- className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${embedFontsEnabled ? "translate-x-4" : ""}`
19116
- }
19117
- )
19118
- ] }),
19119
- /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground", children: t("pptx.fonts.enableEmbedding") })
19120
- ] }),
17553
+ /* @__PURE__ */ jsxs(
17554
+ "label",
17555
+ {
17556
+ className: `flex items-center gap-3 ${canEmbedFonts ? "cursor-pointer" : "cursor-not-allowed opacity-60"}`,
17557
+ children: [
17558
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
17559
+ /* @__PURE__ */ jsx(
17560
+ "input",
17561
+ {
17562
+ type: "checkbox",
17563
+ className: "sr-only",
17564
+ checked: embedFontsEnabled,
17565
+ disabled: !canEmbedFonts,
17566
+ onChange: (e) => onToggleEmbedFonts(e.target.checked)
17567
+ }
17568
+ ),
17569
+ /* @__PURE__ */ jsx(
17570
+ "div",
17571
+ {
17572
+ className: `w-9 h-5 rounded-full transition-colors ${embedFontsEnabled ? "bg-primary" : "bg-muted-foreground"}`
17573
+ }
17574
+ ),
17575
+ /* @__PURE__ */ jsx(
17576
+ "div",
17577
+ {
17578
+ className: `absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${embedFontsEnabled ? "translate-x-4" : ""}`
17579
+ }
17580
+ )
17581
+ ] }),
17582
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground", children: t("pptx.fonts.enableEmbedding") })
17583
+ ]
17584
+ }
17585
+ ),
17586
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: canEmbedFonts ? t("pptx.fonts.embedKeepsExisting") : t(embedUnavailableKey ?? "pptx.fonts.embedUnavailable") }),
19121
17587
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
19122
17588
  /* @__PURE__ */ jsxs("h3", { className: "text-xs font-medium text-foreground", children: [
19123
17589
  t("pptx.fonts.usedFonts"),
@@ -19824,12 +18290,13 @@ function PresenterNotesRail({
19824
18290
  templateElements,
19825
18291
  now,
19826
18292
  elapsed,
18293
+ activeCustomShow,
19827
18294
  onMove,
19828
18295
  onUpdateNotes
19829
18296
  }) {
19830
18297
  const { t } = useTranslation();
19831
18298
  const slide = slides[current];
19832
- const nextSlide = nextPresentedSlide(slides, current);
18299
+ const nextSlide = nextPresentedSlide(slides, current, activeCustomShow);
19833
18300
  const notesText = slide?.notes ?? "";
19834
18301
  const notesSegments = slide?.notesSegments;
19835
18302
  const [notesDraft, setNotesDraft] = useState(notesText);
@@ -20074,6 +18541,7 @@ function PresenterView({
20074
18541
  canvasSize,
20075
18542
  templateElements,
20076
18543
  presentationStartTime,
18544
+ activeCustomShow,
20077
18545
  onMovePresentationSlide,
20078
18546
  onExit,
20079
18547
  onOpenAudienceWindow,
@@ -20184,6 +18652,7 @@ function PresenterView({
20184
18652
  current: currentSlideIndex,
20185
18653
  canvasSize,
20186
18654
  templateElements,
18655
+ activeCustomShow,
20187
18656
  now,
20188
18657
  elapsed,
20189
18658
  onMove: onMovePresentationSlide,
@@ -20560,8 +19029,8 @@ function SelectionPane({
20560
19029
  if (elementId === null) {
20561
19030
  return;
20562
19031
  }
20563
- const trimmed = editingName.trim();
20564
- if (trimmed === editSeedRef.current.trim()) {
19032
+ const commit = resolveSelectionPaneRename(editSeedRef.current, editingName);
19033
+ if (!commit) {
20565
19034
  return;
20566
19035
  }
20567
19036
  setSlides((prevSlides) => {
@@ -20577,7 +19046,7 @@ function SelectionPane({
20577
19046
  }
20578
19047
  nextElements[idx] = {
20579
19048
  ...nextElements[idx],
20580
- name: trimmed.length > 0 ? trimmed : void 0
19049
+ name: commit.name
20581
19050
  };
20582
19051
  nextSlides[activeSlideIndex] = { ...slide, elements: nextElements };
20583
19052
  return nextSlides;
@@ -28834,17 +27303,12 @@ function ElementInspectorBody({
28834
27303
  onMoveLayer
28835
27304
  }) {
28836
27305
  const { t } = useTranslation();
28837
- const isLocked = Boolean(selectedElement.locks?.noMove || selectedElement.locks?.noSelect);
27306
+ const isLocked = isElementLocked(selectedElement);
28838
27307
  const handleToggleLock = () => {
28839
27308
  if (!canEdit) {
28840
27309
  return;
28841
27310
  }
28842
- if (isLocked) {
28843
- onUpdateElement({ locks: void 0 });
28844
- } else {
28845
- const locks = { noMove: true, noResize: true, noSelect: true };
28846
- onUpdateElement({ locks });
28847
- }
27311
+ onUpdateElement({ locks: elementLockTogglePatch(!isLocked) });
28848
27312
  };
28849
27313
  return /* @__PURE__ */ jsxs(Fragment, { children: [
28850
27314
  /* @__PURE__ */ jsxs("div", { className: CARD, children: [
@@ -28972,6 +27436,27 @@ function ElementInspectorBody({
28972
27436
  /* @__PURE__ */ jsx(LayerOrderButtons, { canEdit, onMoveLayer })
28973
27437
  ] });
28974
27438
  }
27439
+ function CommentBody({ text, mentions, className }) {
27440
+ const segments = commentTextSegments(text, mentions);
27441
+ if (!segments.some((segment) => segment.kind === "mention")) {
27442
+ return /* @__PURE__ */ jsx("div", { className, children: text });
27443
+ }
27444
+ return /* @__PURE__ */ jsx("div", { className, children: segments.map(
27445
+ (segment, index) => segment.kind === "mention" ? /* @__PURE__ */ jsx(
27446
+ "span",
27447
+ {
27448
+ className: `${COMMENT_MENTION_CLASS} rounded bg-primary/15 px-0.5 font-semibold text-primary`,
27449
+ ...{ [COMMENT_MENTION_ATTRIBUTE]: segment.personId || "" },
27450
+ title: segment.authorName,
27451
+ children: segment.text
27452
+ },
27453
+ index
27454
+ ) : (
27455
+ // eslint-disable-next-line react/no-array-index-key -- segments are positional
27456
+ /* @__PURE__ */ jsx(React14.Fragment, { children: segment.text }, index)
27457
+ )
27458
+ ) });
27459
+ }
28975
27460
  function InspectorCommentRow({
28976
27461
  comment,
28977
27462
  canEdit,
@@ -29065,7 +27550,14 @@ function InspectorCommentRow({
29065
27550
  )
29066
27551
  ] })
29067
27552
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
29068
- /* @__PURE__ */ jsx("div", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-foreground", children: comment.text }),
27553
+ /* @__PURE__ */ jsx(
27554
+ CommentBody,
27555
+ {
27556
+ text: comment.text,
27557
+ mentions: comment.mentions,
27558
+ className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-foreground"
27559
+ }
27560
+ ),
29069
27561
  canEdit && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 inline-flex items-center rounded bg-muted overflow-hidden", children: [
29070
27562
  /* @__PURE__ */ jsx(
29071
27563
  "button",
@@ -29593,6 +28085,83 @@ function DocumentPropertiesCard({
29593
28085
  ] })
29594
28086
  ] });
29595
28087
  }
28088
+ var CUSTOM_PRESET_VALUE = "";
28089
+ function SlideSizeCard({
28090
+ canvasSize,
28091
+ slideSizeEmu,
28092
+ canEdit,
28093
+ onUpdate,
28094
+ onUpdateSlideSize
28095
+ }) {
28096
+ const { t } = useTranslation();
28097
+ const selection = resolveSlideSizeSelection({ current: slideSizeEmu, canvas: canvasSize });
28098
+ const presetDisabled = !canEdit || !onUpdateSlideSize;
28099
+ const handlePreset = (labelKey) => {
28100
+ const preset = SLIDE_SIZE_PRESETS.find((candidate) => candidate.labelKey === labelKey);
28101
+ if (!preset) {
28102
+ return;
28103
+ }
28104
+ onUpdateSlideSize?.(slideSizeFromPreset(preset, selection.orientation));
28105
+ };
28106
+ const handleOrientation = (orientation) => {
28107
+ onUpdateSlideSize?.(withSlideSizeOrientation(selection.size, orientation));
28108
+ };
28109
+ return /* @__PURE__ */ jsxs("div", { className: CARD, children: [
28110
+ /* @__PURE__ */ jsx("div", { className: HEADING, children: t("pptx.slideSize.title") }),
28111
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 text-[11px]", children: [
28112
+ /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
28113
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("pptx.slideSize.presets") }),
28114
+ /* @__PURE__ */ jsxs(
28115
+ "select",
28116
+ {
28117
+ "data-pptx-slide-size-preset": true,
28118
+ disabled: presetDisabled,
28119
+ className: INPUT,
28120
+ value: selection.preset?.labelKey ?? CUSTOM_PRESET_VALUE,
28121
+ onChange: (e) => handlePreset(e.target.value),
28122
+ children: [
28123
+ selection.preset === void 0 && /* @__PURE__ */ jsx("option", { value: CUSTOM_PRESET_VALUE, children: t("pptx.slideSize.customSize") }),
28124
+ SLIDE_SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsx("option", { value: preset.labelKey, children: t(`pptx.slideSize.preset.${preset.labelKey}`) }, preset.labelKey))
28125
+ ]
28126
+ }
28127
+ )
28128
+ ] }),
28129
+ /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
28130
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("pptx.slideSize.orientation") }),
28131
+ /* @__PURE__ */ jsxs(
28132
+ "select",
28133
+ {
28134
+ "data-pptx-slide-size-orientation": true,
28135
+ disabled: presetDisabled,
28136
+ className: INPUT,
28137
+ value: selection.orientation,
28138
+ onChange: (e) => handleOrientation(e.target.value),
28139
+ children: [
28140
+ /* @__PURE__ */ jsx("option", { value: "landscape", children: t("pptx.slideSize.landscape") }),
28141
+ /* @__PURE__ */ jsx("option", { value: "portrait", children: t("pptx.slideSize.portrait") })
28142
+ ]
28143
+ }
28144
+ )
28145
+ ] }),
28146
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1.5", children: [
28147
+ ["W", "width"],
28148
+ ["H", "height"]
28149
+ ].map(([label, key]) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1", children: [
28150
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
28151
+ /* @__PURE__ */ jsx(
28152
+ "input",
28153
+ {
28154
+ type: "number",
28155
+ className: INPUT,
28156
+ disabled: !canEdit,
28157
+ value: canvasSize[key],
28158
+ onChange: (e) => onUpdate({ ...canvasSize, [key]: Number(e.target.value) })
28159
+ }
28160
+ )
28161
+ ] }, key)) })
28162
+ ] })
28163
+ ] });
28164
+ }
29596
28165
  function CheckboxRow({
29597
28166
  label,
29598
28167
  disabled,
@@ -29750,32 +28319,6 @@ function ThemeSelectorCard({
29750
28319
  ] })
29751
28320
  ] });
29752
28321
  }
29753
- function SlideSizeCard({
29754
- canvasSize,
29755
- canEdit,
29756
- onUpdate
29757
- }) {
29758
- const { t } = useTranslation();
29759
- return /* @__PURE__ */ jsxs("div", { className: CARD, children: [
29760
- /* @__PURE__ */ jsx("div", { className: HEADING, children: t("pptx.slideSize.title") }),
29761
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1.5 text-[11px]", children: [
29762
- ["W", "width"],
29763
- ["H", "height"]
29764
- ].map(([label, key]) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1", children: [
29765
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
29766
- /* @__PURE__ */ jsx(
29767
- "input",
29768
- {
29769
- type: "number",
29770
- className: INPUT,
29771
- disabled: !canEdit,
29772
- value: canvasSize[key],
29773
- onChange: (e) => onUpdate({ ...canvasSize, [key]: Number(e.target.value) })
29774
- }
29775
- )
29776
- ] }, key)) })
29777
- ] });
29778
- }
29779
28322
  var ALIAS_LABELS = {
29780
28323
  bg1: "Background 1",
29781
28324
  tx1: "Text 1",
@@ -30237,6 +28780,8 @@ function PresentationPropertiesPanel({
30237
28780
  onUpdateSlide,
30238
28781
  canvasSize,
30239
28782
  onUpdateCanvasSize,
28783
+ slideSizeEmu,
28784
+ onUpdateSlideSize,
30240
28785
  notesCanvasSize,
30241
28786
  notesMaster,
30242
28787
  handoutMaster,
@@ -30289,7 +28834,16 @@ function PresentationPropertiesPanel({
30289
28834
  }
30290
28835
  )
30291
28836
  ] }),
30292
- /* @__PURE__ */ jsx(SlideSizeCard, { canvasSize, canEdit, onUpdate: onUpdateCanvasSize }),
28837
+ /* @__PURE__ */ jsx(
28838
+ SlideSizeCard,
28839
+ {
28840
+ canvasSize,
28841
+ slideSizeEmu,
28842
+ canEdit,
28843
+ onUpdate: onUpdateCanvasSize,
28844
+ onUpdateSlideSize
28845
+ }
28846
+ ),
30293
28847
  /* @__PURE__ */ jsx(
30294
28848
  SlideTransitionSection,
30295
28849
  {
@@ -30591,6 +29145,8 @@ function InspectorPane(props) {
30591
29145
  replyingToCommentId,
30592
29146
  replyDraftByCommentId,
30593
29147
  onUpdateCanvasSize,
29148
+ slideSizeEmu,
29149
+ onUpdateSlideSize,
30594
29150
  onUpdateElementStyle,
30595
29151
  onUpdateTextStyle,
30596
29152
  onUpdateSlide,
@@ -30710,6 +29266,8 @@ function InspectorPane(props) {
30710
29266
  tagCollections,
30711
29267
  onUpdateTagCollections,
30712
29268
  onUpdateCanvasSize,
29269
+ slideSizeEmu,
29270
+ onUpdateSlideSize,
30713
29271
  activeSlide,
30714
29272
  theme,
30715
29273
  onUpdateSlide
@@ -33787,6 +32345,8 @@ function ViewerInspector({
33787
32345
  onUpdateElement,
33788
32346
  onApplySelection,
33789
32347
  onSetCanvasSize,
32348
+ slideSizeEmu,
32349
+ onSetSlideSize,
33790
32350
  onMoveLayer,
33791
32351
  onMoveLayerToEdge,
33792
32352
  onDeleteElement,
@@ -33889,6 +32449,8 @@ function ViewerInspector({
33889
32449
  replyingToCommentId: comments.replyingToCommentId,
33890
32450
  replyDraftByCommentId: comments.replyDraftByCommentId,
33891
32451
  onUpdateCanvasSize: onSetCanvasSize,
32452
+ slideSizeEmu,
32453
+ onUpdateSlideSize: onSetSlideSize,
33892
32454
  editTemplateMode,
33893
32455
  slideMasters,
33894
32456
  onSetTemplateBackground,
@@ -34165,7 +32727,10 @@ function ViewerToolbarSection(props) {
34165
32727
  propertyHandlers,
34166
32728
  dialogs,
34167
32729
  slideOps,
32730
+ sectionOps,
34168
32731
  onApplyLayout,
32732
+ loadLayoutPreviews,
32733
+ customFontFamilies,
34169
32734
  ops,
34170
32735
  onSetMode,
34171
32736
  onEnterPresenterView,
@@ -34185,6 +32750,7 @@ function ViewerToolbarSection(props) {
34185
32750
  isAiPanelOpen,
34186
32751
  onToggleAiPanel
34187
32752
  } = props;
32753
+ const { t } = useTranslation();
34188
32754
  const handleAddAnimation = useCallback(
34189
32755
  (preset, group) => {
34190
32756
  if (!selectedElement || !activeSlide) {
@@ -34239,6 +32805,34 @@ function ViewerToolbarSection(props) {
34239
32805
  () => scopeLayoutOptionsToActiveSlide(s.layoutOptions, activeSlide),
34240
32806
  [s.layoutOptions, activeSlide]
34241
32807
  );
32808
+ const handleResetSlide = useCallback(() => {
32809
+ const path = resetSlideLayoutPath(activeSlide);
32810
+ if (path) {
32811
+ onApplyLayout?.(path);
32812
+ }
32813
+ }, [activeSlide, onApplyLayout]);
32814
+ const handleAddSection = useCallback(() => {
32815
+ sectionOps?.addSection(t("pptx.sections.defaultName"), activeSlideIndex);
32816
+ }, [sectionOps, activeSlideIndex, t]);
32817
+ const handleOpenSlideSize = useCallback(() => {
32818
+ s.setSelectedElementId(null);
32819
+ s.setSelectedElementIds([]);
32820
+ s.setSidebarPanelMode("properties");
32821
+ s.setIsInspectorPaneOpen(true);
32822
+ }, [s]);
32823
+ const handleSelectAll = useCallback(() => {
32824
+ const allIds = activeSlide?.elements.map((element) => element.id) ?? [];
32825
+ if (allIds.length > 0) {
32826
+ ops.applySelection(allIds[0], allIds);
32827
+ }
32828
+ }, [activeSlide, ops]);
32829
+ const handlePresentationPropertiesChange = useCallback(
32830
+ (updates) => {
32831
+ s.setPresentationProperties((prev) => ({ ...prev, ...updates }));
32832
+ history.markDirty();
32833
+ },
32834
+ [s, history]
32835
+ );
34242
32836
  const handleApplyTransitionToAll = useCallback(() => {
34243
32837
  const transition = activeSlide?.transition;
34244
32838
  if (!transition) {
@@ -34344,7 +32938,7 @@ function ViewerToolbarSection(props) {
34344
32938
  s.setIsThemeGalleryOpen((p) => !p);
34345
32939
  break;
34346
32940
  case "slideSize":
34347
- dialogs.setIsSetUpSlideShowOpen(true);
32941
+ handleOpenSlideSize();
34348
32942
  break;
34349
32943
  }
34350
32944
  break;
@@ -34373,7 +32967,17 @@ function ViewerToolbarSection(props) {
34373
32967
  break;
34374
32968
  }
34375
32969
  },
34376
- [ops, insertHandlers, s, dialogs, zoom, onSetMode, onEnterPresenterView, manipulation]
32970
+ [
32971
+ ops,
32972
+ insertHandlers,
32973
+ s,
32974
+ dialogs,
32975
+ zoom,
32976
+ onSetMode,
32977
+ onEnterPresenterView,
32978
+ manipulation,
32979
+ handleOpenSlideSize
32980
+ ]
34377
32981
  );
34378
32982
  const handleQuickAccessCommand = useCallback(
34379
32983
  (id) => {
@@ -34541,6 +33145,14 @@ function ViewerToolbarSection(props) {
34541
33145
  isOverflowMenuOpen: s.isOverflowMenuOpen,
34542
33146
  onSetOverflowMenuOpen: s.setIsOverflowMenuOpen,
34543
33147
  layoutOptions: scopedLayoutOptions,
33148
+ currentLayoutPath: activeSlide?.layoutPath,
33149
+ loadLayoutPreviews,
33150
+ themeFonts: {
33151
+ heading: s.theme?.fontScheme?.majorFont?.latin,
33152
+ body: s.theme?.fontScheme?.minorFont?.latin
33153
+ },
33154
+ embeddedFontFamilies: s.embeddedFonts.map((font) => font.name),
33155
+ customFontFamilies,
34544
33156
  onInsertSlideFromLayout: slideOps.handleInsertSlideFromLayout,
34545
33157
  onApplyLayout,
34546
33158
  onInsertSlideFromTemplate: slideOps.handleInsertSlideFromTemplate,
@@ -34558,6 +33170,7 @@ function ViewerToolbarSection(props) {
34558
33170
  onToggleVersionHistory: () => propertyHandlers.setIsVersionHistoryOpen((p) => !p),
34559
33171
  onOpenPasswordProtection: () => dialogs.setIsPasswordDialogOpen(true),
34560
33172
  onOpenDocumentProperties: () => dialogs.setIsDocPropsDialogOpen(true),
33173
+ onOpenSlideSize: handleOpenSlideSize,
34561
33174
  onOpenFontEmbedding: () => dialogs.setIsFontEmbeddingOpen(true),
34562
33175
  onOpenDigitalSignatures: () => dialogs.setIsDigitalSigDialogOpen(true),
34563
33176
  onEnterPresenterView,
@@ -34591,6 +33204,11 @@ function ViewerToolbarSection(props) {
34591
33204
  activeSlide,
34592
33205
  onTransitionChange: handleTransitionChange,
34593
33206
  onApplyTransitionToAll: handleApplyTransitionToAll,
33207
+ onResetSlide: handleResetSlide,
33208
+ onAddSection: sectionOps ? handleAddSection : void 0,
33209
+ onSelectAll: handleSelectAll,
33210
+ presentationProperties: s.presentationProperties,
33211
+ onPresentationPropertiesChange: handlePresentationPropertiesChange,
34594
33212
  aiEnabled,
34595
33213
  isAiPanelOpen,
34596
33214
  onToggleAiPanel
@@ -35116,6 +33734,8 @@ function ViewerDialogGroup(props) {
35116
33734
  embedFontsEnabled: dialogs.embedFontsEnabled,
35117
33735
  usedFontFamilies: propertyHandlers.usedFontFamilies,
35118
33736
  embeddedFonts: embeddedFonts.map((f) => f.name),
33737
+ canEmbedFonts: dialogs.fontEmbedding.interactive,
33738
+ embedUnavailableKey: dialogs.fontEmbedding.disabledReasonKey,
35119
33739
  onClose: () => dialogs.setIsFontEmbeddingOpen(false),
35120
33740
  onToggleEmbedFonts: dialogs.setEmbedFontsEnabled
35121
33741
  }
@@ -35229,6 +33849,7 @@ function ViewerPresentationLayer(props) {
35229
33849
  canvasSize,
35230
33850
  templateElements,
35231
33851
  presentation,
33852
+ activeCustomShow,
35232
33853
  onExitPresentation,
35233
33854
  isMobile,
35234
33855
  onUpdateNotes
@@ -35253,6 +33874,7 @@ function ViewerPresentationLayer(props) {
35253
33874
  currentSlideIndex: presentation.presentationSlideIndex,
35254
33875
  canvasSize,
35255
33876
  templateElements,
33877
+ activeCustomShow,
35256
33878
  presentationStartTime: presentation.presentationStartTime,
35257
33879
  onMovePresentationSlide: presentation.movePresentationSlide,
35258
33880
  onExit: onExitPresentation,
@@ -35390,6 +34012,7 @@ function PresentationStage({
35390
34012
  ...getReactSlideBackgroundStyle(activeSlide)
35391
34013
  },
35392
34014
  children: [
34015
+ /* @__PURE__ */ jsx("style", { children: PRESENTATION_HIT_TEST_CSS }),
35393
34016
  presentationKeyframesCss && /* @__PURE__ */ jsx("style", { children: presentationKeyframesCss }),
35394
34017
  templateElements.map((element, index) => /* @__PURE__ */ jsx(
35395
34018
  ElementRenderer,
@@ -35400,6 +34023,7 @@ function PresentationStage({
35400
34023
  isInlineEditing: false,
35401
34024
  inlineEditingText: "",
35402
34025
  canInteract: false,
34026
+ presenting: true,
35403
34027
  spellCheckEnabled: false,
35404
34028
  mediaDataUrls,
35405
34029
  selectionColorClass: "blue-400",
@@ -35409,7 +34033,7 @@ function PresentationStage({
35409
34033
  showResizeHandles: false,
35410
34034
  renderInk: false,
35411
34035
  renderGroups: true,
35412
- adjustmentHandleDescriptor: null,
34036
+ adjustmentHandles: [],
35413
34037
  onResizePointerDown: noop2,
35414
34038
  onAdjustmentPointerDown: noop2,
35415
34039
  onInlineEditChange: noop2,
@@ -35436,6 +34060,7 @@ function PresentationStage({
35436
34060
  isInlineEditing: false,
35437
34061
  inlineEditingText: "",
35438
34062
  canInteract: false,
34063
+ presenting: true,
35439
34064
  spellCheckEnabled: false,
35440
34065
  mediaDataUrls,
35441
34066
  selectionColorClass: "blue-500",
@@ -35445,7 +34070,7 @@ function PresentationStage({
35445
34070
  showResizeHandles: false,
35446
34071
  renderInk: true,
35447
34072
  renderGroups: true,
35448
- adjustmentHandleDescriptor: null,
34073
+ adjustmentHandles: [],
35449
34074
  onResizePointerDown: noop2,
35450
34075
  onAdjustmentPointerDown: noop2,
35451
34076
  onInlineEditChange: noop2,
@@ -35578,7 +34203,7 @@ function ViewerCanvasArea(props) {
35578
34203
  const { isHidden } = useToolbarVisibility(hiddenActions);
35579
34204
  const viewerOptions = useViewerOptionsContext();
35580
34205
  const effectiveSlide = mode === "master" ? masterPseudoSlide : activeSlide;
35581
- const effectiveTemplateElements = mode === "master" ? s.activeLayout ? s.activeMaster?.elements ?? [] : [] : templateElements;
34206
+ const effectiveTemplateElements = mode === "master" ? [] : templateElements;
35582
34207
  const fieldContext = useMemo(
35583
34208
  () => buildFieldSubstitutionContext({
35584
34209
  headerFooter: s.headerFooter,
@@ -36011,7 +34636,7 @@ ${url}`)) {
36011
34636
  var AiChatPanel = lazy(async () => {
36012
34637
  const [{ useChat }, { default: Panel }] = await Promise.all([
36013
34638
  import('@ai-sdk/react'),
36014
- import('./AiChatPanel-XHKYPYPH.mjs')
34639
+ import('./AiChatPanel-SSSBERWJ.mjs')
36015
34640
  ]);
36016
34641
  function BoundAiChatPanel(props) {
36017
34642
  return /* @__PURE__ */ jsx(Panel, { ...props, useChat });
@@ -36916,6 +35541,11 @@ function ViewerSidePanels(props) {
36916
35541
  aiBridge,
36917
35542
  aiPanel
36918
35543
  } = props;
35544
+ const handleSetSlideSize = (size) => {
35545
+ s.setSlideSizeEmu(size);
35546
+ s.setCanvasSize(slideSizeToCanvasPx(size));
35547
+ history.markDirty();
35548
+ };
36919
35549
  const effectiveSlide = mode === "master" ? masterPseudoSlide : activeSlide;
36920
35550
  const currentBuiltInTheme = BUILT_IN_THEMES.find(
36921
35551
  (candidate) => themeColorSchemesEqual(s.theme?.colorScheme, candidate.colorScheme)
@@ -36944,6 +35574,8 @@ function ViewerSidePanels(props) {
36944
35574
  onUpdateElement: ops.updateSelectedElement,
36945
35575
  onApplySelection: ops.applySelection,
36946
35576
  onSetCanvasSize: s.setCanvasSize,
35577
+ slideSizeEmu: s.slideSizeEmu,
35578
+ onSetSlideSize: handleSetSlideSize,
36947
35579
  onMoveLayer: manipulation.handleMoveLayer,
36948
35580
  onMoveLayerToEdge: manipulation.handleMoveLayerToEdge,
36949
35581
  onDeleteElement: manipulation.handleDelete,
@@ -37135,6 +35767,7 @@ function ViewerMainContent(props) {
37135
35767
  onDeleteSection: sectionOps.deleteSection,
37136
35768
  onMoveSectionUp: sectionOps.moveSectionUp,
37137
35769
  onMoveSectionDown: sectionOps.moveSectionDown,
35770
+ onToggleSectionCollapse: sectionOps.toggleSectionCollapse,
37138
35771
  rehearsalTimings: Object.keys(presentation.recordedTimings).length > 0 ? presentation.recordedTimings : void 0,
37139
35772
  panelWidth: leftPanelWidth,
37140
35773
  fieldContext: thumbnailFieldContext,
@@ -37174,6 +35807,22 @@ function ViewerMainContent(props) {
37174
35807
  (master) => master ? { ...master, backgroundColor } : master
37175
35808
  );
37176
35809
  state.setIsDirty(true);
35810
+ },
35811
+ canEdit,
35812
+ onSlidesBackgroundChange: (backgroundColor) => {
35813
+ const write = setMasterViewBackgroundColor(
35814
+ { slideMasters: state.slideMasters },
35815
+ {
35816
+ tab: "slides",
35817
+ masterIndex: state.activeMasterIndex,
35818
+ layoutIndex: state.activeLayoutIndex
35819
+ },
35820
+ backgroundColor
35821
+ );
35822
+ if (write?.slideMasters) {
35823
+ state.setSlideMasters(write.slideMasters);
35824
+ state.setIsDirty(true);
35825
+ }
37177
35826
  }
37178
35827
  }
37179
35828
  ),
@@ -37541,6 +36190,7 @@ function MobileChromeOverlay(props) {
37541
36190
  onDeleteSection: editorOps.sectionOps.deleteSection,
37542
36191
  onMoveSectionUp: editorOps.sectionOps.moveSectionUp,
37543
36192
  onMoveSectionDown: editorOps.sectionOps.moveSectionDown,
36193
+ onToggleSectionCollapse: editorOps.sectionOps.toggleSectionCollapse,
37544
36194
  rehearsalTimings: Object.keys(presentation.recordedTimings).length > 0 ? presentation.recordedTimings : void 0
37545
36195
  }
37546
36196
  ),
@@ -38263,6 +36913,63 @@ function SettingsAppearanceTab({
38263
36913
  );
38264
36914
  }) });
38265
36915
  }
36916
+ function SettingsCustomFontsSection({
36917
+ enabled,
36918
+ families,
36919
+ onRegistered
36920
+ }) {
36921
+ const { t } = useTranslation();
36922
+ const inputRef = useRef(null);
36923
+ const [error, setError] = useState(false);
36924
+ const handleFile = async (file) => {
36925
+ setError(false);
36926
+ try {
36927
+ const registration = await registerCustomFont(file);
36928
+ if (registration) {
36929
+ onRegistered(registration.family);
36930
+ } else {
36931
+ setError(true);
36932
+ }
36933
+ } catch {
36934
+ setError(true);
36935
+ }
36936
+ };
36937
+ return /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
36938
+ /* @__PURE__ */ jsxs(
36939
+ "button",
36940
+ {
36941
+ type: "button",
36942
+ disabled: !enabled,
36943
+ onClick: () => inputRef.current?.click(),
36944
+ className: "inline-flex items-center gap-2 rounded border border-border px-3 py-1.5 text-xs text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
36945
+ children: [
36946
+ /* @__PURE__ */ jsx(LuUpload, { className: "h-3.5 w-3.5" }),
36947
+ t("pptx.options.general.addFontFile")
36948
+ ]
36949
+ }
36950
+ ),
36951
+ /* @__PURE__ */ jsx(
36952
+ "input",
36953
+ {
36954
+ ref: inputRef,
36955
+ type: "file",
36956
+ accept: CUSTOM_FONT_ACCEPT,
36957
+ className: "hidden",
36958
+ onChange: (event) => {
36959
+ const file = event.currentTarget.files?.[0];
36960
+ event.currentTarget.value = "";
36961
+ if (file) {
36962
+ void handleFile(file);
36963
+ }
36964
+ }
36965
+ }
36966
+ ),
36967
+ !enabled && /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: t("pptx.options.general.customFontsDisabled") }),
36968
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-2 text-xs text-destructive", children: t("pptx.options.general.customFontError") }),
36969
+ /* @__PURE__ */ jsx("p", { className: "mt-3 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("pptx.options.general.customFontsAdded") }),
36970
+ families.length === 0 ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("pptx.options.general.customFontsEmpty") }) : /* @__PURE__ */ jsx("ul", { className: "mt-1 flex flex-col gap-0.5", children: families.map((family) => /* @__PURE__ */ jsx("li", { className: "text-xs text-foreground", style: { fontFamily: family }, children: family }, family)) })
36971
+ ] });
36972
+ }
38266
36973
  function SettingsLanguageTab({
38267
36974
  activeLocale,
38268
36975
  locales,
@@ -38290,6 +36997,7 @@ function SettingsLanguageTab({
38290
36997
  }) });
38291
36998
  }
38292
36999
  var AI_TAB_ID = "ai";
37000
+ var NO_CUSTOM_FONT_FAMILIES = [];
38293
37001
  function SettingsDialog({
38294
37002
  isOpen,
38295
37003
  onClose,
@@ -38308,7 +37016,9 @@ function SettingsDialog({
38308
37016
  availableLocales,
38309
37017
  onSelectLocale,
38310
37018
  aiEnabled,
38311
- chatStore
37019
+ chatStore,
37020
+ customFontFamilies = NO_CUSTOM_FONT_FAMILIES,
37021
+ onCustomFontRegistered
38312
37022
  }) {
38313
37023
  const [activeTabId, setActiveTabId] = useState("general");
38314
37024
  const { t } = useTranslation();
@@ -38480,6 +37190,16 @@ function SettingsDialog({
38480
37190
  }
38481
37191
  ) });
38482
37192
  }
37193
+ if (section.special === "customFonts") {
37194
+ return /* @__PURE__ */ jsx(
37195
+ SettingsCustomFontsSection,
37196
+ {
37197
+ enabled: options.general.enableCustomFontUpload,
37198
+ families: customFontFamilies,
37199
+ onRegistered: (family) => onCustomFontRegistered?.(family)
37200
+ }
37201
+ );
37202
+ }
38483
37203
  if (section.special === "clearCache") {
38484
37204
  return /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
38485
37205
  /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs text-muted-foreground", children: t("pptx.options.save.clearCacheDescription") }),
@@ -38942,6 +37662,12 @@ var PowerPointViewer = forwardRef(
38942
37662
  [onThemeChange]
38943
37663
  );
38944
37664
  const themeStyle = useThemeStyle(effectiveTheme);
37665
+ const [customFontFamilies, setCustomFontFamilies] = useState([]);
37666
+ const handleCustomFontRegistered = useCallback((family) => {
37667
+ setCustomFontFamilies(
37668
+ (current) => current.includes(family) ? current : [...current, family]
37669
+ );
37670
+ }, []);
38945
37671
  const { i18n } = useTranslation();
38946
37672
  const [localeCode, setLocaleCode] = useState(
38947
37673
  () => defaultLocale ?? readStoredViewerPrefs().localeCode ?? "en"
@@ -39123,6 +37849,7 @@ var PowerPointViewer = forwardRef(
39123
37849
  activeMaster: state.activeMaster,
39124
37850
  presentationGridSpacing: state.presentationProperties.gridSpacing
39125
37851
  });
37852
+ const activeCustomShow = state.customShows.find((show) => show.id === state.activeCustomShowId) ?? null;
39126
37853
  const hasActivePointerInteraction = useCallback(
39127
37854
  () => Boolean(
39128
37855
  state.dragStateRef.current || state.resizeStateRef.current || state.marqueeStateRef.current || state.shapeAdjustmentDragStateRef.current || state.isDrawingRef.current
@@ -39139,6 +37866,10 @@ var PowerPointViewer = forwardRef(
39139
37866
  canvasSize,
39140
37867
  selectedElements: state.selectedElements
39141
37868
  });
37869
+ const { setIsDirty } = state;
37870
+ const markDocumentDirty = useCallback(() => {
37871
+ setIsDirty(true);
37872
+ }, [setIsDirty]);
39142
37873
  const history = useEditorHistory({
39143
37874
  slides,
39144
37875
  canvasSize,
@@ -39152,6 +37883,7 @@ var PowerPointViewer = forwardRef(
39152
37883
  error,
39153
37884
  hasActivePointerInteraction,
39154
37885
  pointerCommitNonce: state.pointerCommitNonce,
37886
+ onDirty: markDocumentDirty,
39155
37887
  setSlides: state.setSlides,
39156
37888
  setCanvasSize: state.setCanvasSize,
39157
37889
  setActiveSlideIndex: state.setActiveSlideIndex,
@@ -39176,7 +37908,13 @@ var PowerPointViewer = forwardRef(
39176
37908
  setMode: state.setMode,
39177
37909
  setActiveSlideIndex: state.setActiveSlideIndex,
39178
37910
  setSlides: state.setSlides,
39179
- history
37911
+ history,
37912
+ // PowerPoint's bare `J` during a show toggles live captions. It resolves
37913
+ // in the shared slide-show keymap; this is the state it has to reach.
37914
+ onToggleSubtitles: () => state.setPresentationProperties((prev) => ({
37915
+ ...prev,
37916
+ showSubtitles: !prev.showSubtitles
37917
+ }))
39180
37918
  });
39181
37919
  useTouchGestures({
39182
37920
  targetRef: zoom.canvasViewportRef,
@@ -39224,7 +37962,8 @@ var PowerPointViewer = forwardRef(
39224
37962
  preMasterMode: state.preMasterMode,
39225
37963
  hasDigitalSignatures: state.hasDigitalSignatures,
39226
37964
  isDirty: state.isDirty,
39227
- history
37965
+ history,
37966
+ embeddedFontFamilies: state.embeddedFonts.map((font) => font.name)
39228
37967
  });
39229
37968
  useEffect(() => {
39230
37969
  state.setSelectedElementId(null);
@@ -39288,6 +38027,9 @@ var PowerPointViewer = forwardRef(
39288
38027
  content,
39289
38028
  filePath,
39290
38029
  autosaveEnabled,
38030
+ // File > Options > Save > "Save AutoRecover information every N
38031
+ // minutes". React alone ignored it and stayed on the 120s default.
38032
+ autosaveIntervalSeconds: resolveAutosaveIntervalSeconds(viewerOptions),
39291
38033
  canEdit,
39292
38034
  mode,
39293
38035
  slides,
@@ -39405,7 +38147,10 @@ var PowerPointViewer = forwardRef(
39405
38147
  propertyHandlers,
39406
38148
  dialogs,
39407
38149
  slideOps: editorOps.slideOps,
38150
+ sectionOps: editorOps.sectionOps,
39408
38151
  onApplyLayout: (path) => void layoutSwitching.applyLayout(path),
38152
+ loadLayoutPreviews: layoutSwitching.loadLayoutPreviews,
38153
+ customFontFamilies,
39409
38154
  ops: editorOps.ops,
39410
38155
  onSetMode: handleSetMode,
39411
38156
  onEnterPresenterView: handleEnterPresenterView,
@@ -39554,7 +38299,9 @@ var PowerPointViewer = forwardRef(
39554
38299
  localeCode,
39555
38300
  availableLocales: resolvedLocales,
39556
38301
  onSelectLocale: handleLocaleChange,
39557
- aiEnabled: Boolean(ai)
38302
+ aiEnabled: Boolean(ai),
38303
+ customFontFamilies,
38304
+ onCustomFontRegistered: handleCustomFontRegistered
39558
38305
  }
39559
38306
  ),
39560
38307
  isHeaderFooterOpen && /* @__PURE__ */ jsx(
@@ -39652,6 +38399,7 @@ var PowerPointViewer = forwardRef(
39652
38399
  canvasSize,
39653
38400
  templateElements: state.templateElements,
39654
38401
  presentation,
38402
+ activeCustomShow,
39655
38403
  onExitPresentation: () => handleSetMode("edit"),
39656
38404
  onUpdateNotes: propertyHandlers.handleUpdateNotes,
39657
38405
  isMobile