pptx-react-viewer 4.27.0 → 4.29.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 (128) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +3 -0
  3. package/dist/{AiChatPanel-3J6XGIJ4.mjs → AiChatPanel-6EIOUV7J.mjs} +2 -2
  4. package/dist/AiChatPanel-6EIOUV7J.mjs.br +0 -0
  5. package/dist/AiChatPanel-6EIOUV7J.mjs.gz +0 -0
  6. package/dist/{AiChatPanel-EGTSK4T5.js → AiChatPanel-AK6X4NJD.js} +10 -10
  7. package/dist/AiChatPanel-AK6X4NJD.js.br +0 -0
  8. package/dist/AiChatPanel-AK6X4NJD.js.gz +0 -0
  9. package/dist/{Model3DScene-2V2GQ6FY.mjs → Model3DScene-EZDCI7TT.mjs} +1 -1
  10. package/dist/Model3DScene-EZDCI7TT.mjs.br +0 -0
  11. package/dist/Model3DScene-EZDCI7TT.mjs.gz +0 -0
  12. package/dist/{Model3DScene-Q3XEZSWV.js → Model3DScene-KYIYYLMY.js} +1 -1
  13. package/dist/Model3DScene-KYIYYLMY.js.br +0 -0
  14. package/dist/Model3DScene-KYIYYLMY.js.gz +0 -0
  15. package/dist/{chunk-ZK7UEEXE.mjs → chunk-6YOFWVII.mjs} +83 -80
  16. package/dist/chunk-6YOFWVII.mjs.br +0 -0
  17. package/dist/chunk-6YOFWVII.mjs.gz +0 -0
  18. package/dist/{chunk-5RQTZ376.js → chunk-7JMO6R6B.js} +394 -391
  19. package/dist/chunk-7JMO6R6B.js.br +0 -0
  20. package/dist/chunk-7JMO6R6B.js.gz +0 -0
  21. package/dist/chunk-CTL42ZRR.js +4238 -0
  22. package/dist/chunk-CTL42ZRR.js.br +0 -0
  23. package/dist/chunk-CTL42ZRR.js.gz +0 -0
  24. package/dist/{chunk-QGM4M3NI.js → chunk-DFFCFNK2.js} +2 -0
  25. package/dist/chunk-DFFCFNK2.js.br +0 -0
  26. package/dist/chunk-DFFCFNK2.js.gz +0 -0
  27. package/dist/{chunk-34AXBLYH.mjs → chunk-EW45F35P.mjs} +80 -78
  28. package/dist/chunk-EW45F35P.mjs.br +0 -0
  29. package/dist/chunk-EW45F35P.mjs.gz +0 -0
  30. package/dist/{chunk-6DZX6EAA.mjs → chunk-GDQSMJQZ.mjs} +2 -1
  31. package/dist/chunk-GDQSMJQZ.mjs.br +0 -0
  32. package/dist/chunk-GDQSMJQZ.mjs.gz +0 -0
  33. package/dist/{chunk-MQCBMO6A.js → chunk-K3EGXT6W.js} +261 -259
  34. package/dist/chunk-K3EGXT6W.js.br +0 -0
  35. package/dist/chunk-K3EGXT6W.js.gz +0 -0
  36. package/dist/chunk-VI575UU6.mjs +3897 -0
  37. package/dist/chunk-VI575UU6.mjs.br +0 -0
  38. package/dist/chunk-VI575UU6.mjs.gz +0 -0
  39. package/dist/i18n.js +1 -1
  40. package/dist/i18n.js.br +2 -2
  41. package/dist/i18n.js.gz +0 -0
  42. package/dist/i18n.mjs +1 -1
  43. package/dist/i18n.mjs.br +0 -0
  44. package/dist/i18n.mjs.gz +0 -0
  45. package/dist/index.d.ts +17 -0
  46. package/dist/index.js +25 -25
  47. package/dist/index.js.br +0 -0
  48. package/dist/index.js.gz +0 -0
  49. package/dist/index.mjs +4 -4
  50. package/dist/index.mjs.br +0 -0
  51. package/dist/index.mjs.gz +0 -0
  52. package/dist/internals.d.ts +11 -1
  53. package/dist/internals.js +82 -82
  54. package/dist/internals.js.br +0 -0
  55. package/dist/internals.js.gz +0 -0
  56. package/dist/internals.mjs +3 -3
  57. package/dist/internals.mjs.br +0 -0
  58. package/dist/internals.mjs.gz +0 -0
  59. package/dist/{three.module-A6ACXOES.mjs → three.module-2R4ADL7W.mjs} +1 -1
  60. package/dist/three.module-2R4ADL7W.mjs.br +0 -0
  61. package/dist/three.module-2R4ADL7W.mjs.gz +0 -0
  62. package/dist/{three.module-IORLGCSX.js → three.module-LS32MK23.js} +1 -1
  63. package/dist/three.module-LS32MK23.js.br +0 -0
  64. package/dist/three.module-LS32MK23.js.gz +0 -0
  65. package/dist/viewer/index.d.ts +17 -0
  66. package/dist/viewer/index.js +20 -20
  67. package/dist/viewer/index.js.br +0 -0
  68. package/dist/viewer/index.js.gz +0 -0
  69. package/dist/viewer/index.mjs +4 -4
  70. package/dist/viewer/index.mjs.br +0 -0
  71. package/dist/viewer/index.mjs.gz +0 -0
  72. package/dist/{y-webrtc-F5IA7T5S.mjs → y-webrtc-6DZI5KUK.mjs} +1 -1
  73. package/dist/y-webrtc-6DZI5KUK.mjs.br +0 -0
  74. package/dist/y-webrtc-6DZI5KUK.mjs.gz +0 -0
  75. package/dist/{y-webrtc-6LID3XZO.js → y-webrtc-OTRGEYOH.js} +5 -5
  76. package/dist/y-webrtc-OTRGEYOH.js.br +0 -0
  77. package/dist/y-webrtc-OTRGEYOH.js.gz +0 -0
  78. package/dist/{y-websocket-HOY2HFMH.js → y-websocket-AWBYMAHL.js} +1 -1
  79. package/dist/y-websocket-AWBYMAHL.js.br +0 -0
  80. package/dist/y-websocket-AWBYMAHL.js.gz +0 -0
  81. package/dist/{y-websocket-VGLBV2HJ.mjs → y-websocket-VAKUVRQT.mjs} +1 -1
  82. package/dist/y-websocket-VAKUVRQT.mjs.br +0 -0
  83. package/dist/y-websocket-VAKUVRQT.mjs.gz +0 -0
  84. package/package.json +4 -3
  85. package/dist/AiChatPanel-3J6XGIJ4.mjs.br +0 -0
  86. package/dist/AiChatPanel-3J6XGIJ4.mjs.gz +0 -0
  87. package/dist/AiChatPanel-EGTSK4T5.js.br +0 -0
  88. package/dist/AiChatPanel-EGTSK4T5.js.gz +0 -0
  89. package/dist/Model3DScene-2V2GQ6FY.mjs.br +0 -0
  90. package/dist/Model3DScene-2V2GQ6FY.mjs.gz +0 -0
  91. package/dist/Model3DScene-Q3XEZSWV.js.br +0 -2
  92. package/dist/Model3DScene-Q3XEZSWV.js.gz +0 -0
  93. package/dist/UTIF-RPFRSSC5.mjs +0 -6400
  94. package/dist/UTIF-RPFRSSC5.mjs.br +0 -0
  95. package/dist/UTIF-RPFRSSC5.mjs.gz +0 -0
  96. package/dist/UTIF-XWCKRN6V.js +0 -6402
  97. package/dist/UTIF-XWCKRN6V.js.br +0 -0
  98. package/dist/UTIF-XWCKRN6V.js.gz +0 -0
  99. package/dist/chunk-34AXBLYH.mjs.br +0 -0
  100. package/dist/chunk-34AXBLYH.mjs.gz +0 -0
  101. package/dist/chunk-5RQTZ376.js.br +0 -0
  102. package/dist/chunk-5RQTZ376.js.gz +0 -0
  103. package/dist/chunk-6DZX6EAA.mjs.br +0 -0
  104. package/dist/chunk-6DZX6EAA.mjs.gz +0 -0
  105. package/dist/chunk-MQCBMO6A.js.br +0 -0
  106. package/dist/chunk-MQCBMO6A.js.gz +0 -0
  107. package/dist/chunk-QGM4M3NI.js.br +0 -0
  108. package/dist/chunk-QGM4M3NI.js.gz +0 -0
  109. package/dist/chunk-TYJZJJZ6.mjs +0 -208183
  110. package/dist/chunk-TYJZJJZ6.mjs.br +0 -0
  111. package/dist/chunk-TYJZJJZ6.mjs.gz +0 -0
  112. package/dist/chunk-WK6JBJCT.js +0 -208548
  113. package/dist/chunk-WK6JBJCT.js.br +0 -0
  114. package/dist/chunk-WK6JBJCT.js.gz +0 -0
  115. package/dist/chunk-ZK7UEEXE.mjs.br +0 -0
  116. package/dist/chunk-ZK7UEEXE.mjs.gz +0 -0
  117. package/dist/three.module-A6ACXOES.mjs.br +0 -0
  118. package/dist/three.module-A6ACXOES.mjs.gz +0 -0
  119. package/dist/three.module-IORLGCSX.js.br +0 -0
  120. package/dist/three.module-IORLGCSX.js.gz +0 -0
  121. package/dist/y-webrtc-6LID3XZO.js.br +0 -0
  122. package/dist/y-webrtc-6LID3XZO.js.gz +0 -0
  123. package/dist/y-webrtc-F5IA7T5S.mjs.br +0 -0
  124. package/dist/y-webrtc-F5IA7T5S.mjs.gz +0 -0
  125. package/dist/y-websocket-HOY2HFMH.js.br +0 -0
  126. package/dist/y-websocket-HOY2HFMH.js.gz +0 -0
  127. package/dist/y-websocket-VGLBV2HJ.mjs.br +0 -0
  128. package/dist/y-websocket-VGLBV2HJ.mjs.gz +0 -0
@@ -0,0 +1,3897 @@
1
+ import { __reExport } from './chunk-GDQSMJQZ.mjs';
2
+ import { SHAPE_PRESET_DEFS, VIEWER_SHORTCUT_REFERENCE, STROKE_DASH_OPTIONS as STROKE_DASH_OPTIONS$1, getAnimationInitialStyle as getAnimationInitialStyle$1, cloneHistorySnapshot as cloneHistorySnapshot$1, getImageFitStyle, getImageFillCounterTransform, getComputedFillStyle, getCropShapeClipPath as getCropShapeClipPath$1, getImageTilingStyle as getImageTilingStyle$1, mediaPlaybackAttributes, getOnlineVideoEmbed, ONLINE_VIDEO_IFRAME_SANDBOX, ONLINE_VIDEO_IFRAME_ALLOW, buildTextBlockStyle, toCssWritingMode, toCssTextOrientation, toCssVerticalDirection, buildTextBodyLayoutStyle, buildTextBody3DSceneStyle as buildTextBody3DSceneStyle$1, buildParagraphs, followingText, colorWithOpacity, normalizeHexColor, hexToRgbUnit, build3DExtrusionData as build3DExtrusionData$1, getComputedEffectStyle, getComputedStrokeStyle, getAnimationColorBaseStyle, isStrokeOnlyPresetElement, resolveShapeGeometry, normalizeStrokeDashType, getSvgStrokeDasharray, buildSvgGradientDef, buildStrokeOutline, getPresetShapeVectorGeometry, isWedgeCalloutPresetShape, svgGradientFillRef, svgLineCap, getCompoundLineOffsets, getCompoundLineWidths, canDrillDown, computeSelectionRect, DEFAULT_FONT_FAMILY as DEFAULT_FONT_FAMILY$1, cellStyleToCss, TABLE_CELL_TEXT_DEFAULTS, rectToCells, isCellInRect, tableContainerCss, buildChartViewModel, resolveChartKind, chartPreserveAspectRatio, buildNotesPdfBytes, buildTiledSlidesPdfBytes, buildPreviewAnimation, measurePreviewGeometry, buildMotionPathPreview, mediaFallbackVisual, scheduleMediaTrimAndFade, registerCrossSlideAudio, shouldShowMediaFullscreenStopButton, withInlineListDecorationDefaults, segmentStyleToCss, resolveAutoFitFontScale, applyUnderlineVariant, nestedTextDecorationStyle, scaleFontSizeForAutoFit, resolveFontKerning, resolveScriptFontSet, hasDistinctScriptFonts, authoredLetterSpacingPx, eastAsianBreaksForRun, pieceLetterSpacing, resolveMetricTrackingPx, hollowTextFillStyle, splitsUnderlineIntoWords, stripUnderlineDecoration, buildTabContext, TEXT_BUILD_ID_SEP, buildTextBuildSpec, textBuildSpanStyle, apply3dEffects as apply3dEffects$1, buildSubpathPaints, computeColumnBoundaries, getTableCellBandStyle as getTableCellBandStyle$1, parseDrawingColor, parseDrawingColorOpacity, getCellDiagonalBorders as getCellDiagonalBorders$1, cellRunStyle, getChartStylePalette, computeChartLegendLayout, resolveChartSvgDef, mediaFallbackIcon, mediaFallbackLabelKey, MEDIA_CHROME_ATTRIBUTE, convertOmmlToMathMl, sanitizeMathMl, tableCellCss, computeResizedColumnWidths, computeResizedRowHeight, DEFAULT_ROW_HEIGHT, buildRectPathGradient, buildRunTabLines, chartPartToAttrs, splitWordsForUnderline, splitRunForMetrics, splitEastAsianBreaks, splitRunByScriptFont } from 'ooxml-ui/pptx';
3
+ export { ACTION_BUTTON_PRESETS, DEFAULT_COLOR_CHANGE_TOLERANCE, INK_REPLAY_KEYFRAMES, MAX_ZOOM_SCALE, MIN_ZOOM_SCALE, SHAPE_QUICK_STYLES, SLIDE_TRANSITION_OPTIONS, TABLE_STYLE_PRESETS, applyColorChange, applyFormatToElement, applyReroutedConnectors, buildCacheKey, buildContentPartStrokes, buildCssGradientFromShapeStyle, buildInkGroupStrokes, buildSaveSlides, canvasToJpegData, clampCropValue, cloneSlide, cloneTemplateElementsBySlideId, colorWithOpacity, comparePresentation, computeHandoutLayout, computeSnapToShape, copyFormatFromElement, formatCommentTimestamp, generateNoteLineCount, getAriaLabel, getAriaRole, getAriaRoleDescription, getCachedResult, getCompoundLineOffsets, getCompoundLineWidths, getContentPartReplayStyles, getElementLabel, getElementTransform, getElementTransformWithoutRotation, getImageEffectsFilter, getImageEffectsOpacity, getInkReplayStyles, getPatternSvg, getPrintableArea, getShapeAdjustmentHandleDescriptors, getSvgStrokeDasharray, getTextCompensationTransform, hasCopyableFormat, isEditableTextElement, isImageTiled, isPpactionUrl, isTemplateElementId, isUrlSafe, makeCloneId, mergeCells, normalizeHexColor, normalizeStrokeDashType, openNativeEyeDropper, parsePpactionUrl, partitionTemplateElements, rerouteConnectorsForMovedElements, safeOpenUrl, sanitizeGradientStops, setCachedResult, splitCell, stopAllPersistentAudio, substituteFieldText } from 'ooxml-ui/pptx';
4
+ import * as pptx_star from 'ooxml-core/pptx';
5
+ import { clsx } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import React8, { useRef, useState, useEffect, useCallback, useMemo, useLayoutEffect } from 'react';
8
+ import { LuTriangle, LuPlus, LuMoveRight, LuMinus, LuDiamond, LuDatabase, LuCircle, LuSquare } from 'react-icons/lu';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+ import { translationsEn } from 'ooxml-ui/pptx/i18n';
11
+ import { useTranslation } from 'react-i18next';
12
+
13
+ function getAnimationInitialStyle(preset, nativeAnimation) {
14
+ return getAnimationInitialStyle$1(preset, nativeAnimation);
15
+ }
16
+
17
+ // ../core/dist/index.mjs
18
+ var dist_exports = {};
19
+ __reExport(dist_exports, pptx_star);
20
+ function cn(...inputs) {
21
+ return twMerge(clsx(inputs));
22
+ }
23
+ function cloneHistorySnapshot(snapshot) {
24
+ return cloneHistorySnapshot$1(snapshot);
25
+ }
26
+ var DEFAULT_CANVAS_WIDTH = 1280;
27
+ var DEFAULT_CANVAS_HEIGHT = 720;
28
+ var MIN_ELEMENT_SIZE = 12;
29
+ var EMU_PER_PX = 9525;
30
+ var DEFAULT_BODY_INSET_TB_PX = 45720 / 9525;
31
+ var DEFAULT_TEXT_COLOR = "#111827";
32
+ var DEFAULT_FILL_COLOR = "#3b82f6";
33
+ var DEFAULT_STROKE_COLOR = "#1f2937";
34
+ var DEFAULT_FONT_FAMILY = '"Segoe UI", "Helvetica Neue", Arial, sans-serif';
35
+ var DEFAULT_TEXT_FONT_SIZE = 24;
36
+ var HYPERLINK_COLOR = "#0563C1";
37
+ var DEFAULT_TABLE_ROWS = 3;
38
+ var DEFAULT_TABLE_COLUMNS = 3;
39
+ var ZOOM_TO_SELECTION_PADDING = 96;
40
+ var GRID_SIZE = 8;
41
+ var SLIDE_NAV_THUMBNAIL_WIDTH = 156;
42
+ var UNGROUPED_SECTION_ID = "__ungrouped__";
43
+ var DEFAULT_SECTION_GROUP_ID = "default";
44
+ var TOOLBAR_SECTIONS = [
45
+ { id: "file", labelKey: "pptx.ribbon.tab.file" },
46
+ { id: "home", labelKey: "pptx.ribbon.tab.home" },
47
+ { id: "insert", labelKey: "pptx.ribbon.tab.insert" },
48
+ { id: "draw", labelKey: "pptx.ribbon.tab.draw" },
49
+ { id: "design", labelKey: "pptx.ribbon.tab.design" },
50
+ { id: "transitions", labelKey: "pptx.ribbon.tab.transitions" },
51
+ { id: "animations", labelKey: "pptx.ribbon.tab.animations" },
52
+ { id: "slideShow", labelKey: "pptx.ribbon.tab.slideShow" },
53
+ { id: "record", labelKey: "pptx.ribbon.tab.record" },
54
+ { id: "review", labelKey: "pptx.ribbon.tab.review" },
55
+ { id: "view", labelKey: "pptx.ribbon.tab.view" },
56
+ { id: "help", labelKey: "pptx.ribbon.tab.help" }
57
+ ];
58
+ var SHORTCUT_REFERENCE_ITEMS = VIEWER_SHORTCUT_REFERENCE;
59
+ var ANIMATION_PRESET_OPTIONS = [
60
+ { value: "fadeIn", label: "Fade In", i18nKey: "pptx.animation.preset.fadeIn" },
61
+ { value: "flyIn", label: "Fly In", i18nKey: "pptx.animation.preset.flyIn" },
62
+ { value: "zoomIn", label: "Zoom In", i18nKey: "pptx.animation.preset.zoomIn" },
63
+ { value: "fadeOut", label: "Fade Out", i18nKey: "pptx.animation.preset.fadeOut" },
64
+ { value: "flyOut", label: "Fly Out", i18nKey: "pptx.animation.preset.flyOut" },
65
+ { value: "zoomOut", label: "Zoom Out", i18nKey: "pptx.animation.preset.zoomOut" },
66
+ { value: "spin", label: "Spin", i18nKey: "pptx.animation.preset.spin" },
67
+ { value: "pulse", label: "Pulse", i18nKey: "pptx.animation.preset.pulse" },
68
+ { value: "colorWave", label: "Color Wave", i18nKey: "pptx.animation.preset.colorWave" },
69
+ { value: "bounce", label: "Bounce", i18nKey: "pptx.animation.preset.bounce" },
70
+ { value: "flash", label: "Flash", i18nKey: "pptx.animation.preset.flash" },
71
+ { value: "growShrink", label: "Grow/Shrink", i18nKey: "pptx.animation.preset.growShrink" },
72
+ { value: "teeter", label: "Teeter", i18nKey: "pptx.animation.preset.teeter" }
73
+ ];
74
+ var GLYPH_COMPONENTS = {
75
+ square: LuSquare,
76
+ circle: LuCircle,
77
+ database: LuDatabase,
78
+ diamond: LuDiamond,
79
+ minus: LuMinus,
80
+ moveRight: LuMoveRight,
81
+ plus: LuPlus,
82
+ triangle: LuTriangle
83
+ };
84
+ var SHAPE_PRESETS = SHAPE_PRESET_DEFS.map(
85
+ (def) => ({
86
+ type: def.type,
87
+ label: def.label,
88
+ i18nKey: def.i18nKey,
89
+ icon: React8.createElement(GLYPH_COMPONENTS[def.glyph], {
90
+ className: def.glyphClass ? `w-3.5 h-3.5 ${def.glyphClass}` : "w-3.5 h-3.5"
91
+ })
92
+ })
93
+ );
94
+ var CONNECTOR_ARROW_OPTIONS = [
95
+ { value: "none", label: "None", i18nKey: "pptx.arrowhead.none" },
96
+ { value: "triangle", label: "Triangle", i18nKey: "pptx.arrowhead.triangle" },
97
+ { value: "stealth", label: "Stealth", i18nKey: "pptx.arrowhead.stealth" },
98
+ { value: "diamond", label: "Diamond", i18nKey: "pptx.arrowhead.diamond" },
99
+ { value: "oval", label: "Oval", i18nKey: "pptx.arrowhead.oval" },
100
+ { value: "arrow", label: "Open Arrow", i18nKey: "pptx.arrowhead.openArrow" }
101
+ ];
102
+ var ARROW_SIZE_OPTIONS = [
103
+ { value: "sm", label: "Small", i18nKey: "pptx.connectorOptions.sizeSmall" },
104
+ { value: "med", label: "Medium", i18nKey: "pptx.connectorOptions.sizeMedium" },
105
+ { value: "lg", label: "Large", i18nKey: "pptx.connectorOptions.sizeLarge" }
106
+ ];
107
+ var STROKE_DASH_OPTIONS = STROKE_DASH_OPTIONS$1;
108
+
109
+ // src/viewer/utils/shape-types.tsx
110
+ function getShapeType(shapeType) {
111
+ if (!shapeType) {
112
+ return "rect";
113
+ }
114
+ const normalized = shapeType.toLowerCase();
115
+ if (normalized === "rect") {
116
+ return "rect";
117
+ }
118
+ if (normalized === "roundrect") {
119
+ return "roundRect";
120
+ }
121
+ if (normalized === "ellipse" || normalized === "oval") {
122
+ return "ellipse";
123
+ }
124
+ if (normalized === "cylinder" || normalized === "can") {
125
+ return "cylinder";
126
+ }
127
+ if (normalized === "triangle") {
128
+ return "triangle";
129
+ }
130
+ if (normalized === "rttriangle") {
131
+ return "rtTriangle";
132
+ }
133
+ if (normalized === "diamond") {
134
+ return "diamond";
135
+ }
136
+ if (normalized === "line") {
137
+ return "line";
138
+ }
139
+ if (normalized === "parallelogram") {
140
+ return "parallelogram";
141
+ }
142
+ if (normalized === "trapezoid") {
143
+ return "trapezoid";
144
+ }
145
+ if (normalized === "pentagon") {
146
+ return "pentagon";
147
+ }
148
+ if (normalized === "hexagon") {
149
+ return "hexagon";
150
+ }
151
+ if (normalized === "octagon") {
152
+ return "octagon";
153
+ }
154
+ if (normalized === "chevron") {
155
+ return "chevron";
156
+ }
157
+ if (normalized === "star5") {
158
+ return "star5";
159
+ }
160
+ if (normalized === "star6") {
161
+ return "star6";
162
+ }
163
+ if (normalized === "star8") {
164
+ return "star8";
165
+ }
166
+ if (normalized === "plus") {
167
+ return "plus";
168
+ }
169
+ if (normalized === "heart") {
170
+ return "heart";
171
+ }
172
+ if (normalized === "cloud") {
173
+ return "cloud";
174
+ }
175
+ if (normalized === "sun") {
176
+ return "sun";
177
+ }
178
+ if (normalized === "moon") {
179
+ return "moon";
180
+ }
181
+ if (normalized === "pie") {
182
+ return "pie";
183
+ }
184
+ if (normalized === "plaque") {
185
+ return "plaque";
186
+ }
187
+ if (normalized === "teardrop") {
188
+ return "teardrop";
189
+ }
190
+ if (normalized === "rtarrow" || normalized === "rightarrow") {
191
+ return "rtArrow";
192
+ }
193
+ if (normalized === "leftarrow") {
194
+ return "leftArrow";
195
+ }
196
+ if (normalized === "uparrow") {
197
+ return "upArrow";
198
+ }
199
+ if (normalized === "downarrow") {
200
+ return "downArrow";
201
+ }
202
+ if (normalized.includes("connector")) {
203
+ return "connector";
204
+ }
205
+ if (normalized === "connector") {
206
+ return "connector";
207
+ }
208
+ return "rect";
209
+ }
210
+ function isConnectorOrLineElement(element) {
211
+ if (element.type === "connector") {
212
+ return true;
213
+ }
214
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
215
+ return false;
216
+ }
217
+ const st = getShapeType(element.shapeType);
218
+ return st === "connector" || st === "line" || element.shapeType === "line";
219
+ }
220
+ function getImageMaskStyle(element) {
221
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
222
+ return void 0;
223
+ }
224
+ const geometry = resolveShapeGeometry(element);
225
+ if (geometry.kind === "borderRadius") {
226
+ return { borderRadius: geometry.radius };
227
+ }
228
+ return geometry.kind === "clipPath" ? { clipPath: geometry.clipPath } : void 0;
229
+ }
230
+ function getImageRenderStyle(element) {
231
+ const fit = getImageFitStyle(element);
232
+ const counterTransform = getImageFillCounterTransform(element, Boolean(fit.transform));
233
+ return {
234
+ // A translated/scaled crop, or a rotWithShape=false counter-transform,
235
+ // must be masked by the stationary wrapper, not by the moving/counter-
236
+ // transformed bitmap itself.
237
+ ...fit.transform || counterTransform ? {} : getImageMaskStyle(element) || {},
238
+ ...fit,
239
+ ...counterTransform ? { transform: counterTransform, transformOrigin: "center" } : {}
240
+ };
241
+ }
242
+ function getImageSurfaceMaskStyle(element) {
243
+ const mask = getImageMaskStyle(element);
244
+ return {
245
+ overflow: "hidden",
246
+ borderRadius: mask?.borderRadius,
247
+ clipPath: mask?.clipPath ?? getCropShapeClipPath(element)
248
+ };
249
+ }
250
+ function getImageSurfaceStyle(element) {
251
+ const fill = (0, dist_exports.hasShapeProperties)(element) ? getComputedFillStyle(element) : void 0;
252
+ return {
253
+ position: "absolute",
254
+ inset: 0,
255
+ ...getImageSurfaceMaskStyle(element),
256
+ backgroundColor: fill?.backgroundColor ?? "transparent",
257
+ backgroundImage: fill?.backgroundImage,
258
+ backgroundRepeat: fill?.backgroundRepeat,
259
+ backgroundSize: fill?.backgroundSize,
260
+ backgroundPosition: fill?.backgroundPosition
261
+ };
262
+ }
263
+ function getCropShapeClipPath(element) {
264
+ if (!(0, dist_exports.isImageLikeElement)(element)) {
265
+ return void 0;
266
+ }
267
+ return getCropShapeClipPath$1(element.cropShape, element.width, element.height);
268
+ }
269
+ function getImageTilingStyle(element, nativeSize) {
270
+ return getImageTilingStyle$1(element, nativeSize);
271
+ }
272
+
273
+ // src/viewer/utils/geometry-image.ts
274
+ function shouldRenderFallbackLabel(element, isTextElement) {
275
+ if (isTextElement) {
276
+ return false;
277
+ }
278
+ if (element.type === "shape" || element.type === "connector") {
279
+ return false;
280
+ }
281
+ if (element.type === "picture" || element.type === "image") {
282
+ return false;
283
+ }
284
+ if (element.type === "table") {
285
+ return false;
286
+ }
287
+ if (element.type === "media") {
288
+ return false;
289
+ }
290
+ if (element.type === "contentPart") {
291
+ return false;
292
+ }
293
+ if (element.type === "model3d") {
294
+ return false;
295
+ }
296
+ if (element.type === "ole") {
297
+ const previewData = element.previewImageData ?? element.previewImage;
298
+ return !previewData;
299
+ }
300
+ return element.type === "chart" || element.type === "smartArt" || element.type === "unknown";
301
+ }
302
+ function ensureArrayValue(value) {
303
+ if (!value) {
304
+ return [];
305
+ }
306
+ return Array.isArray(value) ? value : [value];
307
+ }
308
+ function buildTrimFragment(element) {
309
+ const start = element.trimStartMs;
310
+ if (start === void 0 || start <= 0) {
311
+ return "";
312
+ }
313
+ return `#t=${(start / 1e3).toFixed(3)}`;
314
+ }
315
+ function useMediaMetadataExtraction(mediaRef, element) {
316
+ const [metadata, setMetadata] = useState(element.metadata);
317
+ useEffect(() => {
318
+ const el = mediaRef.current;
319
+ if (!el) {
320
+ return;
321
+ }
322
+ if (element.metadata?.duration !== void 0) {
323
+ setMetadata(element.metadata);
324
+ return;
325
+ }
326
+ const extract = () => {
327
+ if (!Number.isFinite(el.duration) || el.duration === 0) {
328
+ return;
329
+ }
330
+ const meta = {
331
+ duration: el.duration
332
+ };
333
+ if (el instanceof HTMLVideoElement) {
334
+ if (el.videoWidth > 0) {
335
+ meta.videoWidth = el.videoWidth;
336
+ }
337
+ if (el.videoHeight > 0) {
338
+ meta.videoHeight = el.videoHeight;
339
+ }
340
+ }
341
+ const sourceEl = el.querySelector("source");
342
+ if (sourceEl?.type) {
343
+ meta.codecInfo = sourceEl.type;
344
+ }
345
+ element.metadata = meta;
346
+ setMetadata(meta);
347
+ };
348
+ el.addEventListener("loadedmetadata", extract);
349
+ if (el.readyState >= 1) {
350
+ extract();
351
+ }
352
+ return () => {
353
+ el.removeEventListener("loadedmetadata", extract);
354
+ };
355
+ }, [mediaRef, element]);
356
+ return metadata;
357
+ }
358
+ function CaptionTrackRenderer({
359
+ captionTracks
360
+ }) {
361
+ return /* @__PURE__ */ jsx(Fragment, { children: captionTracks.map((track) => {
362
+ const trackSrc = track.src ?? (track.content ? `data:text/vtt;charset=utf-8,${encodeURIComponent(track.content)}` : void 0);
363
+ if (!trackSrc) {
364
+ return null;
365
+ }
366
+ return /* @__PURE__ */ jsx(
367
+ "track",
368
+ {
369
+ kind: track.kind,
370
+ label: track.label,
371
+ srcLang: track.language,
372
+ src: trackSrc,
373
+ default: track.isDefault
374
+ },
375
+ track.id
376
+ );
377
+ }) });
378
+ }
379
+ function VideoWithMetadata({
380
+ element,
381
+ mediaRef,
382
+ onPlay,
383
+ dataUrl,
384
+ trimFragment,
385
+ mediaMimeType,
386
+ posterUrl,
387
+ shouldLoop,
388
+ shouldAutoPlay,
389
+ isFullScreen,
390
+ isPresentationMode,
391
+ showTransport
392
+ }) {
393
+ useMediaMetadataExtraction(mediaRef, element);
394
+ const captionTracks = element.captionTracks ?? [];
395
+ return /* @__PURE__ */ jsxs(
396
+ "video",
397
+ {
398
+ ref: mediaRef,
399
+ className: "w-full h-full pointer-events-auto",
400
+ style: isFullScreen ? { objectFit: "cover" } : getImageFitStyle(element),
401
+ controls: showTransport,
402
+ preload: "metadata",
403
+ playsInline: true,
404
+ autoPlay: shouldAutoPlay,
405
+ muted: shouldAutoPlay && !isPresentationMode,
406
+ poster: posterUrl,
407
+ loop: shouldLoop,
408
+ onPlay,
409
+ crossOrigin: captionTracks.length > 0 ? "anonymous" : void 0,
410
+ children: [
411
+ /* @__PURE__ */ jsx("source", { src: `${dataUrl}${trimFragment}`, type: mediaMimeType || "video/mp4" }),
412
+ captionTracks.length > 0 && /* @__PURE__ */ jsx(CaptionTrackRenderer, { captionTracks })
413
+ ]
414
+ }
415
+ );
416
+ }
417
+ function AudioWithMetadata({
418
+ element,
419
+ mediaRef,
420
+ onPlay,
421
+ dataUrl,
422
+ trimFragment,
423
+ mediaMimeType,
424
+ shouldLoop,
425
+ shouldAutoPlay,
426
+ showTransport
427
+ }) {
428
+ useMediaMetadataExtraction(mediaRef, element);
429
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center p-2 pointer-events-auto", children: /* @__PURE__ */ jsx(
430
+ "audio",
431
+ {
432
+ ref: mediaRef,
433
+ className: "w-full",
434
+ controls: showTransport,
435
+ preload: "metadata",
436
+ autoPlay: shouldAutoPlay,
437
+ loop: shouldLoop,
438
+ onPlay,
439
+ children: /* @__PURE__ */ jsx("source", { src: `${dataUrl}${trimFragment}`, type: mediaMimeType || "audio/mpeg" })
440
+ }
441
+ ) });
442
+ }
443
+ function PresentationMediaController({
444
+ element,
445
+ isPresentationMode,
446
+ shouldAutoPlay,
447
+ resolvedDataUrl,
448
+ isFullScreen,
449
+ onPlayStateChange,
450
+ children
451
+ }) {
452
+ const { t } = useTranslation();
453
+ const mediaRef = useRef(null);
454
+ const [isMediaPlaying, setIsMediaPlaying] = useState(false);
455
+ const trimStartSec = element.trimStartMs !== void 0 ? element.trimStartMs / 1e3 : 0;
456
+ const hideWhenNotPlaying = isPresentationMode && element.hideWhenNotPlaying === true;
457
+ const { volume: playbackVolume, playbackRate } = mediaPlaybackAttributes({
458
+ loop: element.loop,
459
+ volume: element.volume,
460
+ playbackSpeed: element.playbackSpeed
461
+ });
462
+ useEffect(() => {
463
+ const el = mediaRef.current;
464
+ if (el) {
465
+ el.volume = playbackVolume;
466
+ }
467
+ }, [playbackVolume]);
468
+ useEffect(() => {
469
+ const el = mediaRef.current;
470
+ if (el) {
471
+ el.playbackRate = playbackRate;
472
+ }
473
+ }, [playbackRate]);
474
+ useEffect(() => {
475
+ const el = mediaRef.current;
476
+ if (!el) {
477
+ return;
478
+ }
479
+ const handlePlay = () => {
480
+ setIsMediaPlaying(true);
481
+ onPlayStateChange?.(true);
482
+ };
483
+ const handlePause = () => {
484
+ setIsMediaPlaying(false);
485
+ onPlayStateChange?.(false);
486
+ };
487
+ const handleEnded = () => {
488
+ setIsMediaPlaying(false);
489
+ onPlayStateChange?.(false);
490
+ };
491
+ el.addEventListener("play", handlePlay);
492
+ el.addEventListener("pause", handlePause);
493
+ el.addEventListener("ended", handleEnded);
494
+ return () => {
495
+ el.removeEventListener("play", handlePlay);
496
+ el.removeEventListener("pause", handlePause);
497
+ el.removeEventListener("ended", handleEnded);
498
+ };
499
+ }, [onPlayStateChange]);
500
+ useEffect(() => {
501
+ const el = mediaRef.current;
502
+ if (!el || !isPresentationMode) {
503
+ return;
504
+ }
505
+ return scheduleMediaTrimAndFade(el, {
506
+ trimStartMs: element.trimStartMs,
507
+ trimEndMs: element.trimEndMs,
508
+ fadeInDuration: element.fadeInDuration,
509
+ fadeOutDuration: element.fadeOutDuration,
510
+ volume: playbackVolume
511
+ });
512
+ }, [
513
+ isPresentationMode,
514
+ element.trimStartMs,
515
+ element.trimEndMs,
516
+ element.fadeInDuration,
517
+ element.fadeOutDuration,
518
+ playbackVolume
519
+ ]);
520
+ useEffect(() => {
521
+ if (!isPresentationMode || !shouldAutoPlay) {
522
+ return;
523
+ }
524
+ if (element.playAcrossSlides && element.mediaType === "audio") {
525
+ const dataUrl = element.mediaData ?? resolvedDataUrl;
526
+ if (registerCrossSlideAudio(element, dataUrl)) {
527
+ return;
528
+ }
529
+ }
530
+ const el = mediaRef.current;
531
+ if (!el) {
532
+ return;
533
+ }
534
+ const timer = window.setTimeout(() => {
535
+ if (trimStartSec > 0) {
536
+ el.currentTime = trimStartSec;
537
+ }
538
+ void el.play().catch(() => {
539
+ });
540
+ }, 100);
541
+ return () => window.clearTimeout(timer);
542
+ }, [isPresentationMode, shouldAutoPlay, element, resolvedDataUrl, trimStartSec]);
543
+ const wrapperStyle = hideWhenNotPlaying ? {
544
+ opacity: isMediaPlaying ? 1 : 0,
545
+ transition: "opacity 0.3s ease",
546
+ pointerEvents: isMediaPlaying ? "auto" : "none"
547
+ } : {};
548
+ const handleStopFullScreen = useCallback(() => {
549
+ const el = mediaRef.current;
550
+ if (el && !el.paused) {
551
+ el.pause();
552
+ }
553
+ }, []);
554
+ const noopOnPlay = useCallback(() => {
555
+ }, []);
556
+ return /* @__PURE__ */ jsxs("div", { className: "w-full h-full", style: wrapperStyle, children: [
557
+ children({ mediaRef, onPlay: noopOnPlay, isMediaPlaying }),
558
+ shouldShowMediaFullscreenStopButton({
559
+ fullScreen: isFullScreen,
560
+ presenting: isPresentationMode,
561
+ playing: isMediaPlaying
562
+ }) && /* @__PURE__ */ jsx(
563
+ "button",
564
+ {
565
+ type: "button",
566
+ className: "absolute bottom-3 right-3 z-30 rounded-full bg-black/50 hover:bg-black/70 text-white/80 hover:text-white p-2 transition-colors pointer-events-auto",
567
+ onClick: handleStopFullScreen,
568
+ "aria-label": t("pptx.media.stopFullscreenAria"),
569
+ children: /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", stroke: "none", children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1" }) })
570
+ }
571
+ )
572
+ ] });
573
+ }
574
+ function label(key) {
575
+ return key === void 0 ? void 0 : translationsEn[key];
576
+ }
577
+ function FallbackIcon({
578
+ paths,
579
+ size,
580
+ className
581
+ }) {
582
+ if (paths.length === 0) {
583
+ return null;
584
+ }
585
+ return /* @__PURE__ */ jsx(
586
+ "svg",
587
+ {
588
+ width: size,
589
+ height: size,
590
+ viewBox: "0 0 24 24",
591
+ fill: "none",
592
+ stroke: "currentColor",
593
+ strokeWidth: "1.5",
594
+ className,
595
+ children: paths.map((d) => /* @__PURE__ */ jsx("path", { d }, d))
596
+ }
597
+ );
598
+ }
599
+ function Badge({
600
+ visual,
601
+ element
602
+ }) {
603
+ if (visual.badge === "none") {
604
+ return null;
605
+ }
606
+ const isMissing = visual.badge === "missing";
607
+ return /* @__PURE__ */ jsxs(
608
+ "div",
609
+ {
610
+ className: `absolute inset-0 flex flex-col items-center justify-center ${isMissing ? "gap-1" : ""}`,
611
+ ...{ [MEDIA_CHROME_ATTRIBUTE]: visual.badge },
612
+ children: [
613
+ /* @__PURE__ */ jsx(
614
+ FallbackIcon,
615
+ {
616
+ paths: mediaFallbackIcon(visual, element.mediaType),
617
+ size: isMissing ? 32 : 48,
618
+ className: isMissing ? "text-white/60" : "text-white/80 drop-shadow-md"
619
+ }
620
+ ),
621
+ isMissing && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/60", children: label(mediaFallbackLabelKey(visual, element.mediaType)) })
622
+ ]
623
+ }
624
+ );
625
+ }
626
+ function Placeholder({
627
+ visual,
628
+ element
629
+ }) {
630
+ if (visual.placeholder === "none") {
631
+ return null;
632
+ }
633
+ const isMissing = visual.placeholder === "missing";
634
+ return /* @__PURE__ */ jsxs(
635
+ "div",
636
+ {
637
+ className: `w-full h-full flex flex-col items-center justify-center gap-1 pointer-events-none rounded ${isMissing ? "bg-black/30 border border-dashed border-white/20" : element.mediaType === "audio" ? "bg-black/10" : "bg-black/20"}`,
638
+ ...{ [MEDIA_CHROME_ATTRIBUTE]: visual.placeholder },
639
+ children: [
640
+ /* @__PURE__ */ jsx(
641
+ FallbackIcon,
642
+ {
643
+ paths: mediaFallbackIcon(visual, element.mediaType),
644
+ size: isMissing ? 36 : element.mediaType === "audio" ? 24 : 32,
645
+ className: isMissing ? "text-white/50" : "text-white/70"
646
+ }
647
+ ),
648
+ /* @__PURE__ */ jsx("span", { className: `text-[10px] ${isMissing ? "text-white/50" : "text-white/70"}`, children: label(mediaFallbackLabelKey(visual, element.mediaType)) })
649
+ ]
650
+ }
651
+ );
652
+ }
653
+ function renderMediaFallback({
654
+ element,
655
+ posterUrl,
656
+ surface
657
+ }) {
658
+ const visual = mediaFallbackVisual(surface, {
659
+ hasPoster: Boolean(posterUrl),
660
+ missing: element.mediaMissing === true
661
+ });
662
+ if (visual.poster && posterUrl) {
663
+ return /* @__PURE__ */ jsxs("div", { className: "w-full h-full relative pointer-events-none", children: [
664
+ /* @__PURE__ */ jsx(
665
+ "img",
666
+ {
667
+ src: posterUrl,
668
+ alt: visual.dimPoster ? translationsEn["pptx.media.posterAlt"] : translationsEn["pptx.media.videoPosterAlt"],
669
+ className: `w-full h-full${visual.dimPoster ? " opacity-50" : ""}`,
670
+ style: getImageFitStyle(element)
671
+ }
672
+ ),
673
+ /* @__PURE__ */ jsx(Badge, { visual, element })
674
+ ] });
675
+ }
676
+ return /* @__PURE__ */ jsx(Placeholder, { visual, element });
677
+ }
678
+ function renderMediaElement(element, mediaDataUrls, options) {
679
+ if (element.type !== "media") {
680
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.media.title"] });
681
+ }
682
+ const mediaType = element.mediaType;
683
+ const mediaPath = element.mediaPath;
684
+ const mediaMimeType = element.mediaMimeType;
685
+ const dataUrl = element.mediaData ?? (mediaPath ? mediaDataUrls.get(mediaPath) : void 0);
686
+ const trimFragment = buildTrimFragment(element);
687
+ const posterUrl = element.posterFrameData ?? void 0;
688
+ const shouldLoop = mediaPlaybackAttributes(element).loop;
689
+ const shouldAutoPlay = options?.autoPlay === true || element.autoPlay === true;
690
+ const isFullScreen = options?.fullScreen === true;
691
+ const isPresentationMode = options?.isPresentationMode === true;
692
+ const showTransport = options?.showTransport ?? !isPresentationMode;
693
+ const surface = {
694
+ presenting: isPresentationMode,
695
+ preview: options?.preview === true
696
+ };
697
+ if (element.mediaMissing) {
698
+ return renderMediaFallback({ element, posterUrl, surface });
699
+ }
700
+ if (mediaType === "video") {
701
+ const onlineVideo = getOnlineVideoEmbed(element);
702
+ if (onlineVideo) {
703
+ return /* @__PURE__ */ jsx(
704
+ "iframe",
705
+ {
706
+ src: onlineVideo.embedUrl,
707
+ title: translationsEn["pptx.media.onlineVideoTitle"],
708
+ className: "w-full h-full border-0 pointer-events-auto",
709
+ allow: ONLINE_VIDEO_IFRAME_ALLOW,
710
+ allowFullScreen: true,
711
+ sandbox: ONLINE_VIDEO_IFRAME_SANDBOX
712
+ }
713
+ );
714
+ }
715
+ if (dataUrl) {
716
+ return /* @__PURE__ */ jsx(
717
+ PresentationMediaController,
718
+ {
719
+ element,
720
+ isPresentationMode,
721
+ shouldAutoPlay,
722
+ resolvedDataUrl: dataUrl,
723
+ isFullScreen,
724
+ onPlayStateChange: options?.onPlayStateChange,
725
+ children: ({ mediaRef, onPlay }) => /* @__PURE__ */ jsx(
726
+ VideoWithMetadata,
727
+ {
728
+ element,
729
+ mediaRef,
730
+ onPlay,
731
+ dataUrl,
732
+ trimFragment,
733
+ mediaMimeType,
734
+ posterUrl,
735
+ shouldLoop,
736
+ shouldAutoPlay,
737
+ isFullScreen,
738
+ isPresentationMode,
739
+ showTransport
740
+ }
741
+ )
742
+ }
743
+ );
744
+ }
745
+ return renderMediaFallback({ element, posterUrl, surface });
746
+ }
747
+ if (mediaType === "audio") {
748
+ if (dataUrl) {
749
+ return /* @__PURE__ */ jsx(
750
+ PresentationMediaController,
751
+ {
752
+ element,
753
+ isPresentationMode,
754
+ shouldAutoPlay,
755
+ resolvedDataUrl: dataUrl,
756
+ isFullScreen: false,
757
+ onPlayStateChange: options?.onPlayStateChange,
758
+ children: ({ mediaRef, onPlay }) => /* @__PURE__ */ jsx(
759
+ AudioWithMetadata,
760
+ {
761
+ element,
762
+ mediaRef,
763
+ onPlay,
764
+ dataUrl,
765
+ trimFragment,
766
+ mediaMimeType,
767
+ shouldLoop,
768
+ shouldAutoPlay,
769
+ showTransport
770
+ }
771
+ )
772
+ }
773
+ );
774
+ }
775
+ return renderMediaFallback({ element, posterUrl, surface });
776
+ }
777
+ if (surface.presenting || surface.preview) {
778
+ return null;
779
+ }
780
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.media.title"] });
781
+ }
782
+ function getTextStyleForElement(element, fallbackColor) {
783
+ return buildTextBlockStyle(element, { fallbackColor });
784
+ }
785
+ function getTextLayoutStyle(element) {
786
+ if (!(0, dist_exports.hasTextProperties)(element)) {
787
+ return {};
788
+ }
789
+ const writingMode = toCssWritingMode(element.textStyle?.textDirection);
790
+ const textOrientation = toCssTextOrientation(element.textStyle?.textDirection);
791
+ const verticalDirection = toCssVerticalDirection(element.textStyle?.textDirection);
792
+ const textWrapNone = element.textStyle?.textWrap === "none";
793
+ const shapeAutoFitTextBox = element.textStyle?.autoFitMode === "shrink" && (element.type === "text" || element.locks?.txBox === true);
794
+ const hasParagraphIndents = (0, dist_exports.hasTextProperties)(element) && element.paragraphIndents && element.paragraphIndents.length > 0;
795
+ const paragraphMarginLeft = element.textStyle?.paragraphMarginLeft;
796
+ const paragraphIndent = element.textStyle?.paragraphIndent;
797
+ const marginLeft = !hasParagraphIndents && typeof paragraphMarginLeft === "number" && paragraphMarginLeft !== 0 ? paragraphMarginLeft : void 0;
798
+ const textIndent = !hasParagraphIndents && typeof paragraphIndent === "number" && paragraphIndent !== 0 ? paragraphIndent : void 0;
799
+ const bodyTop = element.textStyle?.bodyInsetTop ?? DEFAULT_BODY_INSET_TB_PX;
800
+ const bodyBottom = element.textStyle?.bodyInsetBottom ?? DEFAULT_BODY_INSET_TB_PX;
801
+ const bodyLayoutStyle = buildTextBodyLayoutStyle(element);
802
+ return {
803
+ ...bodyLayoutStyle,
804
+ // Body inset only. Paragraph spacing (spcBef/spcAft) is applied
805
+ // per-paragraph by the paragraph renderer, not collapsed here.
806
+ paddingTop: bodyTop,
807
+ paddingBottom: bodyBottom,
808
+ writingMode,
809
+ textOrientation,
810
+ direction: verticalDirection ?? bodyLayoutStyle.direction,
811
+ marginLeft: marginLeft ?? bodyLayoutStyle.marginLeft,
812
+ textIndent,
813
+ ...shapeAutoFitTextBox ? {
814
+ width: textWrapNone ? "max-content" : "100%",
815
+ minWidth: "100%",
816
+ height: "max-content",
817
+ minHeight: "100%",
818
+ whiteSpace: "pre-wrap",
819
+ wordBreak: textWrapNone ? "normal" : "break-word",
820
+ overflow: "visible"
821
+ } : {},
822
+ ...textWrapNone ? {
823
+ whiteSpace: "nowrap",
824
+ overflow: "visible"
825
+ } : {}
826
+ };
827
+ }
828
+ function buildTextBody3DSceneStyle(textStyle, elementSize) {
829
+ return buildTextBody3DSceneStyle$1(textStyle, elementSize);
830
+ }
831
+
832
+ // src/viewer/utils/text-warp-css.tsx
833
+ function getTextWarpStyle(textStyle) {
834
+ const preset = textStyle?.textWarpPreset;
835
+ if (!preset || preset === "textNoShape" || preset === "textPlain") {
836
+ return void 0;
837
+ }
838
+ switch (preset) {
839
+ // Arch shapes
840
+ case "textArchUp":
841
+ case "textArchUpPour":
842
+ return {
843
+ transform: "perspective(400px) rotateX(-12deg)",
844
+ transformOrigin: "center bottom"
845
+ };
846
+ case "textArchDown":
847
+ case "textArchDownPour":
848
+ return {
849
+ transform: "perspective(400px) rotateX(12deg)",
850
+ transformOrigin: "center top"
851
+ };
852
+ // Button shapes (concave/convex)
853
+ case "textButton":
854
+ case "textButtonPour":
855
+ return {
856
+ transform: "perspective(500px) rotateX(8deg)",
857
+ transformOrigin: "center top"
858
+ };
859
+ // Chevron / arrow
860
+ case "textChevron":
861
+ case "textChevronInverted":
862
+ return {
863
+ transform: "perspective(600px) rotateY(6deg)",
864
+ transformOrigin: "center center"
865
+ };
866
+ // Circle
867
+ case "textCircle":
868
+ case "textCirclePour":
869
+ return {
870
+ transform: "perspective(350px) rotateX(-5deg) rotateY(5deg)",
871
+ borderRadius: "50%"
872
+ };
873
+ // Slant
874
+ case "textSlantUp":
875
+ return {
876
+ transform: "perspective(500px) rotateY(8deg) skewY(-4deg)",
877
+ transformOrigin: "left center"
878
+ };
879
+ case "textSlantDown":
880
+ return {
881
+ transform: "perspective(500px) rotateY(-8deg) skewY(4deg)",
882
+ transformOrigin: "right center"
883
+ };
884
+ // Fade
885
+ case "textFadeUp":
886
+ return {
887
+ transform: "perspective(400px) rotateX(-10deg)",
888
+ transformOrigin: "center bottom"
889
+ };
890
+ case "textFadeDown":
891
+ return {
892
+ transform: "perspective(400px) rotateX(10deg)",
893
+ transformOrigin: "center top"
894
+ };
895
+ case "textFadeLeft":
896
+ return {
897
+ transform: "perspective(400px) rotateY(10deg)",
898
+ transformOrigin: "right center"
899
+ };
900
+ case "textFadeRight":
901
+ return {
902
+ transform: "perspective(400px) rotateY(-10deg)",
903
+ transformOrigin: "left center"
904
+ };
905
+ // Inflate / Deflate
906
+ case "textInflate":
907
+ case "textInflateBottom":
908
+ case "textInflateTop":
909
+ return {
910
+ transform: "scaleY(1.15) scaleX(1.05)",
911
+ transformOrigin: "center center"
912
+ };
913
+ case "textDeflate":
914
+ case "textDeflateBottom":
915
+ case "textDeflateTop":
916
+ return {
917
+ transform: "scaleY(0.88) scaleX(0.95)",
918
+ transformOrigin: "center center"
919
+ };
920
+ // Wave
921
+ case "textWave1":
922
+ case "textWave2":
923
+ case "textWave4":
924
+ return {
925
+ transform: "perspective(600px) rotateX(-3deg) skewX(3deg)",
926
+ transformOrigin: "center center"
927
+ };
928
+ case "textDoubleWave1":
929
+ return {
930
+ transform: "perspective(600px) rotateX(-2deg) skewX(4deg)",
931
+ transformOrigin: "center center"
932
+ };
933
+ // Triangle / Trapezoid
934
+ case "textTriangle":
935
+ case "textTriangleInverted":
936
+ return {
937
+ transform: "perspective(500px) rotateX(-6deg)",
938
+ transformOrigin: "center bottom"
939
+ };
940
+ // Ring
941
+ case "textRingInside":
942
+ case "textRingOutside":
943
+ return {
944
+ transform: "perspective(350px) rotateX(-4deg) rotateY(4deg)",
945
+ transformOrigin: "center center"
946
+ };
947
+ // Can
948
+ case "textCanUp":
949
+ case "textCanDown":
950
+ return {
951
+ transform: "perspective(400px) rotateX(-6deg)",
952
+ transformOrigin: "center center"
953
+ };
954
+ // Cascade
955
+ case "textCascadeUp":
956
+ return {
957
+ transform: "skewY(-8deg)",
958
+ transformOrigin: "left top"
959
+ };
960
+ case "textCascadeDown":
961
+ return {
962
+ transform: "skewY(8deg)",
963
+ transformOrigin: "left top"
964
+ };
965
+ // Curve
966
+ case "textCurveUp":
967
+ return {
968
+ transform: "perspective(500px) rotateX(-8deg)",
969
+ transformOrigin: "center bottom"
970
+ };
971
+ case "textCurveDown":
972
+ return {
973
+ transform: "perspective(500px) rotateX(8deg)",
974
+ transformOrigin: "center top"
975
+ };
976
+ // Inflate/Deflate compound variants
977
+ case "textDeflateInflate":
978
+ return {
979
+ transform: "scaleY(0.92) scaleX(1.04)",
980
+ transformOrigin: "center center"
981
+ };
982
+ case "textDeflateInflateDeflate":
983
+ return {
984
+ transform: "scaleY(0.85) scaleX(1.06)",
985
+ transformOrigin: "center center"
986
+ };
987
+ // Stop / Octagon
988
+ case "textStop":
989
+ return {
990
+ transform: "scaleX(0.9) scaleY(0.9)",
991
+ transformOrigin: "center center"
992
+ };
993
+ default:
994
+ return void 0;
995
+ }
996
+ }
997
+ function buildAnimStyle(state) {
998
+ if (!state) {
999
+ return void 0;
1000
+ }
1001
+ const style = {};
1002
+ if (state.visible === false) {
1003
+ style.visibility = "hidden";
1004
+ }
1005
+ if (state.cssAnimation) {
1006
+ style.animation = state.cssAnimation;
1007
+ }
1008
+ return Object.keys(style).length > 0 ? style : void 0;
1009
+ }
1010
+ function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, paraRuns, subElementAnimStates, renderPiece) {
1011
+ if (!subElementAnimStates || subElementAnimStates.size === 0) {
1012
+ return renderedSegments;
1013
+ }
1014
+ const paraKey = `${elementId}${TEXT_BUILD_ID_SEP}p${paraIndex}`;
1015
+ const paraState = subElementAnimStates.get(paraKey);
1016
+ if (paraState) {
1017
+ const style = buildAnimStyle(paraState);
1018
+ return /* @__PURE__ */ jsx("span", { "data-anim-id": paraKey, style: { display: "inline", ...style }, children: renderedSegments }, paraKey);
1019
+ }
1020
+ const spec = buildTextBuildSpec(
1021
+ elementId,
1022
+ paraIndex,
1023
+ paraRuns.map((run) => ({ text: run.text, style: run })),
1024
+ subElementAnimStates
1025
+ );
1026
+ if (!spec?.spans) {
1027
+ return renderedSegments;
1028
+ }
1029
+ return spec.spans.map((span, index) => {
1030
+ const content = span.style && renderPiece ? renderPiece(span.style, span.text, String(index)) : span.text;
1031
+ if (!span.animId) {
1032
+ return /* @__PURE__ */ jsx(React8.Fragment, { children: content }, `ws-${index}`);
1033
+ }
1034
+ return /* @__PURE__ */ jsx(
1035
+ "span",
1036
+ {
1037
+ "data-anim-id": span.animId,
1038
+ style: textBuildSpanStyle(span),
1039
+ children: content
1040
+ },
1041
+ span.animId
1042
+ );
1043
+ });
1044
+ }
1045
+ function renderUnderlineWords(text, isUnderlineWords, wordDecoration, metric, keyPrefix, inner) {
1046
+ if (!isUnderlineWords || !text) {
1047
+ return inner(text, keyPrefix, metric);
1048
+ }
1049
+ const pieces = splitWordsForUnderline(text);
1050
+ if (pieces.length === 0) {
1051
+ return inner(text, keyPrefix, metric);
1052
+ }
1053
+ const gapMetric = metric ? {
1054
+ ...metric,
1055
+ nestedStyle: metric.nestedStyle ? stripUnderlineDecoration(metric.nestedStyle) : void 0
1056
+ } : void 0;
1057
+ return pieces.map((piece, i) => {
1058
+ const key = `${keyPrefix}-u${i}`;
1059
+ const following = followingText(pieces, i, metric?.following);
1060
+ if (!piece.underline) {
1061
+ return /* @__PURE__ */ jsx(React8.Fragment, { children: inner(piece.text, key, gapMetric && { ...gapMetric, following }) }, key);
1062
+ }
1063
+ return /* @__PURE__ */ jsx("span", { style: wordDecoration, children: inner(piece.text, key, metric && { ...metric, following }) }, key);
1064
+ });
1065
+ }
1066
+ function makeSegmentPieceRenderer(needsScriptFonts, scriptFonts, baseFontFamily, isUnderlineWords, wordDecoration, metric) {
1067
+ return (text, key, following) => renderUnderlineWords(
1068
+ text,
1069
+ isUnderlineWords,
1070
+ wordDecoration,
1071
+ metric && { ...metric, following },
1072
+ key,
1073
+ (subText, subKey, subMetric) => renderMetricPieces(
1074
+ subText,
1075
+ subMetric,
1076
+ subKey,
1077
+ (pieceText, pieceKey) => renderScriptAwareText(
1078
+ pieceText,
1079
+ needsScriptFonts,
1080
+ scriptFonts,
1081
+ baseFontFamily,
1082
+ pieceKey,
1083
+ subMetric?.nestedStyle
1084
+ )
1085
+ )
1086
+ );
1087
+ }
1088
+ function renderTabbedLine(line, ctx, keyPrefix, renderPiece, nestedStyle, underlineWords = false) {
1089
+ const [lineLayout] = buildRunTabLines(
1090
+ line,
1091
+ ctx,
1092
+ nestedStyle,
1093
+ void 0,
1094
+ underlineWords
1095
+ );
1096
+ return /* @__PURE__ */ jsx("span", { style: { display: "inline-block", whiteSpace: "nowrap" }, children: lineLayout.pieces.map((piece, i) => {
1097
+ const key = `${keyPrefix}-tab-${i}`;
1098
+ return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1099
+ piece.leaderStyle ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: piece.leaderStyle, children: piece.leaderText }) : null,
1100
+ piece.words ? piece.words.map((word, w) => /* @__PURE__ */ jsx("span", { style: word.style, children: renderPiece(word.text, `${key}-w${w}`) }, `${key}-w${w}`)) : /* @__PURE__ */ jsx("span", { style: piece.style, children: renderPiece(piece.text, key) })
1101
+ ] }, key);
1102
+ }) });
1103
+ }
1104
+ function renderEastAsianPieces(text, metric, keyPrefix, inner, baseStyle, following) {
1105
+ const parts = splitEastAsianBreaks(text, metric.eastAsian, metric.font, following);
1106
+ if (parts.length === 1 && !parts[0].style) {
1107
+ return inner(parts[0].text, keyPrefix);
1108
+ }
1109
+ return parts.map((part, i) => {
1110
+ const key = `${keyPrefix}-e${i}`;
1111
+ return part.style ? /* @__PURE__ */ jsx("span", { style: { ...baseStyle ?? metric.nestedStyle, ...part.style }, children: inner(part.text, key) }, key) : /* @__PURE__ */ jsx(React8.Fragment, { children: inner(part.text, key) }, key);
1112
+ });
1113
+ }
1114
+ function renderMetricPieces(text, metric, keyPrefix, inner) {
1115
+ if (!metric || !text) {
1116
+ return inner(text, keyPrefix);
1117
+ }
1118
+ const pieces = splitRunForMetrics(text, metric.font);
1119
+ if (pieces.length <= 1) {
1120
+ return renderEastAsianPieces(text, metric, keyPrefix, inner, void 0, metric.following);
1121
+ }
1122
+ return pieces.map((piece, i) => {
1123
+ const key = `${keyPrefix}-w${i}`;
1124
+ const style = {
1125
+ ...metric.nestedStyle,
1126
+ letterSpacing: pieceLetterSpacing(metric.authoredPx, piece.tracking)
1127
+ };
1128
+ return /* @__PURE__ */ jsx("span", { style, children: renderEastAsianPieces(
1129
+ piece.text,
1130
+ metric,
1131
+ key,
1132
+ inner,
1133
+ style,
1134
+ followingText(pieces, i, metric.following)
1135
+ ) }, key);
1136
+ });
1137
+ }
1138
+ function renderScriptAwareText(text, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix, nestedStyle) {
1139
+ if (!needsScriptFonts || !text) {
1140
+ return text;
1141
+ }
1142
+ const pieces = splitRunByScriptFont(
1143
+ text,
1144
+ scriptFonts,
1145
+ baseFontFamily,
1146
+ nestedStyle
1147
+ );
1148
+ if (!pieces) {
1149
+ return text;
1150
+ }
1151
+ return pieces.map(
1152
+ (piece, i) => piece.style ? /* @__PURE__ */ jsx("span", { style: piece.style, children: piece.text }, `${keyPrefix}-r${i}`) : /* @__PURE__ */ jsx(React8.Fragment, { children: piece.text }, `${keyPrefix}-r${i}`)
1153
+ );
1154
+ }
1155
+ function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsScriptFonts, scriptFonts, baseFontFamily, findHighlights, tabContext, metric, isUnderlineWords = false, wordDecoration) {
1156
+ const segHl = findHighlights?.get(segmentIndex);
1157
+ const renderLeaf = makeSegmentPieceRenderer(
1158
+ needsScriptFonts,
1159
+ scriptFonts,
1160
+ baseFontFamily,
1161
+ isUnderlineWords,
1162
+ wordDecoration,
1163
+ metric
1164
+ );
1165
+ if (!segHl || segHl.length === 0) {
1166
+ return lines.map((line, lineIndex) => {
1167
+ const lineKey = `${elementId}-seg-${segmentIndex}-line-${lineIndex}`;
1168
+ return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1169
+ tabContext && line.includes(" ") ? renderTabbedLine(
1170
+ line,
1171
+ tabContext,
1172
+ lineKey,
1173
+ renderLeaf,
1174
+ metric?.nestedStyle,
1175
+ isUnderlineWords
1176
+ ) : renderLeaf(line, lineKey, lineIndex < lines.length - 1 ? "\n" : metric?.following),
1177
+ lineIndex < lines.length - 1 ? /* @__PURE__ */ jsx("br", {}) : null
1178
+ ] }, lineKey);
1179
+ });
1180
+ }
1181
+ const sorted = [...segHl].sort((a, b) => a.startOffset - b.startOffset);
1182
+ const chunks = [];
1183
+ let cursor = 0;
1184
+ for (const hl of sorted) {
1185
+ if (hl.startOffset > cursor) {
1186
+ chunks.push({
1187
+ text: textValue.slice(cursor, hl.startOffset),
1188
+ highlighted: false,
1189
+ isCurrent: false
1190
+ });
1191
+ }
1192
+ chunks.push({
1193
+ text: textValue.slice(hl.startOffset, hl.startOffset + hl.length),
1194
+ highlighted: true,
1195
+ isCurrent: hl.isCurrent
1196
+ });
1197
+ cursor = hl.startOffset + hl.length;
1198
+ }
1199
+ if (cursor < textValue.length) {
1200
+ chunks.push({
1201
+ text: textValue.slice(cursor),
1202
+ highlighted: false,
1203
+ isCurrent: false
1204
+ });
1205
+ }
1206
+ return chunks.map((chunk, ci) => {
1207
+ const chunkKey = `${elementId}-seg-${segmentIndex}-hl-${ci}`;
1208
+ return chunk.highlighted ? /* @__PURE__ */ jsx(
1209
+ "mark",
1210
+ {
1211
+ style: {
1212
+ ...metric?.nestedStyle,
1213
+ backgroundColor: chunk.isCurrent ? "#f97316" : "#facc15",
1214
+ color: "inherit",
1215
+ borderRadius: 2
1216
+ },
1217
+ children: renderLeaf(chunk.text, chunkKey, followingText(chunks, ci, metric?.following))
1218
+ },
1219
+ chunkKey
1220
+ ) : /* @__PURE__ */ jsx(React8.Fragment, { children: renderLeaf(chunk.text, chunkKey, followingText(chunks, ci, metric?.following)) }, chunkKey);
1221
+ });
1222
+ }
1223
+ function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber, fontSize) {
1224
+ const mathml = convertOmmlToMathMl(equationXml);
1225
+ const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
1226
+ const equationContent = safeMathml ? /* @__PURE__ */ jsx(
1227
+ "span",
1228
+ {
1229
+ className: "inline-block align-middle",
1230
+ style: {
1231
+ fontFamily: '"Cambria Math", "STIX Two Math", serif'
1232
+ },
1233
+ dangerouslySetInnerHTML: { __html: safeMathml }
1234
+ }
1235
+ ) : /* @__PURE__ */ jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: translationsEn["pptx.textSegment.equationFallback"] });
1236
+ if (equationNumber) {
1237
+ return /* @__PURE__ */ jsxs(
1238
+ "span",
1239
+ {
1240
+ style: {
1241
+ display: "flex",
1242
+ justifyContent: "space-between",
1243
+ alignItems: "center",
1244
+ width: "100%",
1245
+ fontSize
1246
+ },
1247
+ children: [
1248
+ /* @__PURE__ */ jsxs("span", { style: { visibility: "hidden", whiteSpace: "nowrap" }, children: [
1249
+ "(",
1250
+ equationNumber,
1251
+ ")"
1252
+ ] }),
1253
+ /* @__PURE__ */ jsx("span", { style: { textAlign: "center", flex: 1 }, children: equationContent }),
1254
+ /* @__PURE__ */ jsxs(
1255
+ "span",
1256
+ {
1257
+ style: {
1258
+ whiteSpace: "nowrap",
1259
+ fontFamily: '"Cambria Math", "STIX Two Math", serif'
1260
+ },
1261
+ children: [
1262
+ "(",
1263
+ equationNumber,
1264
+ ")"
1265
+ ]
1266
+ }
1267
+ )
1268
+ ]
1269
+ },
1270
+ `${elementId}-seg-${segmentIndex}`
1271
+ );
1272
+ }
1273
+ return /* @__PURE__ */ jsx("span", { style: fontSize ? { fontSize } : void 0, children: equationContent }, `${elementId}-seg-${segmentIndex}`);
1274
+ }
1275
+ function renderHyperlink(run, spanNode, key, ctx) {
1276
+ const url = run.hyperlink?.url;
1277
+ const onHyperlinkClick = ctx.onHyperlinkClick;
1278
+ if (!url || !onHyperlinkClick) {
1279
+ return spanNode;
1280
+ }
1281
+ const requireCtrlClick = ctx.requireCtrlClick;
1282
+ const displayUrl = url.startsWith("ppaction://") ? url.replace(/^ppaction:\/\//u, "").split("?")[0] : url;
1283
+ const targetFrame = run.hyperlink?.target;
1284
+ const follow = (modified) => {
1285
+ if (requireCtrlClick && !modified) {
1286
+ return false;
1287
+ }
1288
+ onHyperlinkClick(url, targetFrame);
1289
+ return true;
1290
+ };
1291
+ return /* @__PURE__ */ jsxs(
1292
+ "span",
1293
+ {
1294
+ role: "link",
1295
+ tabIndex: 0,
1296
+ className: requireCtrlClick ? "group/link relative" : void 0,
1297
+ style: { cursor: requireCtrlClick ? void 0 : "pointer", pointerEvents: "auto" },
1298
+ title: run.hyperlink?.tooltip,
1299
+ onClick: (e) => {
1300
+ if (!follow(e.ctrlKey || e.metaKey)) {
1301
+ return;
1302
+ }
1303
+ e.stopPropagation();
1304
+ e.preventDefault();
1305
+ },
1306
+ onKeyDown: (e) => {
1307
+ if (e.key !== "Enter" && e.key !== " ") {
1308
+ return;
1309
+ }
1310
+ if (!follow(e.ctrlKey || e.metaKey)) {
1311
+ return;
1312
+ }
1313
+ e.preventDefault();
1314
+ e.stopPropagation();
1315
+ },
1316
+ children: [
1317
+ spanNode,
1318
+ requireCtrlClick && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-0 top-full z-[9999] mt-1 max-w-64 opacity-0 transition-opacity duration-150 group-hover/link:opacity-100", children: /* @__PURE__ */ jsxs("span", { className: "flex flex-col rounded border border-border bg-popover px-2.5 py-1.5 shadow-lg", children: [
1319
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground", children: displayUrl }),
1320
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] text-muted-foreground", children: /* @__PURE__ */ jsx(FollowLinkHint, {}) })
1321
+ ] }) })
1322
+ ]
1323
+ },
1324
+ `${key}-link`
1325
+ );
1326
+ }
1327
+ function FollowLinkHint() {
1328
+ const { t } = useTranslation();
1329
+ return /* @__PURE__ */ jsx(Fragment, { children: t("pptx.linkTooltip.followLink") });
1330
+ }
1331
+ function wrapWithTextReflection(run, key, spanNode, spanStyle, baseContent) {
1332
+ if (!run.reflection) {
1333
+ return spanNode;
1334
+ }
1335
+ return /* @__PURE__ */ jsxs("span", { style: { position: "relative", display: "inline-block" }, children: [
1336
+ spanNode,
1337
+ /* @__PURE__ */ jsx(
1338
+ "span",
1339
+ {
1340
+ "aria-hidden": "true",
1341
+ className: "pptx-react-text-reflection",
1342
+ style: run.reflection,
1343
+ children: /* @__PURE__ */ jsx("span", { style: spanStyle, children: baseContent })
1344
+ }
1345
+ )
1346
+ ] }, `${key}-reflwrap`);
1347
+ }
1348
+ var BASELINE_FONT_SCALE = 0.65;
1349
+ function renderParagraphRun(run, segment, ctx, keySuffix = "") {
1350
+ const { element, fallbackColor, findHighlights, onHyperlinkClick } = ctx;
1351
+ const segmentIndex = run.segmentIndex ?? 0;
1352
+ const key = `${element.id}-seg-${segmentIndex}${keySuffix}`;
1353
+ if (run.equation) {
1354
+ return renderEquationSegment(
1355
+ element.id,
1356
+ segmentIndex,
1357
+ run.equation.xml,
1358
+ run.equation.number,
1359
+ typeof run.style.fontSize === "string" ? run.style.fontSize : void 0
1360
+ );
1361
+ }
1362
+ const segmentStyle = segment?.style ?? {};
1363
+ const textValue = run.text;
1364
+ const lines = textValue.split("\n");
1365
+ const resolvedSegmentColor = normalizeHexColor(
1366
+ segmentStyle.color || element.textStyle?.color,
1367
+ run.hyperlink ? HYPERLINK_COLOR : fallbackColor
1368
+ );
1369
+ const rawBaseline = typeof segmentStyle.baseline === "number" ? segmentStyle.baseline : 0;
1370
+ const baselineFraction = rawBaseline === 0 ? 0 : Math.abs(rawBaseline) >= 1e3 ? rawBaseline / 1e5 : rawBaseline / 100;
1371
+ const baselineFontScale = baselineFraction !== 0 ? BASELINE_FONT_SCALE : 1;
1372
+ const rawFontSize = segmentStyle.fontSize || element.textStyle?.fontSize || DEFAULT_TEXT_FONT_SIZE;
1373
+ const baseFontSize = scaleFontSizeForAutoFit(
1374
+ rawFontSize,
1375
+ resolveAutoFitFontScale(element.textStyle)
1376
+ );
1377
+ const baselineShift = baselineFraction !== 0 ? `${baselineFraction * baseFontSize}px` : void 0;
1378
+ const fontKerning = resolveFontKerning(segmentStyle.kerning, baseFontSize);
1379
+ const scriptFallbackWins = Boolean(
1380
+ segmentStyle.scriptFallbackFont && segmentStyle.fontFamilyIsCascadeDefault
1381
+ );
1382
+ const rawFontFamily = segmentStyle.fontFamily || element.textStyle?.fontFamily;
1383
+ const baseFontFamily = scriptFallbackWins ? (0, dist_exports.getSubstituteFontFamily)(segmentStyle.scriptFallbackFont) : rawFontFamily ? (0, dist_exports.getSubstituteFontFamily)(
1384
+ rawFontFamily,
1385
+ (0, dist_exports.parsePanoseString)(segmentStyle.latinFontPanose ?? element.textStyle?.latinFontPanose)
1386
+ ) : DEFAULT_FONT_FAMILY;
1387
+ const scriptFonts = resolveScriptFontSet(segmentStyle, element.textStyle, baseFontFamily);
1388
+ const needsScriptFonts = hasDistinctScriptFonts(scriptFonts);
1389
+ const authoredLetterSpacing = authoredLetterSpacingPx(segmentStyle);
1390
+ const metricFont = {
1391
+ fontFamily: baseFontFamily,
1392
+ fontSizePx: baseFontSize * baselineFontScale,
1393
+ bold: Boolean(segmentStyle.bold),
1394
+ italic: Boolean(segmentStyle.italic)
1395
+ };
1396
+ const nestedStyle = nestedTextDecorationStyle(run.style);
1397
+ const metricContext = {
1398
+ font: metricFont,
1399
+ authoredPx: authoredLetterSpacing,
1400
+ nestedStyle,
1401
+ // Resolved per run, so a Korean run keeps its Hangul words whole.
1402
+ eastAsian: eastAsianBreaksForRun(ctx.eastAsianBreaks, segmentStyle.language),
1403
+ following: ctx.eastAsianFollowing
1404
+ };
1405
+ const spanStyle = { ...run.style };
1406
+ spanStyle.color = resolvedSegmentColor;
1407
+ spanStyle.fontSize = baseFontSize * baselineFontScale;
1408
+ spanStyle.fontFamily = baseFontFamily;
1409
+ spanStyle.verticalAlign = baselineShift;
1410
+ spanStyle.fontKerning = fontKerning;
1411
+ spanStyle.letterSpacing = pieceLetterSpacing(
1412
+ authoredLetterSpacing,
1413
+ resolveMetricTrackingPx(textValue, metricFont)
1414
+ );
1415
+ const hollow = hollowTextFillStyle(segmentStyle, {
1416
+ color: resolvedSegmentColor,
1417
+ textStroke: typeof spanStyle.WebkitTextStroke === "string" ? spanStyle.WebkitTextStroke : void 0
1418
+ });
1419
+ if (hollow) {
1420
+ Object.assign(spanStyle, hollow);
1421
+ }
1422
+ const runRtl = segmentStyle.rtl;
1423
+ if (runRtl !== void 0) {
1424
+ spanStyle.direction = runRtl ? "rtl" : "ltr";
1425
+ spanStyle.unicodeBidi = runRtl !== ctx.paragraphRtl ? "bidi-override" : "embed";
1426
+ }
1427
+ const isUnderlineWords = splitsUnderlineIntoWords(segmentStyle);
1428
+ const wordDecoration = isUnderlineWords ? nestedStyle ?? run.underlineWordPieces?.find((piece) => piece.style)?.style : void 0;
1429
+ if (isUnderlineWords) {
1430
+ const stripped = stripUnderlineDecoration(spanStyle);
1431
+ spanStyle.textDecoration = stripped.textDecoration;
1432
+ delete spanStyle.textDecorationStyle;
1433
+ delete spanStyle.textDecorationThickness;
1434
+ delete spanStyle.textUnderlineOffset;
1435
+ delete spanStyle.textDecorationColor;
1436
+ }
1437
+ const baseContent = renderSegmentContent(
1438
+ element.id,
1439
+ segmentIndex,
1440
+ textValue,
1441
+ lines,
1442
+ needsScriptFonts,
1443
+ scriptFonts,
1444
+ baseFontFamily,
1445
+ findHighlights,
1446
+ buildTabContext(
1447
+ element.textStyle?.tabStops,
1448
+ element.textStyle?.defaultTabSize,
1449
+ baseFontSize,
1450
+ baseFontFamily,
1451
+ Boolean(segmentStyle.bold),
1452
+ Boolean(segmentStyle.italic),
1453
+ { language: segmentStyle.language, rtl: ctx.paragraphRtl === true }
1454
+ ),
1455
+ metricContext,
1456
+ isUnderlineWords,
1457
+ wordDecoration
1458
+ );
1459
+ const spanNode = /* @__PURE__ */ jsx("span", { "data-seg-idx": segmentIndex, style: spanStyle, children: renderRubyOrText(run, baseContent) }, key);
1460
+ const reflectedNode = wrapWithTextReflection(run, key, spanNode, spanStyle, baseContent);
1461
+ return run.hyperlink && onHyperlinkClick ? renderHyperlink(run, reflectedNode, key, ctx) : reflectedNode;
1462
+ }
1463
+ function renderRubyOrText(run, baseContent) {
1464
+ if (!run.ruby) {
1465
+ return baseContent;
1466
+ }
1467
+ return /* @__PURE__ */ jsxs("ruby", { children: [
1468
+ baseContent,
1469
+ /* @__PURE__ */ jsx("rp", { children: "(" }),
1470
+ /* @__PURE__ */ jsx("rt", { style: run.ruby.style, children: run.ruby.text }),
1471
+ /* @__PURE__ */ jsx("rp", { children: ")" })
1472
+ ] });
1473
+ }
1474
+ function renderSeededListRun(seed, runs, segments, context) {
1475
+ const sourceRun = runs.find((candidate) => candidate.segmentIndex === seed.segmentIndex);
1476
+ const segment = sourceRun ? segments[seed.segmentIndex] : { text: seed.text, style: seed.style };
1477
+ const resolvedSegment = {
1478
+ ...segment,
1479
+ style: withInlineListDecorationDefaults(segment.style, context.element.textStyle)
1480
+ };
1481
+ const decorationCss = segmentStyleToCss(
1482
+ resolvedSegment,
1483
+ resolveAutoFitFontScale(context.element.textStyle)
1484
+ );
1485
+ applyUnderlineVariant(decorationCss, resolvedSegment);
1486
+ const run = sourceRun ? { ...sourceRun, style: { ...sourceRun.style, ...nestedTextDecorationStyle(decorationCss) } } : {
1487
+ text: seed.text,
1488
+ style: decorationCss,
1489
+ segmentIndex: seed.segmentIndex
1490
+ };
1491
+ const rendered = renderParagraphRun(run, resolvedSegment, context);
1492
+ if (!React8.isValidElement(rendered)) {
1493
+ return rendered;
1494
+ }
1495
+ return React8.cloneElement(rendered, {
1496
+ "data-pptx-list-run": seed.token,
1497
+ ...seed.text === "" ? { children: /* @__PURE__ */ jsx("br", {}) } : {}
1498
+ });
1499
+ }
1500
+ function renderTextSegments(element, fallbackColor, emptyFallback, findHighlights, onHyperlinkClick, fieldContext, subElementAnimStates, segmentOverrides, requireCtrlClick, placeholderPrompt, inlineListSeed) {
1501
+ if (!(0, dist_exports.hasTextProperties)(element)) {
1502
+ return emptyFallback || null;
1503
+ }
1504
+ const segments = segmentOverrides ?? element.textSegments;
1505
+ if (!segments || segments.length === 0) {
1506
+ if (placeholderPrompt) {
1507
+ return /* @__PURE__ */ jsx("span", { style: placeholderPrompt.style, children: placeholderPrompt.text });
1508
+ }
1509
+ return element.text || emptyFallback || "";
1510
+ }
1511
+ const paragraphs = buildParagraphs(element, fieldContext, segmentOverrides, {
1512
+ preserveTrailingEmpty: inlineListSeed ? true : void 0,
1513
+ eastAsianRunPieces: false
1514
+ });
1515
+ const ctx = {
1516
+ element,
1517
+ fallbackColor,
1518
+ findHighlights,
1519
+ onHyperlinkClick,
1520
+ requireCtrlClick
1521
+ };
1522
+ return paragraphs.map(
1523
+ (para, paraIndex) => renderParagraph(
1524
+ para,
1525
+ paraIndex,
1526
+ paragraphs.length,
1527
+ segments,
1528
+ ctx,
1529
+ subElementAnimStates,
1530
+ inlineListSeed
1531
+ )
1532
+ );
1533
+ }
1534
+ function joinRunsBySegment(runs) {
1535
+ const out = [];
1536
+ for (const run of runs) {
1537
+ const last = out[out.length - 1];
1538
+ if (last && run.segmentIndex !== void 0 && last.segmentIndex === run.segmentIndex && !last.equation && !run.equation) {
1539
+ last.text += run.text;
1540
+ continue;
1541
+ }
1542
+ out.push({ ...run });
1543
+ }
1544
+ return out;
1545
+ }
1546
+ function renderParagraph(para, paraIndex, paraCount, segments, ctx, subElementAnimStates, inlineListSeed) {
1547
+ const element = ctx.element;
1548
+ const runCtx = {
1549
+ ...ctx,
1550
+ paragraphRtl: para.rtl,
1551
+ eastAsianBreaks: para.eastAsianBreaks
1552
+ };
1553
+ const runs = joinRunsBySegment(para.runs);
1554
+ const listParagraph = inlineListSeed?.paragraphs[paraIndex];
1555
+ const renderedRuns = listParagraph ? listParagraph.runs.map((run) => renderSeededListRun(run, runs, segments, runCtx)) : runs.map(
1556
+ (run, i) => renderParagraphRun(run, segments[run.segmentIndex ?? -1], {
1557
+ ...runCtx,
1558
+ eastAsianFollowing: para.eastAsianBreaks ? followingText(runs, i) : void 0
1559
+ })
1560
+ );
1561
+ const paraStyle = {
1562
+ // `text-align`, BiDi `direction` / `unicode-bidi` and the kinsoku
1563
+ // line-break rules, resolved by shared from this paragraph's own `a:pPr`.
1564
+ ...para.paragraphStyle
1565
+ };
1566
+ if (para.strutFontSizePx !== void 0) {
1567
+ paraStyle.fontSize = para.strutFontSizePx;
1568
+ }
1569
+ if (para.spaceBeforePx !== void 0) {
1570
+ paraStyle.marginTop = para.spaceBeforePx;
1571
+ }
1572
+ if (para.spaceAfterPx !== void 0) {
1573
+ paraStyle.marginBottom = para.spaceAfterPx;
1574
+ }
1575
+ if (para.lineHeight !== void 0) {
1576
+ paraStyle.lineHeight = para.lineHeight;
1577
+ }
1578
+ if (para.marginLeftPx !== void 0) {
1579
+ if (para.rtl === true) {
1580
+ paraStyle.marginRight = para.marginLeftPx;
1581
+ } else {
1582
+ paraStyle.marginLeft = para.marginLeftPx;
1583
+ }
1584
+ }
1585
+ if (para.textIndentPx !== void 0) {
1586
+ paraStyle.textIndent = para.textIndentPx;
1587
+ }
1588
+ const marker = renderBulletMarker(para, paraIndex, element.id);
1589
+ const wrappedContent = wrapWithTextBuildAnimation(
1590
+ element.id,
1591
+ paraIndex,
1592
+ renderedRuns,
1593
+ runs,
1594
+ subElementAnimStates,
1595
+ // A staged build splits the paragraph but must not flatten it: each piece
1596
+ // is re-rendered through the SAME run renderer, so it keeps its run's
1597
+ // font, size, colour and decoration while it animates.
1598
+ (run, text, pieceKey) => renderParagraphRun(
1599
+ { ...run, text },
1600
+ segments[run.segmentIndex ?? -1],
1601
+ runCtx,
1602
+ `-build-${pieceKey}`
1603
+ )
1604
+ );
1605
+ const needsWrapper = Boolean(listParagraph) || Object.keys(paraStyle).length > 0 || marker !== null;
1606
+ if (!needsWrapper) {
1607
+ return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1608
+ wrappedContent,
1609
+ paraIndex < paraCount - 1 ? /* @__PURE__ */ jsx("br", {}) : null
1610
+ ] }, `${element.id}-para-${paraIndex}`);
1611
+ }
1612
+ return /* @__PURE__ */ jsxs(
1613
+ "div",
1614
+ {
1615
+ style: paraStyle,
1616
+ "data-pptx-list-paragraph": listParagraph?.token,
1617
+ children: [
1618
+ marker,
1619
+ para.isEmpty && !listParagraph ? /* @__PURE__ */ jsx("br", {}) : wrappedContent
1620
+ ]
1621
+ },
1622
+ `${element.id}-para-${paraIndex}`
1623
+ );
1624
+ }
1625
+ function renderBulletMarker(para, paraIndex, elementId) {
1626
+ const picture = para.bulletPicture;
1627
+ if (picture?.src) {
1628
+ return /* @__PURE__ */ jsx(
1629
+ "img",
1630
+ {
1631
+ "data-pptx-bullet-marker": true,
1632
+ contentEditable: false,
1633
+ src: picture.src,
1634
+ alt: picture.accessibleLabel,
1635
+ style: {
1636
+ width: picture.sizePx,
1637
+ height: picture.sizePx,
1638
+ display: "inline-block",
1639
+ verticalAlign: "middle",
1640
+ marginInlineEnd: 4,
1641
+ objectFit: "contain"
1642
+ }
1643
+ },
1644
+ `${elementId}-para-${paraIndex}-bullet-img`
1645
+ );
1646
+ }
1647
+ if (para.bulletMarker === void 0) {
1648
+ return null;
1649
+ }
1650
+ return /* @__PURE__ */ jsx(
1651
+ "span",
1652
+ {
1653
+ className: "pptx-bullet",
1654
+ "data-pptx-bullet-marker": true,
1655
+ contentEditable: false,
1656
+ style: para.bulletStyle,
1657
+ "aria-label": picture?.accessibleLabel,
1658
+ children: para.bulletMarker
1659
+ },
1660
+ `${elementId}-para-${paraIndex}-bullet`
1661
+ );
1662
+ }
1663
+ function getDuotoneColors(element) {
1664
+ if (!(0, dist_exports.isImageLikeElement)(element)) {
1665
+ return void 0;
1666
+ }
1667
+ return element.imageEffects?.duotone;
1668
+ }
1669
+ function buildLineShadowCss(element) {
1670
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
1671
+ return void 0;
1672
+ }
1673
+ const ss = element.shapeStyle;
1674
+ if (!ss?.lineShadowColor || ss.lineShadowColor === "transparent") {
1675
+ return void 0;
1676
+ }
1677
+ const offsetX = typeof ss.lineShadowOffsetX === "number" ? ss.lineShadowOffsetX : 2;
1678
+ const offsetY = typeof ss.lineShadowOffsetY === "number" ? ss.lineShadowOffsetY : 2;
1679
+ const blur = typeof ss.lineShadowBlur === "number" ? Math.max(0, ss.lineShadowBlur) : 4;
1680
+ const opacity = typeof ss.lineShadowOpacity === "number" ? Math.max(0, Math.min(1, ss.lineShadowOpacity)) : 0.35;
1681
+ return `${Math.round(offsetX)}px ${Math.round(offsetY)}px ${Math.round(blur)}px ${colorWithOpacity(
1682
+ normalizeHexColor(ss.lineShadowColor, "#000000"),
1683
+ opacity
1684
+ )}`;
1685
+ }
1686
+ function buildLineGlowFilter(element) {
1687
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
1688
+ return void 0;
1689
+ }
1690
+ const ss = element.shapeStyle;
1691
+ if (!ss?.lineGlowColor || ss.lineGlowColor === "transparent" || !ss.lineGlowRadius) {
1692
+ return void 0;
1693
+ }
1694
+ const glowOpacity = typeof ss.lineGlowOpacity === "number" ? ss.lineGlowOpacity : 0.75;
1695
+ const glowRad = Math.round(Math.max(0, ss.lineGlowRadius));
1696
+ const glowCol = colorWithOpacity(normalizeHexColor(ss.lineGlowColor, "#ffff00"), glowOpacity);
1697
+ return `drop-shadow(0 0 ${glowRad}px ${glowCol})`;
1698
+ }
1699
+ function getDagDuotoneFilterId(elementId) {
1700
+ return `dag-duotone-${elementId}`;
1701
+ }
1702
+ function hasDagDuotoneEffect(element) {
1703
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
1704
+ return false;
1705
+ }
1706
+ return Boolean(element.shapeStyle?.dagDuotone);
1707
+ }
1708
+ function renderDagDuotoneSvgFilter(elementId, color1, color2) {
1709
+ const filterId = getDagDuotoneFilterId(elementId);
1710
+ const c1 = hexToRgbUnit(color1);
1711
+ const c2 = hexToRgbUnit(color2);
1712
+ const grayscaleMatrix = [
1713
+ 0.2126,
1714
+ 0.7152,
1715
+ 0.0722,
1716
+ 0,
1717
+ 0,
1718
+ 0.2126,
1719
+ 0.7152,
1720
+ 0.0722,
1721
+ 0,
1722
+ 0,
1723
+ 0.2126,
1724
+ 0.7152,
1725
+ 0.0722,
1726
+ 0,
1727
+ 0,
1728
+ 0,
1729
+ 0,
1730
+ 0,
1731
+ 1,
1732
+ 0
1733
+ ].join(" ");
1734
+ return /* @__PURE__ */ jsx(
1735
+ "svg",
1736
+ {
1737
+ width: 0,
1738
+ height: 0,
1739
+ style: { position: "absolute", overflow: "hidden" },
1740
+ "aria-hidden": "true",
1741
+ children: /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: [
1742
+ /* @__PURE__ */ jsx("feColorMatrix", { type: "matrix", values: grayscaleMatrix }),
1743
+ /* @__PURE__ */ jsxs("feComponentTransfer", { children: [
1744
+ /* @__PURE__ */ jsx("feFuncR", { type: "linear", slope: c2.r - c1.r, intercept: c1.r }),
1745
+ /* @__PURE__ */ jsx("feFuncG", { type: "linear", slope: c2.g - c1.g, intercept: c1.g }),
1746
+ /* @__PURE__ */ jsx("feFuncB", { type: "linear", slope: c2.b - c1.b, intercept: c1.b })
1747
+ ] })
1748
+ ] }) })
1749
+ }
1750
+ );
1751
+ }
1752
+ function build3DExtrusionData(shape3d, scene3d, fillColor, elementWidth, elementHeight) {
1753
+ return build3DExtrusionData$1(
1754
+ shape3d,
1755
+ scene3d,
1756
+ fillColor,
1757
+ elementWidth,
1758
+ elementHeight
1759
+ );
1760
+ }
1761
+ function apply3dEffects(base, scene3d, shape3d, fillColorFallback, elementSize, elementId) {
1762
+ apply3dEffects$1(
1763
+ base,
1764
+ scene3d,
1765
+ shape3d,
1766
+ fillColorFallback,
1767
+ elementSize,
1768
+ elementId
1769
+ );
1770
+ }
1771
+
1772
+ // src/viewer/utils/shape-visual-style.ts
1773
+ function rendersCustomVectorPath(element) {
1774
+ return (element.type === "shape" || element.type === "image" || element.type === "picture") && Boolean(element.pathData) && typeof element.pathWidth === "number" && element.pathWidth > 0 && typeof element.pathHeight === "number" && element.pathHeight > 0;
1775
+ }
1776
+ function rendersWedgeCalloutVector(element) {
1777
+ return element.type === "shape" && isWedgeCalloutPresetShape(element.shapeType);
1778
+ }
1779
+ function getShapeVisualStyle(element, _hasFill, fillColor, _strokeWidth, _strokeColor, animatesFill, animatesStroke, parentGroupFill) {
1780
+ if (element.type === "group") {
1781
+ const fx2 = getComputedEffectStyle(element);
1782
+ const groupStyle = {};
1783
+ if (fx2.filter) {
1784
+ groupStyle.filter = fx2.filter;
1785
+ }
1786
+ if (fx2.overflowVisible) {
1787
+ groupStyle.overflow = "visible";
1788
+ }
1789
+ return groupStyle;
1790
+ }
1791
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
1792
+ return {};
1793
+ }
1794
+ const ss = element.shapeStyle;
1795
+ const fill = animatesFill ? void 0 : getComputedFillStyle(element, parentGroupFill);
1796
+ const fx = getComputedEffectStyle(element);
1797
+ const stroke = getComputedStrokeStyle(element);
1798
+ const base = {
1799
+ // A gradient fill REPLACES the solid fill, it does not sit on top of it;
1800
+ // the whole precedence lives in shared's `getComputedFillStyle`.
1801
+ backgroundColor: fill?.backgroundColor ?? "transparent",
1802
+ backgroundImage: fill?.backgroundImage,
1803
+ backgroundRepeat: fill?.backgroundRepeat,
1804
+ backgroundSize: fill?.backgroundSize,
1805
+ backgroundPosition: fill?.backgroundPosition,
1806
+ // The container always carries `box-sizing: border-box`, so the default
1807
+ // `background-origin: padding-box` would size the paint 2px smaller than
1808
+ // the shape and make a gradient tile wrap, painting a 1px sliver of its
1809
+ // opposite end along the edge. Paint from the border box instead.
1810
+ backgroundOrigin: "border-box",
1811
+ boxShadow: fx.boxShadow,
1812
+ // Reflection is no longer a single CSS property (`-webkit-box-reflect`
1813
+ // never worked in Firefox): `ElementRenderer` renders a mirrored sibling
1814
+ // node instead, using shared's `getReflectionWrapperStyle` directly (see
1815
+ // `ReflectionOverlay`).
1816
+ filter: fx.filter,
1817
+ opacity: fx.opacity,
1818
+ // Only proxy the DAG blend onto the whole element for the blend-only case:
1819
+ // with an overlay colour, `ShapeEffectOverlay` paints a separate blended
1820
+ // tint layer (shared decides which, and returns only one of the two).
1821
+ mixBlendMode: fx.mixBlendMode,
1822
+ // An unstroked element must occupy EXACTLY its authored box, so the
1823
+ // selection/hover affordance is an `outline` (see `ElementRenderer`) and
1824
+ // the border collapses to 0 rather than to a transparent 1px.
1825
+ borderWidth: stroke.borderWidth,
1826
+ borderColor: animatesStroke ? void 0 : stroke.borderColor,
1827
+ borderStyle: stroke.borderStyle,
1828
+ // Inherited SVG presentation properties: written on the container so the
1829
+ // freeform `<path>` and the stroke overlay both pick them up.
1830
+ strokeLinejoin: stroke.strokeLinejoin,
1831
+ strokeMiterlimit: stroke.strokeMiterlimit,
1832
+ strokeLinecap: stroke.strokeLinecap
1833
+ };
1834
+ Object.assign(
1835
+ base,
1836
+ getAnimationColorBaseStyle(element, {
1837
+ animatesFill,
1838
+ animatesStroke,
1839
+ parentGroupFill
1840
+ })
1841
+ );
1842
+ if (element.type === "image" || element.type === "picture") {
1843
+ base.borderWidth = 0;
1844
+ base.borderColor = void 0;
1845
+ base.borderStyle = void 0;
1846
+ }
1847
+ apply3dEffects(
1848
+ base,
1849
+ ss?.scene3d,
1850
+ ss?.shape3d,
1851
+ ss?.fillColor ?? fillColor,
1852
+ {
1853
+ width: element.width,
1854
+ height: element.height
1855
+ },
1856
+ element.id
1857
+ );
1858
+ if (rendersCustomVectorPath(element) || rendersWedgeCalloutVector(element) || isStrokeOnlyPresetElement(element)) {
1859
+ base.backgroundColor = "transparent";
1860
+ base.backgroundImage = void 0;
1861
+ base.borderWidth = void 0;
1862
+ base.borderColor = void 0;
1863
+ base.borderStyle = void 0;
1864
+ }
1865
+ if (animatesFill) {
1866
+ base.backgroundColor = void 0;
1867
+ base.backgroundImage = void 0;
1868
+ }
1869
+ if (rendersWedgeCalloutVector(element)) {
1870
+ return base;
1871
+ }
1872
+ const geometry = resolveShapeGeometry(element);
1873
+ switch (geometry.kind) {
1874
+ case "bare":
1875
+ return { backgroundColor: "transparent", borderWidth: 0, borderStyle: "none" };
1876
+ case "strokeOnly":
1877
+ return { ...base, backgroundColor: "transparent", backgroundImage: void 0 };
1878
+ case "borderRadius":
1879
+ return { ...base, borderRadius: geometry.radius };
1880
+ case "clipPath":
1881
+ return { ...base, clipPath: geometry.clipPath };
1882
+ case "lineEdge":
1883
+ return {
1884
+ ...base,
1885
+ backgroundColor: "transparent",
1886
+ borderWidth: 0,
1887
+ borderTopWidth: geometry.strokeWidth,
1888
+ borderTopColor: stroke.borderColor,
1889
+ borderTopStyle: stroke.borderStyle ?? "solid"
1890
+ };
1891
+ default:
1892
+ return base;
1893
+ }
1894
+ }
1895
+ function getConnectorPathGeometry(element) {
1896
+ return (0, dist_exports.getConnectorPathGeometry)(element);
1897
+ }
1898
+ var ARROW_SIZE_SCALE = {
1899
+ sm: 0.6,
1900
+ med: 1,
1901
+ lg: 1.5
1902
+ };
1903
+ function renderConnectorMarker(markerId, arrowType, color, arrowWidth, arrowLength) {
1904
+ if (!arrowType || arrowType === "none") {
1905
+ return null;
1906
+ }
1907
+ const wScale = ARROW_SIZE_SCALE[arrowWidth || "med"] ?? 1;
1908
+ const lScale = ARROW_SIZE_SCALE[arrowLength || "med"] ?? 1;
1909
+ const mw = Math.round(8 * lScale);
1910
+ const mh = Math.round(8 * wScale);
1911
+ return /* @__PURE__ */ jsx(
1912
+ "marker",
1913
+ {
1914
+ id: markerId,
1915
+ markerWidth: mw,
1916
+ markerHeight: mh,
1917
+ refX: 8,
1918
+ refY: 5,
1919
+ orient: "auto-start-reverse",
1920
+ viewBox: "0 0 10 10",
1921
+ markerUnits: "strokeWidth",
1922
+ children: arrowType === "triangle" ? /* @__PURE__ */ jsx("polygon", { points: "0,0 10,5 0,10", fill: color }) : arrowType === "stealth" ? /* @__PURE__ */ jsx("polygon", { points: "0,0 10,5 0,10 3.4,5", fill: color }) : arrowType === "diamond" ? /* @__PURE__ */ jsx("polygon", { points: "0,5 5,0 10,5 5,10", fill: color }) : arrowType === "oval" ? /* @__PURE__ */ jsx("ellipse", { cx: 5, cy: 5, rx: 4, ry: 3.3, fill: color }) : /* @__PURE__ */ jsx("path", { d: "M0 0 L10 5 L0 10", fill: "none", stroke: color, strokeWidth: 1.6 })
1923
+ }
1924
+ );
1925
+ }
1926
+ function renderSvgGradientDefs(gradient) {
1927
+ if (!gradient) {
1928
+ return null;
1929
+ }
1930
+ if (gradient.kind === "rectPath") {
1931
+ return /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", { id: gradient.id, patternUnits: "objectBoundingBox", width: 1, height: 1, children: /* @__PURE__ */ jsx("image", { href: gradient.href, x: 0, y: 0, width: 1, height: 1, preserveAspectRatio: "none" }) }) });
1932
+ }
1933
+ const stops = gradient.stops.map((stop, index) => /* @__PURE__ */ jsx(
1934
+ "stop",
1935
+ {
1936
+ offset: stop.offset,
1937
+ stopColor: stop.color,
1938
+ stopOpacity: typeof stop.opacity === "number" ? stop.opacity : void 0
1939
+ },
1940
+ index
1941
+ ));
1942
+ return /* @__PURE__ */ jsx("defs", { children: gradient.kind === "radial" ? /* @__PURE__ */ jsx("radialGradient", { id: gradient.id, cx: gradient.cx, cy: gradient.cy, r: gradient.r, children: stops }) : /* @__PURE__ */ jsx(
1943
+ "linearGradient",
1944
+ {
1945
+ id: gradient.id,
1946
+ x1: gradient.x1,
1947
+ y1: gradient.y1,
1948
+ x2: gradient.x2,
1949
+ y2: gradient.y2,
1950
+ children: stops
1951
+ }
1952
+ ) });
1953
+ }
1954
+ function joinStyle(shapeStyle) {
1955
+ const lineJoin = shapeStyle?.lineJoin === "bevel" ? "bevel" : shapeStyle?.lineJoin === "miter" ? "miter" : "round";
1956
+ const miterLimit = lineJoin === "miter" && typeof shapeStyle?.miterLimit === "number" ? Math.max(shapeStyle.miterLimit / 1e5, 1) : void 0;
1957
+ return { lineJoin, miterLimit };
1958
+ }
1959
+ function strokeContext(shapeStyle, strokePaint, strokeWidth, dashArray) {
1960
+ const { lineJoin, miterLimit } = joinStyle(shapeStyle);
1961
+ return {
1962
+ strokePaint,
1963
+ strokeWidth,
1964
+ dashArray,
1965
+ lineCap: svgLineCap(shapeStyle?.lineCap),
1966
+ lineJoin,
1967
+ miterLimit,
1968
+ offsets: getCompoundLineOffsets(shapeStyle?.compoundLine, strokeWidth),
1969
+ widths: getCompoundLineWidths(shapeStyle?.compoundLine, strokeWidth)
1970
+ };
1971
+ }
1972
+ function strokeStrands(d, keyBase, ctx) {
1973
+ return ctx.offsets.map((offset, idx) => /* @__PURE__ */ jsx(
1974
+ "path",
1975
+ {
1976
+ d,
1977
+ fill: "none",
1978
+ stroke: ctx.strokePaint,
1979
+ strokeWidth: Math.max(ctx.widths[idx] ?? ctx.strokeWidth, 1),
1980
+ strokeDasharray: ctx.dashArray,
1981
+ strokeLinecap: ctx.lineCap,
1982
+ strokeLinejoin: ctx.lineJoin,
1983
+ strokeMiterlimit: ctx.miterLimit,
1984
+ vectorEffect: "non-scaling-stroke",
1985
+ style: offset !== 0 ? { transform: `translate(0, ${offset}px)` } : void 0
1986
+ },
1987
+ `${keyBase}-s${idx}`
1988
+ ));
1989
+ }
1990
+ function renderCustomGeometryVector(pathData, pathWidth, pathHeight, structuredPaths, shapeStyle, hasFill, fillColor, fillOpacity, strokePaint, strokeWidth, dashArray, animatesFill = false, gradient) {
1991
+ const ctx = strokeContext(shapeStyle, strokePaint, strokeWidth, dashArray);
1992
+ const subpaths = structuredPaths && structuredPaths.length > 0 ? (0, dist_exports.customGeometryPathsToSvgSubpaths)(structuredPaths, pathWidth, pathHeight) : void 0;
1993
+ const needsPerSubpath = subpaths !== void 0 && subpaths.some((sp) => sp.fillMode && sp.fillMode !== "norm" || sp.stroke === false);
1994
+ const gradientPaint = gradient ? svgGradientFillRef(gradient) : void 0;
1995
+ const nodes = [];
1996
+ if (subpaths && needsPerSubpath) {
1997
+ const paints = buildSubpathPaints(subpaths, hasFill, fillColor, fillOpacity);
1998
+ paints.forEach((paint, idx) => {
1999
+ if (paint.fill !== "none") {
2000
+ const mode = subpaths[idx]?.fillMode;
2001
+ const useGradient = gradientPaint && (mode === void 0 || mode === "norm");
2002
+ nodes.push(
2003
+ /* @__PURE__ */ jsx(
2004
+ "path",
2005
+ {
2006
+ d: paint.d,
2007
+ fill: animatesFill ? "inherit" : useGradient ? gradientPaint : paint.fill,
2008
+ stroke: "none",
2009
+ vectorEffect: "non-scaling-stroke"
2010
+ },
2011
+ `f${idx}`
2012
+ )
2013
+ );
2014
+ }
2015
+ if (paint.stroked && strokeWidth > 0) {
2016
+ nodes.push(...strokeStrands(paint.d, `p${idx}`, ctx));
2017
+ }
2018
+ });
2019
+ } else {
2020
+ if (hasFill) {
2021
+ nodes.push(
2022
+ /* @__PURE__ */ jsx(
2023
+ "path",
2024
+ {
2025
+ d: pathData,
2026
+ fill: animatesFill ? "inherit" : gradientPaint ?? colorWithOpacity(fillColor, fillOpacity),
2027
+ stroke: "none",
2028
+ vectorEffect: "non-scaling-stroke"
2029
+ },
2030
+ "fill"
2031
+ )
2032
+ );
2033
+ }
2034
+ if (strokeWidth > 0) {
2035
+ nodes.push(...strokeStrands(pathData, "agg", ctx));
2036
+ }
2037
+ }
2038
+ return /* @__PURE__ */ jsxs(
2039
+ "svg",
2040
+ {
2041
+ viewBox: `0 0 ${pathWidth} ${pathHeight}`,
2042
+ className: "absolute inset-0 w-full h-full pointer-events-none",
2043
+ preserveAspectRatio: "none",
2044
+ children: [
2045
+ animatesFill ? null : renderSvgGradientDefs(gradient),
2046
+ nodes
2047
+ ]
2048
+ }
2049
+ );
2050
+ }
2051
+ function renderVectorShape(element, hasFill, fillColor, strokeWidth, strokeColor, animatesFill, animatesStroke) {
2052
+ if (!(0, dist_exports.hasShapeProperties)(element)) {
2053
+ return null;
2054
+ }
2055
+ const normalizedType = (element.shapeType || "").toLowerCase();
2056
+ const fillPaint = animatesFill ? "inherit" : colorWithOpacity(fillColor, element.shapeStyle?.fillOpacity);
2057
+ const strokePaint = animatesStroke ? "inherit" : colorWithOpacity(strokeColor, element.shapeStyle?.strokeOpacity);
2058
+ const dashType = normalizeStrokeDashType(element.shapeStyle?.strokeDash);
2059
+ const dashArray = getSvgStrokeDasharray(
2060
+ dashType,
2061
+ Math.max(strokeWidth, 1),
2062
+ element.shapeStyle?.customDashSegments
2063
+ );
2064
+ if (normalizedType === "can" || normalizedType === "cylinder") {
2065
+ const width = Math.max(element.width, 1);
2066
+ const height = Math.max(element.height, 1);
2067
+ const rim = Math.max(Math.min(height * 0.16, 28), 6);
2068
+ const stroke = Math.max(strokeWidth, 1);
2069
+ return /* @__PURE__ */ jsxs(
2070
+ "svg",
2071
+ {
2072
+ viewBox: `0 0 ${width} ${height}`,
2073
+ className: "w-full h-full pointer-events-none",
2074
+ preserveAspectRatio: "none",
2075
+ children: [
2076
+ /* @__PURE__ */ jsx(
2077
+ "ellipse",
2078
+ {
2079
+ cx: width / 2,
2080
+ cy: rim / 2,
2081
+ rx: width / 2,
2082
+ ry: rim / 2,
2083
+ fill: hasFill ? fillPaint : "none",
2084
+ stroke: strokeWidth > 0 ? strokePaint : "none",
2085
+ strokeWidth: stroke,
2086
+ strokeDasharray: dashArray,
2087
+ vectorEffect: "non-scaling-stroke"
2088
+ }
2089
+ ),
2090
+ /* @__PURE__ */ jsx(
2091
+ "rect",
2092
+ {
2093
+ x: 0,
2094
+ y: rim / 2,
2095
+ width,
2096
+ height: Math.max(height - rim, 1),
2097
+ fill: hasFill ? fillPaint : "none",
2098
+ stroke: strokeWidth > 0 ? strokePaint : "none",
2099
+ strokeWidth: stroke,
2100
+ strokeDasharray: dashArray,
2101
+ vectorEffect: "non-scaling-stroke"
2102
+ }
2103
+ ),
2104
+ /* @__PURE__ */ jsx(
2105
+ "ellipse",
2106
+ {
2107
+ cx: width / 2,
2108
+ cy: height - rim / 2,
2109
+ rx: width / 2,
2110
+ ry: rim / 2,
2111
+ fill: hasFill ? fillPaint : "none",
2112
+ stroke: strokeWidth > 0 ? strokePaint : "none",
2113
+ strokeWidth: stroke,
2114
+ strokeDasharray: dashArray,
2115
+ vectorEffect: "non-scaling-stroke"
2116
+ }
2117
+ )
2118
+ ]
2119
+ }
2120
+ );
2121
+ }
2122
+ if ((element.type === "shape" || element.type === "image" || element.type === "picture") && element.pathData && element.pathWidth && element.pathHeight && element.pathWidth > 0 && element.pathHeight > 0) {
2123
+ return renderCustomGeometryVector(
2124
+ element.pathData,
2125
+ element.pathWidth,
2126
+ element.pathHeight,
2127
+ element.customGeometryPaths,
2128
+ element.shapeStyle,
2129
+ hasFill,
2130
+ fillColor,
2131
+ element.shapeStyle?.fillOpacity,
2132
+ strokePaint,
2133
+ strokeWidth,
2134
+ dashArray,
2135
+ Boolean(animatesFill),
2136
+ // A freeform's `a:gradFill` needs a real SVG paint server: the
2137
+ // container's `background-image` is suppressed for custom geometry, so
2138
+ // without this the shape fell back to the parser's representative solid
2139
+ // and lost every fade (issue #132).
2140
+ buildSvgGradientDef(element.shapeStyle, element.id)
2141
+ );
2142
+ }
2143
+ if ((element.type === "image" || element.type === "picture") && strokeWidth > 0 && !buildStrokeOutline(element)) {
2144
+ const width = Math.max(element.width, 1);
2145
+ const height = Math.max(element.height, 1);
2146
+ const geometry = getPresetShapeVectorGeometry(
2147
+ element.shapeType || "rect",
2148
+ width,
2149
+ height,
2150
+ element.shapeAdjustments
2151
+ );
2152
+ if (geometry) {
2153
+ return /* @__PURE__ */ jsx(
2154
+ "svg",
2155
+ {
2156
+ viewBox: `${geometry.minX} ${geometry.minY} ${geometry.viewWidth} ${geometry.viewHeight}`,
2157
+ className: "w-full h-full pointer-events-none",
2158
+ preserveAspectRatio: "none",
2159
+ children: /* @__PURE__ */ jsx(
2160
+ "path",
2161
+ {
2162
+ d: geometry.d,
2163
+ fill: "none",
2164
+ stroke: strokePaint,
2165
+ strokeWidth: strokeWidth * 2,
2166
+ strokeDasharray: dashArray,
2167
+ vectorEffect: "non-scaling-stroke"
2168
+ }
2169
+ )
2170
+ }
2171
+ );
2172
+ }
2173
+ }
2174
+ if (element.type === "shape" && isWedgeCalloutPresetShape(element.shapeType)) {
2175
+ const width = Math.max(element.width, 1);
2176
+ const height = Math.max(element.height, 1);
2177
+ const geometry = getPresetShapeVectorGeometry(
2178
+ element.shapeType,
2179
+ width,
2180
+ height,
2181
+ element.shapeAdjustments
2182
+ );
2183
+ if (geometry) {
2184
+ const gradient = buildSvgGradientDef(element.shapeStyle, element.id);
2185
+ const gradientPaint = gradient ? svgGradientFillRef(gradient) : void 0;
2186
+ return /* @__PURE__ */ jsxs(
2187
+ "svg",
2188
+ {
2189
+ viewBox: `${geometry.minX} ${geometry.minY} ${geometry.viewWidth} ${geometry.viewHeight}`,
2190
+ className: "pointer-events-none",
2191
+ preserveAspectRatio: "none",
2192
+ style: {
2193
+ position: "absolute",
2194
+ left: geometry.minX,
2195
+ top: geometry.minY,
2196
+ width: geometry.viewWidth,
2197
+ height: geometry.viewHeight,
2198
+ overflow: "visible"
2199
+ },
2200
+ children: [
2201
+ animatesFill ? null : renderSvgGradientDefs(gradient),
2202
+ /* @__PURE__ */ jsx(
2203
+ "path",
2204
+ {
2205
+ d: geometry.d,
2206
+ fill: hasFill ? animatesFill ? "inherit" : gradientPaint ?? fillPaint : "none",
2207
+ stroke: strokeWidth > 0 ? strokePaint : "none",
2208
+ strokeWidth,
2209
+ strokeDasharray: dashArray,
2210
+ vectorEffect: "non-scaling-stroke"
2211
+ }
2212
+ )
2213
+ ]
2214
+ }
2215
+ );
2216
+ }
2217
+ }
2218
+ if ((0, dist_exports.isCalloutShape)(normalizedType)) {
2219
+ const width = Math.max(element.width, 1);
2220
+ const height = Math.max(element.height, 1);
2221
+ const geometry = (0, dist_exports.getCalloutLeaderLineGeometry)(
2222
+ normalizedType,
2223
+ width,
2224
+ height,
2225
+ element.shapeAdjustments
2226
+ );
2227
+ if (geometry && geometry.points.length >= 2) {
2228
+ const leaderPath = (0, dist_exports.buildCalloutLeaderLineSvgPath)(geometry);
2229
+ const bounds = (0, dist_exports.getCalloutViewBoxBounds)(width, height, geometry);
2230
+ const lineStroke = Math.max(strokeWidth, 1);
2231
+ const offsetLeft = bounds.minX;
2232
+ const offsetTop = bounds.minY;
2233
+ return /* @__PURE__ */ jsxs(
2234
+ "svg",
2235
+ {
2236
+ viewBox: `${bounds.minX} ${bounds.minY} ${bounds.viewWidth} ${bounds.viewHeight}`,
2237
+ className: "pointer-events-none",
2238
+ preserveAspectRatio: "none",
2239
+ style: {
2240
+ position: "absolute",
2241
+ left: offsetLeft,
2242
+ top: offsetTop,
2243
+ width: bounds.viewWidth,
2244
+ height: bounds.viewHeight,
2245
+ overflow: "visible"
2246
+ },
2247
+ children: [
2248
+ geometry.hasAccent && /* @__PURE__ */ jsx(
2249
+ "line",
2250
+ {
2251
+ x1: 0,
2252
+ y1: 0,
2253
+ x2: width,
2254
+ y2: 0,
2255
+ stroke: strokePaint,
2256
+ strokeWidth: lineStroke,
2257
+ vectorEffect: "non-scaling-stroke"
2258
+ }
2259
+ ),
2260
+ /* @__PURE__ */ jsx(
2261
+ "path",
2262
+ {
2263
+ d: leaderPath,
2264
+ fill: "none",
2265
+ stroke: strokePaint,
2266
+ strokeWidth: lineStroke,
2267
+ strokeDasharray: dashArray,
2268
+ strokeLinecap: "round",
2269
+ strokeLinejoin: "round",
2270
+ vectorEffect: "non-scaling-stroke"
2271
+ }
2272
+ )
2273
+ ]
2274
+ }
2275
+ );
2276
+ }
2277
+ }
2278
+ if (element.type === "connector" || getShapeType(element.shapeType) === "connector" || element.shapeType === "line") {
2279
+ const viewWidth = Math.max(element.width, 1);
2280
+ const viewHeight = Math.max(element.height, 1);
2281
+ const { pathData } = getConnectorPathGeometry(element);
2282
+ const markerSeed = element.id.replace(/[^a-zA-Z0-9_-]/gu, "_");
2283
+ const startMarkerId = `${markerSeed}-start-arrow`;
2284
+ const endMarkerId = `${markerSeed}-end-arrow`;
2285
+ const startArrow = element.shapeStyle?.connectorStartArrow;
2286
+ const endArrow = element.shapeStyle?.connectorEndArrow;
2287
+ const startArrowW = element.shapeStyle?.connectorStartArrowWidth;
2288
+ const startArrowL = element.shapeStyle?.connectorStartArrowLength;
2289
+ const endArrowW = element.shapeStyle?.connectorEndArrowWidth;
2290
+ const endArrowL = element.shapeStyle?.connectorEndArrowLength;
2291
+ const compoundLine = element.shapeStyle?.compoundLine;
2292
+ const connectorLineCap = svgLineCap(element.shapeStyle?.lineCap);
2293
+ const hitTargetWidth = Math.max(strokeWidth * 3, 12);
2294
+ const offsets = getCompoundLineOffsets(compoundLine, strokeWidth);
2295
+ const widths = getCompoundLineWidths(compoundLine, strokeWidth);
2296
+ return /* @__PURE__ */ jsxs(
2297
+ "svg",
2298
+ {
2299
+ viewBox: `0 0 ${viewWidth} ${viewHeight}`,
2300
+ className: "w-full h-full",
2301
+ preserveAspectRatio: "none",
2302
+ style: { overflow: "visible", pointerEvents: "none" },
2303
+ children: [
2304
+ /* @__PURE__ */ jsxs("defs", { children: [
2305
+ renderConnectorMarker(startMarkerId, startArrow, strokePaint, startArrowW, startArrowL),
2306
+ renderConnectorMarker(endMarkerId, endArrow, strokePaint, endArrowW, endArrowL)
2307
+ ] }),
2308
+ /* @__PURE__ */ jsx(
2309
+ "path",
2310
+ {
2311
+ d: pathData,
2312
+ fill: "none",
2313
+ stroke: "transparent",
2314
+ strokeWidth: hitTargetWidth,
2315
+ strokeLinecap: "round",
2316
+ strokeLinejoin: "round",
2317
+ style: { pointerEvents: "stroke" }
2318
+ }
2319
+ ),
2320
+ offsets.map((offset, idx) => /* @__PURE__ */ jsx(
2321
+ "path",
2322
+ {
2323
+ d: pathData,
2324
+ fill: "none",
2325
+ stroke: strokePaint,
2326
+ strokeWidth: Math.max(widths[idx] ?? strokeWidth, 1),
2327
+ strokeDasharray: dashArray,
2328
+ strokeLinecap: connectorLineCap,
2329
+ strokeLinejoin: "round",
2330
+ markerStart: idx === 0 && startArrow && startArrow !== "none" ? `url(#${startMarkerId})` : void 0,
2331
+ markerEnd: idx === offsets.length - 1 && endArrow && endArrow !== "none" ? `url(#${endMarkerId})` : void 0,
2332
+ vectorEffect: "non-scaling-stroke",
2333
+ style: {
2334
+ pointerEvents: "none",
2335
+ ...offset !== 0 ? { transform: `translate(0, ${offset}px)` } : {}
2336
+ }
2337
+ },
2338
+ idx
2339
+ ))
2340
+ ]
2341
+ }
2342
+ );
2343
+ }
2344
+ return null;
2345
+ }
2346
+
2347
+ // src/viewer/utils/table-cell-fill.tsx
2348
+ function parseGradientFillCss(gradFill) {
2349
+ if (!gradFill) {
2350
+ return void 0;
2351
+ }
2352
+ const gsLstRaw = gradFill["a:gsLst"];
2353
+ if (!gsLstRaw) {
2354
+ return void 0;
2355
+ }
2356
+ const stops = ensureArrayValue(gsLstRaw["a:gs"]);
2357
+ if (stops.length === 0) {
2358
+ return void 0;
2359
+ }
2360
+ const rawStops = stops.map((gs) => {
2361
+ const pos = Number.parseInt(String(gs["@_pos"] || "0"), 10) / 1e3;
2362
+ const color = parseDrawingColor(gs) ?? "#888888";
2363
+ const opacity = parseDrawingColorOpacity(gs);
2364
+ return { position: pos, color, opacity };
2365
+ }).sort((a, b) => a.position - b.position);
2366
+ const stopStrings = rawStops.map((s) => `${colorWithOpacity(s.color, s.opacity)} ${s.position.toFixed(1)}%`).join(", ");
2367
+ const lin = gradFill["a:lin"];
2368
+ if (lin) {
2369
+ const angRaw = Number.parseInt(String(lin["@_ang"] || "0"), 10);
2370
+ const angleDeg = Math.round((0, dist_exports.ooxmlGradientAngleToCssDegrees)(angRaw / 6e4));
2371
+ return `linear-gradient(${angleDeg}deg, ${stopStrings})`;
2372
+ }
2373
+ const path = gradFill["a:path"];
2374
+ if (path) {
2375
+ const pathType = String(path["@_path"] || "circle");
2376
+ const fillToRectXml = path["a:fillToRect"];
2377
+ const fillToRect = fillToRectXml ? {
2378
+ l: Number.parseInt(String(fillToRectXml["@_l"] || "0"), 10) / 1e5,
2379
+ t: Number.parseInt(String(fillToRectXml["@_t"] || "0"), 10) / 1e5,
2380
+ r: Number.parseInt(String(fillToRectXml["@_r"] || "0"), 10) / 1e5,
2381
+ b: Number.parseInt(String(fillToRectXml["@_b"] || "0"), 10) / 1e5
2382
+ } : void 0;
2383
+ if (pathType === "rect") {
2384
+ return buildRectPathGradient(rawStops, void 0, fillToRect);
2385
+ }
2386
+ let cx = 50;
2387
+ let cy = 50;
2388
+ if (fillToRect) {
2389
+ const { l, t, r, b } = fillToRect;
2390
+ cx = (l + (1 - r)) / 2 * 100;
2391
+ cy = (t + (1 - b)) / 2 * 100;
2392
+ }
2393
+ const posX = `${Math.round(cx)}%`;
2394
+ const posY = `${Math.round(cy)}%`;
2395
+ if (pathType === "shape") {
2396
+ return `radial-gradient(farthest-side at ${posX} ${posY}, ${stopStrings})`;
2397
+ }
2398
+ return `radial-gradient(circle at ${posX} ${posY}, ${stopStrings})`;
2399
+ }
2400
+ return `linear-gradient(180deg, ${stopStrings})`;
2401
+ }
2402
+ function parsePatternFillCss(pattFill) {
2403
+ if (!pattFill) {
2404
+ return void 0;
2405
+ }
2406
+ const fgColor = parseDrawingColor(pattFill["a:fgClr"]) ?? "#000000";
2407
+ const bgColor = parseDrawingColor(pattFill["a:bgClr"]) ?? "#ffffff";
2408
+ const preset = String(pattFill["@_prst"] || "ltDnDiag");
2409
+ switch (preset) {
2410
+ case "ltHorz":
2411
+ case "horz":
2412
+ case "narHorz":
2413
+ case "dkHorz":
2414
+ case "wdUpDiag":
2415
+ return `repeating-linear-gradient(0deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2416
+ case "ltVert":
2417
+ case "vert":
2418
+ case "narVert":
2419
+ case "dkVert":
2420
+ case "wdDnDiag":
2421
+ return `repeating-linear-gradient(90deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2422
+ case "ltDnDiag":
2423
+ case "dnDiag":
2424
+ case "dkDnDiag":
2425
+ return `repeating-linear-gradient(135deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2426
+ case "ltUpDiag":
2427
+ case "upDiag":
2428
+ case "dkUpDiag":
2429
+ return `repeating-linear-gradient(45deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2430
+ case "dashHorz":
2431
+ case "dashVert":
2432
+ case "dashDnDiag":
2433
+ case "dashUpDiag":
2434
+ return `repeating-linear-gradient(135deg, ${bgColor}, ${bgColor} 4px, ${fgColor} 4px, ${fgColor} 6px, ${bgColor} 6px, ${bgColor} 10px)`;
2435
+ case "smCheck":
2436
+ case "lgCheck":
2437
+ return `repeating-conic-gradient(${fgColor} 0% 25%, ${bgColor} 0% 50%) 0 0 / 8px 8px`;
2438
+ case "smGrid":
2439
+ case "lgGrid":
2440
+ return `repeating-linear-gradient(0deg, ${fgColor} 0px, ${fgColor} 1px, transparent 1px, transparent 6px), repeating-linear-gradient(90deg, ${fgColor} 0px, ${fgColor} 1px, transparent 1px, transparent 6px), ${bgColor}`;
2441
+ case "dotGrid":
2442
+ case "pct5":
2443
+ case "pct10":
2444
+ case "pct20":
2445
+ case "pct25":
2446
+ case "pct30":
2447
+ case "pct40":
2448
+ case "pct50":
2449
+ case "pct60":
2450
+ case "pct70":
2451
+ case "pct75":
2452
+ case "pct80":
2453
+ case "pct90":
2454
+ return `radial-gradient(${fgColor} 1px, ${bgColor} 1px) 0 0 / 4px 4px`;
2455
+ case "smConfetti":
2456
+ case "lgConfetti":
2457
+ case "zigZag":
2458
+ case "wave":
2459
+ case "sphere":
2460
+ case "divot":
2461
+ case "shingle":
2462
+ case "weave":
2463
+ case "plaid":
2464
+ case "trellis":
2465
+ return `repeating-linear-gradient(45deg, ${bgColor}, ${bgColor} 2px, ${fgColor} 2px, ${fgColor} 3px)`;
2466
+ case "solidDmnd":
2467
+ case "openDmnd":
2468
+ return `repeating-conic-gradient(${fgColor} 0% 25%, ${bgColor} 0% 50%) 0 0 / 10px 10px`;
2469
+ default:
2470
+ return `repeating-linear-gradient(135deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2471
+ }
2472
+ }
2473
+ function ooxmlDashToCss(dashVal) {
2474
+ if (!dashVal) {
2475
+ return "solid";
2476
+ }
2477
+ switch (dashVal) {
2478
+ case "dot":
2479
+ case "sysDot":
2480
+ return "dotted";
2481
+ case "dash":
2482
+ case "sysDash":
2483
+ case "lgDash":
2484
+ return "dashed";
2485
+ case "dashDot":
2486
+ case "lgDashDot":
2487
+ case "sysDashDot":
2488
+ case "lgDashDotDot":
2489
+ case "sysDashDotDot":
2490
+ return "dashed";
2491
+ case "solid":
2492
+ return "solid";
2493
+ default:
2494
+ return "solid";
2495
+ }
2496
+ }
2497
+ function parseCellBorders(cellProperties) {
2498
+ if (!cellProperties) {
2499
+ return {};
2500
+ }
2501
+ const borderStyle = {};
2502
+ const edges = [
2503
+ { xml: "a:lnL", prefix: "borderLeft" },
2504
+ { xml: "a:lnR", prefix: "borderRight" },
2505
+ { xml: "a:lnT", prefix: "borderTop" },
2506
+ { xml: "a:lnB", prefix: "borderBottom" }
2507
+ ];
2508
+ for (const edge of edges) {
2509
+ const ln = cellProperties[edge.xml];
2510
+ if (!ln) {
2511
+ continue;
2512
+ }
2513
+ const widthEmu = Number.parseInt(String(ln["@_w"] || "0"), 10);
2514
+ const widthPx = Math.max(1, Math.round(widthEmu / 12700));
2515
+ const prstDash = ln["a:prstDash"];
2516
+ const dashVal = prstDash ? String(prstDash["@_val"] || "") : void 0;
2517
+ const cssBorderStyle = ooxmlDashToCss(dashVal);
2518
+ const color = parseDrawingColor(ln["a:solidFill"]);
2519
+ if (color) {
2520
+ const opacity = parseDrawingColorOpacity(ln["a:solidFill"]);
2521
+ const key = `${edge.prefix}`;
2522
+ borderStyle[key] = `${widthPx}px ${cssBorderStyle} ${colorWithOpacity(color, opacity)}`;
2523
+ }
2524
+ }
2525
+ return borderStyle;
2526
+ }
2527
+ function parseCellTextEffects(runProps) {
2528
+ const effectLst = runProps?.["a:effectLst"];
2529
+ if (!effectLst) {
2530
+ return void 0;
2531
+ }
2532
+ const textShadowParts = [];
2533
+ const outerShdw = effectLst["a:outerShdw"];
2534
+ if (outerShdw) {
2535
+ const shdwColor = parseDrawingColor(outerShdw);
2536
+ if (shdwColor) {
2537
+ const blurRad = Number.parseInt(String(outerShdw["@_blurRad"] || "0"), 10);
2538
+ const blurPx = blurRad > 0 ? Math.round(blurRad / 12700) : 0;
2539
+ const distVal = Number.parseInt(String(outerShdw["@_dist"] || "0"), 10);
2540
+ const dirVal = Number.parseInt(String(outerShdw["@_dir"] || "0"), 10);
2541
+ let offX = 0;
2542
+ let offY = 0;
2543
+ if (distVal > 0) {
2544
+ const angleRad = dirVal / 6e4 * (Math.PI / 180);
2545
+ offX = Math.round(distVal * Math.cos(angleRad) / 12700);
2546
+ offY = Math.round(distVal * Math.sin(angleRad) / 12700);
2547
+ }
2548
+ textShadowParts.push(`${offX}px ${offY}px ${blurPx}px ${shdwColor}`);
2549
+ }
2550
+ }
2551
+ const glow = effectLst["a:glow"];
2552
+ if (glow) {
2553
+ const glowColor = parseDrawingColor(glow);
2554
+ if (glowColor) {
2555
+ const glowRad = Number.parseInt(String(glow["@_rad"] || "0"), 10);
2556
+ const glowPx = glowRad > 0 ? Math.round(glowRad / 12700) : 2;
2557
+ textShadowParts.push(`0px 0px ${glowPx}px ${glowColor}`);
2558
+ }
2559
+ }
2560
+ return textShadowParts.length > 0 ? textShadowParts.join(", ") : void 0;
2561
+ }
2562
+
2563
+ // src/viewer/utils/table-cell-style.tsx
2564
+ function extractCellText(cellXml) {
2565
+ if (!cellXml) {
2566
+ return "";
2567
+ }
2568
+ const txBody = cellXml["a:txBody"];
2569
+ const paragraphs = ensureArrayValue(txBody?.["a:p"]);
2570
+ const paragraphTexts = [];
2571
+ paragraphs.forEach((paragraph) => {
2572
+ const textParts = [];
2573
+ const runs = ensureArrayValue(paragraph["a:r"]);
2574
+ runs.forEach((run) => {
2575
+ textParts.push((0, dist_exports.xmlText)(run["a:t"]) ?? "");
2576
+ });
2577
+ const fields = ensureArrayValue(paragraph["a:fld"]);
2578
+ fields.forEach((field) => {
2579
+ textParts.push((0, dist_exports.xmlText)(field["a:t"]) ?? "");
2580
+ });
2581
+ paragraphTexts.push(textParts.join(""));
2582
+ });
2583
+ return paragraphTexts.join("\n");
2584
+ }
2585
+ function parseParagraphAlignment(value) {
2586
+ const alignment = String(value || "").toLowerCase();
2587
+ if (alignment === "ctr") {
2588
+ return "center";
2589
+ }
2590
+ if (alignment === "r") {
2591
+ return "right";
2592
+ }
2593
+ if (alignment === "just" || alignment === "justify") {
2594
+ return "justify";
2595
+ }
2596
+ return "left";
2597
+ }
2598
+ function extractTableCellStyle(cellXml, fallbackStyle, schemeColorOverrides) {
2599
+ const { color: _fallbackColor, ...fallbackWithoutColor } = fallbackStyle;
2600
+ const cellStyle = { ...fallbackWithoutColor };
2601
+ const txBody = cellXml["a:txBody"];
2602
+ const paragraphs = ensureArrayValue(txBody?.["a:p"]);
2603
+ const firstParagraph = paragraphs[0];
2604
+ const paragraphProperties = firstParagraph?.["a:pPr"];
2605
+ const runs = ensureArrayValue(firstParagraph?.["a:r"]);
2606
+ const firstRun = runs[0];
2607
+ const runProps = firstRun?.["a:rPr"] || paragraphProperties?.["a:defRPr"];
2608
+ if (runProps?.["@_sz"]) {
2609
+ const pointSize = Number.parseInt(String(runProps["@_sz"]), 10) / 100;
2610
+ if (Number.isFinite(pointSize) && pointSize > 0) {
2611
+ cellStyle.fontSize = pointSize * (96 / 72);
2612
+ }
2613
+ }
2614
+ if (runProps?.["@_b"] !== void 0) {
2615
+ cellStyle.fontWeight = runProps["@_b"] === "1" ? 700 : 400;
2616
+ }
2617
+ if (runProps?.["@_i"] !== void 0) {
2618
+ cellStyle.fontStyle = runProps["@_i"] === "1" ? "italic" : "normal";
2619
+ }
2620
+ if (runProps?.["@_u"] !== void 0) {
2621
+ cellStyle.textDecorationLine = runProps["@_u"] === "sng" ? "underline" : "none";
2622
+ }
2623
+ const runColor = parseDrawingColor(runProps?.["a:solidFill"], schemeColorOverrides) || parseDrawingColor(runProps?.["a:highlight"], schemeColorOverrides);
2624
+ if (runColor) {
2625
+ const opacity = parseDrawingColorOpacity(runProps?.["a:solidFill"]);
2626
+ cellStyle.color = colorWithOpacity(runColor, opacity);
2627
+ }
2628
+ const fontFamily = runProps?.["a:latin"]?.["@_typeface"] || runProps?.["a:ea"]?.["@_typeface"] || runProps?.["a:cs"]?.["@_typeface"];
2629
+ if (fontFamily) {
2630
+ cellStyle.fontFamily = fontFamily;
2631
+ }
2632
+ if (paragraphProperties?.["@_algn"]) {
2633
+ cellStyle.textAlign = parseParagraphAlignment(paragraphProperties["@_algn"]);
2634
+ }
2635
+ const cellProperties = cellXml["a:tcPr"];
2636
+ const cellFill = parseDrawingColor(
2637
+ cellProperties?.["a:solidFill"],
2638
+ schemeColorOverrides
2639
+ );
2640
+ if (cellFill) {
2641
+ const opacity = parseDrawingColorOpacity(
2642
+ cellProperties?.["a:solidFill"]
2643
+ );
2644
+ cellStyle.backgroundColor = colorWithOpacity(cellFill, opacity);
2645
+ } else {
2646
+ const gradCss = parseGradientFillCss(cellProperties?.["a:gradFill"]);
2647
+ if (gradCss) {
2648
+ cellStyle.background = gradCss;
2649
+ } else {
2650
+ const pattCss = parsePatternFillCss(cellProperties?.["a:pattFill"]);
2651
+ if (pattCss) {
2652
+ cellStyle.background = pattCss;
2653
+ }
2654
+ }
2655
+ }
2656
+ const edgeBorders = parseCellBorders(cellProperties);
2657
+ Object.assign(cellStyle, edgeBorders);
2658
+ if (!cellStyle.borderLeftColor && !cellStyle.borderRightColor && !cellStyle.borderTopColor && !cellStyle.borderBottomColor) {
2659
+ const borderColor = parseDrawingColor(
2660
+ cellProperties?.["a:lnL"]?.["a:solidFill"],
2661
+ schemeColorOverrides
2662
+ ) || parseDrawingColor(
2663
+ cellProperties?.["a:lnR"]?.["a:solidFill"],
2664
+ schemeColorOverrides
2665
+ ) || parseDrawingColor(
2666
+ cellProperties?.["a:lnT"]?.["a:solidFill"],
2667
+ schemeColorOverrides
2668
+ ) || parseDrawingColor(
2669
+ cellProperties?.["a:lnB"]?.["a:solidFill"],
2670
+ schemeColorOverrides
2671
+ );
2672
+ if (borderColor) {
2673
+ cellStyle.borderColor = borderColor;
2674
+ }
2675
+ }
2676
+ if (cellProperties) {
2677
+ const margin = (key) => Number.parseInt(String(cellProperties[key] ?? ""), 10) / EMU_PER_PX;
2678
+ const marL = margin("@_marL");
2679
+ const marR = margin("@_marR");
2680
+ const marT = margin("@_marT");
2681
+ const marB = margin("@_marB");
2682
+ if (marL >= 0) {
2683
+ cellStyle.paddingLeft = marL;
2684
+ }
2685
+ if (marR >= 0) {
2686
+ cellStyle.paddingRight = marR;
2687
+ }
2688
+ if (marT >= 0) {
2689
+ cellStyle.paddingTop = marT;
2690
+ }
2691
+ if (marB >= 0) {
2692
+ cellStyle.paddingBottom = marB;
2693
+ }
2694
+ }
2695
+ if (cellProperties?.["@_anchor"]) {
2696
+ const anchor = String(cellProperties["@_anchor"]);
2697
+ if (anchor === "ctr") {
2698
+ cellStyle.verticalAlign = "middle";
2699
+ } else if (anchor === "b") {
2700
+ cellStyle.verticalAlign = "bottom";
2701
+ } else {
2702
+ cellStyle.verticalAlign = "top";
2703
+ }
2704
+ }
2705
+ if (cellProperties?.["@_vert"]) {
2706
+ const vert = String(cellProperties["@_vert"]);
2707
+ switch (vert) {
2708
+ case "vert":
2709
+ case "eaVert":
2710
+ case "wordArtVert":
2711
+ case "wordArtVertRtl":
2712
+ cellStyle.writingMode = "vertical-rl";
2713
+ break;
2714
+ case "vert270":
2715
+ case "mongolianVert":
2716
+ cellStyle.writingMode = "vertical-lr";
2717
+ break;
2718
+ }
2719
+ if (vert === "wordArtVert") {
2720
+ cellStyle.textOrientation = "upright";
2721
+ } else if (cellStyle.writingMode) {
2722
+ cellStyle.textOrientation = "mixed";
2723
+ }
2724
+ if (vert === "wordArtVertRtl") {
2725
+ cellStyle.direction = "rtl";
2726
+ }
2727
+ }
2728
+ const textShadow = parseCellTextEffects(runProps);
2729
+ if (textShadow) {
2730
+ cellStyle.textShadow = textShadow;
2731
+ }
2732
+ return cellStyle;
2733
+ }
2734
+ function getTableCellBandStyle(element, rowIndex, cellIndex, rowCount, columnCount, styleCtx) {
2735
+ if (element.type !== "table") {
2736
+ return void 0;
2737
+ }
2738
+ return getTableCellBandStyle$1(element.tableData, rowIndex, cellIndex, rowCount, columnCount, {
2739
+ tableStyleMap: styleCtx?.tableStyleMap,
2740
+ colorScheme: styleCtx?.theme?.colorScheme,
2741
+ fontScheme: styleCtx?.theme?.fontScheme
2742
+ });
2743
+ }
2744
+
2745
+ // src/viewer/utils/table-data-parse.tsx
2746
+ function getGraphicDataFromElement(element) {
2747
+ return element.rawXml?.["a:graphic"]?.["a:graphicData"];
2748
+ }
2749
+ function getTableXmlFromElement(element) {
2750
+ return getGraphicDataFromElement(element)?.["a:tbl"];
2751
+ }
2752
+ function parseTableElementData(element, fallbackTextStyle) {
2753
+ const table = getTableXmlFromElement(element);
2754
+ if (!table) {
2755
+ return null;
2756
+ }
2757
+ const rows = ensureArrayValue(table["a:tr"]);
2758
+ if (rows.length === 0) {
2759
+ return null;
2760
+ }
2761
+ const tableGrid = table["a:tblGrid"];
2762
+ const gridColumns = ensureArrayValue(
2763
+ tableGrid?.["a:gridCol"]
2764
+ );
2765
+ const columnWidths = gridColumns.map((column) => {
2766
+ const width = Number.parseInt(String(column?.["@_w"] || ""), 10);
2767
+ return Number.isFinite(width) && width > 0 ? width : 0;
2768
+ });
2769
+ const totalColumnWidth = columnWidths.reduce((sum, width) => sum + width, 0);
2770
+ let columnPercentages = totalColumnWidth > 0 ? columnWidths.map((width) => width / totalColumnWidth * 100) : [];
2771
+ const modelWidths = element.type === "table" ? element.tableData?.columnWidths : void 0;
2772
+ if (modelWidths?.length && modelWidths.length === gridColumns.length && modelWidths.every((width) => Number.isFinite(width) && width >= 0) && Math.abs(modelWidths.reduce((sum, width) => sum + width, 0) - 1) <= Number.EPSILON * modelWidths.length * 4) {
2773
+ columnPercentages = modelWidths.map((width) => width * 100);
2774
+ }
2775
+ const cells = [];
2776
+ rows.forEach((row, rowIndex) => {
2777
+ const rowCells = ensureArrayValue(row["a:tc"]);
2778
+ rowCells.forEach((cell, columnIndex) => {
2779
+ cells.push({
2780
+ rowIndex,
2781
+ columnIndex,
2782
+ text: extractCellText(cell),
2783
+ style: extractTableCellStyle(cell, fallbackTextStyle),
2784
+ rawCell: cell
2785
+ });
2786
+ });
2787
+ });
2788
+ const columnCount = Math.max(
2789
+ columnPercentages.length,
2790
+ rows.reduce((maxCount, row) => {
2791
+ const rowCells = ensureArrayValue(row["a:tc"]);
2792
+ return Math.max(maxCount, rowCells.length);
2793
+ }, 0)
2794
+ );
2795
+ return {
2796
+ rowCount: rows.length,
2797
+ columnCount,
2798
+ rows,
2799
+ columnPercentages,
2800
+ cells
2801
+ };
2802
+ }
2803
+ function renderTableCellContent(cell, fallbackText) {
2804
+ const runs = cell?.textRuns;
2805
+ if (!runs || runs.length === 0) {
2806
+ return fallbackText || "\xA0";
2807
+ }
2808
+ return runs.map((run, index) => {
2809
+ if (run.isParagraphBreak) {
2810
+ return /* @__PURE__ */ jsx("div", { style: { display: "block", height: 0 } }, `p-${index}`);
2811
+ }
2812
+ if (run.isLineBreak) {
2813
+ return /* @__PURE__ */ jsx("br", {}, `br-${index}`);
2814
+ }
2815
+ return /* @__PURE__ */ jsx(
2816
+ "span",
2817
+ {
2818
+ style: { position: "relative", ...cellRunStyle(run) },
2819
+ children: run.text
2820
+ },
2821
+ `r-${index}`
2822
+ );
2823
+ });
2824
+ }
2825
+ function getCellDiagonalBorders(style, tableData, pos, styleCtx) {
2826
+ return getCellDiagonalBorders$1(style, tableData, pos, {
2827
+ tableStyleMap: styleCtx?.tableStyleMap,
2828
+ colorScheme: styleCtx?.theme?.colorScheme,
2829
+ fontScheme: styleCtx?.theme?.fontScheme
2830
+ });
2831
+ }
2832
+ function TableCellDiagonalBorders({
2833
+ diag
2834
+ }) {
2835
+ const hasDown = Boolean(diag.diagDownColor && diag.diagDownWidth);
2836
+ const hasUp = Boolean(diag.diagUpColor && diag.diagUpWidth);
2837
+ if (!hasDown && !hasUp) {
2838
+ return null;
2839
+ }
2840
+ return /* @__PURE__ */ jsxs(
2841
+ "svg",
2842
+ {
2843
+ "aria-hidden": "true",
2844
+ style: {
2845
+ position: "absolute",
2846
+ inset: 0,
2847
+ width: "100%",
2848
+ height: "100%",
2849
+ pointerEvents: "none",
2850
+ overflow: "visible"
2851
+ },
2852
+ children: [
2853
+ hasDown && /* @__PURE__ */ jsx(
2854
+ "line",
2855
+ {
2856
+ x1: "0",
2857
+ y1: "0",
2858
+ x2: "100%",
2859
+ y2: "100%",
2860
+ stroke: diag.diagDownColor,
2861
+ strokeWidth: diag.diagDownWidth
2862
+ }
2863
+ ),
2864
+ hasUp && /* @__PURE__ */ jsx(
2865
+ "line",
2866
+ {
2867
+ x1: "0",
2868
+ y1: "100%",
2869
+ x2: "100%",
2870
+ y2: "0",
2871
+ stroke: diag.diagUpColor,
2872
+ strokeWidth: diag.diagUpWidth
2873
+ }
2874
+ )
2875
+ ]
2876
+ }
2877
+ );
2878
+ }
2879
+ function TableCellInput({
2880
+ initialText,
2881
+ style,
2882
+ onCommit,
2883
+ onCancel
2884
+ }) {
2885
+ const inputRef = useRef(null);
2886
+ const committedRef = useRef(false);
2887
+ const onCommitRef = useRef(onCommit);
2888
+ onCommitRef.current = onCommit;
2889
+ useEffect(() => {
2890
+ const el = inputRef.current;
2891
+ if (el) {
2892
+ el.focus();
2893
+ el.select();
2894
+ }
2895
+ }, []);
2896
+ useEffect(() => {
2897
+ const input = inputRef.current;
2898
+ return () => {
2899
+ if (!committedRef.current && input) {
2900
+ onCommitRef.current(input.value);
2901
+ }
2902
+ };
2903
+ }, []);
2904
+ const doCommit = (text) => {
2905
+ if (committedRef.current) {
2906
+ return;
2907
+ }
2908
+ committedRef.current = true;
2909
+ onCommit(text);
2910
+ };
2911
+ const doCancel = () => {
2912
+ committedRef.current = true;
2913
+ onCancel();
2914
+ };
2915
+ return /* @__PURE__ */ jsx(
2916
+ "input",
2917
+ {
2918
+ ref: inputRef,
2919
+ type: "text",
2920
+ defaultValue: initialText,
2921
+ className: "w-full bg-transparent outline-none",
2922
+ style: {
2923
+ ...style,
2924
+ padding: 0,
2925
+ margin: 0,
2926
+ border: "none"
2927
+ },
2928
+ onPointerDown: (e) => e.stopPropagation(),
2929
+ onMouseDown: (e) => e.stopPropagation(),
2930
+ onClick: (e) => e.stopPropagation(),
2931
+ onDoubleClick: (e) => e.stopPropagation(),
2932
+ onBlur: (e) => doCommit(e.currentTarget.value),
2933
+ onKeyDown: (e) => {
2934
+ e.stopPropagation();
2935
+ if (e.key === "Escape") {
2936
+ e.preventDefault();
2937
+ doCancel();
2938
+ } else if (e.key === "Enter") {
2939
+ e.preventDefault();
2940
+ doCommit(e.currentTarget.value);
2941
+ } else if (e.key === "Tab") {
2942
+ e.preventDefault();
2943
+ doCommit(e.currentTarget.value);
2944
+ }
2945
+ }
2946
+ }
2947
+ );
2948
+ }
2949
+ function TableResizeOverlay({
2950
+ children,
2951
+ columnWidths,
2952
+ editable,
2953
+ onResizeColumns,
2954
+ onResizeRow
2955
+ }) {
2956
+ const containerRef = useRef(null);
2957
+ const [rowBounds, setRowBounds] = useState([]);
2958
+ const dragRef = useRef(null);
2959
+ const colBoundaries = useMemo(() => computeColumnBoundaries(columnWidths), [columnWidths]);
2960
+ const measureRows = useCallback(() => {
2961
+ const container = containerRef.current;
2962
+ if (!container) {
2963
+ return;
2964
+ }
2965
+ const table = container.querySelector("table");
2966
+ if (!table) {
2967
+ return;
2968
+ }
2969
+ const trs = table.querySelectorAll("tbody > tr");
2970
+ const bounds = [];
2971
+ let cumHeight = 0;
2972
+ trs.forEach((tr, i) => {
2973
+ cumHeight += tr.offsetHeight;
2974
+ if (i < trs.length - 1) {
2975
+ bounds.push(cumHeight);
2976
+ }
2977
+ });
2978
+ setRowBounds((prev) => {
2979
+ if (prev.length === bounds.length && prev.every((v, i) => v === bounds[i])) {
2980
+ return prev;
2981
+ }
2982
+ return bounds;
2983
+ });
2984
+ }, []);
2985
+ useLayoutEffect(() => {
2986
+ measureRows();
2987
+ });
2988
+ useEffect(() => {
2989
+ const handleMouseMove = (e) => {
2990
+ const drag = dragRef.current;
2991
+ if (!drag) {
2992
+ return;
2993
+ }
2994
+ e.preventDefault();
2995
+ const delta = drag.type === "col" ? e.clientX - drag.startPos : e.clientY - drag.startPos;
2996
+ drag.handleEl.style.transform = drag.type === "col" ? `translateX(${delta}px)` : `translateY(${delta}px)`;
2997
+ };
2998
+ const handleMouseUp = (e) => {
2999
+ const drag = dragRef.current;
3000
+ if (!drag || !containerRef.current) {
3001
+ return;
3002
+ }
3003
+ const rect = containerRef.current.getBoundingClientRect();
3004
+ if (e.clientX !== drag.startPos && drag.type === "col" && drag.initialWidths && onResizeColumns) {
3005
+ const deltaProp = (e.clientX - drag.startPos) / rect.width;
3006
+ onResizeColumns(computeResizedColumnWidths(drag.initialWidths, drag.index, deltaProp));
3007
+ } else if (e.clientY !== drag.startPos && drag.type === "row" && onResizeRow) {
3008
+ const deltaY = e.clientY - drag.startPos;
3009
+ const newHeight = computeResizedRowHeight(
3010
+ drag.initialRowHeight ?? DEFAULT_ROW_HEIGHT,
3011
+ deltaY
3012
+ );
3013
+ onResizeRow(drag.index, newHeight);
3014
+ }
3015
+ drag.handleEl.style.transform = "";
3016
+ document.body.style.cursor = "";
3017
+ document.body.style.userSelect = "";
3018
+ dragRef.current = null;
3019
+ };
3020
+ document.addEventListener("mousemove", handleMouseMove);
3021
+ document.addEventListener("mouseup", handleMouseUp);
3022
+ return () => {
3023
+ document.removeEventListener("mousemove", handleMouseMove);
3024
+ document.removeEventListener("mouseup", handleMouseUp);
3025
+ };
3026
+ }, [onResizeColumns, onResizeRow]);
3027
+ if (!editable) {
3028
+ return children;
3029
+ }
3030
+ const startColDrag = (e, index) => {
3031
+ e.preventDefault();
3032
+ e.stopPropagation();
3033
+ document.body.style.cursor = "col-resize";
3034
+ document.body.style.userSelect = "none";
3035
+ dragRef.current = {
3036
+ type: "col",
3037
+ index,
3038
+ startPos: e.clientX,
3039
+ handleEl: e.currentTarget,
3040
+ initialWidths: [...columnWidths]
3041
+ };
3042
+ };
3043
+ const startRowDrag = (e, index) => {
3044
+ e.preventDefault();
3045
+ e.stopPropagation();
3046
+ const table = containerRef.current?.querySelector("table");
3047
+ const tr = table?.querySelectorAll("tbody > tr")[index];
3048
+ const actualHeight = tr?.offsetHeight ?? DEFAULT_ROW_HEIGHT;
3049
+ document.body.style.cursor = "row-resize";
3050
+ document.body.style.userSelect = "none";
3051
+ dragRef.current = {
3052
+ type: "row",
3053
+ index,
3054
+ startPos: e.clientY,
3055
+ handleEl: e.currentTarget,
3056
+ initialRowHeight: actualHeight
3057
+ };
3058
+ };
3059
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative w-full h-full", children: [
3060
+ children,
3061
+ colBoundaries.map((leftPct, i) => /* @__PURE__ */ jsx(
3062
+ "div",
3063
+ {
3064
+ className: "absolute top-0 bottom-0 w-[6px] cursor-col-resize z-10 pointer-events-auto group",
3065
+ style: { left: `calc(${leftPct}% - 3px)` },
3066
+ onMouseDown: (e) => startColDrag(e, i),
3067
+ children: /* @__PURE__ */ jsx("div", { className: "w-px h-full mx-auto bg-transparent group-hover:bg-blue-400/60 transition-colors" })
3068
+ },
3069
+ `col-h-${i}`
3070
+ )),
3071
+ rowBounds.map((topPx, i) => /* @__PURE__ */ jsx(
3072
+ "div",
3073
+ {
3074
+ className: "absolute left-0 right-0 h-[6px] cursor-row-resize z-10 pointer-events-auto group",
3075
+ style: { top: `${topPx - 3}px` },
3076
+ onMouseDown: (e) => startRowDrag(e, i),
3077
+ children: /* @__PURE__ */ jsx("div", { className: "h-px w-full my-auto bg-transparent group-hover:bg-blue-400/60 transition-colors" })
3078
+ },
3079
+ `row-h-${i}`
3080
+ ))
3081
+ ] });
3082
+ }
3083
+ function renderTableFromTableData(element, textStyle, options) {
3084
+ const tableData = element.tableData;
3085
+ const rowCount = tableData.rows.length;
3086
+ const columnCount = tableData.columnWidths.length;
3087
+ const selectedCell = options?.selectedCell || null;
3088
+ const isEditable = Boolean(options?.editable) && canDrillDown(element);
3089
+ const hasCellSelectionHandler = typeof options?.onSelectCell === "function";
3090
+ const sharedCtx = {
3091
+ tableStyleMap: options?.styleCtx?.tableStyleMap,
3092
+ colorScheme: options?.styleCtx?.theme?.colorScheme,
3093
+ fontScheme: options?.styleCtx?.theme?.fontScheme
3094
+ };
3095
+ const cellCss = (cell, rowIndex, cellIndex) => tableCellCss(
3096
+ tableData,
3097
+ cell,
3098
+ { rowIndex, cellIndex, rowCount, columnCount },
3099
+ sharedCtx,
3100
+ textStyle
3101
+ );
3102
+ const selectionRect = (() => {
3103
+ if (!selectedCell?.selectedCells || selectedCell.selectedCells.length < 2) {
3104
+ return void 0;
3105
+ }
3106
+ const first = selectedCell.selectedCells[0];
3107
+ const last = selectedCell.selectedCells[selectedCell.selectedCells.length - 1];
3108
+ return computeSelectionRect(first.row, first.col, last.row, last.col, tableData);
3109
+ })();
3110
+ return /* @__PURE__ */ jsx(
3111
+ TableResizeOverlay,
3112
+ {
3113
+ columnWidths: tableData.columnWidths,
3114
+ editable: isEditable,
3115
+ onResizeColumns: options?.onResizeColumns,
3116
+ onResizeRow: options?.onResizeRow,
3117
+ children: /* @__PURE__ */ jsx(
3118
+ "div",
3119
+ {
3120
+ className: cn(
3121
+ "w-full h-full overflow-hidden",
3122
+ isEditable && hasCellSelectionHandler ? "pointer-events-auto" : "pointer-events-none"
3123
+ ),
3124
+ children: /* @__PURE__ */ jsxs(
3125
+ "table",
3126
+ {
3127
+ className: "w-full h-full border-collapse table-fixed",
3128
+ style: {
3129
+ fontFamily: DEFAULT_FONT_FAMILY$1,
3130
+ // `a:tblPr@rtl` mirrors the column order for RTL decks.
3131
+ ...tableContainerCss(tableData)
3132
+ },
3133
+ children: [
3134
+ tableData.columnWidths.length > 0 && /* @__PURE__ */ jsx("colgroup", { children: tableData.columnWidths.map((w, ci) => /* @__PURE__ */ jsx(
3135
+ "col",
3136
+ {
3137
+ style: { width: `${(w * 100).toFixed(2)}%` }
3138
+ },
3139
+ `${element.id}-col-${ci}`
3140
+ )) }),
3141
+ /* @__PURE__ */ jsx("tbody", { children: tableData.rows.map((row, rowIndex) => /* @__PURE__ */ jsx(
3142
+ "tr",
3143
+ {
3144
+ style: row.height ? { height: row.height } : void 0,
3145
+ children: row.cells.map((cell, cellIndex) => {
3146
+ if (cell.hMerge || cell.vMerge) {
3147
+ return null;
3148
+ }
3149
+ const isCellSelected = selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex;
3150
+ const isInMultiSelection = isCellInRect(rowIndex, cellIndex, selectionRect);
3151
+ const isCellEditing = isCellSelected && selectedCell?.isEditing;
3152
+ const diag = getCellDiagonalBorders(
3153
+ cell.style,
3154
+ tableData,
3155
+ { rowIndex, cellIndex, rowCount, columnCount },
3156
+ options?.styleCtx
3157
+ );
3158
+ return /* @__PURE__ */ jsxs(
3159
+ "td",
3160
+ {
3161
+ className: cn(
3162
+ "px-1 py-0.5 align-top",
3163
+ // No border of its own: a side the deck gives no border renders with
3164
+ // none, as PowerPoint does. Editing keeps a faint grid.
3165
+ isEditable && hasCellSelectionHandler ? "border border-blue-200/70 cursor-cell" : null,
3166
+ isCellSelected ? "ring-1 ring-inset ring-blue-500" : null,
3167
+ isInMultiSelection && !isCellSelected ? "bg-blue-500/15 ring-1 ring-inset ring-blue-400/50" : null
3168
+ ),
3169
+ colSpan: cell.gridSpan && cell.gridSpan > 1 ? cell.gridSpan : void 0,
3170
+ rowSpan: cell.rowSpan && cell.rowSpan > 1 ? cell.rowSpan : void 0,
3171
+ style: {
3172
+ ...cellCss(cell, rowIndex, cellIndex),
3173
+ ...diag ? { position: "relative" } : void 0
3174
+ },
3175
+ onClick: (event) => {
3176
+ if (!isEditable || !hasCellSelectionHandler) {
3177
+ return;
3178
+ }
3179
+ event.stopPropagation();
3180
+ if (event.shiftKey && selectedCell) {
3181
+ const rect = computeSelectionRect(
3182
+ selectedCell.rowIndex,
3183
+ selectedCell.columnIndex,
3184
+ rowIndex,
3185
+ cellIndex,
3186
+ tableData
3187
+ );
3188
+ options?.onSelectCell?.({
3189
+ rowIndex: selectedCell.rowIndex,
3190
+ columnIndex: selectedCell.columnIndex,
3191
+ selectedCells: rectToCells(rect)
3192
+ });
3193
+ } else {
3194
+ options?.onSelectCell?.({
3195
+ rowIndex,
3196
+ columnIndex: cellIndex
3197
+ });
3198
+ }
3199
+ },
3200
+ onDoubleClick: (event) => {
3201
+ if (!isEditable || !hasCellSelectionHandler) {
3202
+ return;
3203
+ }
3204
+ event.stopPropagation();
3205
+ options?.onSelectCell?.({
3206
+ rowIndex,
3207
+ columnIndex: cellIndex,
3208
+ isEditing: true
3209
+ });
3210
+ },
3211
+ children: [
3212
+ diag ? /* @__PURE__ */ jsx(TableCellDiagonalBorders, { diag }) : null,
3213
+ isCellEditing ? /* @__PURE__ */ jsx(
3214
+ TableCellInput,
3215
+ {
3216
+ initialText: cell.text ?? "",
3217
+ style: cellCss(cell, rowIndex, cellIndex),
3218
+ onCommit: (text) => {
3219
+ options?.onCommitCellEdit?.(rowIndex, cellIndex, text);
3220
+ },
3221
+ onCancel: () => {
3222
+ options?.onSelectCell?.({
3223
+ rowIndex,
3224
+ columnIndex: cellIndex
3225
+ });
3226
+ }
3227
+ }
3228
+ ) : renderTableCellContent(cell, cell.text || "\xA0")
3229
+ ]
3230
+ },
3231
+ `${element.id}-cell-${rowIndex}-${cellIndex}`
3232
+ );
3233
+ })
3234
+ },
3235
+ `${element.id}-row-${rowIndex}`
3236
+ )) })
3237
+ ]
3238
+ }
3239
+ )
3240
+ }
3241
+ )
3242
+ }
3243
+ );
3244
+ }
3245
+ function renderTableElement(element, textStyle, options) {
3246
+ const parsedTable = parseTableElementData(element, textStyle);
3247
+ if (!parsedTable) {
3248
+ const tableEl2 = element;
3249
+ if (tableEl2.tableData && tableEl2.tableData.rows.length > 0) {
3250
+ return renderTableFromTableData(tableEl2, textStyle, options);
3251
+ }
3252
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.table.title"] });
3253
+ }
3254
+ const selectedCell = options?.selectedCell || null;
3255
+ const isEditable = Boolean(options?.editable) && canDrillDown(element);
3256
+ const hasCellSelectionHandler = typeof options?.onSelectCell === "function";
3257
+ const tableEl = element;
3258
+ const xmlSelectionRect = (() => {
3259
+ if (!selectedCell?.selectedCells || selectedCell.selectedCells.length < 2) {
3260
+ return void 0;
3261
+ }
3262
+ if (!tableEl.tableData) {
3263
+ return void 0;
3264
+ }
3265
+ const first = selectedCell.selectedCells[0];
3266
+ const last = selectedCell.selectedCells[selectedCell.selectedCells.length - 1];
3267
+ return computeSelectionRect(first.row, first.col, last.row, last.col, tableEl.tableData);
3268
+ })();
3269
+ const schemeOverrides = options?.styleCtx?.theme?.colorScheme ? options.styleCtx.theme.colorScheme : void 0;
3270
+ return /* @__PURE__ */ jsx(
3271
+ TableResizeOverlay,
3272
+ {
3273
+ columnWidths: parsedTable.columnPercentages.map((p) => p / 100),
3274
+ editable: isEditable,
3275
+ onResizeColumns: options?.onResizeColumns,
3276
+ onResizeRow: options?.onResizeRow,
3277
+ children: /* @__PURE__ */ jsx(
3278
+ "div",
3279
+ {
3280
+ className: cn(
3281
+ "w-full h-full overflow-hidden",
3282
+ isEditable && hasCellSelectionHandler ? "pointer-events-auto" : "pointer-events-none"
3283
+ ),
3284
+ children: /* @__PURE__ */ jsxs(
3285
+ "table",
3286
+ {
3287
+ className: "w-full h-full border-collapse table-fixed",
3288
+ style: {
3289
+ fontFamily: DEFAULT_FONT_FAMILY$1,
3290
+ // `a:tblPr@rtl` mirrors the column order for RTL decks.
3291
+ ...tableContainerCss(tableEl.tableData)
3292
+ },
3293
+ children: [
3294
+ parsedTable.columnPercentages.length > 0 ? /* @__PURE__ */ jsx("colgroup", { children: parsedTable.columnPercentages.map((percentage, columnIndex) => /* @__PURE__ */ jsx(
3295
+ "col",
3296
+ {
3297
+ style: { width: `${percentage.toFixed(2)}%` }
3298
+ },
3299
+ `${element.id}-col-${columnIndex}`
3300
+ )) }) : null,
3301
+ /* @__PURE__ */ jsx("tbody", { children: parsedTable.rows.map((row, rowIndex) => {
3302
+ const cells = ensureArrayValue(row["a:tc"]);
3303
+ const rowHeightRaw = Number.parseInt(String(row["@_h"] || ""), 10);
3304
+ const rowHeight = Number.isFinite(rowHeightRaw) && rowHeightRaw > 0 ? Math.max(16, rowHeightRaw / EMU_PER_PX) : void 0;
3305
+ return /* @__PURE__ */ jsx(
3306
+ "tr",
3307
+ {
3308
+ style: rowHeight ? { height: rowHeight } : void 0,
3309
+ children: cells.map((cell, cellIndex) => {
3310
+ const isHMerged = cell["@_hMerge"] === "1";
3311
+ const isVMerged = cell["@_vMerge"] === "1";
3312
+ if (isHMerged || isVMerged) {
3313
+ return null;
3314
+ }
3315
+ const gridSpanRaw = Number.parseInt(String(cell["@_gridSpan"] || ""), 10);
3316
+ const colSpan = Number.isFinite(gridSpanRaw) && gridSpanRaw > 1 ? gridSpanRaw : void 0;
3317
+ const rowSpanRaw = Number.parseInt(String(cell["@_rowSpan"] || ""), 10);
3318
+ const rSpan = Number.isFinite(rowSpanRaw) && rowSpanRaw > 1 ? rowSpanRaw : void 0;
3319
+ const bandStyle = getTableCellBandStyle(
3320
+ element,
3321
+ rowIndex,
3322
+ cellIndex,
3323
+ parsedTable.rowCount,
3324
+ parsedTable.columnCount,
3325
+ options?.styleCtx
3326
+ );
3327
+ const xmlCellStyle = extractTableCellStyle(cell, textStyle, schemeOverrides);
3328
+ const tdCellOverride = tableEl.tableData?.rows[rowIndex]?.cells[cellIndex]?.style;
3329
+ const diag = getCellDiagonalBorders(
3330
+ tdCellOverride,
3331
+ tableEl.tableData,
3332
+ {
3333
+ rowIndex,
3334
+ cellIndex,
3335
+ rowCount: parsedTable.rowCount,
3336
+ columnCount: parsedTable.columnCount
3337
+ },
3338
+ options?.styleCtx
3339
+ );
3340
+ const mergedStyle = {
3341
+ // Lowest-priority fallback: `xmlCellStyle` never carries a
3342
+ // `color` of its own (see `extractTableCellStyle`), so this
3343
+ // is the only source when neither the table style band nor
3344
+ // an explicit run/cell colour applies.
3345
+ color: textStyle.color,
3346
+ // PowerPoint's line spacing and default margins, as
3347
+ // `tableCellCss` gives the structured-model path.
3348
+ ...TABLE_CELL_TEXT_DEFAULTS,
3349
+ ...bandStyle,
3350
+ ...xmlCellStyle,
3351
+ ...tdCellOverride ? cellStyleToCss(tdCellOverride) : void 0,
3352
+ ...diag ? { position: "relative" } : void 0
3353
+ };
3354
+ return /* @__PURE__ */ jsxs(
3355
+ "td",
3356
+ {
3357
+ className: cn(
3358
+ "px-1 py-0.5 align-top",
3359
+ // No border of its own: a side the deck gives no border renders
3360
+ // with none, as PowerPoint does. Editing keeps a faint grid.
3361
+ isEditable && hasCellSelectionHandler ? "border border-blue-200/70 cursor-cell" : null,
3362
+ selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex ? "ring-1 ring-inset ring-blue-500" : null,
3363
+ isCellInRect(rowIndex, cellIndex, xmlSelectionRect) && !(selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex) ? "bg-blue-500/15 ring-1 ring-inset ring-blue-400/50" : null
3364
+ ),
3365
+ colSpan,
3366
+ rowSpan: rSpan,
3367
+ style: mergedStyle,
3368
+ onClick: (event) => {
3369
+ if (!isEditable || !hasCellSelectionHandler) {
3370
+ return;
3371
+ }
3372
+ event.stopPropagation();
3373
+ if (event.shiftKey && selectedCell && tableEl.tableData) {
3374
+ const rect = computeSelectionRect(
3375
+ selectedCell.rowIndex,
3376
+ selectedCell.columnIndex,
3377
+ rowIndex,
3378
+ cellIndex,
3379
+ tableEl.tableData
3380
+ );
3381
+ options?.onSelectCell?.({
3382
+ rowIndex: selectedCell.rowIndex,
3383
+ columnIndex: selectedCell.columnIndex,
3384
+ selectedCells: rectToCells(rect)
3385
+ });
3386
+ } else {
3387
+ options?.onSelectCell?.({
3388
+ rowIndex,
3389
+ columnIndex: cellIndex
3390
+ });
3391
+ }
3392
+ },
3393
+ onDoubleClick: (event) => {
3394
+ if (!isEditable || !hasCellSelectionHandler) {
3395
+ return;
3396
+ }
3397
+ event.stopPropagation();
3398
+ options?.onSelectCell?.({
3399
+ rowIndex,
3400
+ columnIndex: cellIndex,
3401
+ isEditing: true
3402
+ });
3403
+ },
3404
+ children: [
3405
+ diag ? /* @__PURE__ */ jsx(TableCellDiagonalBorders, { diag }) : null,
3406
+ selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex && selectedCell?.isEditing ? /* @__PURE__ */ jsx(
3407
+ TableCellInput,
3408
+ {
3409
+ initialText: extractCellText(cell) ?? "",
3410
+ style: mergedStyle,
3411
+ onCommit: (text) => {
3412
+ options?.onCommitCellEdit?.(rowIndex, cellIndex, text);
3413
+ },
3414
+ onCancel: () => {
3415
+ options?.onSelectCell?.({
3416
+ rowIndex,
3417
+ columnIndex: cellIndex
3418
+ });
3419
+ }
3420
+ }
3421
+ ) : renderTableCellContent(
3422
+ tableEl.tableData?.rows[rowIndex]?.cells[cellIndex],
3423
+ extractCellText(cell) || "\xA0"
3424
+ )
3425
+ ]
3426
+ },
3427
+ `${element.id}-cell-${rowIndex}-${cellIndex}`
3428
+ );
3429
+ })
3430
+ },
3431
+ `${element.id}-row-${rowIndex}`
3432
+ );
3433
+ }) })
3434
+ ]
3435
+ }
3436
+ )
3437
+ }
3438
+ )
3439
+ }
3440
+ );
3441
+ }
3442
+ function renderGradientStops(stops) {
3443
+ return stops.map((stop, i) => /* @__PURE__ */ jsx("stop", { offset: stop.offset, stopColor: stop.color, stopOpacity: stop.opacity }, i));
3444
+ }
3445
+ function partAttrs(part) {
3446
+ return part ? chartPartToAttrs(part) : void 0;
3447
+ }
3448
+ function resolveReactPalette(chartData) {
3449
+ if (chartData.colorPalette && chartData.colorPalette.length > 0) {
3450
+ return chartData.colorPalette;
3451
+ }
3452
+ return [...getChartStylePalette(chartData.style?.styleId)];
3453
+ }
3454
+ function buildReactChartViewModel(element) {
3455
+ if (element.type !== "chart" || !element.chartData) {
3456
+ return buildChartViewModel(element);
3457
+ }
3458
+ const palette = resolveReactPalette(element.chartData), themedElement = {
3459
+ ...element,
3460
+ chartData: { ...element.chartData, colorPalette: palette }
3461
+ };
3462
+ return buildChartViewModel(themedElement);
3463
+ }
3464
+ function renderPrimitive(prim, key) {
3465
+ switch (prim.kind) {
3466
+ case "rect": {
3467
+ const r = prim;
3468
+ return /* @__PURE__ */ jsx(
3469
+ "rect",
3470
+ {
3471
+ x: r.x,
3472
+ y: r.y,
3473
+ width: r.w,
3474
+ height: r.h,
3475
+ fill: r.fill,
3476
+ rx: r.rx ?? 0,
3477
+ opacity: r.opacity ?? 1,
3478
+ ...partAttrs(r.part),
3479
+ children: r.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: r.title }) : null
3480
+ },
3481
+ key
3482
+ );
3483
+ }
3484
+ case "path": {
3485
+ const p = prim;
3486
+ return /* @__PURE__ */ jsx(
3487
+ "path",
3488
+ {
3489
+ d: p.d,
3490
+ fill: p.fill,
3491
+ stroke: p.stroke ?? "none",
3492
+ strokeWidth: p.strokeWidth ?? 0,
3493
+ strokeDasharray: p.dashArray,
3494
+ fillOpacity: p.opacity ?? 1,
3495
+ ...partAttrs(p.part),
3496
+ children: p.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: p.title }) : null
3497
+ },
3498
+ key
3499
+ );
3500
+ }
3501
+ case "polyline": {
3502
+ const p = prim;
3503
+ return /* @__PURE__ */ jsx(
3504
+ "polyline",
3505
+ {
3506
+ points: p.points,
3507
+ stroke: p.stroke,
3508
+ strokeWidth: p.strokeWidth,
3509
+ strokeDasharray: p.dashArray,
3510
+ fill: p.fill,
3511
+ opacity: p.opacity ?? 1,
3512
+ ...partAttrs(p.part),
3513
+ children: p.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: p.title }) : null
3514
+ },
3515
+ key
3516
+ );
3517
+ }
3518
+ case "circle": {
3519
+ const c = prim;
3520
+ return /* @__PURE__ */ jsx(
3521
+ "circle",
3522
+ {
3523
+ cx: c.cx,
3524
+ cy: c.cy,
3525
+ r: c.r,
3526
+ fill: c.fill,
3527
+ stroke: c.stroke,
3528
+ strokeWidth: c.strokeWidth,
3529
+ opacity: c.opacity ?? 1,
3530
+ ...partAttrs(c.part),
3531
+ children: c.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: c.title }) : null
3532
+ },
3533
+ key
3534
+ );
3535
+ }
3536
+ case "line": {
3537
+ const l = prim;
3538
+ return /* @__PURE__ */ jsx(
3539
+ "line",
3540
+ {
3541
+ x1: l.x1,
3542
+ y1: l.y1,
3543
+ x2: l.x2,
3544
+ y2: l.y2,
3545
+ stroke: l.stroke,
3546
+ strokeWidth: l.strokeWidth,
3547
+ strokeDasharray: l.dashArray,
3548
+ opacity: l.opacity ?? 1,
3549
+ transform: l.transform,
3550
+ children: l.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: l.title }) : null
3551
+ },
3552
+ key
3553
+ );
3554
+ }
3555
+ case "polygon": {
3556
+ const p = prim;
3557
+ return /* @__PURE__ */ jsx(
3558
+ "polygon",
3559
+ {
3560
+ points: p.points,
3561
+ fill: p.fill,
3562
+ stroke: p.stroke,
3563
+ strokeWidth: p.strokeWidth,
3564
+ opacity: p.opacity ?? 1,
3565
+ strokeDasharray: p.dashArray,
3566
+ transform: p.transform,
3567
+ ...partAttrs(p.part),
3568
+ children: p.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: p.title }) : null
3569
+ },
3570
+ key
3571
+ );
3572
+ }
3573
+ case "text": {
3574
+ const t = prim;
3575
+ return /* @__PURE__ */ jsx(
3576
+ "text",
3577
+ {
3578
+ x: t.x,
3579
+ y: t.y,
3580
+ textAnchor: t.textAnchor,
3581
+ fontSize: t.fontSize,
3582
+ fill: t.fill,
3583
+ fontWeight: t.fontWeight ?? "normal",
3584
+ dominantBaseline: t.dominantBaseline,
3585
+ opacity: t.opacity ?? 1,
3586
+ transform: t.transform,
3587
+ children: t.text
3588
+ },
3589
+ key
3590
+ );
3591
+ }
3592
+ default:
3593
+ return null;
3594
+ }
3595
+ }
3596
+ function renderText(t, key) {
3597
+ return /* @__PURE__ */ jsx(
3598
+ "text",
3599
+ {
3600
+ x: t.x,
3601
+ y: t.y,
3602
+ textAnchor: t.textAnchor,
3603
+ fontSize: t.fontSize,
3604
+ fill: t.fill,
3605
+ fontWeight: t.fontWeight ?? "normal",
3606
+ fontStyle: t.fontStyle ?? "normal",
3607
+ fontFamily: t.fontFamily,
3608
+ dominantBaseline: t.dominantBaseline,
3609
+ opacity: t.opacity ?? 1,
3610
+ transform: t.transform,
3611
+ children: t.text
3612
+ },
3613
+ key
3614
+ );
3615
+ }
3616
+ function renderChartViewModel(elementId, vm, preserveAspectRatio = "none") {
3617
+ const legendItems = computeChartLegendLayout(vm);
3618
+ return /* @__PURE__ */ jsxs(
3619
+ "svg",
3620
+ {
3621
+ className: "w-full h-full pointer-events-none",
3622
+ viewBox: `0 0 ${vm.svgWidth} ${vm.svgHeight}`,
3623
+ preserveAspectRatio,
3624
+ children: [
3625
+ vm.defs && vm.defs.length > 0 && /* @__PURE__ */ jsx("defs", { children: vm.defs.map(resolveChartSvgDef).map(
3626
+ (def, i) => def.kind === "linearGradient" ? /* @__PURE__ */ jsx(
3627
+ "linearGradient",
3628
+ {
3629
+ id: def.id,
3630
+ x1: def.x1,
3631
+ y1: def.y1,
3632
+ x2: def.x2,
3633
+ y2: def.y2,
3634
+ children: renderGradientStops(def.stops)
3635
+ },
3636
+ `${elementId}-def-${i}`
3637
+ ) : def.kind === "radialGradient" ? /* @__PURE__ */ jsx(
3638
+ "radialGradient",
3639
+ {
3640
+ id: def.id,
3641
+ cx: def.cx,
3642
+ cy: def.cy,
3643
+ r: def.r,
3644
+ children: renderGradientStops(def.stops)
3645
+ },
3646
+ `${elementId}-def-${i}`
3647
+ ) : /* @__PURE__ */ jsx(
3648
+ "pattern",
3649
+ {
3650
+ id: def.id,
3651
+ patternUnits: def.patternUnits,
3652
+ patternContentUnits: def.patternContentUnits,
3653
+ x: def.x,
3654
+ y: def.y,
3655
+ width: def.width,
3656
+ height: def.height,
3657
+ children: /* @__PURE__ */ jsx(
3658
+ "image",
3659
+ {
3660
+ href: def.href,
3661
+ x: 0,
3662
+ y: 0,
3663
+ width: def.width,
3664
+ height: def.height,
3665
+ preserveAspectRatio: def.preserveAspectRatio
3666
+ }
3667
+ )
3668
+ },
3669
+ `${elementId}-def-${i}`
3670
+ )
3671
+ ) }),
3672
+ vm.areaFill && /* @__PURE__ */ jsx(
3673
+ "rect",
3674
+ {
3675
+ x: 0,
3676
+ y: 0,
3677
+ width: vm.svgWidth,
3678
+ height: vm.svgHeight,
3679
+ rx: vm.areaRadius,
3680
+ fill: vm.areaFill
3681
+ }
3682
+ ),
3683
+ vm.title && /* @__PURE__ */ jsx(
3684
+ "text",
3685
+ {
3686
+ x: vm.titleX,
3687
+ y: vm.titleY,
3688
+ textAnchor: "middle",
3689
+ fontSize: vm.titleStyle?.fontSize ?? 12,
3690
+ fontWeight: vm.titleStyle?.fontWeight ?? 600,
3691
+ fontFamily: vm.titleStyle?.fontFamily,
3692
+ fill: vm.titleStyle?.fill ?? "#1e293b",
3693
+ "data-chart-part": "title",
3694
+ children: vm.titleRunSpans && vm.titleRunSpans.length > 0 ? vm.titleRunSpans.map((run, i) => /* @__PURE__ */ jsx(
3695
+ "tspan",
3696
+ {
3697
+ fontSize: run.fontSize,
3698
+ fontWeight: run.fontWeight,
3699
+ fontStyle: run.fontStyle,
3700
+ fontFamily: run.fontFamily,
3701
+ fill: run.fill,
3702
+ children: run.text
3703
+ },
3704
+ `${elementId}-title-run-${i}`
3705
+ )) : vm.title
3706
+ }
3707
+ ),
3708
+ vm.gridlines.map((gl, i) => /* @__PURE__ */ jsx(
3709
+ "line",
3710
+ {
3711
+ x1: gl.x1,
3712
+ y1: gl.y1,
3713
+ x2: gl.x2,
3714
+ y2: gl.y2,
3715
+ stroke: gl.stroke,
3716
+ strokeWidth: gl.strokeWidth
3717
+ },
3718
+ `${elementId}-gl-${i}`
3719
+ )),
3720
+ (vm.secondaryGridlines ?? []).map((gl, i) => /* @__PURE__ */ jsx(
3721
+ "line",
3722
+ {
3723
+ x1: gl.x1,
3724
+ y1: gl.y1,
3725
+ x2: gl.x2,
3726
+ y2: gl.y2,
3727
+ stroke: gl.stroke,
3728
+ strokeWidth: gl.strokeWidth,
3729
+ strokeDasharray: gl.dashArray,
3730
+ opacity: gl.opacity ?? 1
3731
+ },
3732
+ `${elementId}-sgl-${i}`
3733
+ )),
3734
+ vm.axisLabels.map((lbl, i) => renderText(lbl, `${elementId}-al-${i}`)),
3735
+ (vm.secondaryAxisLabels ?? []).map((lbl, i) => renderText(lbl, `${elementId}-sal-${i}`)),
3736
+ vm.zeroLine && /* @__PURE__ */ jsx(
3737
+ "line",
3738
+ {
3739
+ x1: vm.zeroLine.x1,
3740
+ y1: vm.zeroLine.y1,
3741
+ x2: vm.zeroLine.x2,
3742
+ y2: vm.zeroLine.y2,
3743
+ stroke: vm.zeroLine.stroke,
3744
+ strokeWidth: vm.zeroLine.strokeWidth
3745
+ }
3746
+ ),
3747
+ vm.categoryLabels.map((lbl, i) => renderText(lbl, `${elementId}-cl-${i}`)),
3748
+ vm.primitives.map((prim, i) => renderPrimitive(prim, `${elementId}-p-${i}`)),
3749
+ vm.dataLabels.map((dl, i) => renderText(dl, `${elementId}-dl-${i}`)),
3750
+ legendItems.map((item, i) => /* @__PURE__ */ jsxs(
3751
+ "g",
3752
+ {
3753
+ transform: `translate(${item.x.toFixed(1)},${item.y.toFixed(1)})`,
3754
+ children: [
3755
+ item.lineSwatch ? item.lineSwatch.primitives.map(
3756
+ (prim, si) => renderPrimitive(prim, `${elementId}-lg-${i}-sw-${si}`)
3757
+ ) : /* @__PURE__ */ jsx("rect", { x: 0, y: -7, width: 10, height: 10, rx: 2, fill: item.color }),
3758
+ /* @__PURE__ */ jsx(
3759
+ "text",
3760
+ {
3761
+ x: 13,
3762
+ y: 3,
3763
+ fontSize: item.fontSize,
3764
+ fill: item.fill,
3765
+ fontWeight: item.fontWeight,
3766
+ fontStyle: item.fontStyle,
3767
+ fontFamily: item.fontFamily,
3768
+ children: item.label
3769
+ }
3770
+ )
3771
+ ]
3772
+ },
3773
+ `${elementId}-lg-${i}`
3774
+ ))
3775
+ ]
3776
+ }
3777
+ );
3778
+ }
3779
+ function renderChartElement(element) {
3780
+ if (element.type !== "chart") {
3781
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.chart.heading"] });
3782
+ }
3783
+ const chartData = element.chartData;
3784
+ if (!chartData || chartData.series.length === 0) {
3785
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.chart.heading"] });
3786
+ }
3787
+ const kind = resolveChartKind(chartData.chartType ?? "bar");
3788
+ return renderChartViewModel(
3789
+ element.id,
3790
+ buildReactChartViewModel(element),
3791
+ chartPreserveAspectRatio(kind)
3792
+ );
3793
+ }
3794
+ function buildNotesPdf(pages) {
3795
+ const images = pages.map((page) => {
3796
+ const dataUrl = page.canvas.toDataURL("image/jpeg", 0.92);
3797
+ const base64 = dataUrl.split(",")[1] ?? "";
3798
+ const raw = atob(base64);
3799
+ const bytes2 = new Uint8Array(raw.length);
3800
+ for (let i = 0; i < raw.length; i++) {
3801
+ bytes2[i] = raw.charCodeAt(i);
3802
+ }
3803
+ return { bytes: bytes2, w: page.canvas.width, h: page.canvas.height };
3804
+ });
3805
+ const meta = pages.map((page) => ({
3806
+ notes: page.notes,
3807
+ slideNumber: page.slideNumber
3808
+ }));
3809
+ const bytes = buildNotesPdfBytes(images, meta);
3810
+ const blob = new Blob([bytes], { type: "application/pdf" });
3811
+ return URL.createObjectURL(blob);
3812
+ }
3813
+ function buildPdfFromTiledImageData(pages) {
3814
+ const bytes = buildTiledSlidesPdfBytes(pages);
3815
+ const blob = new Blob([bytes], { type: "application/pdf" });
3816
+ return URL.createObjectURL(blob);
3817
+ }
3818
+ var activePreview = null;
3819
+ function startPreviewAnimation(elementId, preset, options) {
3820
+ stopPreviewAnimation();
3821
+ const domEl = document.querySelector(`[data-element-id="${elementId}"]`);
3822
+ const descriptor = buildPreviewAnimation(preset, {
3823
+ ...options,
3824
+ geometry: domEl ? measurePreviewGeometry(domEl) : void 0
3825
+ });
3826
+ if (!descriptor) {
3827
+ return;
3828
+ }
3829
+ playDescriptor(elementId, descriptor);
3830
+ }
3831
+ function startMotionPathPreview(elementId, path, options) {
3832
+ stopPreviewAnimation();
3833
+ const domEl = document.querySelector(`[data-element-id="${elementId}"]`);
3834
+ if (!domEl) {
3835
+ return;
3836
+ }
3837
+ const stage = domEl.offsetParent;
3838
+ const descriptor = buildMotionPathPreview({
3839
+ path,
3840
+ slideWidth: stage?.offsetWidth || DEFAULT_PREVIEW_SLIDE_WIDTH,
3841
+ slideHeight: stage?.offsetHeight || DEFAULT_PREVIEW_SLIDE_HEIGHT,
3842
+ durationMs: options?.durationMs,
3843
+ delayMs: options?.delayMs,
3844
+ timingCurve: options?.timingCurve
3845
+ });
3846
+ if (!descriptor) {
3847
+ return;
3848
+ }
3849
+ playDescriptor(elementId, descriptor);
3850
+ }
3851
+ var DEFAULT_PREVIEW_SLIDE_WIDTH = 1280;
3852
+ var DEFAULT_PREVIEW_SLIDE_HEIGHT = 720;
3853
+ function playDescriptor(elementId, descriptor) {
3854
+ const domEl = document.querySelector(`[data-element-id="${elementId}"]`);
3855
+ if (!domEl) {
3856
+ return;
3857
+ }
3858
+ const styleEl = document.createElement("style");
3859
+ styleEl.textContent = descriptor.keyframesCss;
3860
+ document.head.appendChild(styleEl);
3861
+ const originalAnimation = domEl.style.animation;
3862
+ const originalVisibility = domEl.style.visibility;
3863
+ domEl.style.visibility = "visible";
3864
+ domEl.style.animation = descriptor.cssAnimation;
3865
+ const timeoutId = setTimeout(() => {
3866
+ domEl.style.animation = originalAnimation;
3867
+ domEl.style.visibility = originalVisibility;
3868
+ styleEl.remove();
3869
+ if (activePreview?.elementId === elementId) {
3870
+ activePreview = null;
3871
+ }
3872
+ }, descriptor.durationMs + 100);
3873
+ activePreview = {
3874
+ elementId,
3875
+ timeoutId,
3876
+ styleEl,
3877
+ originalAnimation,
3878
+ originalVisibility
3879
+ };
3880
+ }
3881
+ function stopPreviewAnimation() {
3882
+ if (!activePreview) {
3883
+ return;
3884
+ }
3885
+ clearTimeout(activePreview.timeoutId);
3886
+ activePreview.styleEl.remove();
3887
+ const domEl = document.querySelector(
3888
+ `[data-element-id="${activePreview.elementId}"]`
3889
+ );
3890
+ if (domEl) {
3891
+ domEl.style.animation = activePreview.originalAnimation;
3892
+ domEl.style.visibility = activePreview.originalVisibility;
3893
+ }
3894
+ activePreview = null;
3895
+ }
3896
+
3897
+ export { ANIMATION_PRESET_OPTIONS, ARROW_SIZE_OPTIONS, CONNECTOR_ARROW_OPTIONS, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_FILL_COLOR, DEFAULT_FONT_FAMILY, DEFAULT_SECTION_GROUP_ID, DEFAULT_STROKE_COLOR, DEFAULT_TABLE_COLUMNS, DEFAULT_TABLE_ROWS, DEFAULT_TEXT_COLOR, DEFAULT_TEXT_FONT_SIZE, GRID_SIZE, HYPERLINK_COLOR, MIN_ELEMENT_SIZE, SHAPE_PRESETS, SHORTCUT_REFERENCE_ITEMS, SLIDE_NAV_THUMBNAIL_WIDTH, STROKE_DASH_OPTIONS, TOOLBAR_SECTIONS, UNGROUPED_SECTION_ID, ZOOM_TO_SELECTION_PADDING, build3DExtrusionData, buildLineGlowFilter, buildLineShadowCss, buildNotesPdf, buildPdfFromTiledImageData, buildReactChartViewModel, buildTextBody3DSceneStyle, cloneHistorySnapshot, cn, dist_exports, getAnimationInitialStyle, getConnectorPathGeometry, getCropShapeClipPath, getDuotoneColors, getImageRenderStyle, getImageSurfaceMaskStyle, getImageSurfaceStyle, getImageTilingStyle, getShapeVisualStyle, getTextLayoutStyle, getTextStyleForElement, getTextWarpStyle, hasDagDuotoneEffect, isConnectorOrLineElement, renderChartElement, renderConnectorMarker, renderDagDuotoneSvgFilter, renderMediaElement, renderTableElement, renderTextSegments, renderVectorShape, shouldRenderFallbackLabel, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation };