pptx-react-viewer 2.16.10 → 2.17.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 (127) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/{AiChatPanel-EPMBL6EA.js → AiChatPanel-HCHCVQN2.js} +24 -24
  3. package/dist/AiChatPanel-HCHCVQN2.js.br +0 -0
  4. package/dist/AiChatPanel-HCHCVQN2.js.gz +0 -0
  5. package/dist/{AiChatPanel-XHKYPYPH.mjs → AiChatPanel-RZ4A7P4H.mjs} +4 -4
  6. package/dist/AiChatPanel-RZ4A7P4H.mjs.br +0 -0
  7. package/dist/AiChatPanel-RZ4A7P4H.mjs.gz +0 -0
  8. package/dist/{Model3DScene-YMIAGRPG.mjs → Model3DScene-I7FR74AD.mjs} +1 -1
  9. package/dist/Model3DScene-I7FR74AD.mjs.br +0 -0
  10. package/dist/Model3DScene-I7FR74AD.mjs.gz +0 -0
  11. package/dist/{Model3DScene-DUJOZA2B.js → Model3DScene-LK7LSQNH.js} +2 -2
  12. package/dist/Model3DScene-LK7LSQNH.js.br +0 -0
  13. package/dist/Model3DScene-LK7LSQNH.js.gz +0 -0
  14. package/dist/PowerPointViewer-Du7u4INi.d.ts.map +1 -1
  15. package/dist/{SurfaceChart3DScene-Q47OEJXS.mjs → SurfaceChart3DScene-DEQP5O3U.mjs} +1 -1
  16. package/dist/SurfaceChart3DScene-DEQP5O3U.mjs.br +0 -0
  17. package/dist/SurfaceChart3DScene-DEQP5O3U.mjs.gz +0 -0
  18. package/dist/{SurfaceChart3DScene-TNCHWQAK.js → SurfaceChart3DScene-ODQY77SZ.js} +2 -2
  19. package/dist/SurfaceChart3DScene-ODQY77SZ.js.br +0 -0
  20. package/dist/SurfaceChart3DScene-ODQY77SZ.js.gz +0 -0
  21. package/dist/{chunk-G3VAE7ZF.mjs → chunk-422DF424.mjs} +729 -2294
  22. package/dist/chunk-422DF424.mjs.br +0 -0
  23. package/dist/chunk-422DF424.mjs.gz +0 -0
  24. package/dist/{chunk-WK22Z7PK.js → chunk-5BXATG54.js} +520 -868
  25. package/dist/chunk-5BXATG54.js.br +0 -0
  26. package/dist/chunk-5BXATG54.js.gz +0 -0
  27. package/dist/{chunk-Y2KIB3QH.js → chunk-7JPLVECA.js} +44829 -39322
  28. package/dist/chunk-7JPLVECA.js.br +0 -0
  29. package/dist/chunk-7JPLVECA.js.gz +0 -0
  30. package/dist/{chunk-DVZF4ARG.js → chunk-AONFHTTJ.js} +22 -0
  31. package/dist/chunk-AONFHTTJ.js.br +0 -0
  32. package/dist/chunk-AONFHTTJ.js.gz +0 -0
  33. package/dist/{chunk-PI5SFTDC.js → chunk-ATOUVRRQ.js} +3 -3
  34. package/dist/chunk-ATOUVRRQ.js.br +0 -0
  35. package/dist/chunk-ATOUVRRQ.js.gz +0 -0
  36. package/dist/{chunk-EL2YF26P.mjs → chunk-FXJOTEPO.mjs} +1 -1
  37. package/dist/chunk-FXJOTEPO.mjs.br +0 -0
  38. package/dist/chunk-FXJOTEPO.mjs.gz +0 -0
  39. package/dist/{chunk-YWKUIUQX.mjs → chunk-K2QU73KB.mjs} +44784 -39286
  40. package/dist/chunk-K2QU73KB.mjs.br +0 -0
  41. package/dist/chunk-K2QU73KB.mjs.gz +0 -0
  42. package/dist/{chunk-VHME5BE6.mjs → chunk-MQHC5TXI.mjs} +318 -665
  43. package/dist/chunk-MQHC5TXI.mjs.br +0 -0
  44. package/dist/chunk-MQHC5TXI.mjs.gz +0 -0
  45. package/dist/{chunk-ESC6IB2F.js → chunk-NQJPPIVH.js} +1668 -3233
  46. package/dist/chunk-NQJPPIVH.js.br +0 -0
  47. package/dist/chunk-NQJPPIVH.js.gz +0 -0
  48. package/dist/{chunk-5IUEPQTK.mjs → chunk-S64T7C4V.mjs} +22 -0
  49. package/dist/chunk-S64T7C4V.mjs.br +0 -0
  50. package/dist/chunk-S64T7C4V.mjs.gz +0 -0
  51. package/dist/{chunk-FSVE6WUY.js → chunk-TCGXGD6E.js} +550 -463
  52. package/dist/chunk-TCGXGD6E.js.br +0 -0
  53. package/dist/chunk-TCGXGD6E.js.gz +0 -0
  54. package/dist/{chunk-J7EFC3GS.mjs → chunk-ZA5JONCW.mjs} +210 -123
  55. package/dist/chunk-ZA5JONCW.mjs.br +0 -0
  56. package/dist/chunk-ZA5JONCW.mjs.gz +0 -0
  57. package/dist/i18n.js +4 -4
  58. package/dist/i18n.js.br +0 -0
  59. package/dist/i18n.js.gz +0 -0
  60. package/dist/i18n.mjs +1 -1
  61. package/dist/i18n.mjs.br +0 -0
  62. package/dist/i18n.mjs.gz +0 -0
  63. package/dist/index.d.ts +205 -21
  64. package/dist/index.js +41 -41
  65. package/dist/index.js.br +0 -0
  66. package/dist/index.js.gz +0 -0
  67. package/dist/index.mjs +6 -6
  68. package/dist/index.mjs.br +0 -0
  69. package/dist/index.mjs.gz +0 -0
  70. package/dist/internals.d.ts +380 -22
  71. package/dist/internals.d.ts.map +1 -1
  72. package/dist/internals.js +79 -79
  73. package/dist/internals.js.br +0 -0
  74. package/dist/internals.js.gz +0 -0
  75. package/dist/internals.mjs +4 -4
  76. package/dist/internals.mjs.br +0 -0
  77. package/dist/internals.mjs.gz +0 -0
  78. package/dist/pptx-viewer.css +1 -1
  79. package/dist/pptx-viewer.css.br +0 -0
  80. package/dist/pptx-viewer.css.gz +0 -0
  81. package/dist/{useViewerBuildingBlocks-Xddn6SFj.d.ts → useViewerBuildingBlocks-C9-prKYB.d.ts} +32 -6
  82. package/dist/useViewerBuildingBlocks-C9-prKYB.d.ts.map +1 -0
  83. package/dist/viewer/index.d.ts +166 -17
  84. package/dist/viewer/index.js +27 -27
  85. package/dist/viewer/index.js.br +0 -0
  86. package/dist/viewer/index.js.gz +0 -0
  87. package/dist/viewer/index.mjs +6 -6
  88. package/dist/viewer/index.mjs.br +0 -0
  89. package/dist/viewer/index.mjs.gz +0 -0
  90. package/package.json +1 -1
  91. package/dist/AiChatPanel-EPMBL6EA.js.br +0 -0
  92. package/dist/AiChatPanel-EPMBL6EA.js.gz +0 -0
  93. package/dist/AiChatPanel-XHKYPYPH.mjs.br +0 -0
  94. package/dist/AiChatPanel-XHKYPYPH.mjs.gz +0 -0
  95. package/dist/Model3DScene-DUJOZA2B.js.br +0 -0
  96. package/dist/Model3DScene-DUJOZA2B.js.gz +0 -0
  97. package/dist/Model3DScene-YMIAGRPG.mjs.br +0 -0
  98. package/dist/Model3DScene-YMIAGRPG.mjs.gz +0 -0
  99. package/dist/SurfaceChart3DScene-Q47OEJXS.mjs.br +0 -0
  100. package/dist/SurfaceChart3DScene-Q47OEJXS.mjs.gz +0 -0
  101. package/dist/SurfaceChart3DScene-TNCHWQAK.js.br +0 -0
  102. package/dist/SurfaceChart3DScene-TNCHWQAK.js.gz +0 -0
  103. package/dist/chunk-5IUEPQTK.mjs.br +0 -0
  104. package/dist/chunk-5IUEPQTK.mjs.gz +0 -0
  105. package/dist/chunk-DVZF4ARG.js.br +0 -0
  106. package/dist/chunk-DVZF4ARG.js.gz +0 -0
  107. package/dist/chunk-EL2YF26P.mjs.br +0 -0
  108. package/dist/chunk-EL2YF26P.mjs.gz +0 -0
  109. package/dist/chunk-ESC6IB2F.js.br +0 -0
  110. package/dist/chunk-ESC6IB2F.js.gz +0 -0
  111. package/dist/chunk-FSVE6WUY.js.br +0 -0
  112. package/dist/chunk-FSVE6WUY.js.gz +0 -0
  113. package/dist/chunk-G3VAE7ZF.mjs.br +0 -0
  114. package/dist/chunk-G3VAE7ZF.mjs.gz +0 -0
  115. package/dist/chunk-J7EFC3GS.mjs.br +0 -0
  116. package/dist/chunk-J7EFC3GS.mjs.gz +0 -0
  117. package/dist/chunk-PI5SFTDC.js.br +0 -0
  118. package/dist/chunk-PI5SFTDC.js.gz +0 -0
  119. package/dist/chunk-VHME5BE6.mjs.br +0 -0
  120. package/dist/chunk-VHME5BE6.mjs.gz +0 -0
  121. package/dist/chunk-WK22Z7PK.js.br +0 -0
  122. package/dist/chunk-WK22Z7PK.js.gz +0 -0
  123. package/dist/chunk-Y2KIB3QH.js.br +0 -0
  124. package/dist/chunk-Y2KIB3QH.js.gz +0 -0
  125. package/dist/chunk-YWKUIUQX.mjs.br +0 -0
  126. package/dist/chunk-YWKUIUQX.mjs.gz +0 -0
  127. 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-S64T7C4V.mjs';
2
+ import { SHAPE_PRESET_DEFS, computeValueRangeForChart, VIEWER_SHORTCUT_REFERENCE, getAnimationInitialStyle, cloneHistorySnapshot, hasShapeProperties, getImageFitStyle, isImageLikeElement, getImageTilingStyle, runMediaCommand, findMediaElementByElementId, mediaPlaybackAttributes, buildTextBlockStyle, hasTextProperties, toCssWritingMode, toCssTextOrientation, toCssVerticalDirection, getKinsokuLineBreakStyles, 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, computeResizedColumnWidths, computeResizedRowHeight, DEFAULT_ROW_HEIGHT, ooxmlGradientAngleToCssDegrees, generateLogTicks, formatAxisValueWithUnits, getDisplayUnitLabel, splitRunForMetrics, hexToRgbChannels, chartPartToAttrs, segmentByScript, resolveFontForScript } from './chunk-K2QU73KB.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,9 +34,6 @@ 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;
@@ -321,86 +318,8 @@ function getCropShapeClipPath(element) {
321
318
  }
322
319
  return CROP_SHAPE_CLIP_PATHS[shape];
323
320
  }
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
- };
321
+ function getImageTilingStyle2(element) {
322
+ return getImageTilingStyle(element);
404
323
  }
405
324
 
406
325
  // src/viewer/utils/geometry-image.ts
@@ -1133,9 +1052,6 @@ function getTextLayoutStyle(element) {
1133
1052
  }
1134
1053
 
1135
1054
  // src/viewer/utils/text-effects.tsx
1136
- function buildTextFillCss2(style) {
1137
- return buildTextFillCss(style);
1138
- }
1139
1055
  function buildTextBody3DSceneStyle2(textStyle) {
1140
1056
  return buildTextBody3DSceneStyle(textStyle);
1141
1057
  }
@@ -1318,7 +1234,7 @@ function buildAnimStyle(state) {
1318
1234
  }
1319
1235
  return Object.keys(style).length > 0 ? style : void 0;
1320
1236
  }
1321
- function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, paraSegments, subElementAnimStates, renderPiece) {
1237
+ function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, paraRuns, subElementAnimStates, renderPiece) {
1322
1238
  if (!subElementAnimStates || subElementAnimStates.size === 0) {
1323
1239
  return renderedSegments;
1324
1240
  }
@@ -1331,14 +1247,14 @@ function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, para
1331
1247
  const spec = buildTextBuildSpec(
1332
1248
  elementId,
1333
1249
  paraIndex,
1334
- paraSegments.map((entry) => ({ text: entry.segment.text, style: entry })),
1250
+ paraRuns.map((run) => ({ text: run.text, style: run })),
1335
1251
  subElementAnimStates
1336
1252
  );
1337
1253
  if (!spec?.spans) {
1338
1254
  return renderedSegments;
1339
1255
  }
1340
1256
  return spec.spans.map((span, index) => {
1341
- const content = span.style && renderPiece ? renderPiece(span.style, span.text) : span.text;
1257
+ const content = span.style && renderPiece ? renderPiece(span.style, span.text, String(index)) : span.text;
1342
1258
  if (!span.animId) {
1343
1259
  return /* @__PURE__ */ jsx(React6.Fragment, { children: content }, `ws-${index}`);
1344
1260
  }
@@ -1640,81 +1556,26 @@ function renderEquationSegment(elementId, segmentIndex, equationXml, equationNum
1640
1556
  }
1641
1557
  return /* @__PURE__ */ jsx("span", { children: equationContent }, `${elementId}-seg-${segmentIndex}`);
1642
1558
  }
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
- );
1666
- }
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
- );
1559
+ var BASELINE_FONT_SCALE = 0.65;
1560
+ function renderParagraphRun(run, segment, ctx, keySuffix = "") {
1561
+ const { element, fallbackColor, findHighlights, onHyperlinkClick } = ctx;
1562
+ const segmentIndex = run.segmentIndex ?? 0;
1563
+ const key = `${element.id}-seg-${segmentIndex}${keySuffix}`;
1564
+ if (run.equation) {
1565
+ return renderEquationSegment(element.id, segmentIndex, run.equation.xml, run.equation.number);
1692
1566
  }
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);
1567
+ const segmentStyle = segment?.style ?? {};
1568
+ const textValue = run.text;
1696
1569
  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
1570
+ const resolvedSegmentColor = normalizeHexColor(
1571
+ segmentStyle.color || element.textStyle?.color,
1572
+ run.hyperlink ? HYPERLINK_COLOR : fallbackColor
1709
1573
  );
1710
1574
  const rawBaseline = typeof segmentStyle.baseline === "number" ? segmentStyle.baseline : 0;
1711
1575
  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);
1576
+ const baselineFontScale = baselineFraction !== 0 ? BASELINE_FONT_SCALE : 1;
1715
1577
  const rawFontSize = segmentStyle.fontSize || element.textStyle?.fontSize || DEFAULT_TEXT_FONT_SIZE;
1716
- const autoFitScale = resolveAutoFitFontScale(element.textStyle);
1717
- const baseFontSize = rawFontSize * autoFitScale;
1578
+ const baseFontSize = rawFontSize * resolveAutoFitFontScale(element.textStyle);
1718
1579
  const baselineShift = baselineFraction !== 0 ? `${baselineFraction * baseFontSize}px` : void 0;
1719
1580
  const baseFontSizePt = baseFontSize * (72 / 96);
1720
1581
  const fontKerning = typeof segmentStyle.kerning === "number" ? segmentStyle.kerning === 0 ? "none" : baseFontSizePt >= segmentStyle.kerning / 100 ? "normal" : "none" : void 0;
@@ -1740,6 +1601,7 @@ function renderSingleSegment(element, segment, segmentIndex, fallbackColor, find
1740
1601
  )
1741
1602
  };
1742
1603
  const needsScriptFonts = hasDistinctScriptFonts(scriptFonts);
1604
+ const authoredLetterSpacing = typeof segmentStyle.characterSpacing === "number" && segmentStyle.characterSpacing !== 0 ? segmentStyle.characterSpacing / 100 * (96 / 72) : 0;
1743
1605
  const metricFont = {
1744
1606
  fontFamily: baseFontFamily,
1745
1607
  fontSizePx: baseFontSize * baselineFontScale,
@@ -1747,73 +1609,21 @@ function renderSingleSegment(element, segment, segmentIndex, fallbackColor, find
1747
1609
  italic: Boolean(segmentStyle.italic)
1748
1610
  };
1749
1611
  const metricContext = { font: metricFont, authoredPx: authoredLetterSpacing };
1750
- const letterSpacing = pieceLetterSpacing(
1612
+ const spanStyle = { ...run.style };
1613
+ spanStyle.color = resolvedSegmentColor;
1614
+ spanStyle.fontSize = baseFontSize * baselineFontScale;
1615
+ spanStyle.fontFamily = baseFontFamily;
1616
+ spanStyle.verticalAlign = baselineShift;
1617
+ spanStyle.fontKerning = fontKerning;
1618
+ spanStyle.letterSpacing = pieceLetterSpacing(
1751
1619
  authoredLetterSpacing,
1752
1620
  resolveMetricTrackingPx(textValue, metricFont)
1753
1621
  );
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
1622
  const runRtl = segmentStyle.rtl;
1782
- if (runRtl !== void 0 && runRtl !== paragraphRtl) {
1623
+ if (runRtl !== void 0) {
1783
1624
  spanStyle.direction = runRtl ? "rtl" : "ltr";
1784
- spanStyle.unicodeBidi = "bidi-override";
1785
- } else if (runRtl !== void 0 && runRtl === paragraphRtl) {
1786
- spanStyle.direction = runRtl ? "rtl" : "ltr";
1787
- spanStyle.unicodeBidi = "embed";
1625
+ spanStyle.unicodeBidi = runRtl !== ctx.paragraphRtl ? "bidi-override" : "embed";
1788
1626
  }
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}`;
1814
- }
1815
- const rubyText = segment.rubyText;
1816
- const hasRuby = typeof rubyText === "string" && rubyText.length > 0;
1817
1627
  const baseContent = renderSegmentContent(
1818
1628
  element.id,
1819
1629
  segmentIndex,
@@ -1833,232 +1643,211 @@ function renderSingleSegment(element, segment, segmentIndex, fallbackColor, find
1833
1643
  ),
1834
1644
  metricContext
1835
1645
  );
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;
1646
+ const spanNode = /* @__PURE__ */ jsx("span", { "data-seg-idx": segmentIndex, style: spanStyle, children: renderRubyOrText(segment, baseContent, baseFontSize, baseFontFamily, resolvedSegmentColor) }, key);
1647
+ return run.hyperlink && onHyperlinkClick ? renderHyperlink(run, spanNode, key, ctx) : spanNode;
1648
+ }
1649
+ function renderRubyOrText(segment, baseContent, baseFontSize, baseFontFamily, resolvedColor) {
1650
+ const rubyText = segment?.rubyText;
1651
+ if (typeof rubyText !== "string" || rubyText.length === 0) {
1652
+ return baseContent;
1653
+ }
1654
+ const rubyStyle = {
1655
+ fontSize: segment?.rubyFontSize ?? baseFontSize * 0.5,
1656
+ fontFamily: segment?.rubyStyle?.fontFamily ?? baseFontFamily,
1657
+ textAlign: segment?.rubyAlignment === "l" ? "left" : segment?.rubyAlignment === "r" ? "right" : segment?.rubyAlignment === "dist" || segment?.rubyAlignment === "distCat" || segment?.rubyAlignment === "distLetter" ? "justify" : "center"
1658
+ };
1659
+ if (segment?.rubyStyle?.color) {
1660
+ rubyStyle.color = normalizeHexColor(segment.rubyStyle.color, resolvedColor);
1661
+ }
1662
+ return /* @__PURE__ */ jsxs("ruby", { children: [
1663
+ baseContent,
1664
+ /* @__PURE__ */ jsx("rp", { children: "(" }),
1665
+ /* @__PURE__ */ jsx("rt", { style: rubyStyle, children: rubyText }),
1666
+ /* @__PURE__ */ jsx("rp", { children: ")" })
1667
+ ] });
1896
1668
  }
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 });
1669
+ function renderHyperlink(run, spanNode, key, ctx) {
1670
+ const url = run.hyperlink?.url;
1671
+ const onHyperlinkClick = ctx.onHyperlinkClick;
1672
+ if (!url || !onHyperlinkClick) {
1673
+ return spanNode;
1911
1674
  }
1912
- return paragraphs;
1675
+ const requireCtrlClick = ctx.requireCtrlClick;
1676
+ const displayUrl = url.startsWith("ppaction://") ? url.replace(/^ppaction:\/\//u, "").split("?")[0] : url;
1677
+ const follow = (modified) => {
1678
+ if (requireCtrlClick && !modified) {
1679
+ return false;
1680
+ }
1681
+ onHyperlinkClick(url);
1682
+ return true;
1683
+ };
1684
+ return /* @__PURE__ */ jsxs(
1685
+ "span",
1686
+ {
1687
+ role: "link",
1688
+ tabIndex: 0,
1689
+ className: requireCtrlClick ? "group/link relative" : void 0,
1690
+ style: { cursor: requireCtrlClick ? void 0 : "pointer", pointerEvents: "auto" },
1691
+ title: run.hyperlink?.tooltip,
1692
+ onClick: (e) => {
1693
+ if (!follow(e.ctrlKey || e.metaKey)) {
1694
+ return;
1695
+ }
1696
+ e.stopPropagation();
1697
+ e.preventDefault();
1698
+ },
1699
+ onKeyDown: (e) => {
1700
+ if (e.key !== "Enter" && e.key !== " ") {
1701
+ return;
1702
+ }
1703
+ if (!follow(e.ctrlKey || e.metaKey)) {
1704
+ return;
1705
+ }
1706
+ e.preventDefault();
1707
+ e.stopPropagation();
1708
+ },
1709
+ children: [
1710
+ spanNode,
1711
+ 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: [
1712
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground", children: displayUrl }),
1713
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Ctrl+Click to follow link" })
1714
+ ] }) })
1715
+ ]
1716
+ },
1717
+ `${key}-link`
1718
+ );
1913
1719
  }
1914
1720
  function renderTextSegments(element, fallbackColor, emptyFallback, findHighlights, onHyperlinkClick, fieldContext, subElementAnimStates, segmentOverrides, requireCtrlClick) {
1915
1721
  if (!hasTextProperties(element)) {
1916
1722
  return emptyFallback || null;
1917
1723
  }
1918
- const effectiveSegments = segmentOverrides ?? element.textSegments;
1919
- if (!effectiveSegments || effectiveSegments.length === 0) {
1724
+ const segments = segmentOverrides ?? element.textSegments;
1725
+ if (!segments || segments.length === 0) {
1920
1726
  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
- );
1727
+ return /* @__PURE__ */ jsx("span", { style: { opacity: 0.5, color: "#888888", pointerEvents: "none" }, children: element.promptText });
1932
1728
  }
1933
1729
  return element.text || emptyFallback || "";
1934
1730
  }
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";
1731
+ const paragraphs = buildParagraphs(element, fieldContext, segmentOverrides);
1732
+ const ctx = {
1733
+ element,
1734
+ fallbackColor,
1735
+ findHighlights,
1736
+ onHyperlinkClick,
1737
+ requireCtrlClick
1738
+ };
1739
+ return paragraphs.map(
1740
+ (para, paraIndex) => renderParagraph(para, paraIndex, paragraphs.length, segments, ctx, subElementAnimStates)
1741
+ );
1742
+ }
1743
+ function joinRunsBySegment(runs) {
1744
+ const out = [];
1745
+ for (const run of runs) {
1746
+ const last = out[out.length - 1];
1747
+ if (last && run.segmentIndex !== void 0 && last.segmentIndex === run.segmentIndex && !last.equation && !run.equation) {
1748
+ last.text += run.text;
1749
+ continue;
2003
1750
  }
2004
- if (cssTextAlign !== void 0) {
2005
- paraStyle.textAlign = cssTextAlign;
1751
+ out.push({ ...run });
1752
+ }
1753
+ return out;
1754
+ }
1755
+ function renderParagraph(para, paraIndex, paraCount, segments, ctx, subElementAnimStates) {
1756
+ const element = ctx.element;
1757
+ const runCtx = { ...ctx, paragraphRtl: para.rtl };
1758
+ const runs = joinRunsBySegment(para.runs);
1759
+ const renderedRuns = runs.map(
1760
+ (run) => renderParagraphRun(run, segments[run.segmentIndex ?? -1], runCtx)
1761
+ );
1762
+ const paraStyle = {
1763
+ // `text-align`, BiDi `direction` / `unicode-bidi` and the kinsoku
1764
+ // line-break rules, resolved by shared from this paragraph's own `a:pPr`.
1765
+ ...para.paragraphStyle
1766
+ };
1767
+ if (para.strutFontSizePx !== void 0) {
1768
+ paraStyle.fontSize = para.strutFontSizePx;
1769
+ }
1770
+ if (para.spaceBeforePx !== void 0) {
1771
+ paraStyle.marginTop = para.spaceBeforePx;
1772
+ }
1773
+ if (para.spaceAfterPx !== void 0) {
1774
+ paraStyle.marginBottom = para.spaceAfterPx;
1775
+ }
1776
+ if (para.lineHeight !== void 0) {
1777
+ paraStyle.lineHeight = para.lineHeight;
1778
+ }
1779
+ if (para.marginLeftPx !== void 0) {
1780
+ if (para.rtl === true) {
1781
+ paraStyle.marginRight = para.marginLeftPx;
1782
+ } else {
1783
+ paraStyle.marginLeft = para.marginLeftPx;
2006
1784
  }
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
- )
1785
+ }
1786
+ if (para.textIndentPx !== void 0) {
1787
+ paraStyle.textIndent = para.textIndentPx;
1788
+ }
1789
+ const marker = renderBulletMarker(para, paraIndex, element.id);
1790
+ const wrappedContent = wrapWithTextBuildAnimation(
1791
+ element.id,
1792
+ paraIndex,
1793
+ renderedRuns,
1794
+ runs,
1795
+ subElementAnimStates,
1796
+ // A staged build splits the paragraph but must not flatten it: each piece
1797
+ // is re-rendered through the SAME run renderer, so it keeps its run's
1798
+ // font, size, colour and decoration while it animates.
1799
+ (run, text, pieceKey) => renderParagraphRun(
1800
+ { ...run, text },
1801
+ segments[run.segmentIndex ?? -1],
1802
+ runCtx,
1803
+ `-build-${pieceKey}`
1804
+ )
1805
+ );
1806
+ const needsWrapper = Object.keys(paraStyle).length > 0 || marker !== null;
1807
+ if (!needsWrapper) {
1808
+ return /* @__PURE__ */ jsxs(React6.Fragment, { children: [
1809
+ wrappedContent,
1810
+ paraIndex < paraCount - 1 ? /* @__PURE__ */ jsx("br", {}) : null
1811
+ ] }, `${element.id}-para-${paraIndex}`);
1812
+ }
1813
+ return /* @__PURE__ */ jsxs("div", { style: paraStyle, children: [
1814
+ marker,
1815
+ para.isEmpty ? /* @__PURE__ */ jsx("br", {}) : wrappedContent
1816
+ ] }, `${element.id}-para-${paraIndex}`);
1817
+ }
1818
+ function renderBulletMarker(para, paraIndex, elementId) {
1819
+ const picture = para.bulletPicture;
1820
+ if (picture?.src) {
1821
+ return /* @__PURE__ */ jsx(
1822
+ "img",
1823
+ {
1824
+ src: picture.src,
1825
+ alt: picture.accessibleLabel,
1826
+ style: {
1827
+ width: picture.sizePx,
1828
+ height: picture.sizePx,
1829
+ display: "inline-block",
1830
+ verticalAlign: "middle",
1831
+ marginInlineEnd: 4,
1832
+ objectFit: "contain"
1833
+ }
1834
+ },
1835
+ `${elementId}-para-${paraIndex}-bullet-img`
2052
1836
  );
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
- });
1837
+ }
1838
+ if (para.bulletMarker === void 0) {
1839
+ return null;
1840
+ }
1841
+ return /* @__PURE__ */ jsx(
1842
+ "span",
1843
+ {
1844
+ className: "pptx-bullet",
1845
+ style: para.bulletStyle,
1846
+ "aria-label": picture?.accessibleLabel,
1847
+ children: para.bulletMarker
1848
+ },
1849
+ `${elementId}-para-${paraIndex}-bullet`
1850
+ );
2062
1851
  }
2063
1852
  function hexToRgbUnit(hex) {
2064
1853
  const clean = hex.replace("#", "");
@@ -2107,23 +1896,6 @@ function buildLineGlowFilter(element) {
2107
1896
  const glowCol = colorWithOpacity(normalizeHexColor(ss.lineGlowColor, "#ffff00"), glowOpacity);
2108
1897
  return `drop-shadow(0 0 ${glowRad}px ${glowCol})`;
2109
1898
  }
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
1899
  function getDagDuotoneFilterId(elementId) {
2128
1900
  return `dag-duotone-${elementId}`;
2129
1901
  }
@@ -2178,17 +1950,6 @@ function renderDagDuotoneSvgFilter(elementId, color1, color2) {
2178
1950
  );
2179
1951
  }
2180
1952
 
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
1953
  // src/viewer/utils/shape-visual-3d.ts
2193
1954
  function build3DExtrusionData2(shape3d, scene3d, fillColor, elementWidth, elementHeight) {
2194
1955
  return build3DExtrusionData(
@@ -2204,162 +1965,52 @@ function apply3dEffects2(base, scene3d, shape3d, fillColorFallback) {
2204
1965
  }
2205
1966
 
2206
1967
  // 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);
1968
+ function rendersCustomVectorPath(element) {
1969
+ 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
1970
  }
2220
- function getShapeVisualStyle(element, hasFill, fillColor, strokeWidth, strokeColor, animatesFill, animatesStroke) {
1971
+ function getShapeVisualStyle(element, _hasFill, fillColor, _strokeWidth, _strokeColor, animatesFill, animatesStroke, parentGroupFill) {
2221
1972
  if (!hasShapeProperties(element)) {
2222
1973
  return {};
2223
1974
  }
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
1975
  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
- }
1976
+ const fill = animatesFill ? void 0 : getComputedFillStyle(element, parentGroupFill);
1977
+ const fx = getComputedEffectStyle(element);
1978
+ const stroke = getComputedStrokeStyle(element);
2311
1979
  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.
1980
+ // A gradient fill REPLACES the solid fill, it does not sit on top of it;
1981
+ // the whole precedence lives in shared's `getComputedFillStyle`.
1982
+ backgroundColor: fill?.backgroundColor ?? "transparent",
1983
+ backgroundImage: fill?.backgroundImage,
1984
+ backgroundRepeat: fill?.backgroundRepeat,
1985
+ backgroundSize: fill?.backgroundSize,
1986
+ backgroundPosition: fill?.backgroundPosition,
1987
+ // The container always carries `box-sizing: border-box`, so the default
1988
+ // `background-origin: padding-box` would size the paint 2px smaller than
1989
+ // the shape and make a gradient tile wrap, painting a 1px sliver of its
1990
+ // opposite end along the edge. Paint from the border box instead.
2331
1991
  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
1992
+ boxShadow: fx.boxShadow,
1993
+ WebkitBoxReflect: fx.webkitBoxReflect,
1994
+ filter: fx.filter,
1995
+ opacity: fx.opacity,
1996
+ // Only proxy the DAG blend onto the whole element for the blend-only case:
1997
+ // with an overlay colour, `ShapeEffectOverlay` paints a separate blended
1998
+ // tint layer (shared decides which, and returns only one of the two).
1999
+ mixBlendMode: fx.mixBlendMode,
2000
+ // An unstroked element must occupy EXACTLY its authored box, so the
2001
+ // selection/hover affordance is an `outline` (see `ElementRenderer`) and
2002
+ // the border collapses to 0 rather than to a transparent 1px.
2003
+ borderWidth: stroke.borderWidth,
2004
+ borderColor: animatesStroke ? void 0 : stroke.borderColor,
2005
+ borderStyle: stroke.borderStyle,
2006
+ // Inherited SVG presentation properties: written on the container so the
2007
+ // freeform `<path>` and the stroke overlay both pick them up.
2008
+ strokeLinejoin: stroke.strokeLinejoin,
2009
+ strokeMiterlimit: stroke.strokeMiterlimit,
2010
+ strokeLinecap: stroke.strokeLinecap
2360
2011
  };
2361
2012
  apply3dEffects2(base, ss?.scene3d, ss?.shape3d, ss?.fillColor ?? fillColor);
2362
- if (rendersCustomVectorPath || rendersStrokeOnlyPreset) {
2013
+ if (rendersCustomVectorPath(element) || isStrokeOnlyPresetElement(element)) {
2363
2014
  base.backgroundColor = "transparent";
2364
2015
  base.backgroundImage = void 0;
2365
2016
  base.borderWidth = void 0;
@@ -2370,59 +2021,28 @@ function getShapeVisualStyle(element, hasFill, fillColor, strokeWidth, strokeCol
2370
2021
  base.backgroundColor = void 0;
2371
2022
  base.backgroundImage = void 0;
2372
2023
  }
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) {
2024
+ const geometry = resolveShapeGeometry(element);
2025
+ switch (geometry.kind) {
2026
+ case "bare":
2027
+ return { backgroundColor: "transparent", borderWidth: 0, borderStyle: "none" };
2028
+ case "strokeOnly":
2029
+ return { ...base, backgroundColor: "transparent", backgroundImage: void 0 };
2030
+ case "borderRadius":
2031
+ return { ...base, borderRadius: geometry.radius };
2032
+ case "clipPath":
2033
+ return { ...base, clipPath: geometry.clipPath };
2034
+ case "lineEdge":
2035
+ return {
2036
+ ...base,
2037
+ backgroundColor: "transparent",
2038
+ borderWidth: 0,
2039
+ borderTopWidth: geometry.strokeWidth,
2040
+ borderTopColor: stroke.borderColor,
2041
+ borderTopStyle: stroke.borderStyle ?? "solid"
2042
+ };
2043
+ default:
2386
2044
  return base;
2387
- }
2388
- return {
2389
- ...base,
2390
- borderRadius: radiusPx
2391
- };
2392
- }
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
2045
  }
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
2046
  }
2427
2047
  function getConnectorAdjustment(element, key, fallback) {
2428
2048
  const direct = element.shapeAdjustments?.[key];
@@ -3402,6 +3022,28 @@ function parseTableElementData(element, fallbackTextStyle) {
3402
3022
  cells
3403
3023
  };
3404
3024
  }
3025
+ function renderTableCellContent(cell, fallbackText) {
3026
+ const runs = cell?.textRuns;
3027
+ if (!runs || runs.length === 0) {
3028
+ return fallbackText || "\xA0";
3029
+ }
3030
+ return runs.map((run, index) => {
3031
+ if (run.isParagraphBreak) {
3032
+ return /* @__PURE__ */ jsx("div", { style: { display: "block", height: 0 } }, `p-${index}`);
3033
+ }
3034
+ if (run.isLineBreak) {
3035
+ return /* @__PURE__ */ jsx("br", {}, `br-${index}`);
3036
+ }
3037
+ return /* @__PURE__ */ jsx(
3038
+ "span",
3039
+ {
3040
+ style: { position: "relative", ...cellRunStyle(run) },
3041
+ children: run.text
3042
+ },
3043
+ `r-${index}`
3044
+ );
3045
+ });
3046
+ }
3405
3047
  function getCellDiagonalBorders2(style, tableData, pos, styleCtx) {
3406
3048
  return getCellDiagonalBorders(style, tableData, pos, {
3407
3049
  tableStyleMap: styleCtx?.tableStyleMap,
@@ -3693,7 +3335,11 @@ function renderTableFromTableData(element, textStyle, options) {
3693
3335
  "table",
3694
3336
  {
3695
3337
  className: "w-full h-full border-collapse table-fixed",
3696
- style: { fontFamily: DEFAULT_FONT_FAMILY },
3338
+ style: {
3339
+ fontFamily: DEFAULT_FONT_FAMILY,
3340
+ // `a:tblPr@rtl` mirrors the column order for RTL decks.
3341
+ ...tableContainerCss(tableData)
3342
+ },
3697
3343
  children: [
3698
3344
  tableData.columnWidths.length > 0 && /* @__PURE__ */ jsx("colgroup", { children: tableData.columnWidths.map((w, ci) => /* @__PURE__ */ jsx(
3699
3345
  "col",
@@ -3791,7 +3437,7 @@ function renderTableFromTableData(element, textStyle, options) {
3791
3437
  });
3792
3438
  }
3793
3439
  }
3794
- ) : cell.text || "\xA0"
3440
+ ) : renderTableCellContent(cell, cell.text || "\xA0")
3795
3441
  ]
3796
3442
  },
3797
3443
  `${element.id}-cell-${rowIndex}-${cellIndex}`
@@ -3851,7 +3497,11 @@ function renderTableElement(element, textStyle, options) {
3851
3497
  "table",
3852
3498
  {
3853
3499
  className: "w-full h-full border-collapse table-fixed",
3854
- style: { fontFamily: DEFAULT_FONT_FAMILY },
3500
+ style: {
3501
+ fontFamily: DEFAULT_FONT_FAMILY,
3502
+ // `a:tblPr@rtl` mirrors the column order for RTL decks.
3503
+ ...tableContainerCss(tableEl.tableData)
3504
+ },
3855
3505
  children: [
3856
3506
  parsedTable.columnPercentages.length > 0 ? /* @__PURE__ */ jsx("colgroup", { children: parsedTable.columnPercentages.map((percentage, columnIndex) => /* @__PURE__ */ jsx(
3857
3507
  "col",
@@ -3970,7 +3620,10 @@ function renderTableElement(element, textStyle, options) {
3970
3620
  });
3971
3621
  }
3972
3622
  }
3973
- ) : extractCellText(cell) || "\xA0"
3623
+ ) : renderTableCellContent(
3624
+ tableEl.tableData?.rows[rowIndex]?.cells[cellIndex],
3625
+ extractCellText(cell) || "\xA0"
3626
+ )
3974
3627
  ]
3975
3628
  },
3976
3629
  `${element.id}-cell-${rowIndex}-${cellIndex}`
@@ -5819,7 +5472,7 @@ function FailedToLoad() {
5819
5472
  var LazySurfaceChart3DScene = React6.lazy(
5820
5473
  async () => {
5821
5474
  try {
5822
- return await import('./SurfaceChart3DScene-Q47OEJXS.mjs');
5475
+ return await import('./SurfaceChart3DScene-DEQP5O3U.mjs');
5823
5476
  } catch {
5824
5477
  return { default: FailedToLoad };
5825
5478
  }
@@ -6599,4 +6252,4 @@ function stopPreviewAnimation() {
6599
6252
  activePreview = null;
6600
6253
  }
6601
6254
 
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 };
6255
+ 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, getImageTilingStyle2 as getImageTilingStyle, getShapeVisualStyle, getTextLayoutStyle, getTextStyleForElement, getTextWarpStyle, hasDagDuotoneEffect, isConnectorOrLineElement, renderChartElement, renderConnectorMarker, renderDagDuotoneSvgFilter, renderMediaElement, renderTableElement, renderTextSegments, renderVectorShape, shouldRenderFallbackLabel, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation };