pptx-react-viewer 2.16.10 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +14 -14
  3. package/dist/{AiChatPanel-XHKYPYPH.mjs → AiChatPanel-SSSBERWJ.mjs} +4 -4
  4. package/dist/AiChatPanel-SSSBERWJ.mjs.br +0 -0
  5. package/dist/AiChatPanel-SSSBERWJ.mjs.gz +0 -0
  6. package/dist/{AiChatPanel-EPMBL6EA.js → AiChatPanel-UN5DHPZY.js} +24 -24
  7. package/dist/AiChatPanel-UN5DHPZY.js.br +0 -0
  8. package/dist/AiChatPanel-UN5DHPZY.js.gz +0 -0
  9. package/dist/{Model3DScene-DUJOZA2B.js → Model3DScene-4RPYKC2I.js} +2 -2
  10. package/dist/Model3DScene-4RPYKC2I.js.br +0 -0
  11. package/dist/Model3DScene-4RPYKC2I.js.gz +0 -0
  12. package/dist/{Model3DScene-YMIAGRPG.mjs → Model3DScene-LYEESYAI.mjs} +1 -1
  13. package/dist/Model3DScene-LYEESYAI.mjs.br +0 -0
  14. package/dist/Model3DScene-LYEESYAI.mjs.gz +0 -0
  15. package/dist/{PowerPointViewer-Du7u4INi.d.ts → PowerPointViewer-Cf4YHjdF.d.ts} +2 -2
  16. package/dist/PowerPointViewer-Cf4YHjdF.d.ts.map +1 -0
  17. package/dist/{SurfaceChart3DScene-TNCHWQAK.js → SurfaceChart3DScene-ACPKCWLJ.js} +2 -2
  18. package/dist/SurfaceChart3DScene-ACPKCWLJ.js.br +0 -0
  19. package/dist/SurfaceChart3DScene-ACPKCWLJ.js.gz +0 -0
  20. package/dist/{SurfaceChart3DScene-Q47OEJXS.mjs → SurfaceChart3DScene-JR62SOYO.mjs} +1 -1
  21. package/dist/SurfaceChart3DScene-JR62SOYO.mjs.br +0 -0
  22. package/dist/SurfaceChart3DScene-JR62SOYO.mjs.gz +0 -0
  23. package/dist/{audience-content-store-BvqKy7Hf.d.ts → audience-content-store-CyS0oP6t.d.ts} +2 -2
  24. package/dist/{audience-content-store-BvqKy7Hf.d.ts.map → audience-content-store-CyS0oP6t.d.ts.map} +1 -1
  25. package/dist/{chunk-WK22Z7PK.js → chunk-4LWGAVAP.js} +526 -928
  26. package/dist/chunk-4LWGAVAP.js.br +0 -0
  27. package/dist/chunk-4LWGAVAP.js.gz +0 -0
  28. package/dist/{chunk-EL2YF26P.mjs → chunk-7XF6S4AM.mjs} +1 -1
  29. package/dist/chunk-7XF6S4AM.mjs.br +0 -0
  30. package/dist/chunk-7XF6S4AM.mjs.gz +0 -0
  31. package/dist/{chunk-FSVE6WUY.js → chunk-ASKYZ5ZY.js} +728 -523
  32. package/dist/chunk-ASKYZ5ZY.js.br +0 -0
  33. package/dist/chunk-ASKYZ5ZY.js.gz +0 -0
  34. package/dist/{chunk-VHME5BE6.mjs → chunk-BZOL6FXF.mjs} +326 -728
  35. package/dist/chunk-BZOL6FXF.mjs.br +0 -0
  36. package/dist/chunk-BZOL6FXF.mjs.gz +0 -0
  37. package/dist/{chunk-YWKUIUQX.mjs → chunk-GODW6RNR.mjs} +87648 -78804
  38. package/dist/chunk-GODW6RNR.mjs.br +0 -0
  39. package/dist/chunk-GODW6RNR.mjs.gz +0 -0
  40. package/dist/{chunk-ESC6IB2F.js → chunk-ICZDMFF2.js} +2147 -3399
  41. package/dist/chunk-ICZDMFF2.js.br +0 -0
  42. package/dist/chunk-ICZDMFF2.js.gz +0 -0
  43. package/dist/{chunk-PI5SFTDC.js → chunk-MAVLV56S.js} +3 -3
  44. package/dist/chunk-MAVLV56S.js.br +0 -0
  45. package/dist/chunk-MAVLV56S.js.gz +0 -0
  46. package/dist/{chunk-G3VAE7ZF.mjs → chunk-O7FTUUXP.mjs} +1212 -2464
  47. package/dist/chunk-O7FTUUXP.mjs.br +0 -0
  48. package/dist/chunk-O7FTUUXP.mjs.gz +0 -0
  49. package/dist/{chunk-J7EFC3GS.mjs → chunk-QUB5OSKY.mjs} +394 -189
  50. package/dist/chunk-QUB5OSKY.mjs.br +0 -0
  51. package/dist/chunk-QUB5OSKY.mjs.gz +0 -0
  52. package/dist/{chunk-DVZF4ARG.js → chunk-RTUGI4HQ.js} +49 -4
  53. package/dist/chunk-RTUGI4HQ.js.br +0 -0
  54. package/dist/chunk-RTUGI4HQ.js.gz +0 -0
  55. package/dist/{chunk-5IUEPQTK.mjs → chunk-SNL6LBRX.mjs} +49 -4
  56. package/dist/chunk-SNL6LBRX.mjs.br +0 -0
  57. package/dist/chunk-SNL6LBRX.mjs.gz +0 -0
  58. package/dist/{chunk-Y2KIB3QH.js → chunk-WVDM36ZM.js} +87730 -78843
  59. package/dist/chunk-WVDM36ZM.js.br +0 -0
  60. package/dist/chunk-WVDM36ZM.js.gz +0 -0
  61. package/dist/i18n.js +4 -4
  62. package/dist/i18n.js.br +0 -0
  63. package/dist/i18n.js.gz +0 -0
  64. package/dist/i18n.mjs +1 -1
  65. package/dist/i18n.mjs.br +0 -0
  66. package/dist/i18n.mjs.gz +0 -0
  67. package/dist/index.d.ts +702 -37
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +65 -41
  70. package/dist/index.js.br +0 -0
  71. package/dist/index.js.gz +0 -0
  72. package/dist/index.mjs +6 -6
  73. package/dist/index.mjs.br +0 -0
  74. package/dist/index.mjs.gz +0 -0
  75. package/dist/internals.d.ts +923 -54
  76. package/dist/internals.d.ts.map +1 -1
  77. package/dist/internals.js +79 -79
  78. package/dist/internals.js.br +0 -0
  79. package/dist/internals.js.gz +0 -0
  80. package/dist/internals.mjs +4 -4
  81. package/dist/internals.mjs.br +0 -0
  82. package/dist/internals.mjs.gz +0 -0
  83. package/dist/pptx-viewer.css +1 -1
  84. package/dist/pptx-viewer.css.br +0 -0
  85. package/dist/pptx-viewer.css.gz +0 -0
  86. package/dist/{types-DU35aAJK.d.ts → types-C_CTI-QE.d.ts} +3 -31
  87. package/dist/types-C_CTI-QE.d.ts.map +1 -0
  88. package/dist/{useViewerBuildingBlocks-Xddn6SFj.d.ts → useViewerBuildingBlocks-DItilDbP.d.ts} +47 -8
  89. package/dist/useViewerBuildingBlocks-DItilDbP.d.ts.map +1 -0
  90. package/dist/viewer/index.d.ts +593 -56
  91. package/dist/viewer/index.js +27 -27
  92. package/dist/viewer/index.js.br +0 -0
  93. package/dist/viewer/index.js.gz +0 -0
  94. package/dist/viewer/index.mjs +6 -6
  95. package/dist/viewer/index.mjs.br +0 -0
  96. package/dist/viewer/index.mjs.gz +0 -0
  97. package/package.json +2 -2
  98. package/dist/AiChatPanel-EPMBL6EA.js.br +0 -0
  99. package/dist/AiChatPanel-EPMBL6EA.js.gz +0 -0
  100. package/dist/AiChatPanel-XHKYPYPH.mjs.br +0 -0
  101. package/dist/AiChatPanel-XHKYPYPH.mjs.gz +0 -0
  102. package/dist/Model3DScene-DUJOZA2B.js.br +0 -0
  103. package/dist/Model3DScene-DUJOZA2B.js.gz +0 -0
  104. package/dist/Model3DScene-YMIAGRPG.mjs.br +0 -0
  105. package/dist/Model3DScene-YMIAGRPG.mjs.gz +0 -0
  106. package/dist/PowerPointViewer-Du7u4INi.d.ts.map +0 -1
  107. package/dist/SurfaceChart3DScene-Q47OEJXS.mjs.br +0 -0
  108. package/dist/SurfaceChart3DScene-Q47OEJXS.mjs.gz +0 -0
  109. package/dist/SurfaceChart3DScene-TNCHWQAK.js.br +0 -0
  110. package/dist/SurfaceChart3DScene-TNCHWQAK.js.gz +0 -0
  111. package/dist/chunk-5IUEPQTK.mjs.br +0 -0
  112. package/dist/chunk-5IUEPQTK.mjs.gz +0 -0
  113. package/dist/chunk-DVZF4ARG.js.br +0 -0
  114. package/dist/chunk-DVZF4ARG.js.gz +0 -0
  115. package/dist/chunk-EL2YF26P.mjs.br +0 -0
  116. package/dist/chunk-EL2YF26P.mjs.gz +0 -0
  117. package/dist/chunk-ESC6IB2F.js.br +0 -0
  118. package/dist/chunk-ESC6IB2F.js.gz +0 -0
  119. package/dist/chunk-FSVE6WUY.js.br +0 -0
  120. package/dist/chunk-FSVE6WUY.js.gz +0 -0
  121. package/dist/chunk-G3VAE7ZF.mjs.br +0 -0
  122. package/dist/chunk-G3VAE7ZF.mjs.gz +0 -0
  123. package/dist/chunk-J7EFC3GS.mjs.br +0 -0
  124. package/dist/chunk-J7EFC3GS.mjs.gz +0 -0
  125. package/dist/chunk-PI5SFTDC.js.br +0 -0
  126. package/dist/chunk-PI5SFTDC.js.gz +0 -0
  127. package/dist/chunk-VHME5BE6.mjs.br +0 -0
  128. package/dist/chunk-VHME5BE6.mjs.gz +0 -0
  129. package/dist/chunk-WK22Z7PK.js.br +0 -0
  130. package/dist/chunk-WK22Z7PK.js.gz +0 -0
  131. package/dist/chunk-Y2KIB3QH.js.br +0 -0
  132. package/dist/chunk-Y2KIB3QH.js.gz +0 -0
  133. package/dist/chunk-YWKUIUQX.mjs.br +0 -0
  134. package/dist/chunk-YWKUIUQX.mjs.gz +0 -0
  135. package/dist/types-DU35aAJK.d.ts.map +0 -1
  136. package/dist/useViewerBuildingBlocks-Xddn6SFj.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
- import { translationsEn } from './chunk-5IUEPQTK.mjs';
2
- import { SHAPE_PRESET_DEFS, computeValueRangeForChart, VIEWER_SHORTCUT_REFERENCE, getAnimationInitialStyle, cloneHistorySnapshot, hasShapeProperties, getImageFitStyle, isImageLikeElement, runMediaCommand, findMediaElementByElementId, mediaPlaybackAttributes, buildTextBlockStyle, hasTextProperties, toCssWritingMode, toCssTextOrientation, toCssVerticalDirection, getKinsokuLineBreakStyles, buildTextBody3DSceneStyle, resolveParagraphRtl, resolveParagraphAlign, resolveCssTextAlign, resolveParagraphSpacing, resolveParagraphStrutFontSize, colorWithOpacity, normalizeHexColor, build3DExtrusionData, isIdentityRectClip, getResolvedShapeClipPath, isStrokeOnlyPresetElement, normalizeStrokeDashType, buildCssGradientFromShapeStyle, buildShadowCssFromShapeStyle, buildInnerShadowCssFromShapeStyle, suppressesCssBorder, buildMultiLayerShadowCss, buildGlowBoxShadow, getCompoundLineBoxShadow, getSoftEdgeFilterId, getEffectDagFilter, buildPatternFillCss, buildReflectionCss, getCssBorderDashStyle, getCompoundLineBorderWidth, getSvgStrokeDasharray, buildSvgGradientDef, isCalloutShape, getCalloutLeaderLineGeometry, buildCalloutLeaderLineSvgPath, getCalloutViewBoxBounds, svgLineCap, getCompoundLineOffsets, getCompoundLineWidths, computeSelectionRect, DEFAULT_FONT_FAMILY, cellStyleToCss, rectToCells, isCellInRect, buildChartViewModel, buildNotesPdfBytes, buildSlidesPdfBytes, buildPreviewAnimation, buildMotionPathPreview, getRoundRectRadiusPx2, mediaFallbackVisual, registerPersistentAudio, substituteFieldText, resolveUnderlineDecorationStyle, resolveAutoFitFontScale, getSubstituteFontFamily, parsePanoseString, hasDistinctScriptFonts, pieceLetterSpacing, resolveMetricTrackingPx, buildTextReflectionCss, getTextAlphaOpacity, buildTextRunFilterChain, buildTextShadowCss, TEXT_BUILD_ID_SEP, buildTextBuildSpec, textBuildSpanStyle, sanitizeGradientStops, getGradientTileFlipCss, getGradientTileRectCss, apply3dEffects, clampUnitInterval, customGeometryPathsToSvgSubpaths, svgGradientFillRef, computeColumnBoundaries, getTableCellBandStyle, parseDrawingColor, parseDrawingColorOpacity, getCellDiagonalBorders, getChartStylePalette, chartAreaFill, computeLayout, valueToY, formatAxisValue, computeLayoutOptions, splitSeriesByAxis, computeValueRange, getSecondaryValueAxis, seriesColor, paletteColor, resolveRegionCode, normalizeValue, sequentialColorScale, mediaFallbackIcon, mediaFallbackLabelKey, MEDIA_CHROME_ATTRIBUTE, convertOmmlToMathMl, sanitizeMathMl, buildTextFillCss, resolvePictureBullet, computeResizedColumnWidths, computeResizedRowHeight, DEFAULT_ROW_HEIGHT, ooxmlGradientAngleToCssDegrees, generateLogTicks, formatAxisValueWithUnits, getDisplayUnitLabel, splitRunForMetrics, hexToRgbChannels, chartPartToAttrs, segmentByScript, resolveFontForScript } from './chunk-YWKUIUQX.mjs';
1
+ import { translationsEn } from './chunk-SNL6LBRX.mjs';
2
+ import { SHAPE_PRESET_DEFS, computeValueRangeForChart, VIEWER_SHORTCUT_REFERENCE, getAnimationInitialStyle, cloneHistorySnapshot, hasShapeProperties, getImageFitStyle, isImageLikeElement, getImageTilingStyle, runMediaCommand, findMediaElementByElementId, mediaPlaybackAttributes, buildTextBlockStyle, hasTextProperties, toCssWritingMode, toCssTextOrientation, toCssVerticalDirection, buildTextBodyLayoutStyle, buildTextBody3DSceneStyle, buildParagraphs, colorWithOpacity, normalizeHexColor, build3DExtrusionData, getComputedFillStyle, getComputedEffectStyle, getComputedStrokeStyle, isStrokeOnlyPresetElement, resolveShapeGeometry, normalizeStrokeDashType, getSvgStrokeDasharray, buildSvgGradientDef, isCalloutShape, getCalloutLeaderLineGeometry, buildCalloutLeaderLineSvgPath, getCalloutViewBoxBounds, svgLineCap, getCompoundLineOffsets, getCompoundLineWidths, computeSelectionRect, DEFAULT_FONT_FAMILY, cellStyleToCss, rectToCells, isCellInRect, tableContainerCss, buildChartViewModel, buildNotesPdfBytes, buildSlidesPdfBytes, buildPreviewAnimation, buildMotionPathPreview, getRoundRectRadiusPx2, getResolvedShapeClipPath, mediaFallbackVisual, registerPersistentAudio, resolveAutoFitFontScale, getSubstituteFontFamily, parsePanoseString, hasDistinctScriptFonts, pieceLetterSpacing, resolveMetricTrackingPx, TEXT_BUILD_ID_SEP, buildTextBuildSpec, textBuildSpanStyle, apply3dEffects, clampUnitInterval, customGeometryPathsToSvgSubpaths, svgGradientFillRef, computeColumnBoundaries, getTableCellBandStyle, parseDrawingColor, parseDrawingColorOpacity, getCellDiagonalBorders, cellRunStyle, getChartStylePalette, chartAreaFill, computeLayout, valueToY, formatAxisValue, computeLayoutOptions, splitSeriesByAxis, computeValueRange, getSecondaryValueAxis, seriesColor, paletteColor, resolveRegionCode, normalizeValue, sequentialColorScale, mediaFallbackIcon, mediaFallbackLabelKey, MEDIA_CHROME_ATTRIBUTE, convertOmmlToMathMl, sanitizeMathMl, tableCellCss, computeResizedColumnWidths, computeResizedRowHeight, DEFAULT_ROW_HEIGHT, ooxmlGradientAngleToCssDegrees, generateLogTicks, formatAxisValueWithUnits, getDisplayUnitLabel, splitRunForMetrics, hexToRgbChannels, chartPartToAttrs, segmentByScript, resolveFontForScript } from './chunk-GODW6RNR.mjs';
3
3
  import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import React6, { useState, useEffect, useMemo, Suspense, useRef, useCallback, useLayoutEffect } from 'react';
@@ -34,13 +34,11 @@ var DEFAULT_TEXT_FONT_SIZE = 24;
34
34
  var HYPERLINK_COLOR = "#0563C1";
35
35
  var DEFAULT_TABLE_ROWS = 3;
36
36
  var DEFAULT_TABLE_COLUMNS = 3;
37
- var SHAPE_ADJUSTMENT_MIN = 0;
38
- var SHAPE_ADJUSTMENT_MAX = 5e4;
39
- var DEFAULT_ROUND_RECT_ADJUSTMENT = 16667;
40
37
  var ZOOM_TO_SELECTION_PADDING = 96;
41
38
  var GRID_SIZE = 8;
42
39
  var SLIDE_NAV_THUMBNAIL_WIDTH = 156;
43
40
  var UNGROUPED_SECTION_ID = "__ungrouped__";
41
+ var DEFAULT_SECTION_GROUP_ID = "default";
44
42
 
45
43
  // src/viewer/constants/toolbar.ts
46
44
  var TOOLBAR_SECTIONS = [
@@ -321,86 +319,8 @@ function getCropShapeClipPath(element) {
321
319
  }
322
320
  return CROP_SHAPE_CLIP_PATHS[shape];
323
321
  }
324
- function isImageTiled(element) {
325
- if (!isImageLikeElement(element)) {
326
- return false;
327
- }
328
- return typeof element.tileScaleX === "number" || typeof element.tileScaleY === "number";
329
- }
330
- function tileAlignmentToCssPosition(alignment) {
331
- switch (alignment) {
332
- case "tl":
333
- return "top left";
334
- case "t":
335
- return "top center";
336
- case "tr":
337
- return "top right";
338
- case "l":
339
- return "center left";
340
- case "ctr":
341
- return "center center";
342
- case "r":
343
- return "center right";
344
- case "bl":
345
- return "bottom left";
346
- case "b":
347
- return "bottom center";
348
- case "br":
349
- return "bottom right";
350
- default:
351
- return void 0;
352
- }
353
- }
354
- function buildMirrorTiledBackground(src, flip, scaleX, scaleY) {
355
- if (!src.startsWith("data:")) {
356
- return void 0;
357
- }
358
- const flipX = flip === "x" || flip === "xy";
359
- const flipY = flip === "y" || flip === "xy";
360
- const cols = flipX ? 2 : 1;
361
- const rows = flipY ? 2 : 1;
362
- const images = [];
363
- for (let cy = 0; cy < rows; cy++) {
364
- for (let cx = 0; cx < cols; cx++) {
365
- const mirrorX = flipX && cx % 2 === 1;
366
- const mirrorY = flipY && cy % 2 === 1;
367
- const sx = mirrorX ? -1 : 1;
368
- const sy = mirrorY ? -1 : 1;
369
- const tx = cx + (mirrorX ? 1 : 0);
370
- const ty = cy + (mirrorY ? 1 : 0);
371
- images.push(
372
- `<image href="${src}" x="0" y="0" width="1" height="1" preserveAspectRatio="none" transform="translate(${tx},${ty}) scale(${sx},${sy})"/>`
373
- );
374
- }
375
- }
376
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${cols} ${rows}" width="${cols}" height="${rows}">${images.join("")}</svg>`;
377
- return {
378
- backgroundImage: `url("data:image/svg+xml,${encodeURIComponent(svg)}")`,
379
- backgroundSize: `${scaleX * cols}% ${scaleY * rows}%`
380
- };
381
- }
382
- function getImageTilingStyle(element) {
383
- if (!isImageLikeElement(element) || !isImageTiled(element)) {
384
- return void 0;
385
- }
386
- const scaleX = typeof element.tileScaleX === "number" ? element.tileScaleX * 100 : 100;
387
- const scaleY = typeof element.tileScaleY === "number" ? element.tileScaleY * 100 : 100;
388
- const offsetX = typeof element.tileOffsetX === "number" ? element.tileOffsetX : 0;
389
- const offsetY = typeof element.tileOffsetY === "number" ? element.tileOffsetY : 0;
390
- const alignmentPosition = tileAlignmentToCssPosition(element.tileAlignment);
391
- const hasExplicitOffset = offsetX !== 0 || offsetY !== 0;
392
- const bgPosition = hasExplicitOffset ? `${offsetX}px ${offsetY}px` : alignmentPosition || `${offsetX}px ${offsetY}px`;
393
- const src = element.svgData || element.imageData;
394
- const flip = element.tileFlip;
395
- const mirror = src && flip && flip !== "none" ? buildMirrorTiledBackground(src, flip, scaleX, scaleY) : void 0;
396
- return {
397
- backgroundImage: mirror ? mirror.backgroundImage : src ? `url(${src})` : void 0,
398
- backgroundRepeat: "repeat",
399
- backgroundSize: mirror ? mirror.backgroundSize : `${scaleX}% ${scaleY}%`,
400
- backgroundPosition: bgPosition,
401
- width: "100%",
402
- height: "100%"
403
- };
322
+ function getImageTilingStyle2(element) {
323
+ return getImageTilingStyle(element);
404
324
  }
405
325
 
406
326
  // src/viewer/utils/geometry-image.ts
@@ -1043,40 +963,13 @@ function getTextStyleForElement(element, fallbackColor) {
1043
963
  }
1044
964
 
1045
965
  // src/viewer/utils/text-layout.tsx
1046
- function computeTabSize(tabStops, defaultTabSize) {
1047
- const fromDefault = typeof defaultTabSize === "number" && defaultTabSize > 0 ? `${Math.round(defaultTabSize)}px` : void 0;
1048
- if (!tabStops || tabStops.length === 0) {
1049
- return fromDefault;
1050
- }
1051
- if (tabStops.length === 1) {
1052
- const pos = tabStops[0].position;
1053
- return typeof pos === "number" && pos > 0 ? `${Math.round(pos)}px` : fromDefault;
1054
- }
1055
- const positions = tabStops.map((t) => t.position).filter((p) => typeof p === "number" && p > 0).sort((a, b) => a - b);
1056
- if (positions.length < 2) {
1057
- return positions.length === 1 ? `${Math.round(positions[0])}px` : fromDefault;
1058
- }
1059
- let totalGap = 0;
1060
- for (let i = 1; i < positions.length; i++) {
1061
- totalGap += positions[i] - positions[i - 1];
1062
- }
1063
- const avgGap = totalGap / (positions.length - 1);
1064
- return avgGap > 0 ? `${Math.round(avgGap)}px` : fromDefault;
1065
- }
1066
966
  function getTextLayoutStyle(element) {
1067
967
  if (!hasTextProperties(element)) {
1068
968
  return {};
1069
969
  }
1070
- const verticalAlign = element.textStyle?.vAlign || "top";
1071
970
  const writingMode = toCssWritingMode(element.textStyle?.textDirection);
1072
971
  const textOrientation = toCssTextOrientation(element.textStyle?.textDirection);
1073
972
  const verticalDirection = toCssVerticalDirection(element.textStyle?.textDirection);
1074
- const parsedColumnCount = Number(element.textStyle?.columnCount);
1075
- const columnCount = Number.isFinite(parsedColumnCount) ? Math.max(1, Math.min(16, Math.round(parsedColumnCount))) : 1;
1076
- const hasColumns = columnCount > 1;
1077
- const justifyContent = verticalAlign === "middle" ? "center" : verticalAlign === "bottom" ? "flex-end" : "flex-start";
1078
- const tabStops = element.textStyle?.tabStops;
1079
- const tabSize = computeTabSize(tabStops, element.textStyle?.defaultTabSize);
1080
973
  const textWrapNone = element.textStyle?.textWrap === "none";
1081
974
  const hasParagraphIndents = hasTextProperties(element) && element.paragraphIndents && element.paragraphIndents.length > 0;
1082
975
  const paragraphMarginLeft = element.textStyle?.paragraphMarginLeft;
@@ -1085,35 +978,9 @@ function getTextLayoutStyle(element) {
1085
978
  const textIndent = !hasParagraphIndents && typeof paragraphIndent === "number" && paragraphIndent !== 0 ? paragraphIndent : void 0;
1086
979
  const bodyTop = element.textStyle?.bodyInsetTop ?? DEFAULT_BODY_INSET_TB_PX;
1087
980
  const bodyBottom = element.textStyle?.bodyInsetBottom ?? DEFAULT_BODY_INSET_TB_PX;
1088
- const parsedColumnSpacing = Number(element.textStyle?.columnSpacing);
1089
- const columnGap = Number.isFinite(parsedColumnSpacing) && parsedColumnSpacing > 0 ? `${parsedColumnSpacing}px` : "0.75em";
1090
- const kinsokuStyles = getKinsokuLineBreakStyles(element.textStyle);
1091
- if (hasColumns) {
1092
- return {
1093
- display: "block",
1094
- columnCount,
1095
- columnGap,
1096
- // Body inset only. Paragraph spacing (spcBef/spcAft) is applied
1097
- // per-paragraph by the paragraph renderer, not collapsed here.
1098
- paddingTop: bodyTop,
1099
- paddingBottom: bodyBottom,
1100
- writingMode,
1101
- textOrientation,
1102
- direction: verticalDirection,
1103
- tabSize,
1104
- marginLeft,
1105
- textIndent,
1106
- ...kinsokuStyles,
1107
- ...textWrapNone ? {
1108
- whiteSpace: "nowrap",
1109
- overflow: "visible"
1110
- } : {}
1111
- };
1112
- }
1113
981
  return {
1114
- display: "flex",
1115
- flexDirection: "column",
1116
- justifyContent,
982
+ // Columns / flex anchoring / anchorCtr / tab-size / kinsoku, from shared.
983
+ ...buildTextBodyLayoutStyle(element),
1117
984
  // Body inset only. Paragraph spacing (spcBef/spcAft) is applied
1118
985
  // per-paragraph by the paragraph renderer, not collapsed here.
1119
986
  paddingTop: bodyTop,
@@ -1121,10 +988,8 @@ function getTextLayoutStyle(element) {
1121
988
  writingMode,
1122
989
  textOrientation,
1123
990
  direction: verticalDirection,
1124
- tabSize,
1125
991
  marginLeft,
1126
992
  textIndent,
1127
- ...kinsokuStyles,
1128
993
  ...textWrapNone ? {
1129
994
  whiteSpace: "nowrap",
1130
995
  overflow: "visible"
@@ -1133,9 +998,6 @@ function getTextLayoutStyle(element) {
1133
998
  }
1134
999
 
1135
1000
  // src/viewer/utils/text-effects.tsx
1136
- function buildTextFillCss2(style) {
1137
- return buildTextFillCss(style);
1138
- }
1139
1001
  function buildTextBody3DSceneStyle2(textStyle) {
1140
1002
  return buildTextBody3DSceneStyle(textStyle);
1141
1003
  }
@@ -1318,7 +1180,7 @@ function buildAnimStyle(state) {
1318
1180
  }
1319
1181
  return Object.keys(style).length > 0 ? style : void 0;
1320
1182
  }
1321
- function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, paraSegments, subElementAnimStates, renderPiece) {
1183
+ function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, paraRuns, subElementAnimStates, renderPiece) {
1322
1184
  if (!subElementAnimStates || subElementAnimStates.size === 0) {
1323
1185
  return renderedSegments;
1324
1186
  }
@@ -1331,14 +1193,14 @@ function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, para
1331
1193
  const spec = buildTextBuildSpec(
1332
1194
  elementId,
1333
1195
  paraIndex,
1334
- paraSegments.map((entry) => ({ text: entry.segment.text, style: entry })),
1196
+ paraRuns.map((run) => ({ text: run.text, style: run })),
1335
1197
  subElementAnimStates
1336
1198
  );
1337
1199
  if (!spec?.spans) {
1338
1200
  return renderedSegments;
1339
1201
  }
1340
1202
  return spec.spans.map((span, index) => {
1341
- const content = span.style && renderPiece ? renderPiece(span.style, span.text) : span.text;
1203
+ const content = span.style && renderPiece ? renderPiece(span.style, span.text, String(index)) : span.text;
1342
1204
  if (!span.animId) {
1343
1205
  return /* @__PURE__ */ jsx(React6.Fragment, { children: content }, `ws-${index}`);
1344
1206
  }
@@ -1640,81 +1502,26 @@ function renderEquationSegment(elementId, segmentIndex, equationXml, equationNum
1640
1502
  }
1641
1503
  return /* @__PURE__ */ jsx("span", { children: equationContent }, `${elementId}-seg-${segmentIndex}`);
1642
1504
  }
1643
- function renderPictureBullet(elementId, segmentIndex, bulletInfo, baseFontSize) {
1644
- const picture = resolvePictureBullet(bulletInfo, baseFontSize) ?? {
1645
- sizePx: baseFontSize,
1646
- fallbackMarker: "\u2022",
1647
- accessibleLabel: "Bullet"
1648
- };
1649
- if (!picture.src) {
1650
- return /* @__PURE__ */ jsx(
1651
- "span",
1652
- {
1653
- style: {
1654
- fontSize: picture.sizePx,
1655
- display: "inline-block",
1656
- verticalAlign: "middle",
1657
- marginInlineEnd: 4,
1658
- color: bulletInfo.color || void 0,
1659
- fontFamily: bulletInfo.fontFamily || void 0
1660
- },
1661
- "aria-label": picture.accessibleLabel,
1662
- children: `${picture.fallbackMarker} `
1663
- },
1664
- `${elementId}-seg-${segmentIndex}-bullet-fallback`
1665
- );
1505
+ var BASELINE_FONT_SCALE = 0.65;
1506
+ function renderParagraphRun(run, segment, ctx, keySuffix = "") {
1507
+ const { element, fallbackColor, findHighlights, onHyperlinkClick } = ctx;
1508
+ const segmentIndex = run.segmentIndex ?? 0;
1509
+ const key = `${element.id}-seg-${segmentIndex}${keySuffix}`;
1510
+ if (run.equation) {
1511
+ return renderEquationSegment(element.id, segmentIndex, run.equation.xml, run.equation.number);
1666
1512
  }
1667
- return /* @__PURE__ */ jsx(
1668
- "img",
1669
- {
1670
- src: picture.src,
1671
- alt: picture.accessibleLabel,
1672
- style: {
1673
- width: picture.sizePx,
1674
- height: picture.sizePx,
1675
- display: "inline-block",
1676
- verticalAlign: "middle",
1677
- marginInlineEnd: 4,
1678
- objectFit: "contain"
1679
- }
1680
- },
1681
- `${elementId}-seg-${segmentIndex}-bullet-img`
1682
- );
1683
- }
1684
- function renderSingleSegment(element, segment, segmentIndex, fallbackColor, findHighlights, bulletInfo, onHyperlinkClick, fieldContext, paragraphRtl, requireCtrlClick, bulletHangPx) {
1685
- if (segment.equationXml) {
1686
- return renderEquationSegment(
1687
- element.id,
1688
- segmentIndex,
1689
- segment.equationXml,
1690
- segment.equationNumber
1691
- );
1692
- }
1693
- const segmentStyle = segment.style || {};
1694
- const rawText = bulletInfo && typeof bulletHangPx === "number" ? (segment.text || "").trimEnd() : segment.text || "";
1695
- const textValue = substituteFieldText(rawText, segment.fieldType, fieldContext);
1513
+ const segmentStyle = segment?.style ?? {};
1514
+ const textValue = run.text;
1696
1515
  const lines = textValue.split("\n");
1697
- const textDecorationTokens = [];
1698
- if (segmentStyle.underline || segmentStyle.hyperlink) {
1699
- textDecorationTokens.push("underline");
1700
- }
1701
- if (segmentStyle.strikethrough) {
1702
- textDecorationTokens.push("line-through");
1703
- }
1704
- const isDoubleStrike = segmentStyle.strikethrough && segmentStyle.strikeType === "dblStrike";
1705
- const resolvedSegmentColor = segmentStyle.hyperlink ? normalizeHexColor(segmentStyle.color || element.textStyle?.color, HYPERLINK_COLOR) : normalizeHexColor(segmentStyle.color || element.textStyle?.color, fallbackColor);
1706
- const underlineDecoration = resolveUnderlineDecorationStyle(
1707
- Boolean(isDoubleStrike),
1708
- segmentStyle.underlineStyle
1516
+ const resolvedSegmentColor = normalizeHexColor(
1517
+ segmentStyle.color || element.textStyle?.color,
1518
+ run.hyperlink ? HYPERLINK_COLOR : fallbackColor
1709
1519
  );
1710
1520
  const rawBaseline = typeof segmentStyle.baseline === "number" ? segmentStyle.baseline : 0;
1711
1521
  const baselineFraction = rawBaseline === 0 ? 0 : Math.abs(rawBaseline) >= 1e3 ? rawBaseline / 1e5 : rawBaseline / 100;
1712
- const baselineFontScale = baselineFraction !== 0 ? 0.65 : 1;
1713
- const authoredLetterSpacing = typeof segmentStyle.characterSpacing === "number" && segmentStyle.characterSpacing !== 0 ? segmentStyle.characterSpacing / 100 * (96 / 72) : 0;
1714
- const textFillStyles = buildTextFillCss2(segmentStyle);
1522
+ const baselineFontScale = baselineFraction !== 0 ? BASELINE_FONT_SCALE : 1;
1715
1523
  const rawFontSize = segmentStyle.fontSize || element.textStyle?.fontSize || DEFAULT_TEXT_FONT_SIZE;
1716
- const autoFitScale = resolveAutoFitFontScale(element.textStyle);
1717
- const baseFontSize = rawFontSize * autoFitScale;
1524
+ const baseFontSize = rawFontSize * resolveAutoFitFontScale(element.textStyle);
1718
1525
  const baselineShift = baselineFraction !== 0 ? `${baselineFraction * baseFontSize}px` : void 0;
1719
1526
  const baseFontSizePt = baseFontSize * (72 / 96);
1720
1527
  const fontKerning = typeof segmentStyle.kerning === "number" ? segmentStyle.kerning === 0 ? "none" : baseFontSizePt >= segmentStyle.kerning / 100 ? "normal" : "none" : void 0;
@@ -1740,6 +1547,7 @@ function renderSingleSegment(element, segment, segmentIndex, fallbackColor, find
1740
1547
  )
1741
1548
  };
1742
1549
  const needsScriptFonts = hasDistinctScriptFonts(scriptFonts);
1550
+ const authoredLetterSpacing = typeof segmentStyle.characterSpacing === "number" && segmentStyle.characterSpacing !== 0 ? segmentStyle.characterSpacing / 100 * (96 / 72) : 0;
1743
1551
  const metricFont = {
1744
1552
  fontFamily: baseFontFamily,
1745
1553
  fontSizePx: baseFontSize * baselineFontScale,
@@ -1747,73 +1555,21 @@ function renderSingleSegment(element, segment, segmentIndex, fallbackColor, find
1747
1555
  italic: Boolean(segmentStyle.italic)
1748
1556
  };
1749
1557
  const metricContext = { font: metricFont, authoredPx: authoredLetterSpacing };
1750
- const letterSpacing = pieceLetterSpacing(
1558
+ const spanStyle = { ...run.style };
1559
+ spanStyle.color = resolvedSegmentColor;
1560
+ spanStyle.fontSize = baseFontSize * baselineFontScale;
1561
+ spanStyle.fontFamily = baseFontFamily;
1562
+ spanStyle.verticalAlign = baselineShift;
1563
+ spanStyle.fontKerning = fontKerning;
1564
+ spanStyle.letterSpacing = pieceLetterSpacing(
1751
1565
  authoredLetterSpacing,
1752
1566
  resolveMetricTrackingPx(textValue, metricFont)
1753
1567
  );
1754
- const spanStyle = {
1755
- color: resolvedSegmentColor,
1756
- fontSize: baseFontSize * baselineFontScale,
1757
- fontWeight: segmentStyle.bold ? 700 : 400,
1758
- fontStyle: segmentStyle.italic ? "italic" : "normal",
1759
- textDecorationLine: textDecorationTokens.length > 0 ? textDecorationTokens.join(" ") : "none",
1760
- textDecorationStyle: underlineDecoration?.textDecorationStyle,
1761
- textDecorationThickness: underlineDecoration?.textDecorationThickness,
1762
- textUnderlineOffset: underlineDecoration?.textUnderlineOffset,
1763
- fontFamily: baseFontFamily,
1764
- verticalAlign: baselineShift,
1765
- // Text capitalization (a:rPr/@cap): all -> force uppercase glyphs;
1766
- // small -> render lowercase as small caps.
1767
- textTransform: segmentStyle.textCaps === "all" ? "uppercase" : void 0,
1768
- fontVariantCaps: segmentStyle.textCaps === "small" ? "small-caps" : void 0,
1769
- letterSpacing,
1770
- fontKerning,
1771
- backgroundColor: textFillStyles ? void 0 : segmentStyle.highlightColor ? normalizeHexColor(segmentStyle.highlightColor, "transparent") : void 0,
1772
- ...textFillStyles,
1773
- textDecorationColor: segmentStyle.underlineColor ? normalizeHexColor(segmentStyle.underlineColor, void 0) : void 0,
1774
- WebkitTextStroke: segmentStyle.textOutlineWidth && segmentStyle.textOutlineColor ? `${segmentStyle.textOutlineWidth}px ${normalizeHexColor(segmentStyle.textOutlineColor, "#000000")}` : segmentStyle.textOutlineWidth ? `${segmentStyle.textOutlineWidth}px currentColor` : void 0,
1775
- paintOrder: segmentStyle.textOutlineWidth ? "stroke fill" : void 0,
1776
- textShadow: buildTextShadowCss(segmentStyle),
1777
- filter: buildTextRunFilterChain(segmentStyle),
1778
- opacity: getTextAlphaOpacity(segmentStyle),
1779
- WebkitBoxReflect: buildTextReflectionCss(segmentStyle)
1780
- };
1781
1568
  const runRtl = segmentStyle.rtl;
1782
- if (runRtl !== void 0 && runRtl !== paragraphRtl) {
1783
- spanStyle.direction = runRtl ? "rtl" : "ltr";
1784
- spanStyle.unicodeBidi = "bidi-override";
1785
- } else if (runRtl !== void 0 && runRtl === paragraphRtl) {
1569
+ if (runRtl !== void 0) {
1786
1570
  spanStyle.direction = runRtl ? "rtl" : "ltr";
1787
- spanStyle.unicodeBidi = "embed";
1788
- }
1789
- if (bulletInfo) {
1790
- if (bulletInfo.fontFamily) {
1791
- spanStyle.fontFamily = bulletInfo.fontFamily;
1792
- }
1793
- if (typeof bulletInfo.sizePts === "number") {
1794
- spanStyle.fontSize = bulletInfo.sizePts * baselineFontScale;
1795
- } else if (typeof bulletInfo.sizePercent === "number") {
1796
- spanStyle.fontSize = baseFontSize * (bulletInfo.sizePercent / 100) * baselineFontScale;
1797
- }
1798
- if (bulletInfo.color) {
1799
- spanStyle.color = normalizeHexColor(bulletInfo.color, resolvedSegmentColor);
1800
- }
1801
- if (typeof bulletHangPx === "number" && bulletHangPx > 0) {
1802
- spanStyle.display = "inline-block";
1803
- spanStyle.minWidth = bulletHangPx;
1804
- spanStyle.textIndent = 0;
1805
- }
1806
- }
1807
- if (bulletInfo?.imageDataUrl || bulletInfo?.imageRelId) {
1808
- return renderPictureBullet(element.id, segmentIndex, bulletInfo, baseFontSize);
1809
- }
1810
- let hyperlinkUrl = segmentStyle.hyperlink || segment.hyperlink;
1811
- if (hyperlinkUrl && typeof segmentStyle.hyperlinkTargetSlideIndex === "number" && hyperlinkUrl.toLowerCase().startsWith("ppaction://")) {
1812
- const separator = hyperlinkUrl.includes("?") ? "&" : "?";
1813
- hyperlinkUrl = `${hyperlinkUrl}${separator}slideIndex=${segmentStyle.hyperlinkTargetSlideIndex}`;
1571
+ spanStyle.unicodeBidi = runRtl !== ctx.paragraphRtl ? "bidi-override" : "embed";
1814
1572
  }
1815
- const rubyText = segment.rubyText;
1816
- const hasRuby = typeof rubyText === "string" && rubyText.length > 0;
1817
1573
  const baseContent = renderSegmentContent(
1818
1574
  element.id,
1819
1575
  segmentIndex,
@@ -1833,232 +1589,202 @@ function renderSingleSegment(element, segment, segmentIndex, fallbackColor, find
1833
1589
  ),
1834
1590
  metricContext
1835
1591
  );
1836
- let innerContent;
1837
- if (hasRuby) {
1838
- const rubyFs = segment.rubyFontSize ?? baseFontSize * 0.5;
1839
- const rubyStyle = {
1840
- fontSize: rubyFs,
1841
- fontFamily: segment.rubyStyle?.fontFamily ?? baseFontFamily,
1842
- textAlign: segment.rubyAlignment === "l" ? "left" : segment.rubyAlignment === "r" ? "right" : segment.rubyAlignment === "dist" || segment.rubyAlignment === "distCat" || segment.rubyAlignment === "distLetter" ? "justify" : "center"
1843
- };
1844
- if (segment.rubyStyle?.color) {
1845
- rubyStyle.color = normalizeHexColor(segment.rubyStyle.color, resolvedSegmentColor);
1846
- }
1847
- innerContent = /* @__PURE__ */ jsxs("ruby", { children: [
1848
- baseContent,
1849
- /* @__PURE__ */ jsx("rp", { children: "(" }),
1850
- /* @__PURE__ */ jsx("rt", { style: rubyStyle, children: rubyText }),
1851
- /* @__PURE__ */ jsx("rp", { children: ")" })
1852
- ] });
1853
- } else {
1854
- innerContent = baseContent;
1855
- }
1856
- const spanNode = /* @__PURE__ */ jsx("span", { "data-seg-idx": segmentIndex, style: spanStyle, children: innerContent }, `${element.id}-seg-${segmentIndex}`);
1857
- if (hyperlinkUrl && onHyperlinkClick) {
1858
- const displayUrl = hyperlinkUrl.startsWith("ppaction://") ? hyperlinkUrl.replace(/^ppaction:\/\//u, "").split("?")[0] : hyperlinkUrl;
1859
- return /* @__PURE__ */ jsxs(
1860
- "span",
1861
- {
1862
- role: "link",
1863
- tabIndex: 0,
1864
- className: requireCtrlClick ? "group/link relative" : void 0,
1865
- style: { cursor: requireCtrlClick ? void 0 : "pointer", pointerEvents: "auto" },
1866
- onClick: (e) => {
1867
- if (requireCtrlClick && !e.ctrlKey && !e.metaKey) {
1868
- return;
1869
- }
1870
- e.stopPropagation();
1871
- e.preventDefault();
1872
- onHyperlinkClick(hyperlinkUrl);
1873
- },
1874
- onKeyDown: (e) => {
1875
- if (e.key === "Enter" || e.key === " ") {
1876
- if (requireCtrlClick && !e.ctrlKey && !e.metaKey) {
1877
- return;
1878
- }
1879
- e.preventDefault();
1880
- e.stopPropagation();
1881
- onHyperlinkClick(hyperlinkUrl);
1882
- }
1883
- },
1884
- children: [
1885
- spanNode,
1886
- 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: [
1887
- /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground", children: displayUrl }),
1888
- /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Ctrl+Click to follow link" })
1889
- ] }) })
1890
- ]
1891
- },
1892
- `${element.id}-seg-${segmentIndex}-link`
1893
- );
1894
- }
1895
- return spanNode;
1592
+ const spanNode = /* @__PURE__ */ jsx("span", { "data-seg-idx": segmentIndex, style: spanStyle, children: renderRubyOrText(run, baseContent) }, key);
1593
+ return run.hyperlink && onHyperlinkClick ? renderHyperlink(run, spanNode, key, ctx) : spanNode;
1594
+ }
1595
+ function renderRubyOrText(run, baseContent) {
1596
+ if (!run.ruby) {
1597
+ return baseContent;
1598
+ }
1599
+ return /* @__PURE__ */ jsxs("ruby", { children: [
1600
+ baseContent,
1601
+ /* @__PURE__ */ jsx("rp", { children: "(" }),
1602
+ /* @__PURE__ */ jsx("rt", { style: run.ruby.style, children: run.ruby.text }),
1603
+ /* @__PURE__ */ jsx("rp", { children: ")" })
1604
+ ] });
1896
1605
  }
1897
- function groupSegmentsIntoParagraphs(segments) {
1898
- const paragraphs = [];
1899
- let current = [];
1900
- for (let i = 0; i < segments.length; i++) {
1901
- const seg = segments[i];
1902
- if (seg.text === "\n") {
1903
- paragraphs.push({ entries: current, terminator: seg });
1904
- current = [];
1905
- } else {
1906
- current.push({ segment: seg, globalIndex: i });
1907
- }
1908
- }
1909
- if (current.length > 0 || paragraphs.length === 0) {
1910
- paragraphs.push({ entries: current });
1606
+ function renderHyperlink(run, spanNode, key, ctx) {
1607
+ const url = run.hyperlink?.url;
1608
+ const onHyperlinkClick = ctx.onHyperlinkClick;
1609
+ if (!url || !onHyperlinkClick) {
1610
+ return spanNode;
1911
1611
  }
1912
- return paragraphs;
1612
+ const requireCtrlClick = ctx.requireCtrlClick;
1613
+ const displayUrl = url.startsWith("ppaction://") ? url.replace(/^ppaction:\/\//u, "").split("?")[0] : url;
1614
+ const follow = (modified) => {
1615
+ if (requireCtrlClick && !modified) {
1616
+ return false;
1617
+ }
1618
+ onHyperlinkClick(url);
1619
+ return true;
1620
+ };
1621
+ return /* @__PURE__ */ jsxs(
1622
+ "span",
1623
+ {
1624
+ role: "link",
1625
+ tabIndex: 0,
1626
+ className: requireCtrlClick ? "group/link relative" : void 0,
1627
+ style: { cursor: requireCtrlClick ? void 0 : "pointer", pointerEvents: "auto" },
1628
+ title: run.hyperlink?.tooltip,
1629
+ onClick: (e) => {
1630
+ if (!follow(e.ctrlKey || e.metaKey)) {
1631
+ return;
1632
+ }
1633
+ e.stopPropagation();
1634
+ e.preventDefault();
1635
+ },
1636
+ onKeyDown: (e) => {
1637
+ if (e.key !== "Enter" && e.key !== " ") {
1638
+ return;
1639
+ }
1640
+ if (!follow(e.ctrlKey || e.metaKey)) {
1641
+ return;
1642
+ }
1643
+ e.preventDefault();
1644
+ e.stopPropagation();
1645
+ },
1646
+ children: [
1647
+ spanNode,
1648
+ 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: [
1649
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground", children: displayUrl }),
1650
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Ctrl+Click to follow link" })
1651
+ ] }) })
1652
+ ]
1653
+ },
1654
+ `${key}-link`
1655
+ );
1913
1656
  }
1914
1657
  function renderTextSegments(element, fallbackColor, emptyFallback, findHighlights, onHyperlinkClick, fieldContext, subElementAnimStates, segmentOverrides, requireCtrlClick) {
1915
1658
  if (!hasTextProperties(element)) {
1916
1659
  return emptyFallback || null;
1917
1660
  }
1918
- const effectiveSegments = segmentOverrides ?? element.textSegments;
1919
- if (!effectiveSegments || effectiveSegments.length === 0) {
1661
+ const segments = segmentOverrides ?? element.textSegments;
1662
+ if (!segments || segments.length === 0) {
1920
1663
  if (!element.text && element.promptText) {
1921
- return /* @__PURE__ */ jsx(
1922
- "span",
1923
- {
1924
- style: {
1925
- opacity: 0.5,
1926
- color: "#888888",
1927
- pointerEvents: "none"
1928
- },
1929
- children: element.promptText
1930
- }
1931
- );
1664
+ return /* @__PURE__ */ jsx("span", { style: { opacity: 0.5, color: "#888888", pointerEvents: "none" }, children: element.promptText });
1932
1665
  }
1933
1666
  return element.text || emptyFallback || "";
1934
1667
  }
1935
- const paragraphs = groupSegmentsIntoParagraphs(effectiveSegments);
1936
- const paragraphIndents = hasTextProperties(element) ? element.paragraphIndents : void 0;
1937
- const elementRtl = hasTextProperties(element) ? element.textStyle?.rtl : void 0;
1938
- const elementAlign = hasTextProperties(element) ? element.textStyle?.align : void 0;
1939
- const bodyStyle = hasTextProperties(element) ? element.textStyle : void 0;
1940
- const spaceFirstLast = bodyStyle?.spaceFirstLastParagraph !== false;
1941
- return paragraphs.map(({ entries: paraSegments, terminator }, paraIndex) => {
1942
- const paraIndent = paragraphIndents?.[paraIndex];
1943
- const rawMarginLeft = typeof paraIndent?.marginLeft === "number" && paraIndent.marginLeft !== 0 ? paraIndent.marginLeft : void 0;
1944
- const rawTextIndent = typeof paraIndent?.indent === "number" && paraIndent.indent !== 0 ? paraIndent.indent : void 0;
1945
- const firstSeg = paraSegments[0];
1946
- const bulletInfo = firstSeg?.segment.bulletInfo;
1947
- const hasVisibleTextContent = paraSegments.some(({ segment }) => {
1948
- if (segment.bulletInfo) {
1949
- return false;
1950
- }
1951
- return Boolean(segment.text) && segment.text.trim().length > 0;
1952
- });
1953
- const hasBullet = bulletInfo && !bulletInfo.none && hasVisibleTextContent;
1954
- const paraRtl = resolveParagraphRtl(paraSegments, elementRtl);
1955
- const isRtlParagraph = paraRtl === true;
1956
- const paraAlign = resolveParagraphAlign(paraSegments, elementAlign);
1957
- const cssTextAlign = resolveCssTextAlign(paraAlign, isRtlParagraph);
1958
- const paraMarginLeft = isRtlParagraph ? void 0 : rawMarginLeft;
1959
- const paraMarginRight = isRtlParagraph ? rawMarginLeft : void 0;
1960
- const paraTextIndent = rawTextIndent;
1961
- const paraKinsokuStyle = getKinsokuLineBreakStyles(firstSeg?.segment.style);
1962
- const hasParaKinsoku = Object.keys(paraKinsokuStyle).length > 0;
1963
- const paraProps = effectiveSegments[firstSeg?.globalIndex ?? -1]?.paragraphProperties ?? (paraSegments.length === 0 ? terminator?.paragraphProperties : void 0);
1964
- const spacing = resolveParagraphSpacing({
1965
- paraProps,
1966
- bodyStyle,
1967
- isFirst: paraIndex === 0,
1968
- isLast: paraIndex === paragraphs.length - 1,
1969
- spaceFirstLast
1970
- });
1971
- const strutFontSize = resolveParagraphStrutFontSize(
1972
- paraSegments.length > 0 ? paraSegments.map(({ segment }) => segment) : terminator ? [terminator] : [],
1973
- bodyStyle?.fontSize
1974
- );
1975
- const hasParaSpacing = spacing.spaceBeforePx !== void 0 || spacing.spaceAfterPx !== void 0 || spacing.lineHeight !== void 0;
1976
- const paraStyle = {
1977
- ...paraKinsokuStyle
1978
- };
1979
- if (strutFontSize !== void 0) {
1980
- paraStyle.fontSize = strutFontSize;
1981
- }
1982
- if (spacing.spaceBeforePx !== void 0) {
1983
- paraStyle.marginTop = spacing.spaceBeforePx;
1984
- }
1985
- if (spacing.spaceAfterPx !== void 0) {
1986
- paraStyle.marginBottom = spacing.spaceAfterPx;
1987
- }
1988
- if (spacing.lineHeight !== void 0) {
1989
- paraStyle.lineHeight = spacing.lineHeight;
1990
- }
1991
- if (paraMarginLeft !== void 0) {
1992
- paraStyle.marginLeft = paraMarginLeft;
1993
- }
1994
- if (paraMarginRight !== void 0) {
1995
- paraStyle.marginRight = paraMarginRight;
1996
- }
1997
- if (paraTextIndent !== void 0) {
1998
- paraStyle.textIndent = paraTextIndent;
1999
- }
2000
- if (paraRtl !== void 0) {
2001
- paraStyle.direction = paraRtl ? "rtl" : "ltr";
2002
- paraStyle.unicodeBidi = "embed";
1668
+ const paragraphs = buildParagraphs(element, fieldContext, segmentOverrides);
1669
+ const ctx = {
1670
+ element,
1671
+ fallbackColor,
1672
+ findHighlights,
1673
+ onHyperlinkClick,
1674
+ requireCtrlClick
1675
+ };
1676
+ return paragraphs.map(
1677
+ (para, paraIndex) => renderParagraph(para, paraIndex, paragraphs.length, segments, ctx, subElementAnimStates)
1678
+ );
1679
+ }
1680
+ function joinRunsBySegment(runs) {
1681
+ const out = [];
1682
+ for (const run of runs) {
1683
+ const last = out[out.length - 1];
1684
+ if (last && run.segmentIndex !== void 0 && last.segmentIndex === run.segmentIndex && !last.equation && !run.equation) {
1685
+ last.text += run.text;
1686
+ continue;
2003
1687
  }
2004
- if (cssTextAlign !== void 0) {
2005
- paraStyle.textAlign = cssTextAlign;
1688
+ out.push({ ...run });
1689
+ }
1690
+ return out;
1691
+ }
1692
+ function renderParagraph(para, paraIndex, paraCount, segments, ctx, subElementAnimStates) {
1693
+ const element = ctx.element;
1694
+ const runCtx = { ...ctx, paragraphRtl: para.rtl };
1695
+ const runs = joinRunsBySegment(para.runs);
1696
+ const renderedRuns = runs.map(
1697
+ (run) => renderParagraphRun(run, segments[run.segmentIndex ?? -1], runCtx)
1698
+ );
1699
+ const paraStyle = {
1700
+ // `text-align`, BiDi `direction` / `unicode-bidi` and the kinsoku
1701
+ // line-break rules, resolved by shared from this paragraph's own `a:pPr`.
1702
+ ...para.paragraphStyle
1703
+ };
1704
+ if (para.strutFontSizePx !== void 0) {
1705
+ paraStyle.fontSize = para.strutFontSizePx;
1706
+ }
1707
+ if (para.spaceBeforePx !== void 0) {
1708
+ paraStyle.marginTop = para.spaceBeforePx;
1709
+ }
1710
+ if (para.spaceAfterPx !== void 0) {
1711
+ paraStyle.marginBottom = para.spaceAfterPx;
1712
+ }
1713
+ if (para.lineHeight !== void 0) {
1714
+ paraStyle.lineHeight = para.lineHeight;
1715
+ }
1716
+ if (para.marginLeftPx !== void 0) {
1717
+ if (para.rtl === true) {
1718
+ paraStyle.marginRight = para.marginLeftPx;
1719
+ } else {
1720
+ paraStyle.marginLeft = para.marginLeftPx;
2006
1721
  }
2007
- const needsWrapper = paraMarginLeft !== void 0 || paraMarginRight !== void 0 || paraTextIndent !== void 0 || hasBullet || paraRtl !== void 0 || cssTextAlign !== void 0 || hasParaKinsoku || hasParaSpacing || strutFontSize !== void 0;
2008
- const renderedSegments = paraSegments.filter(({ segment }) => {
2009
- if (!hasBullet && segment.bulletInfo) {
2010
- return false;
2011
- }
2012
- return true;
2013
- }).map(
2014
- ({ segment, globalIndex }) => renderSingleSegment(
2015
- element,
2016
- segment,
2017
- globalIndex,
2018
- fallbackColor,
2019
- findHighlights,
2020
- hasBullet && globalIndex === firstSeg.globalIndex ? bulletInfo : void 0,
2021
- onHyperlinkClick,
2022
- fieldContext,
2023
- paraRtl,
2024
- requireCtrlClick,
2025
- // Marker box = the hanging distance, so the first line's text
2026
- // lands on the indent stop (see `bulletHangPx` in
2027
- // `renderSingleSegment`).
2028
- hasBullet && globalIndex === firstSeg.globalIndex && typeof paraTextIndent === "number" && paraTextIndent < 0 ? -paraTextIndent : void 0
2029
- )
2030
- );
2031
- const wrappedContent = wrapWithTextBuildAnimation(
2032
- element.id,
2033
- paraIndex,
2034
- renderedSegments,
2035
- paraSegments,
2036
- subElementAnimStates,
2037
- // A staged build splits the paragraph but must not flatten it: each
2038
- // piece is re-rendered through the SAME segment renderer, so it keeps
2039
- // its run's font, size, colour and decoration while it animates.
2040
- ({ segment, globalIndex }, text) => renderSingleSegment(
2041
- element,
2042
- { ...segment, text },
2043
- globalIndex,
2044
- fallbackColor,
2045
- findHighlights,
2046
- void 0,
2047
- onHyperlinkClick,
2048
- fieldContext,
2049
- paraRtl,
2050
- requireCtrlClick
2051
- )
1722
+ }
1723
+ if (para.textIndentPx !== void 0) {
1724
+ paraStyle.textIndent = para.textIndentPx;
1725
+ }
1726
+ const marker = renderBulletMarker(para, paraIndex, element.id);
1727
+ const wrappedContent = wrapWithTextBuildAnimation(
1728
+ element.id,
1729
+ paraIndex,
1730
+ renderedRuns,
1731
+ runs,
1732
+ subElementAnimStates,
1733
+ // A staged build splits the paragraph but must not flatten it: each piece
1734
+ // is re-rendered through the SAME run renderer, so it keeps its run's
1735
+ // font, size, colour and decoration while it animates.
1736
+ (run, text, pieceKey) => renderParagraphRun(
1737
+ { ...run, text },
1738
+ segments[run.segmentIndex ?? -1],
1739
+ runCtx,
1740
+ `-build-${pieceKey}`
1741
+ )
1742
+ );
1743
+ const needsWrapper = Object.keys(paraStyle).length > 0 || marker !== null;
1744
+ if (!needsWrapper) {
1745
+ return /* @__PURE__ */ jsxs(React6.Fragment, { children: [
1746
+ wrappedContent,
1747
+ paraIndex < paraCount - 1 ? /* @__PURE__ */ jsx("br", {}) : null
1748
+ ] }, `${element.id}-para-${paraIndex}`);
1749
+ }
1750
+ return /* @__PURE__ */ jsxs("div", { style: paraStyle, children: [
1751
+ marker,
1752
+ para.isEmpty ? /* @__PURE__ */ jsx("br", {}) : wrappedContent
1753
+ ] }, `${element.id}-para-${paraIndex}`);
1754
+ }
1755
+ function renderBulletMarker(para, paraIndex, elementId) {
1756
+ const picture = para.bulletPicture;
1757
+ if (picture?.src) {
1758
+ return /* @__PURE__ */ jsx(
1759
+ "img",
1760
+ {
1761
+ src: picture.src,
1762
+ alt: picture.accessibleLabel,
1763
+ style: {
1764
+ width: picture.sizePx,
1765
+ height: picture.sizePx,
1766
+ display: "inline-block",
1767
+ verticalAlign: "middle",
1768
+ marginInlineEnd: 4,
1769
+ objectFit: "contain"
1770
+ }
1771
+ },
1772
+ `${elementId}-para-${paraIndex}-bullet-img`
2052
1773
  );
2053
- const isBlankParagraph = renderedSegments.length === 0;
2054
- if (!needsWrapper) {
2055
- return /* @__PURE__ */ jsxs(React6.Fragment, { children: [
2056
- wrappedContent,
2057
- paraIndex < paragraphs.length - 1 ? /* @__PURE__ */ jsx("br", {}) : null
2058
- ] }, `${element.id}-para-${paraIndex}`);
2059
- }
2060
- return /* @__PURE__ */ jsx("div", { style: paraStyle, children: isBlankParagraph ? /* @__PURE__ */ jsx("br", {}) : wrappedContent }, `${element.id}-para-${paraIndex}`);
2061
- });
1774
+ }
1775
+ if (para.bulletMarker === void 0) {
1776
+ return null;
1777
+ }
1778
+ return /* @__PURE__ */ jsx(
1779
+ "span",
1780
+ {
1781
+ className: "pptx-bullet",
1782
+ style: para.bulletStyle,
1783
+ "aria-label": picture?.accessibleLabel,
1784
+ children: para.bulletMarker
1785
+ },
1786
+ `${elementId}-para-${paraIndex}-bullet`
1787
+ );
2062
1788
  }
2063
1789
  function hexToRgbUnit(hex) {
2064
1790
  const clean = hex.replace("#", "");
@@ -2107,23 +1833,6 @@ function buildLineGlowFilter(element) {
2107
1833
  const glowCol = colorWithOpacity(normalizeHexColor(ss.lineGlowColor, "#ffff00"), glowOpacity);
2108
1834
  return `drop-shadow(0 0 ${glowRad}px ${glowCol})`;
2109
1835
  }
2110
- function mapDagBlendModeToCss(blend) {
2111
- if (!blend) {
2112
- return void 0;
2113
- }
2114
- switch (blend) {
2115
- case "mult":
2116
- return "multiply";
2117
- case "screen":
2118
- return "screen";
2119
- case "darken":
2120
- return "darken";
2121
- case "lighten":
2122
- return "lighten";
2123
- default:
2124
- return void 0;
2125
- }
2126
- }
2127
1836
  function getDagDuotoneFilterId(elementId) {
2128
1837
  return `dag-duotone-${elementId}`;
2129
1838
  }
@@ -2178,17 +1887,6 @@ function renderDagDuotoneSvgFilter(elementId, color1, color2) {
2178
1887
  );
2179
1888
  }
2180
1889
 
2181
- // src/viewer/utils/shape-round-rect.ts
2182
- function localClampAdjustment(value) {
2183
- return Math.max(SHAPE_ADJUSTMENT_MIN, Math.min(SHAPE_ADJUSTMENT_MAX, Math.round(value)));
2184
- }
2185
- function getRoundRectRadiusPx(element) {
2186
- const adjustment = element.shapeAdjustments?.adj;
2187
- const adjValue = typeof adjustment === "number" && Number.isFinite(adjustment) ? localClampAdjustment(adjustment) : DEFAULT_ROUND_RECT_ADJUSTMENT;
2188
- const normalizedAdjustment = adjValue / SHAPE_ADJUSTMENT_MAX;
2189
- return Math.min(Math.max(element.width, 1), Math.max(element.height, 1)) * 0.5 * normalizedAdjustment;
2190
- }
2191
-
2192
1890
  // src/viewer/utils/shape-visual-3d.ts
2193
1891
  function build3DExtrusionData2(shape3d, scene3d, fillColor, elementWidth, elementHeight) {
2194
1892
  return build3DExtrusionData(
@@ -2204,162 +1902,52 @@ function apply3dEffects2(base, scene3d, shape3d, fillColorFallback) {
2204
1902
  }
2205
1903
 
2206
1904
  // src/viewer/utils/shape-visual-style.ts
2207
- function getGradientTilingCss(style) {
2208
- if (!style || style.fillMode !== "gradient") {
2209
- return void 0;
2210
- }
2211
- const isStructuredLinear = (style.fillGradientType || "linear") === "linear" && sanitizeGradientStops(style.fillGradientStops).length > 0;
2212
- if (isStructuredLinear) {
2213
- const flip = getGradientTileFlipCss(style.fillGradientFlip);
2214
- if (flip) {
2215
- return flip;
2216
- }
2217
- }
2218
- return getGradientTileRectCss(style.fillGradientTileRect);
1905
+ function rendersCustomVectorPath(element) {
1906
+ 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;
2219
1907
  }
2220
- function getShapeVisualStyle(element, hasFill, fillColor, strokeWidth, strokeColor, animatesFill, animatesStroke) {
1908
+ function getShapeVisualStyle(element, _hasFill, fillColor, _strokeWidth, _strokeColor, animatesFill, animatesStroke, parentGroupFill) {
2221
1909
  if (!hasShapeProperties(element)) {
2222
1910
  return {};
2223
1911
  }
2224
- const normalizedShapeType = getShapeType(element.shapeType);
2225
- const clipPath = isIdentityRectClip(element) ? void 0 : getResolvedShapeClipPath(element);
2226
- const rendersCustomVectorPath = (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;
2227
- const rendersStrokeOnlyPreset = isStrokeOnlyPresetElement(element);
2228
- const fillOpacity = element.shapeStyle?.fillOpacity;
2229
- const strokeOpacity = element.shapeStyle?.strokeOpacity;
2230
- const strokeDash = normalizeStrokeDashType(element.shapeStyle?.strokeDash);
2231
- const fillGradient = buildCssGradientFromShapeStyle(element.shapeStyle, {
2232
- rotation: element.rotation,
2233
- width: element.width,
2234
- height: element.height
2235
- }) || element.shapeStyle?.fillGradient;
2236
- const shadowCss = buildShadowCssFromShapeStyle(element.shapeStyle);
2237
- const innerShadowCss = buildInnerShadowCssFromShapeStyle(element.shapeStyle);
2238
- const resolvedFillColor = colorWithOpacity(fillColor, fillOpacity);
2239
- const resolvedStrokeColor = colorWithOpacity(strokeColor, strokeOpacity);
2240
1912
  const ss = element.shapeStyle;
2241
- const gradientTiling = getGradientTilingCss(ss);
2242
- const paintsGradientOutline = suppressesCssBorder(element);
2243
- const hasFillOverlayColor = Boolean(
2244
- ss?.dagFillOverlayColor && ss.dagFillOverlayColor !== "transparent"
2245
- );
2246
- const combinedShadowParts = [];
2247
- const multiLayerShadow = buildMultiLayerShadowCss(element.shapeStyle);
2248
- if (multiLayerShadow) {
2249
- combinedShadowParts.push(multiLayerShadow);
2250
- } else if (shadowCss) {
2251
- combinedShadowParts.push(shadowCss);
2252
- }
2253
- if (innerShadowCss) {
2254
- combinedShadowParts.push(innerShadowCss);
2255
- }
2256
- const glowBoxShadow = buildGlowBoxShadow(ss?.glowColor, ss?.glowRadius, ss?.glowOpacity);
2257
- if (glowBoxShadow) {
2258
- combinedShadowParts.push(glowBoxShadow);
2259
- }
2260
- const lineShadow = buildLineShadowCss(element);
2261
- if (lineShadow) {
2262
- combinedShadowParts.push(lineShadow);
2263
- }
2264
- const compoundLineShadow = getCompoundLineBoxShadow(
2265
- element.shapeStyle?.compoundLine,
2266
- strokeWidth,
2267
- resolvedStrokeColor
2268
- );
2269
- if (compoundLineShadow) {
2270
- combinedShadowParts.push(compoundLineShadow);
2271
- }
2272
- const combinedBoxShadow = combinedShadowParts.length > 0 ? combinedShadowParts.join(", ") : void 0;
2273
- const filterParts = [];
2274
- if (ss?.glowColor && ss.glowColor !== "transparent" && ss.glowRadius) {
2275
- const glowOpacity = typeof ss.glowOpacity === "number" ? ss.glowOpacity : 0.75;
2276
- const glowRad = Math.round(Math.max(0, ss.glowRadius));
2277
- const glowCol = colorWithOpacity(normalizeHexColor(ss.glowColor, "#ffff00"), glowOpacity);
2278
- filterParts.push(`drop-shadow(0 0 ${glowRad}px ${glowCol})`);
2279
- }
2280
- if (typeof ss?.softEdgeRadius === "number" && ss.softEdgeRadius > 0) {
2281
- filterParts.push(`url(#${getSoftEdgeFilterId(element.id)})`);
2282
- }
2283
- if (typeof ss?.blurRadius === "number" && ss.blurRadius > 0) {
2284
- filterParts.push(`blur(${Math.round(ss.blurRadius)}px)`);
2285
- }
2286
- const lineGlowCss = buildLineGlowFilter(element);
2287
- if (lineGlowCss) {
2288
- filterParts.push(lineGlowCss);
2289
- }
2290
- const dagFilter = getEffectDagFilter(ss, element.id);
2291
- if (dagFilter) {
2292
- filterParts.push(dagFilter);
2293
- }
2294
- const lineJoinCss = ss?.lineJoin === "round" ? "round" : ss?.lineJoin === "bevel" ? "bevel" : ss?.lineJoin === "miter" ? "miter" : void 0;
2295
- const miterLimitCss = ss?.lineJoin === "miter" && typeof ss?.miterLimit === "number" ? Math.max(ss.miterLimit / 1e5, 1) : void 0;
2296
- const patternFill = buildPatternFillCss(element.shapeStyle);
2297
- const imageFillUrl = ss?.fillMode === "image" && ss.fillImageUrl ? ss.fillImageUrl : void 0;
2298
- const imageFillMode = ss?.fillImageMode || "stretch";
2299
- let reflectCss;
2300
- if (ss) {
2301
- const hasReflection = typeof ss.reflectionStartOpacity === "number" && ss.reflectionStartOpacity > 0 || typeof ss.reflectionDistance === "number" && ss.reflectionDistance > 0 || typeof ss.reflectionBlurRadius === "number" && ss.reflectionBlurRadius > 0;
2302
- if (hasReflection) {
2303
- const distance = ss.reflectionDistance ?? 0;
2304
- const startOpacity = typeof ss.reflectionStartOpacity === "number" ? ss.reflectionStartOpacity : 0.5;
2305
- const endOpacity = typeof ss.reflectionEndOpacity === "number" ? ss.reflectionEndOpacity : 0;
2306
- const fadeLength = typeof ss.reflectionEndPosition === "number" ? Math.round(ss.reflectionEndPosition * Math.max(element.height, 1)) : 100;
2307
- const blurRadius = typeof ss.reflectionBlurRadius === "number" ? ss.reflectionBlurRadius : 0;
2308
- reflectCss = buildReflectionCss(distance, startOpacity, endOpacity, fadeLength, blurRadius);
2309
- }
2310
- }
1913
+ const fill = animatesFill ? void 0 : getComputedFillStyle(element, parentGroupFill);
1914
+ const fx = getComputedEffectStyle(element);
1915
+ const stroke = getComputedStrokeStyle(element);
2311
1916
  const base = {
2312
- // A gradient fill REPLACES the solid fill, it does not sit on top of it.
2313
- // The parser also records a representative `fillColor`/`fillOpacity` for a
2314
- // `a:gradFill` (used by thumbnails and the inspector), and painting that as
2315
- // a `backgroundColor` under the gradient washed the whole shape: wherever
2316
- // the gradient's own stops were transparent the solid still showed, so a
2317
- // fade-to-transparent overlay dimmed everything behind it and its edge read
2318
- // as a hard line. Mirrors the fill precedence in shared's
2319
- // `getComputedFillStyle` (image -> gradient -> pattern -> solid).
2320
- backgroundColor: imageFillUrl ? "transparent" : patternFill ? patternFill.backgroundColor : hasFill && !fillGradient ? resolvedFillColor : "transparent",
2321
- backgroundImage: imageFillUrl ? `url(${imageFillUrl})` : patternFill ? patternFill.backgroundImage : hasFill && fillGradient ? fillGradient : void 0,
2322
- backgroundRepeat: imageFillUrl ? imageFillMode === "tile" ? "repeat" : "no-repeat" : patternFill ? "repeat" : hasFill && fillGradient ? gradientTiling?.backgroundRepeat ?? "no-repeat" : void 0,
2323
- backgroundSize: imageFillUrl ? imageFillMode === "tile" ? "auto" : "100% 100%" : patternFill ? "auto" : hasFill && fillGradient ? gradientTiling?.backgroundSize ?? "100% 100%" : void 0,
2324
- // The container always carries a 1px border (transparent unless the shape
2325
- // has a stroke or is hovered) and `box-sizing: border-box`, so the default
2326
- // `background-origin: padding-box` sizes the paint 2px smaller than the
2327
- // shape. Combined with the default `background-repeat: repeat` that made a
2328
- // gradient tile wrap, painting a 1px sliver of its opposite end along the
2329
- // edge - a fade-to-transparent overlay grew a hard opaque line down its
2330
- // side. Paint from the border box so the fill matches the shape outline.
1917
+ // A gradient fill REPLACES the solid fill, it does not sit on top of it;
1918
+ // the whole precedence lives in shared's `getComputedFillStyle`.
1919
+ backgroundColor: fill?.backgroundColor ?? "transparent",
1920
+ backgroundImage: fill?.backgroundImage,
1921
+ backgroundRepeat: fill?.backgroundRepeat,
1922
+ backgroundSize: fill?.backgroundSize,
1923
+ backgroundPosition: fill?.backgroundPosition,
1924
+ // The container always carries `box-sizing: border-box`, so the default
1925
+ // `background-origin: padding-box` would size the paint 2px smaller than
1926
+ // the shape and make a gradient tile wrap, painting a 1px sliver of its
1927
+ // opposite end along the edge. Paint from the border box instead.
2331
1928
  backgroundOrigin: "border-box",
2332
- backgroundPosition: imageFillUrl ? "center" : hasFill && fillGradient ? gradientTiling?.backgroundPosition : void 0,
2333
- boxShadow: combinedBoxShadow,
2334
- WebkitBoxReflect: reflectCss,
2335
- filter: filterParts.length > 0 ? filterParts.join(" ") : void 0,
2336
- opacity: typeof ss?.dagAlphaModFix === "number" ? Math.max(0, Math.min(1, ss.dagAlphaModFix / 100)) : void 0,
2337
- // Only proxy the DAG blend onto the whole element for the legacy
2338
- // blend-only case (no overlay colour parsed). When a fill-overlay colour
2339
- // is present, `ShapeEffectOverlay` paints a separate blended tint layer so
2340
- // the colour is actually rendered (and text/children are not tinted).
2341
- mixBlendMode: hasFillOverlayColor ? void 0 : mapDagBlendModeToCss(ss?.dagFillOverlayBlend),
2342
- // An unstroked element must occupy EXACTLY its authored box. The
2343
- // container class carries a 1px (transparent) border for the
2344
- // selection/hover affordance and `box-sizing: border-box`, which
2345
- // silently shrank every unstroked element's content by 2px and shifted
2346
- // it 1px down-right. On a 40x40 icon that is a visible 5% shrink, and
2347
- // two adjacent 40px buttons rendered with a 2px gap PowerPoint does not
2348
- // draw. The affordance is an `outline` instead (see `ElementRenderer`),
2349
- // which paints in the same place without taking part in layout.
2350
- // A gradient outline is painted by `ShapeEffectOverlay` as a stroked SVG
2351
- // path following the shape's geometry, because a CSS border can only take
2352
- // one colour. Keeping the border too would draw the parser's averaged
2353
- // solid underneath the gradient.
2354
- borderWidth: strokeWidth > 0 && !paintsGradientOutline ? getCompoundLineBorderWidth(ss?.compoundLine, strokeWidth) : 0,
2355
- borderColor: strokeWidth > 0 && !paintsGradientOutline ? resolvedStrokeColor : void 0,
2356
- borderStyle: strokeWidth > 0 && !paintsGradientOutline ? getCssBorderDashStyle(strokeDash, ss?.compoundLine) : void 0,
2357
- strokeLinejoin: lineJoinCss,
2358
- strokeMiterlimit: miterLimitCss,
2359
- strokeLinecap: ss?.lineCap === "rnd" ? "round" : ss?.lineCap === "sq" ? "square" : ss?.lineCap === "flat" ? "butt" : void 0
1929
+ boxShadow: fx.boxShadow,
1930
+ WebkitBoxReflect: fx.webkitBoxReflect,
1931
+ filter: fx.filter,
1932
+ opacity: fx.opacity,
1933
+ // Only proxy the DAG blend onto the whole element for the blend-only case:
1934
+ // with an overlay colour, `ShapeEffectOverlay` paints a separate blended
1935
+ // tint layer (shared decides which, and returns only one of the two).
1936
+ mixBlendMode: fx.mixBlendMode,
1937
+ // An unstroked element must occupy EXACTLY its authored box, so the
1938
+ // selection/hover affordance is an `outline` (see `ElementRenderer`) and
1939
+ // the border collapses to 0 rather than to a transparent 1px.
1940
+ borderWidth: stroke.borderWidth,
1941
+ borderColor: animatesStroke ? void 0 : stroke.borderColor,
1942
+ borderStyle: stroke.borderStyle,
1943
+ // Inherited SVG presentation properties: written on the container so the
1944
+ // freeform `<path>` and the stroke overlay both pick them up.
1945
+ strokeLinejoin: stroke.strokeLinejoin,
1946
+ strokeMiterlimit: stroke.strokeMiterlimit,
1947
+ strokeLinecap: stroke.strokeLinecap
2360
1948
  };
2361
1949
  apply3dEffects2(base, ss?.scene3d, ss?.shape3d, ss?.fillColor ?? fillColor);
2362
- if (rendersCustomVectorPath || rendersStrokeOnlyPreset) {
1950
+ if (rendersCustomVectorPath(element) || isStrokeOnlyPresetElement(element)) {
2363
1951
  base.backgroundColor = "transparent";
2364
1952
  base.backgroundImage = void 0;
2365
1953
  base.borderWidth = void 0;
@@ -2370,59 +1958,28 @@ function getShapeVisualStyle(element, hasFill, fillColor, strokeWidth, strokeCol
2370
1958
  base.backgroundColor = void 0;
2371
1959
  base.backgroundImage = void 0;
2372
1960
  }
2373
- if (animatesStroke) {
2374
- base.borderColor = void 0;
2375
- }
2376
- if (element.type === "connector" || normalizedShapeType === "connector") {
2377
- return {
2378
- backgroundColor: "transparent",
2379
- borderWidth: 0,
2380
- borderStyle: "none"
2381
- };
2382
- }
2383
- if (normalizedShapeType === "roundRect") {
2384
- const radiusPx = getRoundRectRadiusPx(element);
2385
- if (radiusPx <= 0.01) {
1961
+ const geometry = resolveShapeGeometry(element);
1962
+ switch (geometry.kind) {
1963
+ case "bare":
1964
+ return { backgroundColor: "transparent", borderWidth: 0, borderStyle: "none" };
1965
+ case "strokeOnly":
1966
+ return { ...base, backgroundColor: "transparent", backgroundImage: void 0 };
1967
+ case "borderRadius":
1968
+ return { ...base, borderRadius: geometry.radius };
1969
+ case "clipPath":
1970
+ return { ...base, clipPath: geometry.clipPath };
1971
+ case "lineEdge":
1972
+ return {
1973
+ ...base,
1974
+ backgroundColor: "transparent",
1975
+ borderWidth: 0,
1976
+ borderTopWidth: geometry.strokeWidth,
1977
+ borderTopColor: stroke.borderColor,
1978
+ borderTopStyle: stroke.borderStyle ?? "solid"
1979
+ };
1980
+ default:
2386
1981
  return base;
2387
- }
2388
- return {
2389
- ...base,
2390
- borderRadius: radiusPx
2391
- };
2392
1982
  }
2393
- if (normalizedShapeType === "ellipse") {
2394
- return {
2395
- ...base,
2396
- // `50%`, not a huge px value: CSS clamps over-large radii by scaling
2397
- // them all down uniformly, so `9999px` on a non-square box collapses to
2398
- // half the SHORT side on every corner - a pill with flat long edges,
2399
- // not an ellipse. `50%` resolves per-axis and inscribes correctly.
2400
- borderRadius: "50%"
2401
- };
2402
- }
2403
- if (clipPath && !rendersStrokeOnlyPreset) {
2404
- return {
2405
- ...base,
2406
- clipPath
2407
- };
2408
- }
2409
- if (normalizedShapeType === "line" && !rendersStrokeOnlyPreset) {
2410
- return {
2411
- ...base,
2412
- backgroundColor: "transparent",
2413
- borderWidth: 0,
2414
- borderTopWidth: Math.max(strokeWidth, 2),
2415
- borderTopColor: resolvedStrokeColor,
2416
- borderTopStyle: getCssBorderDashStyle(strokeDash)
2417
- };
2418
- }
2419
- if (normalizedShapeType === "cylinder") {
2420
- return {
2421
- ...base,
2422
- borderRadius: "48% / 12%"
2423
- };
2424
- }
2425
- return base;
2426
1983
  }
2427
1984
  function getConnectorAdjustment(element, key, fallback) {
2428
1985
  const direct = element.shapeAdjustments?.[key];
@@ -3402,6 +2959,28 @@ function parseTableElementData(element, fallbackTextStyle) {
3402
2959
  cells
3403
2960
  };
3404
2961
  }
2962
+ function renderTableCellContent(cell, fallbackText) {
2963
+ const runs = cell?.textRuns;
2964
+ if (!runs || runs.length === 0) {
2965
+ return fallbackText || "\xA0";
2966
+ }
2967
+ return runs.map((run, index) => {
2968
+ if (run.isParagraphBreak) {
2969
+ return /* @__PURE__ */ jsx("div", { style: { display: "block", height: 0 } }, `p-${index}`);
2970
+ }
2971
+ if (run.isLineBreak) {
2972
+ return /* @__PURE__ */ jsx("br", {}, `br-${index}`);
2973
+ }
2974
+ return /* @__PURE__ */ jsx(
2975
+ "span",
2976
+ {
2977
+ style: { position: "relative", ...cellRunStyle(run) },
2978
+ children: run.text
2979
+ },
2980
+ `r-${index}`
2981
+ );
2982
+ });
2983
+ }
3405
2984
  function getCellDiagonalBorders2(style, tableData, pos, styleCtx) {
3406
2985
  return getCellDiagonalBorders(style, tableData, pos, {
3407
2986
  tableStyleMap: styleCtx?.tableStyleMap,
@@ -3667,6 +3246,18 @@ function renderTableFromTableData(element, textStyle, options) {
3667
3246
  const selectedCell = options?.selectedCell || null;
3668
3247
  const isEditable = Boolean(options?.editable);
3669
3248
  const hasCellSelectionHandler = typeof options?.onSelectCell === "function";
3249
+ const sharedCtx = {
3250
+ tableStyleMap: options?.styleCtx?.tableStyleMap,
3251
+ colorScheme: options?.styleCtx?.theme?.colorScheme,
3252
+ fontScheme: options?.styleCtx?.theme?.fontScheme
3253
+ };
3254
+ const cellCss = (cell, rowIndex, cellIndex) => tableCellCss(
3255
+ tableData,
3256
+ cell,
3257
+ { rowIndex, cellIndex, rowCount, columnCount },
3258
+ sharedCtx,
3259
+ textStyle
3260
+ );
3670
3261
  const selectionRect = (() => {
3671
3262
  if (!selectedCell?.selectedCells || selectedCell.selectedCells.length < 2) {
3672
3263
  return void 0;
@@ -3693,7 +3284,11 @@ function renderTableFromTableData(element, textStyle, options) {
3693
3284
  "table",
3694
3285
  {
3695
3286
  className: "w-full h-full border-collapse table-fixed",
3696
- style: { fontFamily: DEFAULT_FONT_FAMILY },
3287
+ style: {
3288
+ fontFamily: DEFAULT_FONT_FAMILY,
3289
+ // `a:tblPr@rtl` mirrors the column order for RTL decks.
3290
+ ...tableContainerCss(tableData)
3291
+ },
3697
3292
  children: [
3698
3293
  tableData.columnWidths.length > 0 && /* @__PURE__ */ jsx("colgroup", { children: tableData.columnWidths.map((w, ci) => /* @__PURE__ */ jsx(
3699
3294
  "col",
@@ -3731,8 +3326,7 @@ function renderTableFromTableData(element, textStyle, options) {
3731
3326
  colSpan: cell.gridSpan && cell.gridSpan > 1 ? cell.gridSpan : void 0,
3732
3327
  rowSpan: cell.rowSpan && cell.rowSpan > 1 ? cell.rowSpan : void 0,
3733
3328
  style: {
3734
- ...textStyle,
3735
- ...cellStyleToCss(cell.style),
3329
+ ...cellCss(cell, rowIndex, cellIndex),
3736
3330
  ...diag ? { position: "relative" } : void 0
3737
3331
  },
3738
3332
  onClick: (event) => {
@@ -3777,10 +3371,7 @@ function renderTableFromTableData(element, textStyle, options) {
3777
3371
  TableCellInput,
3778
3372
  {
3779
3373
  initialText: cell.text ?? "",
3780
- style: {
3781
- ...textStyle,
3782
- ...cellStyleToCss(cell.style)
3783
- },
3374
+ style: cellCss(cell, rowIndex, cellIndex),
3784
3375
  onCommit: (text) => {
3785
3376
  options?.onCommitCellEdit?.(rowIndex, cellIndex, text);
3786
3377
  },
@@ -3791,7 +3382,7 @@ function renderTableFromTableData(element, textStyle, options) {
3791
3382
  });
3792
3383
  }
3793
3384
  }
3794
- ) : cell.text || "\xA0"
3385
+ ) : renderTableCellContent(cell, cell.text || "\xA0")
3795
3386
  ]
3796
3387
  },
3797
3388
  `${element.id}-cell-${rowIndex}-${cellIndex}`
@@ -3851,7 +3442,11 @@ function renderTableElement(element, textStyle, options) {
3851
3442
  "table",
3852
3443
  {
3853
3444
  className: "w-full h-full border-collapse table-fixed",
3854
- style: { fontFamily: DEFAULT_FONT_FAMILY },
3445
+ style: {
3446
+ fontFamily: DEFAULT_FONT_FAMILY,
3447
+ // `a:tblPr@rtl` mirrors the column order for RTL decks.
3448
+ ...tableContainerCss(tableEl.tableData)
3449
+ },
3855
3450
  children: [
3856
3451
  parsedTable.columnPercentages.length > 0 ? /* @__PURE__ */ jsx("colgroup", { children: parsedTable.columnPercentages.map((percentage, columnIndex) => /* @__PURE__ */ jsx(
3857
3452
  "col",
@@ -3970,7 +3565,10 @@ function renderTableElement(element, textStyle, options) {
3970
3565
  });
3971
3566
  }
3972
3567
  }
3973
- ) : extractCellText(cell) || "\xA0"
3568
+ ) : renderTableCellContent(
3569
+ tableEl.tableData?.rows[rowIndex]?.cells[cellIndex],
3570
+ extractCellText(cell) || "\xA0"
3571
+ )
3974
3572
  ]
3975
3573
  },
3976
3574
  `${element.id}-cell-${rowIndex}-${cellIndex}`
@@ -5819,7 +5417,7 @@ function FailedToLoad() {
5819
5417
  var LazySurfaceChart3DScene = React6.lazy(
5820
5418
  async () => {
5821
5419
  try {
5822
- return await import('./SurfaceChart3DScene-Q47OEJXS.mjs');
5420
+ return await import('./SurfaceChart3DScene-JR62SOYO.mjs');
5823
5421
  } catch {
5824
5422
  return { default: FailedToLoad };
5825
5423
  }
@@ -6599,4 +6197,4 @@ function stopPreviewAnimation() {
6599
6197
  activePreview = null;
6600
6198
  }
6601
6199
 
6602
- export { ANIMATION_PRESET_OPTIONS, ARROW_SIZE_OPTIONS, CONNECTOR_ARROW_OPTIONS, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_FILL_COLOR, DEFAULT_FONT_FAMILY2 as DEFAULT_FONT_FAMILY, DEFAULT_STROKE_COLOR, DEFAULT_TABLE_COLUMNS, DEFAULT_TABLE_ROWS, DEFAULT_TEXT_COLOR, DEFAULT_TEXT_FONT_SIZE, EMU_PER_PX, GRID_SIZE, HYPERLINK_COLOR, MIN_ELEMENT_SIZE, SHAPE_PRESETS, SHORTCUT_REFERENCE_ITEMS, SLIDE_NAV_THUMBNAIL_WIDTH, STROKE_DASH_OPTIONS, THEME_COLOR_SWATCHES, TOOLBAR_SECTIONS, UNGROUPED_SECTION_ID, ZOOM_TO_SELECTION_PADDING, build3DExtrusionData2 as build3DExtrusionData, buildLineGlowFilter, buildLineShadowCss, buildNotesPdf, buildPdfFromImageData, buildReactChartViewModel, buildTextBody3DSceneStyle2 as buildTextBody3DSceneStyle, cloneHistorySnapshot2 as cloneHistorySnapshot, cn, executeMediaCommand, getAnimationInitialStyle2 as getAnimationInitialStyle, getConnectorPathGeometry, getCropShapeClipPath, getDuotoneColors, getImageRenderStyle, getImageTilingStyle, getShapeVisualStyle, getTextLayoutStyle, getTextStyleForElement, getTextWarpStyle, hasDagDuotoneEffect, isConnectorOrLineElement, isImageTiled, renderChartElement, renderConnectorMarker, renderDagDuotoneSvgFilter, renderMediaElement, renderTableElement, renderTextSegments, renderVectorShape, shouldRenderFallbackLabel, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation };
6200
+ export { ANIMATION_PRESET_OPTIONS, ARROW_SIZE_OPTIONS, CONNECTOR_ARROW_OPTIONS, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_FILL_COLOR, DEFAULT_FONT_FAMILY2 as DEFAULT_FONT_FAMILY, DEFAULT_SECTION_GROUP_ID, DEFAULT_STROKE_COLOR, DEFAULT_TABLE_COLUMNS, DEFAULT_TABLE_ROWS, DEFAULT_TEXT_COLOR, DEFAULT_TEXT_FONT_SIZE, EMU_PER_PX, GRID_SIZE, HYPERLINK_COLOR, MIN_ELEMENT_SIZE, SHAPE_PRESETS, SHORTCUT_REFERENCE_ITEMS, SLIDE_NAV_THUMBNAIL_WIDTH, STROKE_DASH_OPTIONS, THEME_COLOR_SWATCHES, TOOLBAR_SECTIONS, UNGROUPED_SECTION_ID, ZOOM_TO_SELECTION_PADDING, build3DExtrusionData2 as build3DExtrusionData, buildLineGlowFilter, buildLineShadowCss, buildNotesPdf, buildPdfFromImageData, buildReactChartViewModel, buildTextBody3DSceneStyle2 as buildTextBody3DSceneStyle, cloneHistorySnapshot2 as cloneHistorySnapshot, cn, executeMediaCommand, getAnimationInitialStyle2 as getAnimationInitialStyle, getConnectorPathGeometry, getCropShapeClipPath, getDuotoneColors, getImageRenderStyle, getImageTilingStyle2 as getImageTilingStyle, getShapeVisualStyle, getTextLayoutStyle, getTextStyleForElement, getTextWarpStyle, hasDagDuotoneEffect, isConnectorOrLineElement, renderChartElement, renderConnectorMarker, renderDagDuotoneSvgFilter, renderMediaElement, renderTableElement, renderTextSegments, renderVectorShape, shouldRenderFallbackLabel, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation };