pptx-angular-viewer 1.31.3 → 1.31.5

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.
@@ -2,7 +2,7 @@ import { NgStyle, NgTemplateOutlet, NgClass } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
3
  import { InjectionToken, input, output, computed, ChangeDetectionStrategy, Component, signal, Injectable, inject, Injector, DestroyRef, effect, viewChild, ElementRef, HostListener, afterNextRender, forwardRef, untracked } from '@angular/core';
4
4
  import { TranslatePipe, TranslateService, translate } from '@ngx-translate/core';
5
- import { guideEmuToPx, getShapeClipPath, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, hasShapeProperties, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, isImageLikeElement, hasTextProperties, stripXmlOrderSuffix, orderedXmlKey, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$2, THEME_COLOR_SCHEME_KEYS, getLinkedTextBoxSegments, svgPathToPolygons, deobfuscateFont, detectFontFormat, getSubstituteFontFamily, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, formatCommentTimestamp as formatCommentTimestamp$1, PptxHandler, EncryptedFileError, parseSignatureXml, cloneSlide as cloneSlide$1, cloneTemplateElementsBySlideId as cloneTemplateElementsBySlideId$1, cloneElement as cloneElement$1, updateSmartArtNodeText, setSmartArtNodeStyle, isInkElement, parseDataUrlToBytes, isZoomElement, SvgExporter, applyThemeToData, pptxActionToElementAction, elementActionToPptxAction, setChartTitle, setChartLegend, setChartDataLabels, setChartAxis, setChartSeriesTrendline, setChartSeriesErrorBars, setChartDataPointLabel, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, chartDataChangeType, getOleObjectTypeLabel, addSmartArtNodeAsChild, removeSmartArtNode, promoteSmartArtNode, demoteSmartArtNode, reorderSmartArtNode, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, applyThemeOverrideToSlide, THEME_PRESETS, getAnimationPresetInfo } from 'pptx-viewer-core';
5
+ import { guideEmuToPx, getShapeClipPath, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, hasShapeProperties, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, isImageLikeElement, hasTextProperties, stripXmlOrderSuffix, orderedXmlKey, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$2, THEME_COLOR_SCHEME_KEYS, getLinkedTextBoxSegments, createBuiltinVariables, resolveCoordinate, svgPathToPolygons, deobfuscateFont, detectFontFormat, getSubstituteFontFamily, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, formatCommentTimestamp as formatCommentTimestamp$1, PptxHandler, EncryptedFileError, parseSignatureXml, cloneSlide as cloneSlide$1, cloneTemplateElementsBySlideId as cloneTemplateElementsBySlideId$1, cloneElement as cloneElement$1, updateSmartArtNodeText, setSmartArtNodeStyle, isInkElement, parseDataUrlToBytes, isZoomElement, SvgExporter, applyThemeToData, pptxActionToElementAction, elementActionToPptxAction, setChartTitle, setChartLegend, setChartDataLabels, setChartAxis, setChartSeriesTrendline, setChartSeriesErrorBars, setChartDataPointLabel, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, chartDataChangeType, getOleObjectTypeLabel, addSmartArtNodeAsChild, removeSmartArtNode, promoteSmartArtNode, demoteSmartArtNode, reorderSmartArtNode, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, applyThemeOverrideToSlide, THEME_PRESETS, getAnimationPresetInfo } from 'pptx-viewer-core';
6
6
  export { SWITCHABLE_LAYOUT_TYPES, addSmartArtNode, addSmartArtNodeAsChild, chartDataAddCategory, chartDataAddSeries, chartDataChangeType, chartDataRemoveCategory, chartDataRemoveSeries, chartDataUpdatePoint, demoteSmartArtNode, promoteSmartArtNode, removeSmartArtNode, reorderSmartArtNode, switchSmartArtLayout, updateSmartArtNodeText } from 'pptx-viewer-core';
7
7
  import DOMPurify from 'dompurify';
8
8
  import { LucideX, LucideSquare, LucideCircle, LucideSlash, LucideCopy, LucideTrash2, LucideChevronsUp, LucideChevronsDown, LucideArrowUp, LucideArrowDown, LucideGroup, LucideUngroup, LucideTextAlignStart, LucideTextAlignCenter, LucideTextAlignEnd, LucideChevronUp, LucideChevronDown, LucideAlignHorizontalSpaceAround, LucideAlignVerticalSpaceAround, LucideChevronRight, LucidePenTool, LucideHighlighter, LucideEraser, LucideMousePointer2, LucideChevronLeft, LucidePlay, LucideSparkles, LucidePanelRight, LucideMoveRight, LucidePencil, LucideType, LucideMinus, LucideSpline, LucideArrowLeft, LucideDownload, LucideFilePlus2, LucideFolderOpen, LucideHome, LucideInfo, LucidePrinter, LucideSave, LucideSettings, LucideShare2, LucideUpload, LucideUserRound, LucideAArrowUp, LucideAArrowDown, LucideRemoveFormatting, LucideList, LucideListOrdered, LucideListIndentDecrease, LucideListIndentIncrease, LucideTextAlignJustify, LucidePlus, LucideClipboardPaste, LucideFolderPlus, LucideLayoutGrid, LucidePaintbrush, LucideRotateCcw, LucideScissors, LucideImage, LucideVideo, LucideDatabase, LucideLayers, LucidePanelLeft, LucideMessageSquare, LucideEllipsis, LucideEye, LucideEyeOff, LucideSettings2, LucideStickyNote, LucideMonitor, LucideColumns2, LucidePresentation, LucideCheck, LucideUndo, LucideRedo, LucideSearch } from '@lucide/angular';
@@ -770,6 +770,36 @@ function getGradientTileFlipCss(mode) {
770
770
  return undefined;
771
771
  }
772
772
  }
773
+ /**
774
+ * Builds the `background-size` / `background-position` / `background-repeat`
775
+ * that confine a gradient to the rectangle described by `a:gradFill/a:tileRect`.
776
+ *
777
+ * `tileRect` LTRB values are 0..1 insets from each edge, so the tile covers
778
+ * `(1 - l - r)` x `(1 - t - b)` of the shape, offset by `l`/`t`. Returns
779
+ * `undefined` when the rect is missing, degenerate, or effectively full-bleed
780
+ * (no meaningful inset), so callers fall through to the plain gradient.
781
+ */
782
+ function getGradientTileRectCss(tileRect) {
783
+ if (!tileRect) {
784
+ return undefined;
785
+ }
786
+ const { l, t, r, b } = tileRect;
787
+ const sizeW = 1 - l - r;
788
+ const sizeH = 1 - t - b;
789
+ if (sizeW <= 0 || sizeH <= 0) {
790
+ return undefined;
791
+ }
792
+ if (Math.abs(sizeW - 1) < 0.001 && Math.abs(sizeH - 1) < 0.001) {
793
+ return undefined;
794
+ }
795
+ const posX = 1 - sizeW > 0.001 ? (l / (1 - sizeW)) * 100 : 0;
796
+ const posY = 1 - sizeH > 0.001 ? (t / (1 - sizeH)) * 100 : 0;
797
+ return {
798
+ backgroundSize: `${Math.round(sizeW * 100)}% ${Math.round(sizeH * 100)}%`,
799
+ backgroundPosition: `${Math.round(posX)}% ${Math.round(posY)}%`,
800
+ backgroundRepeat: 'no-repeat',
801
+ };
802
+ }
773
803
  /**
774
804
  * Creates a reflected (mirrored) copy of gradient stops for tile-flip
775
805
  * rendering. The original stops run 0->100; the result packs one full
@@ -796,6 +826,36 @@ function buildReflectedGradientStops(stops) {
796
826
  }));
797
827
  return [...forward, ...reversed];
798
828
  }
829
+ /** Normalise a degree value into the [0, 360) range. */
830
+ function normalizeDegrees(deg) {
831
+ return ((deg % 360) + 360) % 360;
832
+ }
833
+ /**
834
+ * Apply `a:lin/@scaled` (aspect-ratio) and `a:gradFill/@rotWithShape`
835
+ * corrections to a linear-gradient angle. A no-op when no geometry context is
836
+ * supplied or neither attribute is in force, so callers that pass only a style
837
+ * keep the raw authored angle.
838
+ */
839
+ function adjustLinearGradientAngle(angle, gradient, context) {
840
+ if (!context) {
841
+ return angle;
842
+ }
843
+ let result = angle;
844
+ const { width, height, rotation } = context;
845
+ if (gradient.fillGradientScaled !== false &&
846
+ typeof width === 'number' &&
847
+ typeof height === 'number' &&
848
+ width > 0 &&
849
+ height > 0 &&
850
+ width !== height) {
851
+ const rad = (result * Math.PI) / 180;
852
+ result = normalizeDegrees((Math.atan2(height * Math.sin(rad), width * Math.cos(rad)) * 180) / Math.PI);
853
+ }
854
+ if (gradient.fillGradientRotWithShape === false && typeof rotation === 'number') {
855
+ result = normalizeDegrees(result - rotation);
856
+ }
857
+ return result;
858
+ }
799
859
  /**
800
860
  * Converts a structured OOXML gradient on a `ShapeStyle` to a CSS
801
861
  * `linear-gradient(...)` / `radial-gradient(...)` string.
@@ -810,9 +870,11 @@ function buildReflectedGradientStops(stops) {
810
870
  * a radial), matching the React port.
811
871
  *
812
872
  * @param gradient - The shape style carrying gradient configuration.
873
+ * @param context - Optional element geometry enabling `@scaled` /
874
+ * `@rotWithShape` angle corrections (linear only).
813
875
  * @returns A CSS gradient string, or `undefined`.
814
876
  */
815
- function buildGradientCss(gradient) {
877
+ function buildGradientCss(gradient, context) {
816
878
  if (!gradient || gradient.fillMode !== 'gradient') {
817
879
  return undefined;
818
880
  }
@@ -836,12 +898,13 @@ function buildGradientCss(gradient) {
836
898
  const normalizedAngle = typeof gradient.fillGradientAngle === 'number' && Number.isFinite(gradient.fillGradientAngle)
837
899
  ? gradient.fillGradientAngle
838
900
  : 90;
901
+ const effectiveAngle = adjustLinearGradientAngle(normalizedAngle, gradient, context);
839
902
  // Tile-flip: reflect the stops so a single tile contains one mirrored
840
903
  // forward-backward cycle. The repeating/halved tiling is applied by the
841
904
  // caller via getGradientTileFlipCss (wired into getComputedFillStyle).
842
905
  const flip = gradient.fillGradientFlip;
843
906
  const linearStops = flip && flip !== 'none' ? buildReflectedGradientStops(stops) : stops;
844
- return `linear-gradient(${Math.round(normalizedAngle)}deg, ${linearStops
907
+ return `linear-gradient(${Math.round(effectiveAngle)}deg, ${linearStops
845
908
  .map(toCssGradientStop)
846
909
  .join(', ')})`;
847
910
  }
@@ -1251,9 +1314,16 @@ const OOXML_PATTERN_PRESETS = [
1251
1314
  * empty-ish object when no fill applies (so callers can spread the result
1252
1315
  * safely).
1253
1316
  *
1254
- * @param element - The PPTX element to resolve a fill for.
1317
+ * A child of a group whose fill is `a:grpFill` (`fillMode === "group"`)
1318
+ * inherits the group's resolved fill: pass the parent group's `ShapeStyle`
1319
+ * (`GroupPptxElement.groupFill`) as {@link parentGroupFill} and it is resolved
1320
+ * in the child's own box.
1321
+ *
1322
+ * @param element - The PPTX element to resolve a fill for.
1323
+ * @param parentGroupFill - The enclosing group's fill style, used only when the
1324
+ * element's own fill is `a:grpFill`.
1255
1325
  */
1256
- function getComputedFillStyle(element) {
1326
+ function getComputedFillStyle(element, parentGroupFill) {
1257
1327
  if (!hasShapeProperties(element)) {
1258
1328
  return undefined;
1259
1329
  }
@@ -1261,6 +1331,20 @@ function getComputedFillStyle(element) {
1261
1331
  if (!ss) {
1262
1332
  return {};
1263
1333
  }
1334
+ // grpFill inheritance: paint the parent group's resolved fill in this
1335
+ // child's box. Without a parent fill there is nothing to inherit.
1336
+ if (ss.fillMode === 'group') {
1337
+ return parentGroupFill ? resolveComputedFill(parentGroupFill, element) : {};
1338
+ }
1339
+ return resolveComputedFill(ss, element);
1340
+ }
1341
+ /**
1342
+ * Resolves a concrete {@link ComputedFillStyle} from a {@link ShapeStyle} in the
1343
+ * context of `element` (id namespaces pattern filters; geometry drives gradient
1344
+ * `@scaled`/`@rotWithShape` corrections). Split out so grpFill children can
1345
+ * resolve the parent group's style through the identical pipeline.
1346
+ */
1347
+ function resolveComputedFill(ss, element) {
1264
1348
  // 1. Image fill — highest priority.
1265
1349
  const imageFillUrl = ss.fillMode === 'image' && ss.fillImageUrl ? ss.fillImageUrl : undefined;
1266
1350
  if (imageFillUrl) {
@@ -1273,7 +1357,12 @@ function getComputedFillStyle(element) {
1273
1357
  };
1274
1358
  }
1275
1359
  // 2. Gradient (structured, with prebuilt-string fallback).
1276
- const gradient = buildGradientCss(ss) ?? (ss.fillMode === 'gradient' ? ss.fillGradient : undefined);
1360
+ const geometry = {
1361
+ rotation: element.rotation,
1362
+ width: element.width,
1363
+ height: element.height,
1364
+ };
1365
+ const gradient = buildGradientCss(ss, geometry) ?? (ss.fillMode === 'gradient' ? ss.fillGradient : undefined);
1277
1366
  if (gradient) {
1278
1367
  // Tile-flip applies only to structured linear gradients: buildGradientCss
1279
1368
  // reflects the stops, and the matching halved/repeating background-size +
@@ -1290,6 +1379,16 @@ function getComputedFillStyle(element) {
1290
1379
  backgroundRepeat: tileFlip.backgroundRepeat,
1291
1380
  };
1292
1381
  }
1382
+ // tileRect confines the gradient to a sub-rectangle of the shape.
1383
+ const tileRect = ss.fillMode === 'gradient' ? getGradientTileRectCss(ss.fillGradientTileRect) : undefined;
1384
+ if (tileRect) {
1385
+ return {
1386
+ backgroundImage: gradient,
1387
+ backgroundSize: tileRect.backgroundSize,
1388
+ backgroundPosition: tileRect.backgroundPosition,
1389
+ backgroundRepeat: tileRect.backgroundRepeat,
1390
+ };
1391
+ }
1293
1392
  return { backgroundImage: gradient };
1294
1393
  }
1295
1394
  // 3. Pattern fill.
@@ -6333,6 +6432,16 @@ function resolveDataPointFill(series, pointIndex, fallbackColor) {
6333
6432
  const point = findDataPoint(series, pointIndex);
6334
6433
  return point?.spPr?.fillColor ?? series.color ?? fallbackColor;
6335
6434
  }
6435
+ /**
6436
+ * Resolve the fill for a data point in a "vary colours" context, where every
6437
+ * point in a single series is drawn with a distinct palette colour (pie /
6438
+ * doughnut slices, or a bar/column series with `c:varyColors=1`). A per-point
6439
+ * `c:dPt` fill still wins; otherwise the supplied per-point `paletteColor` is
6440
+ * used (NOT the single series colour, which would make every point identical).
6441
+ */
6442
+ function resolveVaryColorFill(series, pointIndex, paletteColor) {
6443
+ return findDataPoint(series, pointIndex)?.spPr?.fillColor ?? paletteColor;
6444
+ }
6336
6445
  /**
6337
6446
  * Resolve the slice explosion (pull-out distance, 0-100) for a pie/doughnut
6338
6447
  * data point: the per-point `c:dPt` explosion when present, otherwise the
@@ -6998,6 +7107,38 @@ function buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, cat
6998
7107
  function categoryTotals(series, catCount) {
6999
7108
  return Array.from({ length: catCount }, (_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0));
7000
7109
  }
7110
+ /**
7111
+ * Blend a `#RRGGBB` colour halfway toward white. Returns the input unchanged
7112
+ * when it is not a parseable 6-digit hex (e.g. a named colour or gradient ref).
7113
+ */
7114
+ function blendToWhite(color) {
7115
+ const match = /^#?([0-9a-f]{6})$/iu.exec(color.trim());
7116
+ if (!match) {
7117
+ return color;
7118
+ }
7119
+ const value = Number.parseInt(match[1], 16);
7120
+ const mix = (channel) => Math.round(channel + (255 - channel) * 0.5);
7121
+ const r = mix((value >> 16) & 0xff);
7122
+ const g = mix((value >> 8) & 0xff);
7123
+ const b = mix(value & 0xff);
7124
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0').toUpperCase()}`;
7125
+ }
7126
+ /**
7127
+ * Apply PowerPoint's `c:invertIfNegative` treatment to a bar fill: when the
7128
+ * point value is negative and invert-if-negative is set (a per-point `c:dPt`
7129
+ * override wins over the series-level flag), the bar is drawn in a lightened
7130
+ * fill. Convention: a 50% blend of the base fill toward white. PowerPoint has no
7131
+ * single canonical inverted colour; a lightened same-hue fill echoes its default
7132
+ * "hollow" appearance and is deterministic across bindings.
7133
+ */
7134
+ function invertNegativeFill(series, pointIndex, value, baseFill) {
7135
+ if (value >= 0) {
7136
+ return baseFill;
7137
+ }
7138
+ const point = series.dataPoints?.find((p) => p.idx === pointIndex);
7139
+ const invert = point?.invertIfNegative ?? series.invertIfNegative ?? false;
7140
+ return invert ? blendToWhite(baseFill) : baseFill;
7141
+ }
7001
7142
  /**
7002
7143
  * Bar primitives for clustered / stacked / percentStacked, honouring a secondary
7003
7144
  * value range for secondary-mapped series (clustered only). Returns rects + data
@@ -7009,28 +7150,45 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
7009
7150
  const series = chartData.series;
7010
7151
  const palette = chartData.colorPalette;
7011
7152
  const showLabels = chartData.style?.hasDataLabels;
7153
+ // Single-series bar/column with c:varyColors=1 gives every category a distinct
7154
+ // palette colour (a per-point c:dPt fill still wins). Multi-series charts keep
7155
+ // their per-series colours (varyColors has no cross-series meaning there).
7156
+ const varyColorsSingle = chartData.varyColors === true && series.length === 1;
7012
7157
  if (grouping === 'clustered') {
7013
7158
  const seriesCount = Math.max(series.length, 1);
7014
7159
  const barGroupWidth = layout.plotWidth / Math.max(catCount, 1);
7015
- const singleBarWidth = (barGroupWidth * 0.7) / seriesCount;
7016
- const groupOffset = (barGroupWidth - singleBarWidth * seriesCount) / 2;
7160
+ // Honour c:gapWidth (gap between clusters, % of a bar width) when parsed;
7161
+ // otherwise keep the legacy 0.7-of-group heuristic byte-for-byte.
7162
+ const singleBarWidth = chartData.barGapWidth !== undefined
7163
+ ? barGroupWidth / (seriesCount + Math.max(chartData.barGapWidth, 0) / 100)
7164
+ : (barGroupWidth * 0.7) / seriesCount;
7165
+ // Honour c:overlap (% overlap between adjacent series). overlap=0 reproduces
7166
+ // the original side-by-side layout exactly.
7167
+ const overlap = chartData.barOverlap ?? 0;
7168
+ const step = singleBarWidth * (1 - overlap / 100);
7169
+ const clusterWidth = singleBarWidth + step * (seriesCount - 1);
7170
+ const groupOffset = (barGroupWidth - clusterWidth) / 2;
7017
7171
  for (let displayIndex = 0; displayIndex < catCount; displayIndex++) {
7018
7172
  const sourceIndex = sourceIndices[displayIndex] ?? displayIndex;
7019
7173
  for (let si = 0; si < series.length; si++) {
7020
7174
  const val = series[si].values[sourceIndex] ?? 0;
7021
- const x = layout.plotLeft + barGroupWidth * displayIndex + groupOffset + singleBarWidth * si;
7175
+ const x = layout.plotLeft + barGroupWidth * displayIndex + groupOffset + step * si;
7022
7176
  const activeRange = secondaryIdx.has(si) && secondaryRange ? secondaryRange : primaryRange;
7023
7177
  const zeroY = valueToY(0, activeRange, layout.plotTop, layout.plotBottom);
7024
7178
  const valY = valueToY(val, activeRange, layout.plotTop, layout.plotBottom);
7025
7179
  const y = Math.min(zeroY, valY);
7026
7180
  const h = Math.max(Math.abs(zeroY - valY), 1);
7181
+ const baseFill = varyColorsSingle
7182
+ ? resolveVaryColorFill(series[si], sourceIndex, paletteColor(sourceIndex, palette))
7183
+ : (resolveDataPointFill(series[si], sourceIndex, paletteColor(si, palette)) ??
7184
+ seriesColor(series[si], si, palette));
7027
7185
  primitives.push({
7028
7186
  kind: 'rect',
7029
7187
  x,
7030
7188
  y,
7031
7189
  w: singleBarWidth,
7032
7190
  h,
7033
- fill: seriesColor(series[si], si, palette),
7191
+ fill: invertNegativeFill(series[si], sourceIndex, val, baseFill),
7034
7192
  rx: 1,
7035
7193
  part: { role: 'dataPoint', seriesIndex: si, pointIndex: sourceIndex },
7036
7194
  });
@@ -7060,22 +7218,16 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
7060
7218
  }));
7061
7219
  const rects = computeStackedBarRects(displaySeries, catCount, layout, primaryRange, palette);
7062
7220
  for (const r of rects) {
7063
- primitives.push({
7064
- kind: 'rect',
7065
- x: r.x,
7066
- y: r.y,
7067
- w: r.w,
7068
- h: r.h,
7069
- fill: r.fill,
7070
- rx: 1,
7071
- part: r.seriesIndex !== undefined && r.pointIndex !== undefined
7072
- ? {
7073
- role: 'dataPoint',
7074
- seriesIndex: r.seriesIndex,
7075
- pointIndex: sourceIndices[r.pointIndex] ?? r.pointIndex,
7076
- }
7077
- : undefined,
7078
- });
7221
+ let fill = r.fill;
7222
+ let part;
7223
+ if (r.seriesIndex !== undefined && r.pointIndex !== undefined) {
7224
+ const sourcePointIndex = sourceIndices[r.pointIndex] ?? r.pointIndex;
7225
+ fill = resolveDataPointFill(series[r.seriesIndex], sourcePointIndex, r.fill) ?? r.fill;
7226
+ const value = series[r.seriesIndex].values[sourcePointIndex] ?? 0;
7227
+ fill = invertNegativeFill(series[r.seriesIndex], sourcePointIndex, value, fill);
7228
+ part = { role: 'dataPoint', seriesIndex: r.seriesIndex, pointIndex: sourcePointIndex };
7229
+ }
7230
+ primitives.push({ kind: 'rect', x: r.x, y: r.y, w: r.w, h: r.h, fill, rx: 1, part });
7079
7231
  }
7080
7232
  if (showLabels) {
7081
7233
  pushClusteredStackedLabels(series, sourceIndices, catCount, layout, primaryRange, dataLabels);
@@ -7107,13 +7259,15 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
7107
7259
  const topY = valueToY(top, primaryRange, layout.plotTop, layout.plotBottom);
7108
7260
  const y = Math.min(baseY, topY);
7109
7261
  const h = Math.max(Math.abs(baseY - topY), 0.5);
7262
+ const pctBaseFill = resolveDataPointFill(series[si], sourceIndex, paletteColor(si, palette)) ??
7263
+ seriesColor(series[si], si, palette);
7110
7264
  primitives.push({
7111
7265
  kind: 'rect',
7112
7266
  x,
7113
7267
  y,
7114
7268
  w: barW,
7115
7269
  h,
7116
- fill: seriesColor(series[si], si, palette),
7270
+ fill: invertNegativeFill(series[si], sourceIndex, rawVal, pctBaseFill),
7117
7271
  part: { role: 'dataPoint', seriesIndex: si, pointIndex: sourceIndex },
7118
7272
  });
7119
7273
  if (showLabels && Math.abs(val) > 0) {
@@ -7174,6 +7328,181 @@ function pushClusteredStackedLabels(series, sourceIndices, catCount, layout, ran
7174
7328
  }
7175
7329
  }
7176
7330
 
7331
+ /**
7332
+ * chart-line-path.ts: bezier smoothing for line-series paths.
7333
+ *
7334
+ * OOXML line/scatter series may set `c:smooth`, which PowerPoint renders as a
7335
+ * smoothed curve through the data points rather than straight segments. This
7336
+ * helper converts a point list into an SVG path `d` string using a Catmull-Rom
7337
+ * spline expressed as cubic beziers, so every binding can render the smoothed
7338
+ * line identically.
7339
+ *
7340
+ * @module chart-line-path
7341
+ */
7342
+ /**
7343
+ * Build a smoothed cubic-bezier path `d` through `points` (Catmull-Rom spline).
7344
+ * Returns a straight `M…L…` fallback for fewer than three points.
7345
+ */
7346
+ function smoothLinePath(points) {
7347
+ if (points.length === 0) {
7348
+ return '';
7349
+ }
7350
+ if (points.length === 1) {
7351
+ return `M${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
7352
+ }
7353
+ if (points.length === 2) {
7354
+ return `M${points[0].x.toFixed(2)},${points[0].y.toFixed(2)} L${points[1].x.toFixed(2)},${points[1].y.toFixed(2)}`;
7355
+ }
7356
+ let d = `M${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
7357
+ for (let i = 0; i < points.length - 1; i++) {
7358
+ const p0 = points[i - 1] ?? points[i];
7359
+ const p1 = points[i];
7360
+ const p2 = points[i + 1];
7361
+ const p3 = points[i + 2] ?? p2;
7362
+ const cp1x = p1.x + (p2.x - p0.x) / 6;
7363
+ const cp1y = p1.y + (p2.y - p0.y) / 6;
7364
+ const cp2x = p2.x - (p3.x - p1.x) / 6;
7365
+ const cp2y = p2.y - (p3.y - p1.y) / 6;
7366
+ d += ` C${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(2)},${cp2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
7367
+ }
7368
+ return d;
7369
+ }
7370
+
7371
+ /**
7372
+ * chart-marker-shape.ts: framework-agnostic marker-symbol primitive builder for
7373
+ * line / scatter / area chart data points.
7374
+ *
7375
+ * OOXML line/scatter series carry a `c:marker` with a `c:symbol` (circle,
7376
+ * diamond, square, triangle, star, x, plus, dot, dash, none, ...) and an
7377
+ * optional `c:size` (2-72 points). The base view-model historically drew every
7378
+ * data point as a fixed-radius circle, ignoring both. This helper resolves the
7379
+ * parsed marker into the correct `SvgPrimitive` at the requested size, and
7380
+ * returns `null` for `symbol === 'none'` so no dot is drawn.
7381
+ *
7382
+ * @module chart-marker-shape
7383
+ */
7384
+ /** Resolve the drawn radius (px) from the parsed point size or the default. */
7385
+ function markerRadius(size, defaultRadius) {
7386
+ if (size === undefined || !Number.isFinite(size) || size <= 0) {
7387
+ return defaultRadius;
7388
+ }
7389
+ // OOXML marker size is a point diameter; treat 1pt ~ 1px at chart scale.
7390
+ return size / 2;
7391
+ }
7392
+ /** Build the SVG points string for a regular/irregular polygon vertex list. */
7393
+ function polygonPoints(vertices) {
7394
+ return vertices.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(' ');
7395
+ }
7396
+ function starVertices(cx, cy, r) {
7397
+ const inner = r * 0.5;
7398
+ const out = [];
7399
+ for (let i = 0; i < 10; i++) {
7400
+ const radius = i % 2 === 0 ? r : inner;
7401
+ const angle = -Math.PI / 2 + (Math.PI * i) / 5;
7402
+ out.push([cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)]);
7403
+ }
7404
+ return out;
7405
+ }
7406
+ /**
7407
+ * Build the marker primitive for one data point, honouring `symbol` and `size`.
7408
+ * Returns `null` when `symbol === 'none'` (draw nothing).
7409
+ */
7410
+ function buildMarkerPrimitive(input) {
7411
+ const { symbol, cx, cy, fill, part } = input;
7412
+ if (symbol === 'none') {
7413
+ return null;
7414
+ }
7415
+ const r = markerRadius(input.size, input.defaultRadius);
7416
+ const stroke = fill;
7417
+ switch (symbol) {
7418
+ case 'square':
7419
+ return { kind: 'rect', x: cx - r, y: cy - r, w: r * 2, h: r * 2, fill, part };
7420
+ case 'dash':
7421
+ return { kind: 'rect', x: cx - r, y: cy - r * 0.32, w: r * 2, h: r * 0.64, fill, part };
7422
+ case 'diamond':
7423
+ return {
7424
+ kind: 'polygon',
7425
+ points: polygonPoints([
7426
+ [cx, cy - r],
7427
+ [cx + r, cy],
7428
+ [cx, cy + r],
7429
+ [cx - r, cy],
7430
+ ]),
7431
+ fill,
7432
+ stroke,
7433
+ strokeWidth: 0,
7434
+ part,
7435
+ };
7436
+ case 'triangle':
7437
+ return {
7438
+ kind: 'polygon',
7439
+ points: polygonPoints([
7440
+ [cx, cy - r],
7441
+ [cx + r * 0.9, cy + r * 0.75],
7442
+ [cx - r * 0.9, cy + r * 0.75],
7443
+ ]),
7444
+ fill,
7445
+ stroke,
7446
+ strokeWidth: 0,
7447
+ part,
7448
+ };
7449
+ case 'star':
7450
+ return {
7451
+ kind: 'polygon',
7452
+ points: polygonPoints(starVertices(cx, cy, r)),
7453
+ fill,
7454
+ stroke,
7455
+ strokeWidth: 0,
7456
+ part,
7457
+ };
7458
+ case 'plus':
7459
+ return {
7460
+ kind: 'path',
7461
+ d: `M${(cx - r).toFixed(2)},${cy.toFixed(2)} L${(cx + r).toFixed(2)},${cy.toFixed(2)} M${cx.toFixed(2)},${(cy - r).toFixed(2)} L${cx.toFixed(2)},${(cy + r).toFixed(2)}`,
7462
+ fill: 'none',
7463
+ stroke,
7464
+ strokeWidth: Math.max(1, r * 0.4),
7465
+ part,
7466
+ };
7467
+ case 'x':
7468
+ return {
7469
+ kind: 'path',
7470
+ d: `M${(cx - r).toFixed(2)},${(cy - r).toFixed(2)} L${(cx + r).toFixed(2)},${(cy + r).toFixed(2)} M${(cx + r).toFixed(2)},${(cy - r).toFixed(2)} L${(cx - r).toFixed(2)},${(cy + r).toFixed(2)}`,
7471
+ fill: 'none',
7472
+ stroke,
7473
+ strokeWidth: Math.max(1, r * 0.4),
7474
+ part,
7475
+ };
7476
+ case 'dot':
7477
+ return { kind: 'circle', cx, cy, r: Math.max(r * 0.6, 1), fill, part };
7478
+ default:
7479
+ // circle / auto / picture / undefined -> filled circle.
7480
+ return { kind: 'circle', cx, cy, r, fill, part };
7481
+ }
7482
+ }
7483
+
7484
+ /**
7485
+ * Build the marker for a data point (honouring `marker.symbol`/`size`) and push
7486
+ * it, unless the symbol is `none`. Shared by the line / area / scatter builders.
7487
+ */
7488
+ function pushMarker(out, series, cx, cy, fill, defaultRadius, part, opacity) {
7489
+ const m = buildMarkerPrimitive({
7490
+ symbol: series.marker?.symbol,
7491
+ size: series.marker?.size,
7492
+ cx,
7493
+ cy,
7494
+ fill,
7495
+ defaultRadius,
7496
+ part,
7497
+ });
7498
+ if (!m) {
7499
+ return;
7500
+ }
7501
+ if (opacity !== undefined) {
7502
+ m.opacity = opacity;
7503
+ }
7504
+ out.push(m);
7505
+ }
7177
7506
  /** Build line-chart primitives, honouring a secondary value range per series. */
7178
7507
  function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, sourceIndices, xPositions) {
7179
7508
  const primitives = [];
@@ -7191,24 +7520,29 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
7191
7520
  x: xPositions?.[index] ?? point.x,
7192
7521
  }));
7193
7522
  const c = seriesColor(series, si, chartData.colorPalette);
7194
- primitives.push({
7195
- kind: 'polyline',
7196
- points: linePointsToSvgString(pts),
7197
- stroke: c,
7198
- strokeWidth: 2.4,
7199
- fill: 'none',
7200
- part: { role: 'series', seriesIndex: si },
7201
- });
7202
- pts.forEach((pt, displayIndex) => {
7203
- const sourceIndex = sourceIndices[displayIndex] ?? displayIndex;
7204
- primitives.push({
7205
- kind: 'circle',
7206
- cx: pt.x,
7207
- cy: pt.y,
7208
- r: 2.5,
7209
- fill: c,
7210
- part: { role: 'dataPoint', seriesIndex: si, pointIndex: sourceIndex },
7523
+ // c:smooth draws a bezier path through the points; otherwise a polyline.
7524
+ const seriesPart = { role: 'series', seriesIndex: si };
7525
+ primitives.push(series.smooth
7526
+ ? {
7527
+ kind: 'path',
7528
+ d: smoothLinePath(pts),
7529
+ stroke: c,
7530
+ strokeWidth: 2.4,
7531
+ fill: 'none',
7532
+ part: seriesPart,
7533
+ }
7534
+ : {
7535
+ kind: 'polyline',
7536
+ points: linePointsToSvgString(pts),
7537
+ stroke: c,
7538
+ strokeWidth: 2.4,
7539
+ fill: 'none',
7540
+ part: seriesPart,
7211
7541
  });
7542
+ pts.forEach((pt, displayIndex) => {
7543
+ const idx = sourceIndices[displayIndex] ?? displayIndex;
7544
+ const part = { role: 'dataPoint', seriesIndex: si, pointIndex: idx };
7545
+ pushMarker(primitives, series, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2.5, part);
7212
7546
  });
7213
7547
  if (showLabels) {
7214
7548
  displayValues.forEach((val, displayIndex) => {
@@ -7270,18 +7604,9 @@ function buildAreas(chartData, catCount, layout, range, sourceIndices, xPosition
7270
7604
  part: { role: 'series', seriesIndex: si },
7271
7605
  });
7272
7606
  pts.forEach((pt, displayIndex) => {
7273
- primitives.push({
7274
- kind: 'circle',
7275
- cx: pt.x,
7276
- cy: pt.y,
7277
- r: 2,
7278
- fill: c,
7279
- part: {
7280
- role: 'dataPoint',
7281
- seriesIndex: si,
7282
- pointIndex: sourceIndices[displayIndex] ?? displayIndex,
7283
- },
7284
- });
7607
+ const idx = sourceIndices[displayIndex] ?? displayIndex;
7608
+ const part = { role: 'dataPoint', seriesIndex: si, pointIndex: idx };
7609
+ pushMarker(primitives, series, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2, part);
7285
7610
  });
7286
7611
  if (showLabels) {
7287
7612
  displayValues.forEach((val, displayIndex) => {
@@ -7316,15 +7641,8 @@ function buildScatter(chartData, layout, range) {
7316
7641
  const c = seriesColor(series, si, chartData.colorPalette);
7317
7642
  const dots = computeScatterDots(series.values, maxXIndex, layout, range, xValues);
7318
7643
  dots.forEach((dot, vi) => {
7319
- primitives.push({
7320
- kind: 'circle',
7321
- cx: dot.cx,
7322
- cy: dot.cy,
7323
- r: 4,
7324
- fill: c,
7325
- opacity: 0.85,
7326
- part: { role: 'dataPoint', seriesIndex: si, pointIndex: vi },
7327
- });
7644
+ const part = { role: 'dataPoint', seriesIndex: si, pointIndex: vi };
7645
+ pushMarker(primitives, series, dot.cx, dot.cy, resolveDataPointFill(series, vi, c) ?? c, 4, part, 0.85);
7328
7646
  });
7329
7647
  if (showLabels) {
7330
7648
  series.values.forEach((val, vi) => {
@@ -7369,7 +7687,7 @@ function buildBubbles(chartData, layout, range) {
7369
7687
  cx: dot.cx,
7370
7688
  cy: dot.cy,
7371
7689
  r,
7372
- fill: c,
7690
+ fill: resolveDataPointFill(series, vi, c) ?? c,
7373
7691
  opacity: 0.6,
7374
7692
  part: { role: 'dataPoint', seriesIndex: si, pointIndex: vi },
7375
7693
  });
@@ -7395,6 +7713,163 @@ function buildBubbles(chartData, layout, range) {
7395
7713
  return { primitives, dataLabels };
7396
7714
  }
7397
7715
 
7716
+ /**
7717
+ * chart-helper-lines.ts: shared drop-line / hi-low-line / up-down-bar primitives.
7718
+ *
7719
+ * OOXML line and stock charts may declare `c:dropLines`, `c:hiLowLines`, and
7720
+ * `c:upDownBars` on the chart-type container. Historically these rendered only
7721
+ * in the React chart chrome (drop/hi-low), and up-down bars nowhere, so the
7722
+ * framework-agnostic view-model (Vue / Angular / Vanilla / Svelte) never drew
7723
+ * them. These pure builders project each into `SvgPrimitive[]` so every binding
7724
+ * renders them from a single source.
7725
+ *
7726
+ * dropLines - a vertical line from each data point down to the axis baseline.
7727
+ * hiLowLines - a vertical line spanning the highest and lowest series value
7728
+ * in each category (requires >= 2 series).
7729
+ * upDownBars - a bar between the first and last series value in each category,
7730
+ * coloured by whether the last value rose or fell (>= 2 series).
7731
+ *
7732
+ * @module chart-helper-lines
7733
+ */
7734
+ /** X pixel for a category index, preferring an explicit x-position. */
7735
+ function categoryX$2(index, catCount, layout, options) {
7736
+ const explicit = options.xPositions?.[index];
7737
+ if (explicit !== undefined) {
7738
+ return explicit;
7739
+ }
7740
+ if (options.mode === 'bar') {
7741
+ const slot = layout.plotWidth / Math.max(catCount, 1);
7742
+ return layout.plotLeft + slot * index + slot / 2;
7743
+ }
7744
+ const maxIdx = Math.max(catCount - 1, 1);
7745
+ return layout.plotLeft + (index / maxIdx) * layout.plotWidth;
7746
+ }
7747
+ /** SVG dash pattern for a helper-line dash style, if any. */
7748
+ function dashFor(dashStyle) {
7749
+ if (!dashStyle || dashStyle === 'solid') {
7750
+ return undefined;
7751
+ }
7752
+ return dashStyle === 'dot' || dashStyle === 'sysDot' ? '1 2' : '4 3';
7753
+ }
7754
+ /**
7755
+ * Drop lines: a vertical line from every data point down to the value-axis
7756
+ * baseline (`range.min`). Returns `[]` when `c:dropLines` is absent.
7757
+ */
7758
+ function computeDropLinePrimitives(chartData, layout, range, catCount, options) {
7759
+ const style = chartData.dropLines;
7760
+ if (!style) {
7761
+ return [];
7762
+ }
7763
+ const baselineY = valueToY(range.min, range, layout.plotTop, layout.plotBottom);
7764
+ const dashArray = dashFor(style.dashStyle);
7765
+ const out = [];
7766
+ for (const series of chartData.series) {
7767
+ series.values.forEach((val, vi) => {
7768
+ const x = categoryX$2(vi, catCount, layout, options);
7769
+ out.push({
7770
+ kind: 'line',
7771
+ x1: x,
7772
+ y1: valueToY(val, range, layout.plotTop, layout.plotBottom),
7773
+ x2: x,
7774
+ y2: baselineY,
7775
+ stroke: style.color ?? '#94a3b8',
7776
+ strokeWidth: style.width ?? 0.8,
7777
+ dashArray,
7778
+ });
7779
+ });
7780
+ }
7781
+ return out;
7782
+ }
7783
+ /**
7784
+ * Hi-low lines: a vertical line joining the highest and lowest series value in
7785
+ * each category. Returns `[]` when `c:hiLowLines` is absent or fewer than two
7786
+ * series are present.
7787
+ */
7788
+ function computeHiLowLinePrimitives(chartData, layout, range, catCount, options) {
7789
+ const style = chartData.hiLowLines;
7790
+ if (!style || chartData.series.length < 2) {
7791
+ return [];
7792
+ }
7793
+ const dashArray = dashFor(style.dashStyle);
7794
+ const out = [];
7795
+ for (let vi = 0; vi < catCount; vi++) {
7796
+ let high = Number.NEGATIVE_INFINITY;
7797
+ let low = Number.POSITIVE_INFINITY;
7798
+ for (const series of chartData.series) {
7799
+ const v = series.values[vi];
7800
+ if (v !== undefined) {
7801
+ high = Math.max(high, v);
7802
+ low = Math.min(low, v);
7803
+ }
7804
+ }
7805
+ if (!Number.isFinite(high) || !Number.isFinite(low)) {
7806
+ continue;
7807
+ }
7808
+ const x = categoryX$2(vi, catCount, layout, options);
7809
+ out.push({
7810
+ kind: 'line',
7811
+ x1: x,
7812
+ y1: valueToY(high, range, layout.plotTop, layout.plotBottom),
7813
+ x2: x,
7814
+ y2: valueToY(low, range, layout.plotTop, layout.plotBottom),
7815
+ stroke: style.color ?? '#334155',
7816
+ strokeWidth: style.width ?? 1,
7817
+ dashArray,
7818
+ });
7819
+ }
7820
+ return out;
7821
+ }
7822
+ /**
7823
+ * Up-down bars: a bar between the first and last series value in each category.
7824
+ * Rising categories (last >= first) use the `upBars` fill, falling ones the
7825
+ * `downBars` fill. Returns `[]` when `c:upDownBars` is absent or fewer than two
7826
+ * series are present.
7827
+ */
7828
+ function computeUpDownBarPrimitives(chartData, layout, range, catCount, options) {
7829
+ const bars = chartData.upDownBars;
7830
+ if (!bars || chartData.series.length < 2) {
7831
+ return [];
7832
+ }
7833
+ const first = chartData.series[0];
7834
+ const last = chartData.series[chartData.series.length - 1];
7835
+ const slot = layout.plotWidth / Math.max(catCount, 1);
7836
+ const gapWidth = bars.gapWidth ?? 150;
7837
+ const barW = Math.max(slot / (1 + Math.max(gapWidth, 0) / 100), 1);
7838
+ const upFill = bars.upBars?.fillColor ?? '#e2e8f0';
7839
+ const downFill = bars.downBars?.fillColor ?? '#334155';
7840
+ const out = [];
7841
+ for (let vi = 0; vi < catCount; vi++) {
7842
+ const firstVal = first.values[vi];
7843
+ const lastVal = last.values[vi];
7844
+ if (firstVal === undefined || lastVal === undefined) {
7845
+ continue;
7846
+ }
7847
+ const firstY = valueToY(firstVal, range, layout.plotTop, layout.plotBottom);
7848
+ const lastY = valueToY(lastVal, range, layout.plotTop, layout.plotBottom);
7849
+ const x = categoryX$2(vi, catCount, layout, options) - barW / 2;
7850
+ out.push({
7851
+ kind: 'rect',
7852
+ x,
7853
+ y: Math.min(firstY, lastY),
7854
+ w: barW,
7855
+ h: Math.max(Math.abs(firstY - lastY), 1),
7856
+ fill: lastVal >= firstVal ? upFill : downFill,
7857
+ });
7858
+ }
7859
+ return out;
7860
+ }
7861
+ /**
7862
+ * Convenience: all three helper-line families for a chart, concatenated.
7863
+ * Drawn under the series marks by the caller (they push the data marks first).
7864
+ */
7865
+ function computeHelperLinePrimitives(chartData, layout, range, catCount, options) {
7866
+ return [
7867
+ ...computeUpDownBarPrimitives(chartData, layout, range, catCount, options),
7868
+ ...computeDropLinePrimitives(chartData, layout, range, catCount, options),
7869
+ ...computeHiLowLinePrimitives(chartData, layout, range, catCount, options),
7870
+ ];
7871
+ }
7872
+
7398
7873
  function categoryX$1(displayIndex, count, layout, spacing) {
7399
7874
  if (spacing === 'bar') {
7400
7875
  return layout.plotLeft + (layout.plotWidth / Math.max(count, 1)) * (displayIndex + 0.5);
@@ -8568,7 +9043,19 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
8568
9043
  else {
8569
9044
  plot = buildBubbles(chartData, layout, primaryRange);
8570
9045
  }
8571
- const primitives = [...plot.primitives, ...horizontalAxis.tickMarks];
9046
+ // Drop lines / hi-low lines / up-down bars (line + area kinds). Drawn behind
9047
+ // the series marks so the data stays legible on top.
9048
+ const helperLines = kind === 'line' || kind === 'area'
9049
+ ? computeHelperLinePrimitives(chartData, layout, primaryRange, catCount, {
9050
+ mode: 'line',
9051
+ xPositions: horizontalAxis.xPositions,
9052
+ })
9053
+ : [];
9054
+ const primitives = [
9055
+ ...helperLines,
9056
+ ...plot.primitives,
9057
+ ...horizontalAxis.tickMarks,
9058
+ ];
8572
9059
  // Overlays (depth): regression trendlines, error bars, axis titles, data table.
8573
9060
  const overlays = [
8574
9061
  ...computeTrendlinePrimitives(displayChartData, catCount, layout, primaryRange, catAxisStyle, chartData.colorPalette),
@@ -8651,6 +9138,9 @@ function buildComboViewModel(element, chartData, categoryLabels) {
8651
9138
  const { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop);
8652
9139
  const primitives = [];
8653
9140
  const dataLabels = [];
9141
+ // Drop / hi-low / up-down helper lines, drawn behind the combo marks.
9142
+ const helperOpts = { mode: 'line', xPositions: horizontalAxis.xPositions };
9143
+ primitives.push(...computeHelperLinePrimitives(chartData, layout, primaryRange, catCount, helperOpts));
8654
9144
  const barSeries = chartData.series.slice(0, 1);
8655
9145
  if (barSeries[0]) {
8656
9146
  const barRange = rangeForSeries$1(0, primaryRange, secondaryRange, secondaryIndexes);
@@ -11315,16 +11805,28 @@ function computePieLayout(elementWidth, elementHeight, chartData, isDoughnut) {
11315
11805
  const cx = size / 2;
11316
11806
  const cy = titleOffset + (size - titleOffset - legendOffset) / 2;
11317
11807
  const outerR = Math.max((size - titleOffset - legendOffset) * 0.42, 0);
11318
- const innerR = isDoughnut ? outerR * 0.55 : 0;
11808
+ // Honour c:holeSize (10-90% of the outer diameter) when parsed; otherwise
11809
+ // keep the legacy 0.55 ratio byte-for-byte.
11810
+ const holeRatio = isDoughnut && chartData.doughnutHoleSize !== undefined
11811
+ ? Math.min(Math.max(chartData.doughnutHoleSize, 10), 90) / 100
11812
+ : 0.55;
11813
+ const innerR = isDoughnut ? outerR * holeRatio : 0;
11319
11814
  return { cx, cy, outerR, innerR, size };
11320
11815
  }
11321
- function computePieSlices(values, cx, cy, outerR, innerR) {
11816
+ function computePieSlices(values, cx, cy, outerR, innerR, options) {
11322
11817
  const total = values.reduce((s, v) => s + Math.abs(v), 0) || 1;
11323
- let cumAngle = -Math.PI / 2;
11324
- return values.map((val) => {
11818
+ let cumAngle = options?.startAngle ?? -Math.PI / 2;
11819
+ return values.map((val, i) => {
11325
11820
  const sliceAngle = (Math.abs(val) / total) * Math.PI * 2;
11326
11821
  const startAngle = cumAngle;
11327
11822
  cumAngle += sliceAngle;
11823
+ // A c:explosion pulls the slice outward along its bisector.
11824
+ const explosion = options?.explosions?.[i] ?? 0;
11825
+ if (explosion > 0) {
11826
+ const mid = (startAngle + cumAngle) / 2;
11827
+ const offset = outerR * (explosion / 100);
11828
+ return computePieSlicePath(cx + Math.cos(mid) * offset, cy + Math.sin(mid) * offset, outerR, innerR, startAngle, cumAngle);
11829
+ }
11328
11830
  return computePieSlicePath(cx, cy, outerR, innerR, startAngle, cumAngle);
11329
11831
  });
11330
11832
  }
@@ -11534,12 +12036,23 @@ function buildPieViewModel(element, chartData, categoryLabels, isDoughnut) {
11534
12036
  const { cx, cy, outerR, innerR, size } = computePieLayout(element.width, element.height, chartData, isDoughnut);
11535
12037
  const svgWidth = Math.max(size, 100);
11536
12038
  const svgHeight = Math.max(size, 60);
11537
- const values = chartData.series[0]?.values ?? [];
11538
- const slices = computePieSlices(values, cx, cy, outerR, innerR);
12039
+ const pieSeries = chartData.series[0];
12040
+ const values = pieSeries?.values ?? [];
12041
+ // c:firstSliceAng rotates the pie clockwise from 12 o'clock; c:explosion (per
12042
+ // series or per c:dPt) pulls slices outward.
12043
+ const startAngle = -Math.PI / 2 + ((chartData.firstSliceAngle ?? 0) * Math.PI) / 180;
12044
+ const explosions = pieSeries
12045
+ ? values.map((_v, i) => resolveDataPointExplosion(pieSeries, i))
12046
+ : undefined;
12047
+ const slices = computePieSlices(values, cx, cy, outerR, innerR, { startAngle, explosions });
11539
12048
  const primitives = slices.map(({ d }, i) => ({
11540
12049
  kind: 'path',
11541
12050
  d,
11542
- fill: chartData.series[0]?.color ?? paletteColor(i, chartData.colorPalette),
12051
+ // Pie/doughnut vary colours per slice (c:varyColors defaults on), so each
12052
+ // slice takes its palette colour, with a per-point c:dPt fill overriding.
12053
+ fill: pieSeries
12054
+ ? resolveVaryColorFill(pieSeries, i, paletteColor(i, chartData.colorPalette))
12055
+ : paletteColor(i, chartData.colorPalette),
11543
12056
  stroke: '#ffffff',
11544
12057
  strokeWidth: 1.5,
11545
12058
  part: { role: 'dataPoint', seriesIndex: 0, pointIndex: i },
@@ -12893,6 +13406,25 @@ const PRESET_ID_TO_EFFECT = {
12893
13406
  26: 'pulse',
12894
13407
  },
12895
13408
  };
13409
+ /**
13410
+ * Map an OOXML `p:cTn/@presetSubtype` code to a {@link FlyEdge} for Fly In and
13411
+ * Fly Out effects. PowerPoint encodes the direction as a bitmask on the object
13412
+ * origin edge: 1=top, 2=right, 4=bottom, 8=left. Corners combine two bits
13413
+ * (e.g. 12 = 8|4 = bottom-left) and fall back to their horizontal edge, which
13414
+ * is the more visually distinct component. Unknown/absent codes are left to the
13415
+ * caller (which keeps the preset default of bottom).
13416
+ */
13417
+ const FLY_SUBTYPE_TO_EDGE = {
13418
+ 1: 'top',
13419
+ 2: 'right',
13420
+ 4: 'bottom',
13421
+ 8: 'left',
13422
+ // Corners -> nearest (horizontal) edge.
13423
+ 3: 'right', // top-right (1|2)
13424
+ 6: 'right', // bottom-right (4|2)
13425
+ 9: 'left', // top-left (8|1)
13426
+ 12: 'left', // bottom-left (8|4)
13427
+ };
12896
13428
 
12897
13429
  /**
12898
13430
  * `animation-keyframes` — CSS `@keyframes` definitions for every static
@@ -13354,10 +13886,10 @@ function resolveEffect(anim) {
13354
13886
  return undefined;
13355
13887
  }
13356
13888
  if (cls === 'entr') {
13357
- return PRESET_ID_TO_EFFECT.entr[id];
13889
+ return applyFlyDirection(PRESET_ID_TO_EFFECT.entr[id], anim.presetSubtype);
13358
13890
  }
13359
13891
  if (cls === 'exit') {
13360
- return PRESET_ID_TO_EFFECT.exit[id];
13892
+ return applyFlyDirection(PRESET_ID_TO_EFFECT.exit[id], anim.presetSubtype);
13361
13893
  }
13362
13894
  if (cls === 'emph') {
13363
13895
  return PRESET_ID_TO_EFFECT.emph[id];
@@ -13365,6 +13897,28 @@ function resolveEffect(anim) {
13365
13897
  // For path/motion/rotation/scale, return undefined — handled dynamically.
13366
13898
  return undefined;
13367
13899
  }
13900
+ /**
13901
+ * Redirect a Fly In / Fly Out effect according to its `presetSubtype` code.
13902
+ * The preset tables default the fly family to the bottom edge; when a subtype
13903
+ * is present and maps to a known edge, swap in the matching directional effect.
13904
+ * A missing or unrecognised subtype preserves the bottom default so existing
13905
+ * decks are unaffected.
13906
+ */
13907
+ function applyFlyDirection(effect, subtype) {
13908
+ if (effect !== 'flyInBottom' && effect !== 'flyOutBottom') {
13909
+ return effect;
13910
+ }
13911
+ if (subtype === undefined) {
13912
+ return effect;
13913
+ }
13914
+ const edge = FLY_SUBTYPE_TO_EDGE[subtype];
13915
+ if (!edge) {
13916
+ return effect;
13917
+ }
13918
+ const prefix = effect === 'flyInBottom' ? 'flyIn' : 'flyOut';
13919
+ const suffix = `${edge.charAt(0).toUpperCase()}${edge.slice(1)}`;
13920
+ return `${prefix}${suffix}`;
13921
+ }
13368
13922
  // ==========================================================================
13369
13923
  // Dynamic keyframe generation (motion path / rotation / scale / colour)
13370
13924
  // ==========================================================================
@@ -14171,6 +14725,51 @@ class AnimationSequencer {
14171
14725
  * @module render/animation-timeline-builder
14172
14726
  */
14173
14727
  // ==========================================================================
14728
+ // Unmapped-preset safety net
14729
+ // ==========================================================================
14730
+ /**
14731
+ * Resolve a fallback {@link EffectName} for an animation whose preset we do
14732
+ * not model (no static effect and no dynamic keyframe).
14733
+ *
14734
+ * Without this, an unmapped animation was silently dropped, which broke slide
14735
+ * visibility semantics: an unmapped **entrance** was never registered as
14736
+ * hidden-until-its-start, so it stayed visible from the very first frame; an
14737
+ * unmapped **exit** never hid its element. We substitute a neutral fade so the
14738
+ * element still transitions in (entrance) or out (exit) at the correct time.
14739
+ *
14740
+ * Emphasis / motion-path presets carry no show/hide semantics, so a missing
14741
+ * one is safe to skip and returns `undefined`.
14742
+ */
14743
+ /**
14744
+ * Map an animation's parsed `accel`/`decel` fractions to a CSS timing function.
14745
+ * PowerPoint's acceleration eases the effect in, deceleration eases it out, and
14746
+ * both together ease in-and-out. With neither set we keep the neutral `ease`
14747
+ * default so existing decks are unchanged.
14748
+ */
14749
+ function cssEasingForAnimation(anim) {
14750
+ const hasAccel = anim.accel !== undefined && anim.accel > 0;
14751
+ const hasDecel = anim.decel !== undefined && anim.decel > 0;
14752
+ if (hasAccel && hasDecel) {
14753
+ return 'ease-in-out';
14754
+ }
14755
+ if (hasAccel) {
14756
+ return 'ease-in';
14757
+ }
14758
+ if (hasDecel) {
14759
+ return 'ease-out';
14760
+ }
14761
+ return 'ease';
14762
+ }
14763
+ function fallbackEffectForClass(presetClass) {
14764
+ if (presetClass === 'entr') {
14765
+ return 'fadeIn';
14766
+ }
14767
+ if (presetClass === 'exit') {
14768
+ return 'fadeOut';
14769
+ }
14770
+ return undefined;
14771
+ }
14772
+ // ==========================================================================
14174
14773
  // Timeline builder
14175
14774
  // ==========================================================================
14176
14775
  /**
@@ -14233,10 +14832,15 @@ function buildTimeline(nativeAnimations) {
14233
14832
  for (const anim of regularAnims) {
14234
14833
  const expandedSteps = expandIterateAnimation(anim);
14235
14834
  for (const singleAnim of expandedSteps) {
14236
- const effect = resolveEffect(singleAnim);
14835
+ let effect = resolveEffect(singleAnim);
14237
14836
  const dynamic = effect ? undefined : buildDynamicKeyframe(singleAnim, dynamicUid++);
14238
14837
  if (!effect && !dynamic) {
14239
- continue;
14838
+ // Unmapped preset: fall back so an entrance is still hidden until
14839
+ // its start and an exit still hides, rather than being dropped.
14840
+ effect = fallbackEffectForClass(singleAnim.presetClass);
14841
+ if (!effect) {
14842
+ continue;
14843
+ }
14240
14844
  }
14241
14845
  const keyframe = effect ? cssKeyframeName(effect) : dynamic.keyframeName;
14242
14846
  if (effect) {
@@ -14300,7 +14904,8 @@ function buildTimeline(nativeAnimations) {
14300
14904
  delayMs = animDelay + triggerDelay;
14301
14905
  }
14302
14906
  const iterStr = iterCount === Infinity ? 'infinite' : String(iterCount);
14303
- const cssAnimation = `${keyframe} ${duration}ms ease ${delayMs}ms ${iterStr} ${direction} ${fill}`;
14907
+ const easing = cssEasingForAnimation(singleAnim);
14908
+ const cssAnimation = `${keyframe} ${duration}ms ${easing} ${delayMs}ms ${iterStr} ${direction} ${fill}`;
14304
14909
  currentGroup.push({
14305
14910
  elementId,
14306
14911
  cssAnimation,
@@ -14401,10 +15006,14 @@ function buildSequenceGroups(animsByKey, entranceIds, neededKeyframes, dynamicBl
14401
15006
  const seqGroups = [];
14402
15007
  let seqGroup = [];
14403
15008
  for (const anim of anims) {
14404
- const effect = resolveEffect(anim);
15009
+ let effect = resolveEffect(anim);
14405
15010
  const dynamic = effect ? undefined : buildDynamicKeyframe(anim, dynamicUid++);
14406
15011
  if (!effect && !dynamic) {
14407
- continue;
15012
+ // Same unmapped-preset safety net as the main timeline loop.
15013
+ effect = fallbackEffectForClass(anim.presetClass);
15014
+ if (!effect) {
15015
+ continue;
15016
+ }
14408
15017
  }
14409
15018
  const keyframe = effect ? cssKeyframeName(effect) : dynamic.keyframeName;
14410
15019
  if (effect) {
@@ -14444,7 +15053,8 @@ function buildSequenceGroups(animsByKey, entranceIds, neededKeyframes, dynamicBl
14444
15053
  delayMs = animDelay + triggerDelay;
14445
15054
  }
14446
15055
  const iterStr = iterCount === Infinity ? 'infinite' : String(iterCount);
14447
- const cssAnimation = `${keyframe} ${duration}ms ease ${delayMs}ms ${iterStr} ${direction} ${fill}`;
15056
+ const easing = cssEasingForAnimation(anim);
15057
+ const cssAnimation = `${keyframe} ${duration}ms ${easing} ${delayMs}ms ${iterStr} ${direction} ${fill}`;
14448
15058
  seqGroup.push({
14449
15059
  elementId,
14450
15060
  cssAnimation,
@@ -16098,66 +16708,123 @@ function getComputed3dStyle(el) {
16098
16708
  }
16099
16709
 
16100
16710
  /**
16101
- * Convert a {@link CellTextRun}'s per-run formatting to an inline CSS style
16102
- * object suitable for a `<span>` element.
16711
+ * Map a section's border set onto a cell's four edges given the cell's
16712
+ * position relative to that section's region. Outer region edges use the
16713
+ * `top/bottom/left/right` sides; interior edges use `insideH`/`insideV`.
16103
16714
  */
16104
- function cellRunStyle$1(run) {
16105
- const css = {};
16106
- if (run.fontFamily) {
16107
- css.fontFamily = run.fontFamily;
16715
+ function edgesFromSection(borders, regionTop, regionBottom, regionLeft, regionRight) {
16716
+ if (!borders) {
16717
+ return undefined;
16108
16718
  }
16109
- if (typeof run.fontSize === 'number') {
16110
- css.fontSize = `${run.fontSize}pt`;
16719
+ return {
16720
+ top: regionTop ? (borders.top ?? borders.insideH) : borders.insideH,
16721
+ bottom: regionBottom ? (borders.bottom ?? borders.insideH) : borders.insideH,
16722
+ left: regionLeft ? (borders.left ?? borders.insideV) : borders.insideV,
16723
+ right: regionRight ? (borders.right ?? borders.insideV) : borders.insideV,
16724
+ };
16725
+ }
16726
+ /** Convert a resolved border side to a CSS `border-*` shorthand value. */
16727
+ function borderToCss(border, resolve) {
16728
+ if (!border) {
16729
+ return undefined;
16111
16730
  }
16112
- if (run.color) {
16113
- css.color = run.color;
16731
+ if (border.noFill) {
16732
+ return 'none';
16114
16733
  }
16115
- if (run.bold) {
16116
- css.fontWeight = 'bold';
16734
+ const width = border.width ?? 1;
16735
+ const dash = ooxmlDashToCssBorderStyle(border.dash);
16736
+ const color = border.color ?? resolve(border.fill) ?? '#000000';
16737
+ return `${width}px ${dash} ${color}`;
16738
+ }
16739
+ /**
16740
+ * Build the ordered (low -> high precedence) list of edge candidates from the
16741
+ * sections that apply to this cell.
16742
+ */
16743
+ function collectLayers(entry, tableData, pos) {
16744
+ const { rowIndex, cellIndex, rowCount, columnCount } = pos;
16745
+ const isTop = rowIndex === 0;
16746
+ const isBottom = rowIndex === rowCount - 1;
16747
+ const isLeft = cellIndex === 0;
16748
+ const isRight = cellIndex === columnCount - 1;
16749
+ const layers = [];
16750
+ // Whole table: region spans the entire table.
16751
+ layers.push(edgesFromSection(entry.wholeTblBorders, isTop, isBottom, isLeft, isRight));
16752
+ // Banded rows: treat each banded row as its own single-row region.
16753
+ if (tableData.bandedRows) {
16754
+ const bandStartRow = tableData.firstRowHeader ? 1 : 0;
16755
+ const bandEndRow = tableData.lastRow ? rowCount - 1 : rowCount;
16756
+ if (rowIndex >= bandStartRow && rowIndex < bandEndRow) {
16757
+ const rowCycle = Math.max(tableData.bandRowCycle ?? 1, 1);
16758
+ const bandGroup = Math.floor((rowIndex - bandStartRow) / rowCycle) % 2;
16759
+ const bands = bandGroup === 0 ? entry.band1HBorders : entry.band2HBorders;
16760
+ layers.push(edgesFromSection(bands, true, true, isLeft, isRight));
16761
+ }
16762
+ }
16763
+ // Banded columns: treat each banded column as its own single-column region.
16764
+ if (tableData.bandedColumns) {
16765
+ const colStart = tableData.firstCol ? 1 : 0;
16766
+ const colEnd = tableData.lastCol ? columnCount - 1 : columnCount;
16767
+ if (cellIndex >= colStart && cellIndex < colEnd) {
16768
+ const colCycle = Math.max(tableData.bandColCycle ?? 1, 1);
16769
+ const colGroup = Math.floor((cellIndex - colStart) / colCycle) % 2;
16770
+ const bands = colGroup === 0 ? entry.band1VBorders : entry.band2VBorders;
16771
+ layers.push(edgesFromSection(bands, isTop, isBottom, true, true));
16772
+ }
16117
16773
  }
16118
- if (run.italic) {
16119
- css.fontStyle = 'italic';
16774
+ // First/last row: single-row region spanning all columns.
16775
+ if (tableData.firstRowHeader && isTop) {
16776
+ layers.push(edgesFromSection(entry.firstRowBorders, true, true, isLeft, isRight));
16120
16777
  }
16121
- const deco = [];
16122
- if (run.underline) {
16123
- deco.push('underline');
16778
+ if (tableData.lastRow && isBottom) {
16779
+ layers.push(edgesFromSection(entry.lastRowBorders, true, true, isLeft, isRight));
16124
16780
  }
16125
- if (run.strikethrough) {
16126
- deco.push('line-through');
16781
+ // First/last column: single-column region spanning all rows.
16782
+ if (tableData.firstCol && isLeft) {
16783
+ layers.push(edgesFromSection(entry.firstColBorders, isTop, isBottom, true, true));
16127
16784
  }
16128
- if (deco.length > 0) {
16129
- css.textDecoration = deco.join(' ');
16785
+ if (tableData.lastCol && isRight) {
16786
+ layers.push(edgesFromSection(entry.lastColBorders, isTop, isBottom, true, true));
16130
16787
  }
16131
- return css;
16788
+ return layers.filter((layer) => layer !== undefined);
16132
16789
  }
16133
16790
  /**
16134
- * Resolve a preset pattern fill from a {@link PptxTableCellStyle} to CSS
16135
- * background properties. Mirrors the pattern-fill branch of the React
16136
- * `cellStyleToCss` but returns the two properties separately so callers
16137
- * can apply them individually (e.g. Vue's `:style` binding).
16138
- *
16139
- * Returns `null` when the cell style carries no pattern fill.
16791
+ * Resolve the CSS borders a cell inherits from its table style. Returns
16792
+ * `undefined` when the style defines no applicable borders (so the caller can
16793
+ * keep its own fallback border, e.g. the hardcoded total-row line).
16140
16794
  */
16141
- function cellPatternFillCss(style) {
16142
- if (style.fillMode !== 'pattern' || !style.patternFillPreset) {
16143
- return null;
16795
+ function resolveCellBorderCss(entry, tableData, pos, resolve) {
16796
+ if (!entry) {
16797
+ return undefined;
16144
16798
  }
16145
- const fg = normalizeHexColor$1(style.patternFillForeground, '#000000');
16146
- const bg = normalizeHexColor$1(style.patternFillBackground, '#ffffff');
16147
- const svgMarkup = getPatternSvg$1(style.patternFillPreset, fg, bg);
16148
- if (svgMarkup) {
16149
- const encoded = encodeURIComponent(svgMarkup);
16150
- return {
16151
- backgroundImage: `url("data:image/svg+xml,${encoded}")`,
16152
- backgroundColor: bg,
16153
- };
16799
+ const layers = collectLayers(entry, tableData, pos);
16800
+ if (layers.length === 0) {
16801
+ return undefined;
16154
16802
  }
16155
- // Unknown preset fall back to solid background colour.
16156
- const fallback = style.patternFillBackground ?? style.backgroundColor;
16157
- return fallback ? { backgroundColor: fallback } : null;
16803
+ const pick = (edge) => {
16804
+ let winner;
16805
+ for (const layer of layers) {
16806
+ if (layer[edge]) {
16807
+ winner = layer[edge];
16808
+ }
16809
+ }
16810
+ return winner;
16811
+ };
16812
+ const css = {};
16813
+ let applied = false;
16814
+ const edges = ['top', 'bottom', 'left', 'right'];
16815
+ for (const edge of edges) {
16816
+ const value = borderToCss(pick(edge), resolve);
16817
+ if (value) {
16818
+ const key = `border${edge[0].toUpperCase()}${edge.slice(1)}`;
16819
+ css[key] = value;
16820
+ applied = true;
16821
+ }
16822
+ }
16823
+ return applied ? css : undefined;
16158
16824
  }
16825
+
16159
16826
  // ---------------------------------------------------------------------------
16160
- // Theme colour helpers (tint / shade) — mirrors React viewer/utils/theme.ts
16827
+ // Theme colour helpers (tint / shade) — mirrors React viewer/utils/theme.ts
16161
16828
  // ---------------------------------------------------------------------------
16162
16829
  /** Parse a 6-digit hex colour (`#RRGGBB` or `RRGGBB`) into RGB components. */
16163
16830
  function hexToRgb$2(hex) {
@@ -16173,50 +16840,114 @@ function rgbToHex$1(r, g, b) {
16173
16840
  const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
16174
16841
  return `#${clamp(r).toString(16).padStart(2, '0').toUpperCase()}${clamp(g).toString(16).padStart(2, '0').toUpperCase()}${clamp(b).toString(16).padStart(2, '0').toUpperCase()}`;
16175
16842
  }
16176
- /**
16177
- * Compute a tinted (lighter) version of a colour.
16178
- * `tintFactor` is 0–1 where 1 = white and 0 = original.
16179
- */
16843
+ /** Compute a tinted (lighter) colour. `tintFactor` in 0-1 (1 = white). */
16180
16844
  function tintColor$1(hex, tintFactor) {
16181
16845
  const { r, g, b } = hexToRgb$2(hex);
16182
16846
  return rgbToHex$1(r + (255 - r) * tintFactor, g + (255 - g) * tintFactor, b + (255 - b) * tintFactor);
16183
16847
  }
16184
- /**
16185
- * Compute a shaded (darker) version of a colour.
16186
- * `shadeFactor` is 0–1 where 1 = black and 0 = original.
16187
- */
16848
+ /** Compute a shaded (darker) colour. `shadeFactor` in 0-1 (1 = black). */
16188
16849
  function shadeColor$1(hex, shadeFactor) {
16189
16850
  const { r, g, b } = hexToRgb$2(hex);
16190
16851
  return rgbToHex$1(r * (1 - shadeFactor), g * (1 - shadeFactor), b * (1 - shadeFactor));
16191
16852
  }
16853
+ /** Apply a fill's tint/shade to a resolved base hex colour. */
16854
+ function applyTintShade(base, fill) {
16855
+ let color = base;
16856
+ if (fill.tint !== undefined && fill.tint > 0) {
16857
+ color = tintColor$1(color, fill.tint / 100_000);
16858
+ }
16859
+ if (fill.shade !== undefined && fill.shade > 0) {
16860
+ color = shadeColor$1(color, 1 - fill.shade / 100_000);
16861
+ }
16862
+ return color;
16863
+ }
16192
16864
  /**
16193
- * Resolve a {@link ParsedTableStyleFill} to a CSS hex colour using the
16194
- * supplied colour scheme. Returns `undefined` when the scheme colour key
16195
- * cannot be found.
16865
+ * Resolve a {@link ParsedTableStyleFill} to a concrete CSS hex colour using the
16866
+ * supplied colour scheme. Honours a scheme colour first, then an explicit sRGB
16867
+ * `color`. Returns `undefined` when neither resolves (e.g. gradient/pattern/no
16868
+ * fill, or a scheme key absent from the theme).
16196
16869
  */
16197
16870
  function resolveStyleFillColor(fill, colorScheme) {
16198
16871
  if (!fill) {
16199
16872
  return undefined;
16200
16873
  }
16201
- if (!colorScheme) {
16202
- return undefined;
16874
+ let base;
16875
+ if (fill.schemeColor && colorScheme) {
16876
+ base = colorScheme[fill.schemeColor];
16877
+ }
16878
+ if (!base && fill.color) {
16879
+ base = fill.color;
16203
16880
  }
16204
- const base = colorScheme[fill.schemeColor];
16205
16881
  if (!base) {
16206
16882
  return undefined;
16207
16883
  }
16208
- let color = base;
16209
- if (fill.tint !== undefined && fill.tint > 0) {
16210
- color = tintColor$1(color, fill.tint / 100_000);
16884
+ return applyTintShade(base, fill);
16885
+ }
16886
+ /** Clear every background-related key so a higher layer replaces cleanly. */
16887
+ function clearBackground(css) {
16888
+ delete css.background;
16889
+ delete css.backgroundColor;
16890
+ delete css.backgroundImage;
16891
+ }
16892
+ /** Build a CSS gradient string from a parsed table-style gradient fill. */
16893
+ function gradientCssFromFill(fill, colorScheme) {
16894
+ const gradient = fill.gradient;
16895
+ if (!gradient || gradient.stops.length === 0) {
16896
+ return undefined;
16211
16897
  }
16212
- if (fill.shade !== undefined && fill.shade > 0) {
16213
- color = shadeColor$1(color, 1 - fill.shade / 100_000);
16898
+ const shapeLike = {
16899
+ fillMode: 'gradient',
16900
+ fillGradientType: gradient.type,
16901
+ fillGradientAngle: gradient.angle ?? 90,
16902
+ fillGradientStops: gradient.stops.map((stop) => ({
16903
+ color: resolveStyleFillColor(stop.fill, colorScheme) ?? stop.fill.color ?? '#000000',
16904
+ position: stop.position,
16905
+ })),
16906
+ };
16907
+ return buildGradientCss(shapeLike);
16908
+ }
16909
+ /**
16910
+ * Apply a {@link ParsedTableStyleFill} onto a {@link TableCellCss} background.
16911
+ * Handles no-fill (transparent), gradient, preset pattern, and solid (scheme or
16912
+ * sRGB) fills. When the fill is absent or unresolvable, falls back to
16913
+ * `fallback` as a solid background colour. Returns `true` when anything was set.
16914
+ */
16915
+ function applyStyleFill(fill, colorScheme, css, fallback) {
16916
+ if (fill?.noFill) {
16917
+ clearBackground(css);
16918
+ css.backgroundColor = 'transparent';
16919
+ return true;
16214
16920
  }
16215
- return color;
16921
+ if (fill?.gradient) {
16922
+ const gradientCss = gradientCssFromFill(fill, colorScheme);
16923
+ if (gradientCss) {
16924
+ clearBackground(css);
16925
+ css.background = gradientCss;
16926
+ return true;
16927
+ }
16928
+ }
16929
+ if (fill?.pattern) {
16930
+ const fg = normalizeHexColor$1(resolveStyleFillColor(fill.pattern.foreground, colorScheme), '#000000');
16931
+ const bg = normalizeHexColor$1(resolveStyleFillColor(fill.pattern.background, colorScheme), '#ffffff');
16932
+ const svg = getPatternSvg$1(fill.pattern.preset, fg, bg);
16933
+ clearBackground(css);
16934
+ if (svg) {
16935
+ css.backgroundImage = `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
16936
+ }
16937
+ css.backgroundColor = bg;
16938
+ return true;
16939
+ }
16940
+ const color = resolveStyleFillColor(fill, colorScheme) ?? fallback;
16941
+ if (color) {
16942
+ clearBackground(css);
16943
+ css.backgroundColor = color;
16944
+ return true;
16945
+ }
16946
+ return false;
16216
16947
  }
16217
16948
  /**
16218
16949
  * Apply text properties from a {@link ParsedTableStyleText} entry into a
16219
- * {@link TableCellCss} object. Returns `true` when any property was set.
16950
+ * {@link TableCellCss} object. Returns `true` when any property was set.
16220
16951
  */
16221
16952
  function applyStyleText(text, colorScheme, css) {
16222
16953
  if (!text) {
@@ -16231,6 +16962,14 @@ function applyStyleText(text, colorScheme, css) {
16231
16962
  css.fontStyle = 'italic';
16232
16963
  applied = true;
16233
16964
  }
16965
+ if (text.underline) {
16966
+ css.textDecorationLine = 'underline';
16967
+ applied = true;
16968
+ }
16969
+ if (text.fontFace) {
16970
+ css.fontFamily = text.fontFace;
16971
+ applied = true;
16972
+ }
16234
16973
  if (text.fontSchemeColor && colorScheme) {
16235
16974
  const base = colorScheme[text.fontSchemeColor];
16236
16975
  if (base) {
@@ -16245,8 +16984,72 @@ function applyStyleText(text, colorScheme, css) {
16245
16984
  applied = true;
16246
16985
  }
16247
16986
  }
16987
+ else if (text.fontColor) {
16988
+ css.color = text.fontColor;
16989
+ applied = true;
16990
+ }
16248
16991
  return applied;
16249
16992
  }
16993
+
16994
+ /**
16995
+ * Convert a {@link CellTextRun}'s per-run formatting to an inline CSS style
16996
+ * object suitable for a `<span>` element.
16997
+ */
16998
+ function cellRunStyle$1(run) {
16999
+ const css = {};
17000
+ if (run.fontFamily) {
17001
+ css.fontFamily = run.fontFamily;
17002
+ }
17003
+ if (typeof run.fontSize === 'number') {
17004
+ css.fontSize = `${run.fontSize}pt`;
17005
+ }
17006
+ if (run.color) {
17007
+ css.color = run.color;
17008
+ }
17009
+ if (run.bold) {
17010
+ css.fontWeight = 'bold';
17011
+ }
17012
+ if (run.italic) {
17013
+ css.fontStyle = 'italic';
17014
+ }
17015
+ const deco = [];
17016
+ if (run.underline) {
17017
+ deco.push('underline');
17018
+ }
17019
+ if (run.strikethrough) {
17020
+ deco.push('line-through');
17021
+ }
17022
+ if (deco.length > 0) {
17023
+ css.textDecoration = deco.join(' ');
17024
+ }
17025
+ return css;
17026
+ }
17027
+ /**
17028
+ * Resolve a preset pattern fill from a {@link PptxTableCellStyle} to CSS
17029
+ * background properties. Mirrors the pattern-fill branch of the React
17030
+ * `cellStyleToCss` but returns the two properties separately so callers
17031
+ * can apply them individually (e.g. Vue's `:style` binding).
17032
+ *
17033
+ * Returns `null` when the cell style carries no pattern fill.
17034
+ */
17035
+ function cellPatternFillCss(style) {
17036
+ if (style.fillMode !== 'pattern' || !style.patternFillPreset) {
17037
+ return null;
17038
+ }
17039
+ const fg = normalizeHexColor$1(style.patternFillForeground, '#000000');
17040
+ const bg = normalizeHexColor$1(style.patternFillBackground, '#ffffff');
17041
+ const svgMarkup = getPatternSvg$1(style.patternFillPreset, fg, bg);
17042
+ if (svgMarkup) {
17043
+ const encoded = encodeURIComponent(svgMarkup);
17044
+ return {
17045
+ backgroundImage: `url("data:image/svg+xml,${encoded}")`,
17046
+ backgroundColor: bg,
17047
+ };
17048
+ }
17049
+ // Unknown preset — fall back to solid background colour.
17050
+ const fallback = style.patternFillBackground ?? style.backgroundColor;
17051
+ return fallback ? { backgroundColor: fallback } : null;
17052
+ }
16250
17053
  /**
16251
17054
  * Look up the table style entry for a style GUID, trying both the raw value
16252
17055
  * and the braced-upper-case normalisation that OOXML uses.
@@ -16475,9 +17278,7 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16475
17278
  let applied = false;
16476
17279
  // ── Whole-table fill (lowest priority layer). ──
16477
17280
  if (styleEntry?.wholeTblFill) {
16478
- const wholeBg = resolveFill(styleEntry.wholeTblFill, '');
16479
- if (wholeBg) {
16480
- style.backgroundColor = wholeBg;
17281
+ if (applyStyleFill(styleEntry.wholeTblFill, colorScheme, style, '')) {
16481
17282
  applied = true;
16482
17283
  }
16483
17284
  }
@@ -16492,16 +17293,12 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16492
17293
  const rowCycle = Math.max(tableData.bandRowCycle ?? 1, 1);
16493
17294
  const bandGroup = Math.floor(bandIndex / rowCycle) % 2;
16494
17295
  if (bandGroup === 0) {
16495
- style.backgroundColor = resolveFill(styleEntry?.band1HFill, 'rgba(217, 226, 243, 0.5)');
16496
- if (applyStyleText(styleEntry?.band1HText, colorScheme, style)) {
16497
- applied = true;
16498
- }
17296
+ applyStyleFill(styleEntry?.band1HFill, colorScheme, style, 'rgba(217, 226, 243, 0.5)');
17297
+ applyStyleText(styleEntry?.band1HText, colorScheme, style);
16499
17298
  applied = true;
16500
17299
  }
16501
17300
  else if (styleEntry?.band2HFill) {
16502
- const band2Bg = resolveFill(styleEntry.band2HFill, '');
16503
- if (band2Bg) {
16504
- style.backgroundColor = band2Bg;
17301
+ if (applyStyleFill(styleEntry.band2HFill, colorScheme, style, '')) {
16505
17302
  applyStyleText(styleEntry.band2HText, colorScheme, style);
16506
17303
  applied = true;
16507
17304
  }
@@ -16516,21 +17313,19 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16516
17313
  if (!skipCol) {
16517
17314
  const colCycle = Math.max(tableData.bandColCycle ?? 1, 1);
16518
17315
  const colBandGroup = Math.floor(colBandIndex / colCycle) % 2;
17316
+ // Column banding yields to row banding when both apply to this cell.
17317
+ const canOverride = !style.backgroundColor || !tableData.bandedRows;
16519
17318
  if (colBandGroup === 0) {
16520
- if (!style.backgroundColor || !tableData.bandedRows) {
16521
- style.backgroundColor = resolveFill(styleEntry?.band1VFill, 'rgba(217, 226, 243, 0.35)');
17319
+ if (canOverride) {
17320
+ applyStyleFill(styleEntry?.band1VFill, colorScheme, style, 'rgba(217, 226, 243, 0.35)');
16522
17321
  applyStyleText(styleEntry?.band1VText, colorScheme, style);
16523
17322
  applied = true;
16524
17323
  }
16525
17324
  }
16526
- else if (styleEntry?.band2VFill) {
16527
- if (!style.backgroundColor || !tableData.bandedRows) {
16528
- const band2Bg = resolveFill(styleEntry.band2VFill, '');
16529
- if (band2Bg) {
16530
- style.backgroundColor = band2Bg;
16531
- applyStyleText(styleEntry.band2VText, colorScheme, style);
16532
- applied = true;
16533
- }
17325
+ else if (styleEntry?.band2VFill && canOverride) {
17326
+ if (applyStyleFill(styleEntry.band2VFill, colorScheme, style, '')) {
17327
+ applyStyleText(styleEntry.band2VText, colorScheme, style);
17328
+ applied = true;
16534
17329
  }
16535
17330
  }
16536
17331
  }
@@ -16538,7 +17333,7 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16538
17333
  // ── Header row (first row). ──
16539
17334
  if (tableData.firstRowHeader && rowIndex === 0) {
16540
17335
  style.fontWeight = 700;
16541
- style.backgroundColor = resolveFill(styleEntry?.firstRowFill, 'rgba(68, 114, 196, 0.85)');
17336
+ applyStyleFill(styleEntry?.firstRowFill, colorScheme, style, 'rgba(68, 114, 196, 0.85)');
16542
17337
  style.color = '#ffffff';
16543
17338
  applyStyleText(styleEntry?.firstRowText, colorScheme, style);
16544
17339
  applied = true;
@@ -16547,10 +17342,7 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16547
17342
  if (tableData.lastRow && rowIndex === rowCount - 1) {
16548
17343
  style.fontWeight = 700;
16549
17344
  if (styleEntry?.lastRowFill) {
16550
- const lastRowBg = resolveFill(styleEntry.lastRowFill, '');
16551
- if (lastRowBg) {
16552
- style.backgroundColor = lastRowBg;
16553
- }
17345
+ applyStyleFill(styleEntry.lastRowFill, colorScheme, style, '');
16554
17346
  }
16555
17347
  const borderColor = resolveFill(styleEntry?.firstRowFill, 'rgba(68, 114, 196, 0.7)');
16556
17348
  style.borderTop = `2px solid ${borderColor}`;
@@ -16561,10 +17353,7 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16561
17353
  if (tableData.firstCol && cellIndex === 0) {
16562
17354
  style.fontWeight = 700;
16563
17355
  if (styleEntry?.firstColFill) {
16564
- const firstColBg = resolveFill(styleEntry.firstColFill, '');
16565
- if (firstColBg) {
16566
- style.backgroundColor = firstColBg;
16567
- }
17356
+ applyStyleFill(styleEntry.firstColFill, colorScheme, style, '');
16568
17357
  }
16569
17358
  applyStyleText(styleEntry?.firstColText, colorScheme, style);
16570
17359
  applied = true;
@@ -16573,14 +17362,56 @@ function getTableCellBandStyle(tableData, rowIndex, cellIndex, rowCount, columnC
16573
17362
  if (tableData.lastCol && cellIndex === columnCount - 1) {
16574
17363
  style.fontWeight = 700;
16575
17364
  if (styleEntry?.lastColFill) {
16576
- const lastColBg = resolveFill(styleEntry.lastColFill, '');
16577
- if (lastColBg) {
16578
- style.backgroundColor = lastColBg;
16579
- }
17365
+ applyStyleFill(styleEntry.lastColFill, colorScheme, style, '');
16580
17366
  }
16581
17367
  applyStyleText(styleEntry?.lastColText, colorScheme, style);
16582
17368
  applied = true;
16583
17369
  }
17370
+ // ── Corner cells (highest fill/text precedence, issue #95). ──
17371
+ // Each corner overrides the intersection of a first/last row with a
17372
+ // first/last column (CT_TableStyle, ECMA-376 §21.1.3.16): nw = top-left,
17373
+ // ne = top-right, sw = bottom-left, se = bottom-right. Only applies when
17374
+ // both the row and column emphasis are active for this cell.
17375
+ if (styleEntry) {
17376
+ const atTop = Boolean(tableData.firstRowHeader) && rowIndex === 0;
17377
+ const atBottom = Boolean(tableData.lastRow) && rowIndex === rowCount - 1;
17378
+ const atLeft = Boolean(tableData.firstCol) && cellIndex === 0;
17379
+ const atRight = Boolean(tableData.lastCol) && cellIndex === columnCount - 1;
17380
+ let cornerFill;
17381
+ let cornerText = undefined;
17382
+ if (atTop && atLeft) {
17383
+ cornerFill = styleEntry.nwCellFill;
17384
+ cornerText = styleEntry.nwCellText;
17385
+ }
17386
+ else if (atTop && atRight) {
17387
+ cornerFill = styleEntry.neCellFill;
17388
+ cornerText = styleEntry.neCellText;
17389
+ }
17390
+ else if (atBottom && atLeft) {
17391
+ cornerFill = styleEntry.swCellFill;
17392
+ cornerText = styleEntry.swCellText;
17393
+ }
17394
+ else if (atBottom && atRight) {
17395
+ cornerFill = styleEntry.seCellFill;
17396
+ cornerText = styleEntry.seCellText;
17397
+ }
17398
+ if (cornerFill && applyStyleFill(cornerFill, colorScheme, style, '')) {
17399
+ applied = true;
17400
+ }
17401
+ if (applyStyleText(cornerText, colorScheme, style)) {
17402
+ applied = true;
17403
+ }
17404
+ }
17405
+ // ── Table-style borders (issue #71). ──
17406
+ // Resolve gridlines/edges the cell inherits from the table style and let
17407
+ // them supersede the hardcoded total-row line above. Per-cell explicit
17408
+ // `a:lnX` borders (parsed into the cell style) are applied on top of this
17409
+ // layer by the renderer, so they still win.
17410
+ const borderCss = resolveCellBorderCss(styleEntry, tableData, { rowIndex, cellIndex, rowCount, columnCount }, (fill) => resolveStyleFillColor(fill, colorScheme));
17411
+ if (borderCss) {
17412
+ Object.assign(style, borderCss);
17413
+ applied = true;
17414
+ }
16584
17415
  return applied ? style : undefined;
16585
17416
  }
16586
17417
 
@@ -23761,6 +24592,9 @@ function waypointsToPathD(waypoints) {
23761
24592
  /**
23762
24593
  * Compute connection sites for a rectangular bounding box. Returns the four
23763
24594
  * edge midpoints in element-local coordinates: top, right, bottom, left.
24595
+ *
24596
+ * This is the fallback used for shapes whose real connection sites are
24597
+ * unknown (preset shapes without a parsed `a:cxnLst`).
23764
24598
  */
23765
24599
  function getConnectionSites(width, height) {
23766
24600
  return [
@@ -23770,6 +24604,36 @@ function getConnectionSites(width, height) {
23770
24604
  { x: 0, y: height / 2, index: 3 }, // left center
23771
24605
  ];
23772
24606
  }
24607
+ /**
24608
+ * Resolve the connection sites of a shape element in element-local pixel
24609
+ * coordinates.
24610
+ *
24611
+ * When the shape carries typed custom-geometry connection sites (parsed from
24612
+ * `a:custGeom/a:cxnLst/a:cxn`), each `a:pos` formula is evaluated against the
24613
+ * shape's path coordinate space and scaled to the element's pixel box, so a
24614
+ * connector referencing `stCxn/@idx` on a non-rectangular shape attaches near
24615
+ * the real site rather than collapsing to an edge midpoint. Shapes with no
24616
+ * known sites fall back to the four edge midpoints.
24617
+ */
24618
+ function getShapeConnectionSites(shape) {
24619
+ const geo = shape;
24620
+ const cxn = geo.customGeometryConnectionSites;
24621
+ if (!cxn || cxn.length === 0) {
24622
+ return getConnectionSites(shape.width, shape.height);
24623
+ }
24624
+ // Path coordinate space the `a:pos` formulas are expressed in. Fall back to
24625
+ // the element's pixel dimensions (scale factor 1) when unavailable.
24626
+ const pathW = geo.pathWidth && geo.pathWidth > 0 ? geo.pathWidth : shape.width;
24627
+ const pathH = geo.pathHeight && geo.pathHeight > 0 ? geo.pathHeight : shape.height;
24628
+ const vars = createBuiltinVariables({ w: pathW, h: pathH });
24629
+ const scaleX = pathW > 0 ? shape.width / pathW : 1;
24630
+ const scaleY = pathH > 0 ? shape.height / pathH : 1;
24631
+ return cxn.map((site, index) => ({
24632
+ x: resolveCoordinate(site.posX, vars) * scaleX,
24633
+ y: resolveCoordinate(site.posY, vars) * scaleY,
24634
+ index,
24635
+ }));
24636
+ }
23773
24637
  /**
23774
24638
  * Find all connectors on the slide that reference any of the given element IDs
23775
24639
  * via `connectorStartConnection`/`connectorEndConnection`, and recalculate
@@ -23829,7 +24693,7 @@ function computeConnectorGeometry(connector, startConn, endConn, elementMap) {
23829
24693
  if (!startShape) {
23830
24694
  return null;
23831
24695
  }
23832
- const sites = getConnectionSites(startShape.width, startShape.height);
24696
+ const sites = getShapeConnectionSites(startShape);
23833
24697
  const siteIndex = startConn.connectionSiteIndex ?? 0;
23834
24698
  const site = sites[siteIndex] ?? sites[0];
23835
24699
  sx = startShape.x + site.x;
@@ -23847,7 +24711,7 @@ function computeConnectorGeometry(connector, startConn, endConn, elementMap) {
23847
24711
  if (!endShape) {
23848
24712
  return null;
23849
24713
  }
23850
- const sites = getConnectionSites(endShape.width, endShape.height);
24714
+ const sites = getShapeConnectionSites(endShape);
23851
24715
  const siteIndex = endConn.connectionSiteIndex ?? 0;
23852
24716
  const site = sites[siteIndex] ?? sites[0];
23853
24717
  ex = endShape.x + site.x;
@@ -23857,12 +24721,25 @@ function computeConnectorGeometry(connector, startConn, endConn, elementMap) {
23857
24721
  ex = connector.x + connector.width;
23858
24722
  ey = connector.y + connector.height;
23859
24723
  }
24724
+ // Recompute the flip flags from the relative order of the resolved
24725
+ // endpoints. `getConnectorPathGeometry` derives the start/end corners
24726
+ // (and hence line direction / arrowheads) from these flags:
24727
+ // flipH => start at local x=width, end at x=0
24728
+ // flipV => start at local y=height, end at y=0
24729
+ // Since the bounding box is anchored at min(sx,ex)/min(sy,ey), a start that
24730
+ // sits to the right of / below the end must be drawn from the far corner,
24731
+ // i.e. flipH when ex < sx and flipV when ey < sy. Equal coordinates keep the
24732
+ // flag false so a purely vertical / horizontal line is not spuriously flipped.
24733
+ const flipHorizontal = ex < sx;
24734
+ const flipVertical = ey < sy;
23860
24735
  return {
23861
24736
  id: connector.id,
23862
24737
  x: Math.min(sx, ex),
23863
24738
  y: Math.min(sy, ey),
23864
24739
  width: Math.abs(ex - sx) || 1,
23865
24740
  height: Math.abs(ey - sy) || 1,
24741
+ flipHorizontal,
24742
+ flipVertical,
23866
24743
  };
23867
24744
  }
23868
24745
  /**
@@ -23883,13 +24760,23 @@ function applyReroutedConnectors(elements, rerouted) {
23883
24760
  if (!update) {
23884
24761
  return el;
23885
24762
  }
23886
- return {
24763
+ const next = {
23887
24764
  ...el,
23888
24765
  x: update.x,
23889
24766
  y: update.y,
23890
24767
  width: update.width,
23891
24768
  height: update.height,
23892
24769
  };
24770
+ // Only overwrite the flip flags when the reroute recomputed them, so
24771
+ // callers that build ReroutedConnector without flip data (e.g. tests or
24772
+ // non-endpoint updates) leave the connector's existing flags intact.
24773
+ if (update.flipHorizontal !== undefined) {
24774
+ next.flipHorizontal = update.flipHorizontal;
24775
+ }
24776
+ if (update.flipVertical !== undefined) {
24777
+ next.flipVertical = update.flipVertical;
24778
+ }
24779
+ return next;
23893
24780
  });
23894
24781
  }
23895
24782
 
@@ -27706,160 +28593,481 @@ const DEFAULT_TRANSITION_DURATION_MS$1 = 1000;
27706
28593
  const EASE = 'ease-in-out';
27707
28594
 
27708
28595
  /**
27709
- * `slide-transition-keyframes` — the full `@keyframes` block backing every
27710
- * resolved slide transition. Inject it once per overlay via a `<style>` element
27711
- * (the same pattern the element-animation engine uses for
27712
- * `ANIMATION_KEYFRAMES_CSS`).
27713
- *
27714
- * Keyframe names are `pptx-tr-*`-prefixed so they never collide with the
27715
- * element-animation keyframes (`pptx-vue-*`) or the native-animation timeline
27716
- * keyframes (`pptx-tl-*`).
28596
+ * `p14-transition-keyframes` — CSS `@keyframes` for the 18 additional Office
28597
+ * 2010 (`p14` namespace) slide transitions, complementing the core
28598
+ * `slide-transition-keyframes`. All names share the `pptx-tr-*` prefix.
27717
28599
  *
27718
- * Both {@link SLIDE_TRANSITION_KEYFRAMES} and the `*_CSS` alias point at the
27719
- * same string the bindings historically exported it under both names, so the
27720
- * alias preserves their public surface.
28600
+ * Split into two string blocks purely to keep individual definitions grouped;
28601
+ * {@link P14_TRANSITION_KEYFRAMES_ALL} concatenates both for one-shot injection,
28602
+ * while {@link P14_TRANSITION_KEYFRAMES} / {@link P14_TRANSITION_KEYFRAMES_2}
28603
+ * preserve the React binding's original two-part public surface.
27721
28604
  *
27722
- * @module render/slide-transition-keyframes
28605
+ * @module render/p14-transition-keyframes
27723
28606
  */
27724
- const SLIDE_TRANSITION_KEYFRAMES = `
27725
- /* ── Fade ───────────────────────────────────────────────────────────── */
27726
- @keyframes pptx-tr-fade-in {
27727
- from { opacity: 0; }
27728
- to { opacity: 1; }
28607
+ /** Part 1: conveyor / doors / ferris / flash / flythrough / gallery / glitter / honeycomb / pan. */
28608
+ const P14_TRANSITION_KEYFRAMES = `
28609
+ /* ── Conveyor (translate X with staggered timing) ──────────────────── */
28610
+ @keyframes pptx-tr-conveyor-in-from-right {
28611
+ from { transform: translateX(100%) rotateY(-30deg); }
28612
+ 60% { transform: translateX(20%) rotateY(-10deg); }
28613
+ to { transform: translateX(0) rotateY(0deg); }
27729
28614
  }
27730
- @keyframes pptx-tr-fade-out {
28615
+ @keyframes pptx-tr-conveyor-out-to-left {
28616
+ from { transform: translateX(0) rotateY(0deg); }
28617
+ 40% { transform: translateX(-20%) rotateY(10deg); }
28618
+ to { transform: translateX(-100%) rotateY(30deg); }
28619
+ }
28620
+ @keyframes pptx-tr-conveyor-in-from-left {
28621
+ from { transform: translateX(-100%) rotateY(30deg); }
28622
+ 60% { transform: translateX(-20%) rotateY(10deg); }
28623
+ to { transform: translateX(0) rotateY(0deg); }
28624
+ }
28625
+ @keyframes pptx-tr-conveyor-out-to-right {
28626
+ from { transform: translateX(0) rotateY(0deg); }
28627
+ 40% { transform: translateX(20%) rotateY(-10deg); }
28628
+ to { transform: translateX(100%) rotateY(-30deg); }
28629
+ }
28630
+
28631
+ /* ── Doors (clip-path from center split) ──────────────────────────── */
28632
+ @keyframes pptx-tr-doors-horz {
28633
+ from { clip-path: inset(0 50%); }
28634
+ to { clip-path: inset(0 0); }
28635
+ }
28636
+ @keyframes pptx-tr-doors-vert {
28637
+ from { clip-path: inset(50% 0); }
28638
+ to { clip-path: inset(0 0); }
28639
+ }
28640
+
28641
+ /* ── Ferris (rotate elements around center) ───────────────────────── */
28642
+ @keyframes pptx-tr-ferris-in-from-right {
28643
+ from { transform: translateX(80%) rotate(45deg) scale(0.6); opacity: 0; }
28644
+ to { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28645
+ }
28646
+ @keyframes pptx-tr-ferris-out-to-left {
28647
+ from { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28648
+ to { transform: translateX(-80%) rotate(-45deg) scale(0.6); opacity: 0; }
28649
+ }
28650
+ @keyframes pptx-tr-ferris-in-from-left {
28651
+ from { transform: translateX(-80%) rotate(-45deg) scale(0.6); opacity: 0; }
28652
+ to { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28653
+ }
28654
+ @keyframes pptx-tr-ferris-out-to-right {
28655
+ from { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28656
+ to { transform: translateX(80%) rotate(45deg) scale(0.6); opacity: 0; }
28657
+ }
28658
+
28659
+ /* ── Flash (bright flash opacity burst) ──────────────────────────── */
28660
+ @keyframes pptx-tr-flash-white {
28661
+ 0% { opacity: 1; }
28662
+ 30% { opacity: 0; }
28663
+ 50% { opacity: 0; }
28664
+ 100% { opacity: 1; }
28665
+ }
28666
+ @keyframes pptx-tr-flash-in {
28667
+ 0% { opacity: 0; }
28668
+ 50% { opacity: 0; }
28669
+ 70% { opacity: 1; }
28670
+ 100% { opacity: 1; }
28671
+ }
28672
+
28673
+ /* ── Flythrough (scale + translate Z-axis feel) ──────────────────── */
28674
+ @keyframes pptx-tr-flythrough-in {
28675
+ from { transform: scale(4) translateZ(200px); opacity: 0; filter: blur(8px); }
28676
+ to { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28677
+ }
28678
+ @keyframes pptx-tr-flythrough-out {
28679
+ from { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28680
+ to { transform: scale(0.1) translateZ(-200px); opacity: 0; filter: blur(8px); }
28681
+ }
28682
+ @keyframes pptx-tr-flythrough-reverse-in {
28683
+ from { transform: scale(0.1) translateZ(-200px); opacity: 0; filter: blur(8px); }
28684
+ to { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28685
+ }
28686
+ @keyframes pptx-tr-flythrough-reverse-out {
28687
+ from { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28688
+ to { transform: scale(4) translateZ(200px); opacity: 0; filter: blur(8px); }
28689
+ }
28690
+
28691
+ /* ── Gallery (translate with perspective) ─────────────────────────── */
28692
+ @keyframes pptx-tr-gallery-in-from-right {
28693
+ from { transform: perspective(800px) translateX(100%) rotateY(-45deg); opacity: 0.5; }
28694
+ to { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28695
+ }
28696
+ @keyframes pptx-tr-gallery-out-to-left {
28697
+ from { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28698
+ to { transform: perspective(800px) translateX(-100%) rotateY(45deg); opacity: 0.5; }
28699
+ }
28700
+ @keyframes pptx-tr-gallery-in-from-left {
28701
+ from { transform: perspective(800px) translateX(-100%) rotateY(45deg); opacity: 0.5; }
28702
+ to { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28703
+ }
28704
+ @keyframes pptx-tr-gallery-out-to-right {
28705
+ from { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28706
+ to { transform: perspective(800px) translateX(100%) rotateY(-45deg); opacity: 0.5; }
28707
+ }
28708
+
28709
+ /* ── Glitter (particle dissolve effect) ──────────────────────────── */
28710
+ @keyframes pptx-tr-glitter-in {
28711
+ from { opacity: 0; filter: brightness(1.5) contrast(1.3) blur(2px); }
28712
+ 60% { opacity: 0.7; filter: brightness(1.2) contrast(1.1) blur(1px); }
28713
+ to { opacity: 1; filter: brightness(1) contrast(1) blur(0); }
28714
+ }
28715
+
28716
+ /* ── Honeycomb (hexagonal reveal) ────────────────────────────────── */
28717
+ @keyframes pptx-tr-honeycomb-in {
28718
+ from {
28719
+ clip-path: polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%);
28720
+ opacity: 0;
28721
+ }
28722
+ to {
28723
+ clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
28724
+ opacity: 1;
28725
+ }
28726
+ }
28727
+ @keyframes pptx-tr-honeycomb-out {
27731
28728
  from { opacity: 1; }
27732
- to { opacity: 0; }
28729
+ to { opacity: 0; filter: blur(2px); }
27733
28730
  }
27734
28731
 
27735
- /* ── Push ───────────────────────────────────────────────────────────── */
27736
- @keyframes pptx-tr-push-in-from-right {
28732
+ /* ── Pan (large-scale translate) ─────────────────────────────────── */
28733
+ @keyframes pptx-tr-pan-from-right {
27737
28734
  from { transform: translateX(100%); }
27738
28735
  to { transform: translateX(0); }
27739
28736
  }
27740
- @keyframes pptx-tr-push-out-to-left {
28737
+ @keyframes pptx-tr-pan-to-left {
27741
28738
  from { transform: translateX(0); }
27742
28739
  to { transform: translateX(-100%); }
27743
28740
  }
27744
- @keyframes pptx-tr-push-in-from-left {
28741
+ @keyframes pptx-tr-pan-from-left {
27745
28742
  from { transform: translateX(-100%); }
27746
28743
  to { transform: translateX(0); }
27747
28744
  }
27748
- @keyframes pptx-tr-push-out-to-right {
28745
+ @keyframes pptx-tr-pan-to-right {
27749
28746
  from { transform: translateX(0); }
27750
28747
  to { transform: translateX(100%); }
27751
28748
  }
27752
- @keyframes pptx-tr-push-in-from-bottom {
28749
+ @keyframes pptx-tr-pan-from-bottom {
27753
28750
  from { transform: translateY(100%); }
27754
28751
  to { transform: translateY(0); }
27755
28752
  }
27756
- @keyframes pptx-tr-push-out-to-top {
28753
+ @keyframes pptx-tr-pan-to-top {
27757
28754
  from { transform: translateY(0); }
27758
28755
  to { transform: translateY(-100%); }
27759
28756
  }
27760
- @keyframes pptx-tr-push-in-from-top {
28757
+ @keyframes pptx-tr-pan-from-top {
27761
28758
  from { transform: translateY(-100%); }
27762
28759
  to { transform: translateY(0); }
27763
28760
  }
27764
- @keyframes pptx-tr-push-out-to-bottom {
28761
+ @keyframes pptx-tr-pan-to-bottom {
27765
28762
  from { transform: translateY(0); }
27766
28763
  to { transform: translateY(100%); }
27767
28764
  }
27768
-
27769
- /* ── Cover (incoming slides over stationary outgoing) ───────────────── */
27770
- @keyframes pptx-tr-cover-from-right {
27771
- from { transform: translateX(100%); }
27772
- to { transform: translateX(0); }
28765
+ `;
28766
+ /** Part 2: prism / reveal / ripple / shred / switch / vortex / warp / wheelReverse / window. */
28767
+ const P14_TRANSITION_KEYFRAMES_2 = `
28768
+ /* ── Prism (3D rotation via perspective transform) ───────────────── */
28769
+ @keyframes pptx-tr-prism-in-from-right {
28770
+ from { transform: perspective(800px) rotateY(-90deg) translateX(50%); opacity: 0; }
28771
+ to { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
27773
28772
  }
27774
- @keyframes pptx-tr-cover-from-left {
27775
- from { transform: translateX(-100%); }
27776
- to { transform: translateX(0); }
28773
+ @keyframes pptx-tr-prism-out-to-left {
28774
+ from { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
28775
+ to { transform: perspective(800px) rotateY(90deg) translateX(-50%); opacity: 0; }
27777
28776
  }
27778
- @keyframes pptx-tr-cover-from-bottom {
27779
- from { transform: translateY(100%); }
27780
- to { transform: translateY(0); }
28777
+ @keyframes pptx-tr-prism-in-from-left {
28778
+ from { transform: perspective(800px) rotateY(90deg) translateX(-50%); opacity: 0; }
28779
+ to { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
27781
28780
  }
27782
- @keyframes pptx-tr-cover-from-top {
27783
- from { transform: translateY(-100%); }
27784
- to { transform: translateY(0); }
28781
+ @keyframes pptx-tr-prism-out-to-right {
28782
+ from { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
28783
+ to { transform: perspective(800px) rotateY(-90deg) translateX(50%); opacity: 0; }
27785
28784
  }
27786
- @keyframes pptx-tr-cover-from-lu {
27787
- from { transform: translate(-100%, -100%); }
27788
- to { transform: translate(0, 0); }
28785
+ @keyframes pptx-tr-prism-in-from-bottom {
28786
+ from { transform: perspective(800px) rotateX(90deg) translateY(50%); opacity: 0; }
28787
+ to { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
27789
28788
  }
27790
- @keyframes pptx-tr-cover-from-ld {
27791
- from { transform: translate(-100%, 100%); }
27792
- to { transform: translate(0, 0); }
28789
+ @keyframes pptx-tr-prism-out-to-top {
28790
+ from { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
28791
+ to { transform: perspective(800px) rotateX(-90deg) translateY(-50%); opacity: 0; }
27793
28792
  }
27794
- @keyframes pptx-tr-cover-from-ru {
27795
- from { transform: translate(100%, -100%); }
27796
- to { transform: translate(0, 0); }
28793
+ @keyframes pptx-tr-prism-in-from-top {
28794
+ from { transform: perspective(800px) rotateX(-90deg) translateY(-50%); opacity: 0; }
28795
+ to { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
27797
28796
  }
27798
- @keyframes pptx-tr-cover-from-rd {
27799
- from { transform: translate(100%, 100%); }
27800
- to { transform: translate(0, 0); }
28797
+ @keyframes pptx-tr-prism-out-to-bottom {
28798
+ from { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
28799
+ to { transform: perspective(800px) rotateX(90deg) translateY(50%); opacity: 0; }
27801
28800
  }
27802
28801
 
27803
- /* ── Uncover (outgoing slides away revealing stationary incoming) ──── */
27804
- @keyframes pptx-tr-uncover-to-left {
27805
- from { transform: translateX(0); }
27806
- to { transform: translateX(-100%); }
27807
- }
27808
- @keyframes pptx-tr-uncover-to-right {
28802
+ /* ── Reveal (slide away reveal) ──────────────────────────────────── */
28803
+ @keyframes pptx-tr-reveal-out-to-right {
27809
28804
  from { transform: translateX(0); }
27810
28805
  to { transform: translateX(100%); }
27811
28806
  }
27812
- @keyframes pptx-tr-uncover-to-top {
27813
- from { transform: translateY(0); }
27814
- to { transform: translateY(-100%); }
28807
+ @keyframes pptx-tr-reveal-out-to-left {
28808
+ from { transform: translateX(0); }
28809
+ to { transform: translateX(-100%); }
27815
28810
  }
27816
- @keyframes pptx-tr-uncover-to-bottom {
27817
- from { transform: translateY(0); }
27818
- to { transform: translateY(100%); }
28811
+ @keyframes pptx-tr-reveal-in {
28812
+ from { opacity: 0.5; }
28813
+ to { opacity: 1; }
27819
28814
  }
27820
- @keyframes pptx-tr-uncover-to-lu {
27821
- from { transform: translate(0, 0); }
27822
- to { transform: translate(-100%, -100%); }
28815
+
28816
+ /* ── Ripple (expanding ring clip-path) ───────────────────────────── */
28817
+ @keyframes pptx-tr-ripple-in {
28818
+ from { clip-path: circle(0% at 50% 50%); opacity: 0.5; }
28819
+ 30% { clip-path: circle(20% at 50% 50%); opacity: 0.7; }
28820
+ 60% { clip-path: circle(50% at 50% 50%); opacity: 0.9; }
28821
+ to { clip-path: circle(75% at 50% 50%); opacity: 1; }
27823
28822
  }
27824
- @keyframes pptx-tr-uncover-to-ld {
27825
- from { transform: translate(0, 0); }
27826
- to { transform: translate(-100%, 100%); }
28823
+
28824
+ /* ── Shred (fragmented clip-path pieces) ─────────────────────────── */
28825
+ @keyframes pptx-tr-shred-strips-in {
28826
+ from { clip-path: inset(0 100% 0 0); opacity: 0; }
28827
+ 30% { clip-path: inset(0 60% 0 0); opacity: 0.5; }
28828
+ to { clip-path: inset(0); opacity: 1; }
27827
28829
  }
27828
- @keyframes pptx-tr-uncover-to-ru {
27829
- from { transform: translate(0, 0); }
27830
- to { transform: translate(100%, -100%); }
28830
+ @keyframes pptx-tr-shred-rectangles-in {
28831
+ from { clip-path: inset(50%); opacity: 0; }
28832
+ 40% { clip-path: inset(20%); opacity: 0.6; }
28833
+ to { clip-path: inset(0); opacity: 1; }
27831
28834
  }
27832
- @keyframes pptx-tr-uncover-to-rd {
27833
- from { transform: translate(0, 0); }
27834
- to { transform: translate(100%, 100%); }
28835
+ @keyframes pptx-tr-shred-out {
28836
+ from { opacity: 1; }
28837
+ to { opacity: 0; filter: blur(2px); }
27835
28838
  }
27836
28839
 
27837
- /* ── Wipe (clip-path reveal) ────────────────────────────────────────── */
27838
- @keyframes pptx-tr-wipe-from-left {
27839
- from { clip-path: inset(0 100% 0 0); }
27840
- to { clip-path: inset(0 0 0 0); }
28840
+ /* ── Switch (flip/rotate swap) ───────────────────────────────────── */
28841
+ @keyframes pptx-tr-switch-in-from-right {
28842
+ from { transform: perspective(800px) rotateY(-180deg); opacity: 0; }
28843
+ to { transform: perspective(800px) rotateY(0deg); opacity: 1; }
27841
28844
  }
27842
- @keyframes pptx-tr-wipe-from-right {
27843
- from { clip-path: inset(0 0 0 100%); }
27844
- to { clip-path: inset(0 0 0 0); }
28845
+ @keyframes pptx-tr-switch-out-to-left {
28846
+ from { transform: perspective(800px) rotateY(0deg); opacity: 1; }
28847
+ to { transform: perspective(800px) rotateY(180deg); opacity: 0; }
27845
28848
  }
27846
- @keyframes pptx-tr-wipe-from-top {
27847
- from { clip-path: inset(0 0 100% 0); }
27848
- to { clip-path: inset(0 0 0 0); }
28849
+ @keyframes pptx-tr-switch-in-from-left {
28850
+ from { transform: perspective(800px) rotateY(180deg); opacity: 0; }
28851
+ to { transform: perspective(800px) rotateY(0deg); opacity: 1; }
27849
28852
  }
27850
- @keyframes pptx-tr-wipe-from-bottom {
27851
- from { clip-path: inset(100% 0 0 0); }
27852
- to { clip-path: inset(0 0 0 0); }
28853
+ @keyframes pptx-tr-switch-out-to-right {
28854
+ from { transform: perspective(800px) rotateY(0deg); opacity: 1; }
28855
+ to { transform: perspective(800px) rotateY(-180deg); opacity: 0; }
27853
28856
  }
27854
28857
 
27855
- /* ── Split ──────────────────────────────────────────────────────────── */
27856
- @keyframes pptx-tr-split-h-out {
27857
- from { clip-path: inset(0 50%); }
27858
- to { clip-path: inset(0 0); }
28858
+ /* ── Vortex (rotate + scale spiral) ──────────────────────────────── */
28859
+ @keyframes pptx-tr-vortex-in {
28860
+ from { transform: rotate(720deg) scale(0); opacity: 0; }
28861
+ to { transform: rotate(0deg) scale(1); opacity: 1; }
27859
28862
  }
27860
- @keyframes pptx-tr-split-v-out {
27861
- from { clip-path: inset(50% 0); }
27862
- to { clip-path: inset(0 0); }
28863
+ @keyframes pptx-tr-vortex-out {
28864
+ from { transform: rotate(0deg) scale(1); opacity: 1; }
28865
+ to { transform: rotate(-720deg) scale(0); opacity: 0; }
28866
+ }
28867
+
28868
+ /* ── Warp (skew distortion) ──────────────────────────────────────── */
28869
+ @keyframes pptx-tr-warp-in {
28870
+ from { transform: scale(0.3) skewX(30deg) skewY(15deg); opacity: 0; }
28871
+ 50% { transform: scale(0.8) skewX(-5deg) skewY(-3deg); opacity: 0.7; }
28872
+ to { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; }
28873
+ }
28874
+ @keyframes pptx-tr-warp-out {
28875
+ from { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; }
28876
+ 50% { transform: scale(0.8) skewX(5deg) skewY(3deg); opacity: 0.7; }
28877
+ to { transform: scale(0.3) skewX(-30deg) skewY(-15deg); opacity: 0; }
28878
+ }
28879
+ @keyframes pptx-tr-warp-reverse-in {
28880
+ from { transform: scale(3) skewX(-20deg) skewY(-10deg); opacity: 0; filter: blur(4px); }
28881
+ to { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; filter: blur(0); }
28882
+ }
28883
+ @keyframes pptx-tr-warp-reverse-out {
28884
+ from { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; filter: blur(0); }
28885
+ to { transform: scale(3) skewX(20deg) skewY(10deg); opacity: 0; filter: blur(4px); }
28886
+ }
28887
+
28888
+ /* ── WheelReverse (reverse wheel rotation) ───────────────────────── */
28889
+ @keyframes pptx-tr-wheel-reverse-in {
28890
+ from { clip-path: circle(0% at 50% 50%); transform: rotate(180deg); }
28891
+ to { clip-path: circle(75% at 50% 50%); transform: rotate(0deg); }
28892
+ }
28893
+
28894
+ /* ── Window (scale from center with border) ──────────────────────── */
28895
+ @keyframes pptx-tr-window-horz {
28896
+ from { clip-path: inset(0 50%); }
28897
+ to { clip-path: inset(0 0); }
28898
+ }
28899
+ @keyframes pptx-tr-window-vert {
28900
+ from { clip-path: inset(50% 0); }
28901
+ to { clip-path: inset(0 0); }
28902
+ }
28903
+ @keyframes pptx-tr-window-out {
28904
+ from { opacity: 1; transform: scale(1); }
28905
+ to { opacity: 0; transform: scale(0.9); }
28906
+ }
28907
+ `;
28908
+ /** Both p14 keyframe blocks concatenated, for single `<style>` injection. */
28909
+ const P14_TRANSITION_KEYFRAMES_ALL = `${P14_TRANSITION_KEYFRAMES}\n${P14_TRANSITION_KEYFRAMES_2}`;
28910
+
28911
+ /**
28912
+ * `slide-transition-keyframes` — the full `@keyframes` block backing every
28913
+ * resolved slide transition. Inject it once per overlay via a `<style>` element
28914
+ * (the same pattern the element-animation engine uses for
28915
+ * `ANIMATION_KEYFRAMES_CSS`).
28916
+ *
28917
+ * Keyframe names are `pptx-tr-*`-prefixed so they never collide with the
28918
+ * element-animation keyframes (`pptx-vue-*`) or the native-animation timeline
28919
+ * keyframes (`pptx-tl-*`).
28920
+ *
28921
+ * Both {@link SLIDE_TRANSITION_KEYFRAMES} and the `*_CSS` alias point at the
28922
+ * same string — the bindings historically exported it under both names, so the
28923
+ * alias preserves their public surface.
28924
+ *
28925
+ * The faithful Office 2010 (`p14`) keyframes are appended (via
28926
+ * {@link P14_TRANSITION_KEYFRAMES_ALL}) so the exotic / 3-D transitions routed
28927
+ * through `getP14TransitionAnimations` animate wherever this block is injected,
28928
+ * with no per-binding wiring.
28929
+ *
28930
+ * @module render/slide-transition-keyframes
28931
+ */
28932
+ const CORE_SLIDE_TRANSITION_KEYFRAMES = `
28933
+ /* ── Fade ───────────────────────────────────────────────────────────── */
28934
+ @keyframes pptx-tr-fade-in {
28935
+ from { opacity: 0; }
28936
+ to { opacity: 1; }
28937
+ }
28938
+ @keyframes pptx-tr-fade-out {
28939
+ from { opacity: 1; }
28940
+ to { opacity: 0; }
28941
+ }
28942
+
28943
+ /* ── Push ───────────────────────────────────────────────────────────── */
28944
+ @keyframes pptx-tr-push-in-from-right {
28945
+ from { transform: translateX(100%); }
28946
+ to { transform: translateX(0); }
28947
+ }
28948
+ @keyframes pptx-tr-push-out-to-left {
28949
+ from { transform: translateX(0); }
28950
+ to { transform: translateX(-100%); }
28951
+ }
28952
+ @keyframes pptx-tr-push-in-from-left {
28953
+ from { transform: translateX(-100%); }
28954
+ to { transform: translateX(0); }
28955
+ }
28956
+ @keyframes pptx-tr-push-out-to-right {
28957
+ from { transform: translateX(0); }
28958
+ to { transform: translateX(100%); }
28959
+ }
28960
+ @keyframes pptx-tr-push-in-from-bottom {
28961
+ from { transform: translateY(100%); }
28962
+ to { transform: translateY(0); }
28963
+ }
28964
+ @keyframes pptx-tr-push-out-to-top {
28965
+ from { transform: translateY(0); }
28966
+ to { transform: translateY(-100%); }
28967
+ }
28968
+ @keyframes pptx-tr-push-in-from-top {
28969
+ from { transform: translateY(-100%); }
28970
+ to { transform: translateY(0); }
28971
+ }
28972
+ @keyframes pptx-tr-push-out-to-bottom {
28973
+ from { transform: translateY(0); }
28974
+ to { transform: translateY(100%); }
28975
+ }
28976
+
28977
+ /* ── Cover (incoming slides over stationary outgoing) ───────────────── */
28978
+ @keyframes pptx-tr-cover-from-right {
28979
+ from { transform: translateX(100%); }
28980
+ to { transform: translateX(0); }
28981
+ }
28982
+ @keyframes pptx-tr-cover-from-left {
28983
+ from { transform: translateX(-100%); }
28984
+ to { transform: translateX(0); }
28985
+ }
28986
+ @keyframes pptx-tr-cover-from-bottom {
28987
+ from { transform: translateY(100%); }
28988
+ to { transform: translateY(0); }
28989
+ }
28990
+ @keyframes pptx-tr-cover-from-top {
28991
+ from { transform: translateY(-100%); }
28992
+ to { transform: translateY(0); }
28993
+ }
28994
+ @keyframes pptx-tr-cover-from-lu {
28995
+ from { transform: translate(-100%, -100%); }
28996
+ to { transform: translate(0, 0); }
28997
+ }
28998
+ @keyframes pptx-tr-cover-from-ld {
28999
+ from { transform: translate(-100%, 100%); }
29000
+ to { transform: translate(0, 0); }
29001
+ }
29002
+ @keyframes pptx-tr-cover-from-ru {
29003
+ from { transform: translate(100%, -100%); }
29004
+ to { transform: translate(0, 0); }
29005
+ }
29006
+ @keyframes pptx-tr-cover-from-rd {
29007
+ from { transform: translate(100%, 100%); }
29008
+ to { transform: translate(0, 0); }
29009
+ }
29010
+
29011
+ /* ── Uncover (outgoing slides away revealing stationary incoming) ──── */
29012
+ @keyframes pptx-tr-uncover-to-left {
29013
+ from { transform: translateX(0); }
29014
+ to { transform: translateX(-100%); }
29015
+ }
29016
+ @keyframes pptx-tr-uncover-to-right {
29017
+ from { transform: translateX(0); }
29018
+ to { transform: translateX(100%); }
29019
+ }
29020
+ @keyframes pptx-tr-uncover-to-top {
29021
+ from { transform: translateY(0); }
29022
+ to { transform: translateY(-100%); }
29023
+ }
29024
+ @keyframes pptx-tr-uncover-to-bottom {
29025
+ from { transform: translateY(0); }
29026
+ to { transform: translateY(100%); }
29027
+ }
29028
+ @keyframes pptx-tr-uncover-to-lu {
29029
+ from { transform: translate(0, 0); }
29030
+ to { transform: translate(-100%, -100%); }
29031
+ }
29032
+ @keyframes pptx-tr-uncover-to-ld {
29033
+ from { transform: translate(0, 0); }
29034
+ to { transform: translate(-100%, 100%); }
29035
+ }
29036
+ @keyframes pptx-tr-uncover-to-ru {
29037
+ from { transform: translate(0, 0); }
29038
+ to { transform: translate(100%, -100%); }
29039
+ }
29040
+ @keyframes pptx-tr-uncover-to-rd {
29041
+ from { transform: translate(0, 0); }
29042
+ to { transform: translate(100%, 100%); }
29043
+ }
29044
+
29045
+ /* ── Wipe (clip-path reveal) ────────────────────────────────────────── */
29046
+ @keyframes pptx-tr-wipe-from-left {
29047
+ from { clip-path: inset(0 100% 0 0); }
29048
+ to { clip-path: inset(0 0 0 0); }
29049
+ }
29050
+ @keyframes pptx-tr-wipe-from-right {
29051
+ from { clip-path: inset(0 0 0 100%); }
29052
+ to { clip-path: inset(0 0 0 0); }
29053
+ }
29054
+ @keyframes pptx-tr-wipe-from-top {
29055
+ from { clip-path: inset(0 0 100% 0); }
29056
+ to { clip-path: inset(0 0 0 0); }
29057
+ }
29058
+ @keyframes pptx-tr-wipe-from-bottom {
29059
+ from { clip-path: inset(100% 0 0 0); }
29060
+ to { clip-path: inset(0 0 0 0); }
29061
+ }
29062
+
29063
+ /* ── Split ──────────────────────────────────────────────────────────── */
29064
+ @keyframes pptx-tr-split-h-out {
29065
+ from { clip-path: inset(0 50%); }
29066
+ to { clip-path: inset(0 0); }
29067
+ }
29068
+ @keyframes pptx-tr-split-v-out {
29069
+ from { clip-path: inset(50% 0); }
29070
+ to { clip-path: inset(0 0); }
27863
29071
  }
27864
29072
  @keyframes pptx-tr-split-h-in {
27865
29073
  from { clip-path: inset(0 0); }
@@ -27984,641 +29192,18 @@ const SLIDE_TRANSITION_KEYFRAMES = `
27984
29192
  to { clip-path: circle(75% at 50% 50%); transform: rotate(0deg); }
27985
29193
  }
27986
29194
  `;
29195
+ /**
29196
+ * The full slide-transition `@keyframes` block: the classic set plus the
29197
+ * faithful Office 2010 (`p14`) keyframes, so both the classic and exotic / 3-D
29198
+ * transitions animate wherever this single string is injected.
29199
+ */
29200
+ const SLIDE_TRANSITION_KEYFRAMES = `${CORE_SLIDE_TRANSITION_KEYFRAMES}\n${P14_TRANSITION_KEYFRAMES_ALL}`;
27987
29201
  /**
27988
29202
  * Alias of {@link SLIDE_TRANSITION_KEYFRAMES} under the `_CSS` name the Vue
27989
29203
  * binding historically exported.
27990
29204
  */
27991
29205
  const SLIDE_TRANSITION_KEYFRAMES_CSS = SLIDE_TRANSITION_KEYFRAMES;
27992
29206
 
27993
- /**
27994
- * `slide-transition-css` — pure mapping from a {@link PptxSlideTransition}
27995
- * (type + duration + direction/orientation) to the CSS `animation` shorthand
27996
- * pieces used by each binding's presentation-mode transition overlay during a
27997
- * slide change.
27998
- *
27999
- * Consolidates the React `transition-resolver` / `transition-helpers` /
28000
- * `transition-keyframes` trio, the Vue `slide-transition-css`, and the Angular
28001
- * `transition-helpers` into one framework-agnostic module:
28002
- *
28003
- * - {@link getSlideTransitionAnimations} resolves a transition to the
28004
- * `animation` shorthand strings for the outgoing (old) and incoming (new)
28005
- * slide layers, plus whether the outgoing layer sits on top.
28006
- * - the `@keyframes` referenced live in `slide-transition-keyframes`
28007
- * (`SLIDE_TRANSITION_KEYFRAMES`), injected once via a `<style>` element.
28008
- *
28009
- * Superset note: the exotic / 3-D transition family (`pan`/`gallery`/
28010
- * `conveyor`/`reveal`/`doors`/`switch`/`flythrough`/`ferris`/`prism`/`ripple`/
28011
- * `honeycomb`/`glitter`/`shred`/`flash`) is approximated with the closest
28012
- * well-tested 2-D case (originally an Angular-only refinement) rather than a
28013
- * plain fade. Truly-unknown types still fall back to a symmetrical cross-fade.
28014
- * A more faithful per-effect p14 keyframe set is available separately via
28015
- * `p14-transition-keyframes` + {@link getP14TransitionAnimations}.
28016
- *
28017
- * Everything here is pure and unit-testable: no framework imports, no DOM.
28018
- *
28019
- * @module render/slide-transition-css
28020
- */
28021
- /**
28022
- * Map a {@link PptxTransitionType} (+ duration/direction/orient/spokes) to the
28023
- * concrete CSS `animation` descriptors for the outgoing and incoming layers.
28024
- *
28025
- * Unknown types fall back to a symmetrical cross-fade.
28026
- */
28027
- function getSlideTransitionAnimations(type, durationMs, direction, orient, spokes) {
28028
- const dur = `${durationMs}ms`;
28029
- // `spokes` is reserved for future wheel spoke-count support; forwarded only
28030
- // through recursive calls (random/pull/exotic aliases).
28031
- void spokes;
28032
- switch (type) {
28033
- case 'none':
28034
- case 'cut':
28035
- return INSTANT;
28036
- case 'fade':
28037
- return {
28038
- outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
28039
- incoming: `pptx-tr-fade-in ${dur} ${EASE} forwards`,
28040
- outgoingOnTop: true,
28041
- };
28042
- case 'push': {
28043
- const dir = resolveDirection(direction, 'left');
28044
- const configs = {
28045
- left: {
28046
- outgoing: `pptx-tr-push-out-to-left ${dur} ${EASE} forwards`,
28047
- incoming: `pptx-tr-push-in-from-right ${dur} ${EASE} forwards`,
28048
- outgoingOnTop: false,
28049
- },
28050
- right: {
28051
- outgoing: `pptx-tr-push-out-to-right ${dur} ${EASE} forwards`,
28052
- incoming: `pptx-tr-push-in-from-left ${dur} ${EASE} forwards`,
28053
- outgoingOnTop: false,
28054
- },
28055
- up: {
28056
- outgoing: `pptx-tr-push-out-to-top ${dur} ${EASE} forwards`,
28057
- incoming: `pptx-tr-push-in-from-bottom ${dur} ${EASE} forwards`,
28058
- outgoingOnTop: false,
28059
- },
28060
- down: {
28061
- outgoing: `pptx-tr-push-out-to-bottom ${dur} ${EASE} forwards`,
28062
- incoming: `pptx-tr-push-in-from-top ${dur} ${EASE} forwards`,
28063
- outgoingOnTop: false,
28064
- },
28065
- };
28066
- return configs[dir];
28067
- }
28068
- case 'wipe': {
28069
- const dir = resolveDirection(direction, 'left');
28070
- const wipeNames = {
28071
- left: `pptx-tr-wipe-from-left ${dur} ${EASE} forwards`,
28072
- right: `pptx-tr-wipe-from-right ${dur} ${EASE} forwards`,
28073
- up: `pptx-tr-wipe-from-top ${dur} ${EASE} forwards`,
28074
- down: `pptx-tr-wipe-from-bottom ${dur} ${EASE} forwards`,
28075
- };
28076
- return {
28077
- outgoing: 'none',
28078
- incoming: wipeNames[dir],
28079
- outgoingOnTop: false,
28080
- };
28081
- }
28082
- case 'cover': {
28083
- const dir = resolveDirection8(direction, 'left');
28084
- const coverMap = {
28085
- left: `pptx-tr-cover-from-left ${dur} ${EASE} forwards`,
28086
- right: `pptx-tr-cover-from-right ${dur} ${EASE} forwards`,
28087
- up: `pptx-tr-cover-from-top ${dur} ${EASE} forwards`,
28088
- down: `pptx-tr-cover-from-bottom ${dur} ${EASE} forwards`,
28089
- lu: `pptx-tr-cover-from-lu ${dur} ${EASE} forwards`,
28090
- ld: `pptx-tr-cover-from-ld ${dur} ${EASE} forwards`,
28091
- ru: `pptx-tr-cover-from-ru ${dur} ${EASE} forwards`,
28092
- rd: `pptx-tr-cover-from-rd ${dur} ${EASE} forwards`,
28093
- };
28094
- return {
28095
- outgoing: 'none',
28096
- incoming: coverMap[dir],
28097
- outgoingOnTop: false,
28098
- };
28099
- }
28100
- case 'uncover': {
28101
- const dir = resolveDirection8(direction, 'left');
28102
- const uncoverMap = {
28103
- left: `pptx-tr-uncover-to-left ${dur} ${EASE} forwards`,
28104
- right: `pptx-tr-uncover-to-right ${dur} ${EASE} forwards`,
28105
- up: `pptx-tr-uncover-to-top ${dur} ${EASE} forwards`,
28106
- down: `pptx-tr-uncover-to-bottom ${dur} ${EASE} forwards`,
28107
- lu: `pptx-tr-uncover-to-lu ${dur} ${EASE} forwards`,
28108
- ld: `pptx-tr-uncover-to-ld ${dur} ${EASE} forwards`,
28109
- ru: `pptx-tr-uncover-to-ru ${dur} ${EASE} forwards`,
28110
- rd: `pptx-tr-uncover-to-rd ${dur} ${EASE} forwards`,
28111
- };
28112
- return {
28113
- outgoing: uncoverMap[dir],
28114
- incoming: 'none',
28115
- outgoingOnTop: true,
28116
- };
28117
- }
28118
- case 'split': {
28119
- const o = resolveOrientation(undefined, orient);
28120
- const isOut = direction !== 'in';
28121
- if (isOut) {
28122
- return {
28123
- outgoing: 'none',
28124
- incoming: o === 'vert'
28125
- ? `pptx-tr-split-v-out ${dur} ${EASE} forwards`
28126
- : `pptx-tr-split-h-out ${dur} ${EASE} forwards`,
28127
- outgoingOnTop: false,
28128
- };
28129
- }
28130
- return {
28131
- outgoing: o === 'vert'
28132
- ? `pptx-tr-split-v-in ${dur} ${EASE} forwards`
28133
- : `pptx-tr-split-h-in ${dur} ${EASE} forwards`,
28134
- incoming: 'none',
28135
- outgoingOnTop: true,
28136
- };
28137
- }
28138
- case 'dissolve':
28139
- return {
28140
- outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
28141
- incoming: `pptx-tr-dissolve-in ${dur} ${EASE} forwards`,
28142
- outgoingOnTop: true,
28143
- };
28144
- case 'circle':
28145
- return {
28146
- outgoing: 'none',
28147
- incoming: `pptx-tr-circle-in ${dur} ${EASE} forwards`,
28148
- outgoingOnTop: false,
28149
- };
28150
- case 'diamond':
28151
- return {
28152
- outgoing: 'none',
28153
- incoming: `pptx-tr-diamond-in ${dur} ${EASE} forwards`,
28154
- outgoingOnTop: false,
28155
- };
28156
- case 'plus':
28157
- return {
28158
- outgoing: 'none',
28159
- incoming: `pptx-tr-plus-in ${dur} ${EASE} forwards`,
28160
- outgoingOnTop: false,
28161
- };
28162
- case 'wedge':
28163
- return {
28164
- outgoing: 'none',
28165
- incoming: `pptx-tr-wedge-in ${dur} ${EASE} forwards`,
28166
- outgoingOnTop: false,
28167
- };
28168
- case 'wheel':
28169
- return {
28170
- outgoing: 'none',
28171
- incoming: `pptx-tr-wheel-in ${dur} ${EASE} forwards`,
28172
- outgoingOnTop: false,
28173
- };
28174
- case 'zoom':
28175
- return {
28176
- outgoing: `pptx-tr-zoom-out ${dur} ${EASE} forwards`,
28177
- incoming: `pptx-tr-zoom-in ${dur} ${EASE} forwards`,
28178
- outgoingOnTop: true,
28179
- };
28180
- case 'blinds': {
28181
- const o = resolveOrientation(direction, orient);
28182
- return {
28183
- outgoing: 'none',
28184
- incoming: o === 'vert'
28185
- ? `pptx-tr-blinds-v ${dur} ${EASE} forwards`
28186
- : `pptx-tr-blinds-h ${dur} ${EASE} forwards`,
28187
- outgoingOnTop: false,
28188
- };
28189
- }
28190
- case 'checker':
28191
- return {
28192
- outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
28193
- incoming: `pptx-tr-checker-in ${dur} ${EASE} forwards`,
28194
- outgoingOnTop: true,
28195
- };
28196
- case 'comb': {
28197
- const o = resolveOrientation(direction, orient);
28198
- return {
28199
- outgoing: 'none',
28200
- incoming: o === 'vert'
28201
- ? `pptx-tr-comb-v ${dur} ${EASE} forwards`
28202
- : `pptx-tr-comb-h ${dur} ${EASE} forwards`,
28203
- outgoingOnTop: false,
28204
- };
28205
- }
28206
- case 'strips': {
28207
- const stripDir = direction === 'lu' || direction === 'ld' || direction === 'ru' || direction === 'rd'
28208
- ? direction
28209
- : 'lu';
28210
- return {
28211
- outgoing: 'none',
28212
- incoming: `pptx-tr-strips-${stripDir} ${dur} ${EASE} forwards`,
28213
- outgoingOnTop: false,
28214
- };
28215
- }
28216
- case 'randomBar': {
28217
- const o = resolveOrientation(direction, orient);
28218
- return {
28219
- outgoing: 'none',
28220
- incoming: o === 'vert'
28221
- ? `pptx-tr-randombar-v ${dur} ${EASE} forwards`
28222
- : `pptx-tr-randombar-h ${dur} ${EASE} forwards`,
28223
- outgoingOnTop: false,
28224
- };
28225
- }
28226
- case 'newsflash':
28227
- return {
28228
- outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
28229
- incoming: `pptx-tr-newsflash-in ${dur} ${EASE} forwards`,
28230
- outgoingOnTop: false,
28231
- };
28232
- // `pull` is the directional alias of `uncover`.
28233
- case 'pull':
28234
- return getSlideTransitionAnimations('uncover', durationMs, direction, orient, spokes);
28235
- case 'morph':
28236
- return {
28237
- outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
28238
- incoming: `pptx-tr-fade-in ${dur} ${EASE} forwards`,
28239
- outgoingOnTop: true,
28240
- };
28241
- case 'random': {
28242
- const randomType = RANDOM_ELIGIBLE_TYPES[Math.floor(Math.random() * RANDOM_ELIGIBLE_TYPES.length)];
28243
- return getSlideTransitionAnimations(randomType, durationMs, direction, orient, spokes);
28244
- }
28245
- // ── Exotic / 3-D transitions — approximated with the closest well-tested
28246
- // 2-D CSS behaviour (delegates to an existing case rather than emitting a
28247
- // plain fade). A faithful per-effect keyframe set is available via
28248
- // `getP14TransitionAnimations`.
28249
- case 'pan':
28250
- case 'gallery':
28251
- case 'conveyor':
28252
- return getSlideTransitionAnimations('push', durationMs, direction, orient, spokes);
28253
- case 'reveal':
28254
- return getSlideTransitionAnimations('wipe', durationMs, direction, orient, spokes);
28255
- case 'doors':
28256
- return getSlideTransitionAnimations('split', durationMs, direction, orient, spokes);
28257
- case 'switch':
28258
- case 'flythrough':
28259
- case 'ferris':
28260
- case 'prism':
28261
- return getSlideTransitionAnimations('zoom', durationMs, direction, orient, spokes);
28262
- case 'ripple':
28263
- case 'honeycomb':
28264
- case 'glitter':
28265
- case 'shred':
28266
- return getSlideTransitionAnimations('dissolve', durationMs, direction, orient, spokes);
28267
- case 'flash':
28268
- return getSlideTransitionAnimations('fade', durationMs, direction, orient, spokes);
28269
- // Fallback (unknown / not-yet-mapped type) — symmetrical cross-fade.
28270
- default:
28271
- return {
28272
- outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
28273
- incoming: `pptx-tr-fade-in ${dur} ${EASE} forwards`,
28274
- outgoingOnTop: true,
28275
- };
28276
- }
28277
- }
28278
- /**
28279
- * Convenience wrapper that reads everything off a {@link PptxSlideTransition},
28280
- * applying the React/Vue default duration when none is set. `none`/`cut` and a
28281
- * transition with a non-positive duration resolve to {@link INSTANT}.
28282
- */
28283
- function resolveSlideTransition(transition) {
28284
- if (!transition || transition.type === 'none' || transition.type === 'cut') {
28285
- return INSTANT;
28286
- }
28287
- const durationMs = typeof transition.durationMs === 'number' && transition.durationMs > 0
28288
- ? transition.durationMs
28289
- : DEFAULT_TRANSITION_DURATION_MS$1;
28290
- return getSlideTransitionAnimations(transition.type, durationMs, transition.direction, transition.orient, transition.spokes);
28291
- }
28292
- /**
28293
- * The effective duration (ms) for a transition under the React/Vue policy — the
28294
- * configured `durationMs`, or {@link DEFAULT_TRANSITION_DURATION_MS} when
28295
- * unset/invalid. `none`/`cut` resolve to `0` (instant).
28296
- */
28297
- function resolveTransitionDurationMs(transition) {
28298
- if (!transition || transition.type === 'none' || transition.type === 'cut') {
28299
- return 0;
28300
- }
28301
- return typeof transition.durationMs === 'number' && transition.durationMs > 0
28302
- ? transition.durationMs
28303
- : DEFAULT_TRANSITION_DURATION_MS$1;
28304
- }
28305
-
28306
- /**
28307
- * `p14-transition-keyframes` — CSS `@keyframes` for the 18 additional Office
28308
- * 2010 (`p14` namespace) slide transitions, complementing the core
28309
- * `slide-transition-keyframes`. All names share the `pptx-tr-*` prefix.
28310
- *
28311
- * Split into two string blocks purely to keep individual definitions grouped;
28312
- * {@link P14_TRANSITION_KEYFRAMES_ALL} concatenates both for one-shot injection,
28313
- * while {@link P14_TRANSITION_KEYFRAMES} / {@link P14_TRANSITION_KEYFRAMES_2}
28314
- * preserve the React binding's original two-part public surface.
28315
- *
28316
- * @module render/p14-transition-keyframes
28317
- */
28318
- /** Part 1: conveyor / doors / ferris / flash / flythrough / gallery / glitter / honeycomb / pan. */
28319
- const P14_TRANSITION_KEYFRAMES = `
28320
- /* ── Conveyor (translate X with staggered timing) ──────────────────── */
28321
- @keyframes pptx-tr-conveyor-in-from-right {
28322
- from { transform: translateX(100%) rotateY(-30deg); }
28323
- 60% { transform: translateX(20%) rotateY(-10deg); }
28324
- to { transform: translateX(0) rotateY(0deg); }
28325
- }
28326
- @keyframes pptx-tr-conveyor-out-to-left {
28327
- from { transform: translateX(0) rotateY(0deg); }
28328
- 40% { transform: translateX(-20%) rotateY(10deg); }
28329
- to { transform: translateX(-100%) rotateY(30deg); }
28330
- }
28331
- @keyframes pptx-tr-conveyor-in-from-left {
28332
- from { transform: translateX(-100%) rotateY(30deg); }
28333
- 60% { transform: translateX(-20%) rotateY(10deg); }
28334
- to { transform: translateX(0) rotateY(0deg); }
28335
- }
28336
- @keyframes pptx-tr-conveyor-out-to-right {
28337
- from { transform: translateX(0) rotateY(0deg); }
28338
- 40% { transform: translateX(20%) rotateY(-10deg); }
28339
- to { transform: translateX(100%) rotateY(-30deg); }
28340
- }
28341
-
28342
- /* ── Doors (clip-path from center split) ──────────────────────────── */
28343
- @keyframes pptx-tr-doors-horz {
28344
- from { clip-path: inset(0 50%); }
28345
- to { clip-path: inset(0 0); }
28346
- }
28347
- @keyframes pptx-tr-doors-vert {
28348
- from { clip-path: inset(50% 0); }
28349
- to { clip-path: inset(0 0); }
28350
- }
28351
-
28352
- /* ── Ferris (rotate elements around center) ───────────────────────── */
28353
- @keyframes pptx-tr-ferris-in-from-right {
28354
- from { transform: translateX(80%) rotate(45deg) scale(0.6); opacity: 0; }
28355
- to { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28356
- }
28357
- @keyframes pptx-tr-ferris-out-to-left {
28358
- from { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28359
- to { transform: translateX(-80%) rotate(-45deg) scale(0.6); opacity: 0; }
28360
- }
28361
- @keyframes pptx-tr-ferris-in-from-left {
28362
- from { transform: translateX(-80%) rotate(-45deg) scale(0.6); opacity: 0; }
28363
- to { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28364
- }
28365
- @keyframes pptx-tr-ferris-out-to-right {
28366
- from { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; }
28367
- to { transform: translateX(80%) rotate(45deg) scale(0.6); opacity: 0; }
28368
- }
28369
-
28370
- /* ── Flash (bright flash opacity burst) ──────────────────────────── */
28371
- @keyframes pptx-tr-flash-white {
28372
- 0% { opacity: 1; }
28373
- 30% { opacity: 0; }
28374
- 50% { opacity: 0; }
28375
- 100% { opacity: 1; }
28376
- }
28377
- @keyframes pptx-tr-flash-in {
28378
- 0% { opacity: 0; }
28379
- 50% { opacity: 0; }
28380
- 70% { opacity: 1; }
28381
- 100% { opacity: 1; }
28382
- }
28383
-
28384
- /* ── Flythrough (scale + translate Z-axis feel) ──────────────────── */
28385
- @keyframes pptx-tr-flythrough-in {
28386
- from { transform: scale(4) translateZ(200px); opacity: 0; filter: blur(8px); }
28387
- to { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28388
- }
28389
- @keyframes pptx-tr-flythrough-out {
28390
- from { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28391
- to { transform: scale(0.1) translateZ(-200px); opacity: 0; filter: blur(8px); }
28392
- }
28393
- @keyframes pptx-tr-flythrough-reverse-in {
28394
- from { transform: scale(0.1) translateZ(-200px); opacity: 0; filter: blur(8px); }
28395
- to { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28396
- }
28397
- @keyframes pptx-tr-flythrough-reverse-out {
28398
- from { transform: scale(1) translateZ(0); opacity: 1; filter: blur(0); }
28399
- to { transform: scale(4) translateZ(200px); opacity: 0; filter: blur(8px); }
28400
- }
28401
-
28402
- /* ── Gallery (translate with perspective) ─────────────────────────── */
28403
- @keyframes pptx-tr-gallery-in-from-right {
28404
- from { transform: perspective(800px) translateX(100%) rotateY(-45deg); opacity: 0.5; }
28405
- to { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28406
- }
28407
- @keyframes pptx-tr-gallery-out-to-left {
28408
- from { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28409
- to { transform: perspective(800px) translateX(-100%) rotateY(45deg); opacity: 0.5; }
28410
- }
28411
- @keyframes pptx-tr-gallery-in-from-left {
28412
- from { transform: perspective(800px) translateX(-100%) rotateY(45deg); opacity: 0.5; }
28413
- to { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28414
- }
28415
- @keyframes pptx-tr-gallery-out-to-right {
28416
- from { transform: perspective(800px) translateX(0) rotateY(0deg); opacity: 1; }
28417
- to { transform: perspective(800px) translateX(100%) rotateY(-45deg); opacity: 0.5; }
28418
- }
28419
-
28420
- /* ── Glitter (particle dissolve effect) ──────────────────────────── */
28421
- @keyframes pptx-tr-glitter-in {
28422
- from { opacity: 0; filter: brightness(1.5) contrast(1.3) blur(2px); }
28423
- 60% { opacity: 0.7; filter: brightness(1.2) contrast(1.1) blur(1px); }
28424
- to { opacity: 1; filter: brightness(1) contrast(1) blur(0); }
28425
- }
28426
-
28427
- /* ── Honeycomb (hexagonal reveal) ────────────────────────────────── */
28428
- @keyframes pptx-tr-honeycomb-in {
28429
- from {
28430
- clip-path: polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%);
28431
- opacity: 0;
28432
- }
28433
- to {
28434
- clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
28435
- opacity: 1;
28436
- }
28437
- }
28438
- @keyframes pptx-tr-honeycomb-out {
28439
- from { opacity: 1; }
28440
- to { opacity: 0; filter: blur(2px); }
28441
- }
28442
-
28443
- /* ── Pan (large-scale translate) ─────────────────────────────────── */
28444
- @keyframes pptx-tr-pan-from-right {
28445
- from { transform: translateX(100%); }
28446
- to { transform: translateX(0); }
28447
- }
28448
- @keyframes pptx-tr-pan-to-left {
28449
- from { transform: translateX(0); }
28450
- to { transform: translateX(-100%); }
28451
- }
28452
- @keyframes pptx-tr-pan-from-left {
28453
- from { transform: translateX(-100%); }
28454
- to { transform: translateX(0); }
28455
- }
28456
- @keyframes pptx-tr-pan-to-right {
28457
- from { transform: translateX(0); }
28458
- to { transform: translateX(100%); }
28459
- }
28460
- @keyframes pptx-tr-pan-from-bottom {
28461
- from { transform: translateY(100%); }
28462
- to { transform: translateY(0); }
28463
- }
28464
- @keyframes pptx-tr-pan-to-top {
28465
- from { transform: translateY(0); }
28466
- to { transform: translateY(-100%); }
28467
- }
28468
- @keyframes pptx-tr-pan-from-top {
28469
- from { transform: translateY(-100%); }
28470
- to { transform: translateY(0); }
28471
- }
28472
- @keyframes pptx-tr-pan-to-bottom {
28473
- from { transform: translateY(0); }
28474
- to { transform: translateY(100%); }
28475
- }
28476
- `;
28477
- /** Part 2: prism / reveal / ripple / shred / switch / vortex / warp / wheelReverse / window. */
28478
- const P14_TRANSITION_KEYFRAMES_2 = `
28479
- /* ── Prism (3D rotation via perspective transform) ───────────────── */
28480
- @keyframes pptx-tr-prism-in-from-right {
28481
- from { transform: perspective(800px) rotateY(-90deg) translateX(50%); opacity: 0; }
28482
- to { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
28483
- }
28484
- @keyframes pptx-tr-prism-out-to-left {
28485
- from { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
28486
- to { transform: perspective(800px) rotateY(90deg) translateX(-50%); opacity: 0; }
28487
- }
28488
- @keyframes pptx-tr-prism-in-from-left {
28489
- from { transform: perspective(800px) rotateY(90deg) translateX(-50%); opacity: 0; }
28490
- to { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
28491
- }
28492
- @keyframes pptx-tr-prism-out-to-right {
28493
- from { transform: perspective(800px) rotateY(0deg) translateX(0); opacity: 1; }
28494
- to { transform: perspective(800px) rotateY(-90deg) translateX(50%); opacity: 0; }
28495
- }
28496
- @keyframes pptx-tr-prism-in-from-bottom {
28497
- from { transform: perspective(800px) rotateX(90deg) translateY(50%); opacity: 0; }
28498
- to { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
28499
- }
28500
- @keyframes pptx-tr-prism-out-to-top {
28501
- from { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
28502
- to { transform: perspective(800px) rotateX(-90deg) translateY(-50%); opacity: 0; }
28503
- }
28504
- @keyframes pptx-tr-prism-in-from-top {
28505
- from { transform: perspective(800px) rotateX(-90deg) translateY(-50%); opacity: 0; }
28506
- to { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
28507
- }
28508
- @keyframes pptx-tr-prism-out-to-bottom {
28509
- from { transform: perspective(800px) rotateX(0deg) translateY(0); opacity: 1; }
28510
- to { transform: perspective(800px) rotateX(90deg) translateY(50%); opacity: 0; }
28511
- }
28512
-
28513
- /* ── Reveal (slide away reveal) ──────────────────────────────────── */
28514
- @keyframes pptx-tr-reveal-out-to-right {
28515
- from { transform: translateX(0); }
28516
- to { transform: translateX(100%); }
28517
- }
28518
- @keyframes pptx-tr-reveal-out-to-left {
28519
- from { transform: translateX(0); }
28520
- to { transform: translateX(-100%); }
28521
- }
28522
- @keyframes pptx-tr-reveal-in {
28523
- from { opacity: 0.5; }
28524
- to { opacity: 1; }
28525
- }
28526
-
28527
- /* ── Ripple (expanding ring clip-path) ───────────────────────────── */
28528
- @keyframes pptx-tr-ripple-in {
28529
- from { clip-path: circle(0% at 50% 50%); opacity: 0.5; }
28530
- 30% { clip-path: circle(20% at 50% 50%); opacity: 0.7; }
28531
- 60% { clip-path: circle(50% at 50% 50%); opacity: 0.9; }
28532
- to { clip-path: circle(75% at 50% 50%); opacity: 1; }
28533
- }
28534
-
28535
- /* ── Shred (fragmented clip-path pieces) ─────────────────────────── */
28536
- @keyframes pptx-tr-shred-strips-in {
28537
- from { clip-path: inset(0 100% 0 0); opacity: 0; }
28538
- 30% { clip-path: inset(0 60% 0 0); opacity: 0.5; }
28539
- to { clip-path: inset(0); opacity: 1; }
28540
- }
28541
- @keyframes pptx-tr-shred-rectangles-in {
28542
- from { clip-path: inset(50%); opacity: 0; }
28543
- 40% { clip-path: inset(20%); opacity: 0.6; }
28544
- to { clip-path: inset(0); opacity: 1; }
28545
- }
28546
- @keyframes pptx-tr-shred-out {
28547
- from { opacity: 1; }
28548
- to { opacity: 0; filter: blur(2px); }
28549
- }
28550
-
28551
- /* ── Switch (flip/rotate swap) ───────────────────────────────────── */
28552
- @keyframes pptx-tr-switch-in-from-right {
28553
- from { transform: perspective(800px) rotateY(-180deg); opacity: 0; }
28554
- to { transform: perspective(800px) rotateY(0deg); opacity: 1; }
28555
- }
28556
- @keyframes pptx-tr-switch-out-to-left {
28557
- from { transform: perspective(800px) rotateY(0deg); opacity: 1; }
28558
- to { transform: perspective(800px) rotateY(180deg); opacity: 0; }
28559
- }
28560
- @keyframes pptx-tr-switch-in-from-left {
28561
- from { transform: perspective(800px) rotateY(180deg); opacity: 0; }
28562
- to { transform: perspective(800px) rotateY(0deg); opacity: 1; }
28563
- }
28564
- @keyframes pptx-tr-switch-out-to-right {
28565
- from { transform: perspective(800px) rotateY(0deg); opacity: 1; }
28566
- to { transform: perspective(800px) rotateY(-180deg); opacity: 0; }
28567
- }
28568
-
28569
- /* ── Vortex (rotate + scale spiral) ──────────────────────────────── */
28570
- @keyframes pptx-tr-vortex-in {
28571
- from { transform: rotate(720deg) scale(0); opacity: 0; }
28572
- to { transform: rotate(0deg) scale(1); opacity: 1; }
28573
- }
28574
- @keyframes pptx-tr-vortex-out {
28575
- from { transform: rotate(0deg) scale(1); opacity: 1; }
28576
- to { transform: rotate(-720deg) scale(0); opacity: 0; }
28577
- }
28578
-
28579
- /* ── Warp (skew distortion) ──────────────────────────────────────── */
28580
- @keyframes pptx-tr-warp-in {
28581
- from { transform: scale(0.3) skewX(30deg) skewY(15deg); opacity: 0; }
28582
- 50% { transform: scale(0.8) skewX(-5deg) skewY(-3deg); opacity: 0.7; }
28583
- to { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; }
28584
- }
28585
- @keyframes pptx-tr-warp-out {
28586
- from { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; }
28587
- 50% { transform: scale(0.8) skewX(5deg) skewY(3deg); opacity: 0.7; }
28588
- to { transform: scale(0.3) skewX(-30deg) skewY(-15deg); opacity: 0; }
28589
- }
28590
- @keyframes pptx-tr-warp-reverse-in {
28591
- from { transform: scale(3) skewX(-20deg) skewY(-10deg); opacity: 0; filter: blur(4px); }
28592
- to { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; filter: blur(0); }
28593
- }
28594
- @keyframes pptx-tr-warp-reverse-out {
28595
- from { transform: scale(1) skewX(0deg) skewY(0deg); opacity: 1; filter: blur(0); }
28596
- to { transform: scale(3) skewX(20deg) skewY(10deg); opacity: 0; filter: blur(4px); }
28597
- }
28598
-
28599
- /* ── WheelReverse (reverse wheel rotation) ───────────────────────── */
28600
- @keyframes pptx-tr-wheel-reverse-in {
28601
- from { clip-path: circle(0% at 50% 50%); transform: rotate(180deg); }
28602
- to { clip-path: circle(75% at 50% 50%); transform: rotate(0deg); }
28603
- }
28604
-
28605
- /* ── Window (scale from center with border) ──────────────────────── */
28606
- @keyframes pptx-tr-window-horz {
28607
- from { clip-path: inset(0 50%); }
28608
- to { clip-path: inset(0 0); }
28609
- }
28610
- @keyframes pptx-tr-window-vert {
28611
- from { clip-path: inset(50% 0); }
28612
- to { clip-path: inset(0 0); }
28613
- }
28614
- @keyframes pptx-tr-window-out {
28615
- from { opacity: 1; transform: scale(1); }
28616
- to { opacity: 0; transform: scale(0.9); }
28617
- }
28618
- `;
28619
- /** Both p14 keyframe blocks concatenated, for single `<style>` injection. */
28620
- const P14_TRANSITION_KEYFRAMES_ALL = `${P14_TRANSITION_KEYFRAMES}\n${P14_TRANSITION_KEYFRAMES_2}`;
28621
-
28622
29207
  /**
28623
29208
  * `p14-transition-css` — animation resolver for the Office 2010 (`p14`
28624
29209
  * namespace) slide transitions. Maps each p14 transition type to the faithful
@@ -28663,7 +29248,10 @@ function resolveCardinal(direction) {
28663
29248
  return 'left';
28664
29249
  }
28665
29250
  }
28666
- /** Build a directional transition pair given a per-direction keyframe prefix. */
29251
+ /**
29252
+ * Build a directional pair using the bare `to-X` / `from-Y` suffixes (the `pan`
29253
+ * keyframes use this naming).
29254
+ */
28667
29255
  function directionalPair(direction, prefix, dur, ease) {
28668
29256
  const dir = resolveCardinal(direction);
28669
29257
  const opposites = {
@@ -28679,6 +29267,25 @@ function directionalPair(direction, prefix, dur, ease) {
28679
29267
  outgoingOnTop: false,
28680
29268
  };
28681
29269
  }
29270
+ /**
29271
+ * Build a directional pair using the explicit `out-to-X` / `in-from-Y` suffixes
29272
+ * (the `prism` keyframes use this naming, matching conveyor/switch/gallery).
29273
+ */
29274
+ function cardinalOutInPair(direction, prefix, dur, ease) {
29275
+ const dir = resolveCardinal(direction);
29276
+ const map = {
29277
+ left: ['out-to-left', 'in-from-right'],
29278
+ right: ['out-to-right', 'in-from-left'],
29279
+ up: ['out-to-top', 'in-from-bottom'],
29280
+ down: ['out-to-bottom', 'in-from-top'],
29281
+ };
29282
+ const [outSuffix, inSuffix] = map[dir];
29283
+ return {
29284
+ outgoing: `${prefix}-${outSuffix} ${dur} ${ease} forwards`,
29285
+ incoming: `${prefix}-${inSuffix} ${dur} ${ease} forwards`,
29286
+ outgoingOnTop: false,
29287
+ };
29288
+ }
28682
29289
  /**
28683
29290
  * Return CSS animation descriptors for p14 transition types. Returns
28684
29291
  * `undefined` when the type is not a p14 transition.
@@ -28778,7 +29385,7 @@ function getP14TransitionAnimations(type, durationMs, direction, orient) {
28778
29385
  case 'pan':
28779
29386
  return directionalPair(direction, 'pptx-tr-pan', dur, ease);
28780
29387
  case 'prism':
28781
- return directionalPair(direction, 'pptx-tr-prism', dur, ease);
29388
+ return cardinalOutInPair(direction, 'pptx-tr-prism', dur, ease);
28782
29389
  case 'reveal': {
28783
29390
  const lr = resolveLR(direction);
28784
29391
  return {
@@ -28862,6 +29469,314 @@ function getP14TransitionAnimations(type, durationMs, direction, orient) {
28862
29469
  }
28863
29470
  }
28864
29471
 
29472
+ /**
29473
+ * `slide-transition-css` — pure mapping from a {@link PptxSlideTransition}
29474
+ * (type + duration + direction/orientation) to the CSS `animation` shorthand
29475
+ * pieces used by each binding's presentation-mode transition overlay during a
29476
+ * slide change.
29477
+ *
29478
+ * Consolidates the React `transition-resolver` / `transition-helpers` /
29479
+ * `transition-keyframes` trio, the Vue `slide-transition-css`, and the Angular
29480
+ * `transition-helpers` into one framework-agnostic module:
29481
+ *
29482
+ * - {@link getSlideTransitionAnimations} resolves a transition to the
29483
+ * `animation` shorthand strings for the outgoing (old) and incoming (new)
29484
+ * slide layers, plus whether the outgoing layer sits on top.
29485
+ * - the `@keyframes` referenced live in `slide-transition-keyframes`
29486
+ * (`SLIDE_TRANSITION_KEYFRAMES`), injected once via a `<style>` element.
29487
+ *
29488
+ * Superset note: the exotic / 3-D transition family (`pan`/`gallery`/
29489
+ * `conveyor`/`reveal`/`doors`/`switch`/`flythrough`/`ferris`/`prism`/`ripple`/
29490
+ * `honeycomb`/`glitter`/`shred`/`flash`/`vortex`/`warp`/`wheelReverse`/`window`)
29491
+ * is rendered faithfully by delegating to {@link getP14TransitionAnimations}
29492
+ * (its `@keyframes` live in `p14-transition-keyframes` and are folded into
29493
+ * `SLIDE_TRANSITION_KEYFRAMES`). The classic set and the newer p15 cinematic
29494
+ * types with no dedicated keyframes (`cube`/`flip`/`rotate`/`orbit`/`fracture`/
29495
+ * `peelOff`/`pageCurl*`/`airplane`/`origami`, etc.) fall back to a symmetrical
29496
+ * cross-fade.
29497
+ *
29498
+ * Everything here is pure and unit-testable: no framework imports, no DOM.
29499
+ *
29500
+ * @module render/slide-transition-css
29501
+ */
29502
+ /**
29503
+ * Map a {@link PptxTransitionType} (+ duration/direction/orient/spokes) to the
29504
+ * concrete CSS `animation` descriptors for the outgoing and incoming layers.
29505
+ *
29506
+ * Unknown types fall back to a symmetrical cross-fade.
29507
+ */
29508
+ function getSlideTransitionAnimations(type, durationMs, direction, orient, spokes) {
29509
+ const dur = `${durationMs}ms`;
29510
+ // `spokes` is reserved for future wheel spoke-count support; forwarded only
29511
+ // through recursive calls (random / pull aliases).
29512
+ void spokes;
29513
+ // Prefer the faithful Office 2010 (p14) keyframe set for the exotic / 3-D
29514
+ // transition family (conveyor/doors/ferris/flash/flythrough/gallery/glitter/
29515
+ // honeycomb/pan/prism/reveal/ripple/shred/switch/vortex/warp/wheelReverse/
29516
+ // window). Returns `undefined` for the classic set, which falls through to
29517
+ // the 2-D CSS cases below. The p14 `@keyframes` are folded into
29518
+ // `SLIDE_TRANSITION_KEYFRAMES`, so every binding that injects that block
29519
+ // animates these faithfully with no per-binding wiring.
29520
+ const p14 = getP14TransitionAnimations(type, durationMs, direction, orient);
29521
+ if (p14) {
29522
+ return p14;
29523
+ }
29524
+ switch (type) {
29525
+ case 'none':
29526
+ case 'cut':
29527
+ return INSTANT;
29528
+ case 'fade':
29529
+ return {
29530
+ outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
29531
+ incoming: `pptx-tr-fade-in ${dur} ${EASE} forwards`,
29532
+ outgoingOnTop: true,
29533
+ };
29534
+ case 'push': {
29535
+ const dir = resolveDirection(direction, 'left');
29536
+ const configs = {
29537
+ left: {
29538
+ outgoing: `pptx-tr-push-out-to-left ${dur} ${EASE} forwards`,
29539
+ incoming: `pptx-tr-push-in-from-right ${dur} ${EASE} forwards`,
29540
+ outgoingOnTop: false,
29541
+ },
29542
+ right: {
29543
+ outgoing: `pptx-tr-push-out-to-right ${dur} ${EASE} forwards`,
29544
+ incoming: `pptx-tr-push-in-from-left ${dur} ${EASE} forwards`,
29545
+ outgoingOnTop: false,
29546
+ },
29547
+ up: {
29548
+ outgoing: `pptx-tr-push-out-to-top ${dur} ${EASE} forwards`,
29549
+ incoming: `pptx-tr-push-in-from-bottom ${dur} ${EASE} forwards`,
29550
+ outgoingOnTop: false,
29551
+ },
29552
+ down: {
29553
+ outgoing: `pptx-tr-push-out-to-bottom ${dur} ${EASE} forwards`,
29554
+ incoming: `pptx-tr-push-in-from-top ${dur} ${EASE} forwards`,
29555
+ outgoingOnTop: false,
29556
+ },
29557
+ };
29558
+ return configs[dir];
29559
+ }
29560
+ case 'wipe': {
29561
+ const dir = resolveDirection(direction, 'left');
29562
+ const wipeNames = {
29563
+ left: `pptx-tr-wipe-from-left ${dur} ${EASE} forwards`,
29564
+ right: `pptx-tr-wipe-from-right ${dur} ${EASE} forwards`,
29565
+ up: `pptx-tr-wipe-from-top ${dur} ${EASE} forwards`,
29566
+ down: `pptx-tr-wipe-from-bottom ${dur} ${EASE} forwards`,
29567
+ };
29568
+ return {
29569
+ outgoing: 'none',
29570
+ incoming: wipeNames[dir],
29571
+ outgoingOnTop: false,
29572
+ };
29573
+ }
29574
+ case 'cover': {
29575
+ const dir = resolveDirection8(direction, 'left');
29576
+ const coverMap = {
29577
+ left: `pptx-tr-cover-from-left ${dur} ${EASE} forwards`,
29578
+ right: `pptx-tr-cover-from-right ${dur} ${EASE} forwards`,
29579
+ up: `pptx-tr-cover-from-top ${dur} ${EASE} forwards`,
29580
+ down: `pptx-tr-cover-from-bottom ${dur} ${EASE} forwards`,
29581
+ lu: `pptx-tr-cover-from-lu ${dur} ${EASE} forwards`,
29582
+ ld: `pptx-tr-cover-from-ld ${dur} ${EASE} forwards`,
29583
+ ru: `pptx-tr-cover-from-ru ${dur} ${EASE} forwards`,
29584
+ rd: `pptx-tr-cover-from-rd ${dur} ${EASE} forwards`,
29585
+ };
29586
+ return {
29587
+ outgoing: 'none',
29588
+ incoming: coverMap[dir],
29589
+ outgoingOnTop: false,
29590
+ };
29591
+ }
29592
+ case 'uncover': {
29593
+ const dir = resolveDirection8(direction, 'left');
29594
+ const uncoverMap = {
29595
+ left: `pptx-tr-uncover-to-left ${dur} ${EASE} forwards`,
29596
+ right: `pptx-tr-uncover-to-right ${dur} ${EASE} forwards`,
29597
+ up: `pptx-tr-uncover-to-top ${dur} ${EASE} forwards`,
29598
+ down: `pptx-tr-uncover-to-bottom ${dur} ${EASE} forwards`,
29599
+ lu: `pptx-tr-uncover-to-lu ${dur} ${EASE} forwards`,
29600
+ ld: `pptx-tr-uncover-to-ld ${dur} ${EASE} forwards`,
29601
+ ru: `pptx-tr-uncover-to-ru ${dur} ${EASE} forwards`,
29602
+ rd: `pptx-tr-uncover-to-rd ${dur} ${EASE} forwards`,
29603
+ };
29604
+ return {
29605
+ outgoing: uncoverMap[dir],
29606
+ incoming: 'none',
29607
+ outgoingOnTop: true,
29608
+ };
29609
+ }
29610
+ case 'split': {
29611
+ const o = resolveOrientation(undefined, orient);
29612
+ const isOut = direction !== 'in';
29613
+ if (isOut) {
29614
+ return {
29615
+ outgoing: 'none',
29616
+ incoming: o === 'vert'
29617
+ ? `pptx-tr-split-v-out ${dur} ${EASE} forwards`
29618
+ : `pptx-tr-split-h-out ${dur} ${EASE} forwards`,
29619
+ outgoingOnTop: false,
29620
+ };
29621
+ }
29622
+ return {
29623
+ outgoing: o === 'vert'
29624
+ ? `pptx-tr-split-v-in ${dur} ${EASE} forwards`
29625
+ : `pptx-tr-split-h-in ${dur} ${EASE} forwards`,
29626
+ incoming: 'none',
29627
+ outgoingOnTop: true,
29628
+ };
29629
+ }
29630
+ case 'dissolve':
29631
+ return {
29632
+ outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
29633
+ incoming: `pptx-tr-dissolve-in ${dur} ${EASE} forwards`,
29634
+ outgoingOnTop: true,
29635
+ };
29636
+ case 'circle':
29637
+ return {
29638
+ outgoing: 'none',
29639
+ incoming: `pptx-tr-circle-in ${dur} ${EASE} forwards`,
29640
+ outgoingOnTop: false,
29641
+ };
29642
+ case 'diamond':
29643
+ return {
29644
+ outgoing: 'none',
29645
+ incoming: `pptx-tr-diamond-in ${dur} ${EASE} forwards`,
29646
+ outgoingOnTop: false,
29647
+ };
29648
+ case 'plus':
29649
+ return {
29650
+ outgoing: 'none',
29651
+ incoming: `pptx-tr-plus-in ${dur} ${EASE} forwards`,
29652
+ outgoingOnTop: false,
29653
+ };
29654
+ case 'wedge':
29655
+ return {
29656
+ outgoing: 'none',
29657
+ incoming: `pptx-tr-wedge-in ${dur} ${EASE} forwards`,
29658
+ outgoingOnTop: false,
29659
+ };
29660
+ case 'wheel':
29661
+ return {
29662
+ outgoing: 'none',
29663
+ incoming: `pptx-tr-wheel-in ${dur} ${EASE} forwards`,
29664
+ outgoingOnTop: false,
29665
+ };
29666
+ case 'zoom':
29667
+ return {
29668
+ outgoing: `pptx-tr-zoom-out ${dur} ${EASE} forwards`,
29669
+ incoming: `pptx-tr-zoom-in ${dur} ${EASE} forwards`,
29670
+ outgoingOnTop: true,
29671
+ };
29672
+ case 'blinds': {
29673
+ const o = resolveOrientation(direction, orient);
29674
+ return {
29675
+ outgoing: 'none',
29676
+ incoming: o === 'vert'
29677
+ ? `pptx-tr-blinds-v ${dur} ${EASE} forwards`
29678
+ : `pptx-tr-blinds-h ${dur} ${EASE} forwards`,
29679
+ outgoingOnTop: false,
29680
+ };
29681
+ }
29682
+ case 'checker':
29683
+ return {
29684
+ outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
29685
+ incoming: `pptx-tr-checker-in ${dur} ${EASE} forwards`,
29686
+ outgoingOnTop: true,
29687
+ };
29688
+ case 'comb': {
29689
+ const o = resolveOrientation(direction, orient);
29690
+ return {
29691
+ outgoing: 'none',
29692
+ incoming: o === 'vert'
29693
+ ? `pptx-tr-comb-v ${dur} ${EASE} forwards`
29694
+ : `pptx-tr-comb-h ${dur} ${EASE} forwards`,
29695
+ outgoingOnTop: false,
29696
+ };
29697
+ }
29698
+ case 'strips': {
29699
+ const stripDir = direction === 'lu' || direction === 'ld' || direction === 'ru' || direction === 'rd'
29700
+ ? direction
29701
+ : 'lu';
29702
+ return {
29703
+ outgoing: 'none',
29704
+ incoming: `pptx-tr-strips-${stripDir} ${dur} ${EASE} forwards`,
29705
+ outgoingOnTop: false,
29706
+ };
29707
+ }
29708
+ case 'randomBar': {
29709
+ const o = resolveOrientation(direction, orient);
29710
+ return {
29711
+ outgoing: 'none',
29712
+ incoming: o === 'vert'
29713
+ ? `pptx-tr-randombar-v ${dur} ${EASE} forwards`
29714
+ : `pptx-tr-randombar-h ${dur} ${EASE} forwards`,
29715
+ outgoingOnTop: false,
29716
+ };
29717
+ }
29718
+ case 'newsflash':
29719
+ return {
29720
+ outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
29721
+ incoming: `pptx-tr-newsflash-in ${dur} ${EASE} forwards`,
29722
+ outgoingOnTop: false,
29723
+ };
29724
+ // `pull` is the directional alias of `uncover`.
29725
+ case 'pull':
29726
+ return getSlideTransitionAnimations('uncover', durationMs, direction, orient, spokes);
29727
+ case 'morph':
29728
+ return {
29729
+ outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
29730
+ incoming: `pptx-tr-fade-in ${dur} ${EASE} forwards`,
29731
+ outgoingOnTop: true,
29732
+ };
29733
+ case 'random': {
29734
+ const randomType = RANDOM_ELIGIBLE_TYPES[Math.floor(Math.random() * RANDOM_ELIGIBLE_TYPES.length)];
29735
+ return getSlideTransitionAnimations(randomType, durationMs, direction, orient, spokes);
29736
+ }
29737
+ // The exotic / 3-D transition family (conveyor/doors/ferris/flash/
29738
+ // flythrough/gallery/glitter/honeycomb/pan/prism/reveal/ripple/shred/
29739
+ // switch/vortex/warp/wheelReverse/window) is handled faithfully above via
29740
+ // `getP14TransitionAnimations` and never reaches this switch.
29741
+ // Fallback (unknown / not-yet-mapped type, e.g. the p15 cinematic set:
29742
+ // cube/flip/rotate/orbit/fracture/peelOff/pageCurl*/airplane/origami)
29743
+ // — symmetrical cross-fade.
29744
+ default:
29745
+ return {
29746
+ outgoing: `pptx-tr-fade-out ${dur} ${EASE} forwards`,
29747
+ incoming: `pptx-tr-fade-in ${dur} ${EASE} forwards`,
29748
+ outgoingOnTop: true,
29749
+ };
29750
+ }
29751
+ }
29752
+ /**
29753
+ * Convenience wrapper that reads everything off a {@link PptxSlideTransition},
29754
+ * applying the React/Vue default duration when none is set. `none`/`cut` and a
29755
+ * transition with a non-positive duration resolve to {@link INSTANT}.
29756
+ */
29757
+ function resolveSlideTransition(transition) {
29758
+ if (!transition || transition.type === 'none' || transition.type === 'cut') {
29759
+ return INSTANT;
29760
+ }
29761
+ const durationMs = typeof transition.durationMs === 'number' && transition.durationMs > 0
29762
+ ? transition.durationMs
29763
+ : DEFAULT_TRANSITION_DURATION_MS$1;
29764
+ return getSlideTransitionAnimations(transition.type, durationMs, transition.direction, transition.orient, transition.spokes);
29765
+ }
29766
+ /**
29767
+ * The effective duration (ms) for a transition under the React/Vue policy — the
29768
+ * configured `durationMs`, or {@link DEFAULT_TRANSITION_DURATION_MS} when
29769
+ * unset/invalid. `none`/`cut` resolve to `0` (instant).
29770
+ */
29771
+ function resolveTransitionDurationMs(transition) {
29772
+ if (!transition || transition.type === 'none' || transition.type === 'cut') {
29773
+ return 0;
29774
+ }
29775
+ return typeof transition.durationMs === 'number' && transition.durationMs > 0
29776
+ ? transition.durationMs
29777
+ : DEFAULT_TRANSITION_DURATION_MS$1;
29778
+ }
29779
+
28865
29780
  /**
28866
29781
  * SmartArt layout engine — pure colour / style / tree / family-resolution
28867
29782
  * helpers shared by the React, Vue, and Angular bindings.
@@ -29695,6 +30610,29 @@ function computeTargetLayout(nodes, box, palette, style, elementId) {
29695
30610
  * `import … from 'pptx-viewer-shared'` (or a thin binding shim) yields the full
29696
30611
  * surface the renderers and colocated tests expect.
29697
30612
  */
30613
+ /**
30614
+ * Expand a colour palette across `count` nodes.
30615
+ *
30616
+ * For `span`, distributes the nodes evenly along the palette and interpolates
30617
+ * intermediate colours in HSL space (honouring `hueDirection`), so e.g. a
30618
+ * two-colour "colorful range" scheme fades smoothly across many nodes. For
30619
+ * `cycle`/`repeat`/unset, wraps the palette by index (the historic behaviour).
30620
+ */
30621
+ function interpolateSmartArtPalette(palette, count, method, hueDirection) {
30622
+ if (count <= 0 || palette.length === 0) {
30623
+ return [];
30624
+ }
30625
+ if (method !== 'span' || palette.length < 2 || count <= palette.length) {
30626
+ return Array.from({ length: count }, (_, i) => palette[i % palette.length]);
30627
+ }
30628
+ const segments = palette.length - 1;
30629
+ return Array.from({ length: count }, (_, i) => {
30630
+ const pos = (i / (count - 1)) * segments;
30631
+ const lower = Math.min(Math.floor(pos), segments - 1);
30632
+ const frac = pos - lower;
30633
+ return interpolateColor(palette[lower], palette[lower + 1], frac, 'hsl', hueDirection);
30634
+ });
30635
+ }
29698
30636
  /**
29699
30637
  * Compute the SVG layout for a SmartArt element when drawing shapes are absent.
29700
30638
  *
@@ -29705,32 +30643,40 @@ function computeTargetLayout(nodes, box, palette, style, elementId) {
29705
30643
  * @param elementId - Element ID (used for stable SVG key generation).
29706
30644
  * @param resolvedLayoutType - Layout type string from the core parser.
29707
30645
  * @param layout - Named layout preset.
30646
+ * @param interpolation - Optional colour-list span/cycle interpolation
30647
+ * (from the diagram's colour scheme). When `span`,
30648
+ * the palette is expanded into a per-node gradient
30649
+ * before layout; otherwise the palette is used
30650
+ * as-is and cycled by the family computers.
29708
30651
  * @returns Complete layout geometry for the resolved family.
29709
30652
  */
29710
- function computeSmartArtLayout(nodes, box, palette, style, elementId, resolvedLayoutType, layout) {
30653
+ function computeSmartArtLayout(nodes, box, palette, style, elementId, resolvedLayoutType, layout, interpolation) {
29711
30654
  const flat = flattenNodes(nodes);
29712
30655
  const family = resolveLayoutFamily(nodes, resolvedLayoutType, layout);
30656
+ const pal = interpolation?.method === 'span'
30657
+ ? interpolateSmartArtPalette(palette, flat.length, 'span', interpolation.hueDirection)
30658
+ : palette;
29713
30659
  switch (family) {
29714
30660
  case 'list':
29715
- return computeListLayout(flat, box, palette, style, elementId);
30661
+ return computeListLayout(flat, box, pal, style, elementId);
29716
30662
  case 'process':
29717
- return computeProcessLayout(flat, box, palette, style, elementId);
30663
+ return computeProcessLayout(flat, box, pal, style, elementId);
29718
30664
  case 'cycle':
29719
- return computeCycleLayout(flat, box, palette, style, elementId);
30665
+ return computeCycleLayout(flat, box, pal, style, elementId);
29720
30666
  case 'hierarchy':
29721
- return computeHierarchyLayout(nodes, box, palette, style, elementId);
30667
+ return computeHierarchyLayout(nodes, box, pal, style, elementId);
29722
30668
  case 'matrix':
29723
- return computeMatrixLayout(flat, box, palette, style, elementId);
30669
+ return computeMatrixLayout(flat, box, pal, style, elementId);
29724
30670
  case 'radial':
29725
- return computeRadialLayout(flat, box, palette, style, elementId);
30671
+ return computeRadialLayout(flat, box, pal, style, elementId);
29726
30672
  case 'pyramid':
29727
- return computePyramidLayout(flat, box, palette, style, elementId);
30673
+ return computePyramidLayout(flat, box, pal, style, elementId);
29728
30674
  case 'venn':
29729
- return computeVennLayout(flat, box, palette, style, elementId);
30675
+ return computeVennLayout(flat, box, pal, style, elementId);
29730
30676
  case 'funnel':
29731
- return computeFunnelLayout(flat, box, palette, style, elementId);
30677
+ return computeFunnelLayout(flat, box, pal, style, elementId);
29732
30678
  case 'target':
29733
- return computeTargetLayout(flat, box, palette, style, elementId);
30679
+ return computeTargetLayout(flat, box, pal, style, elementId);
29734
30680
  }
29735
30681
  }
29736
30682
 
@@ -37146,6 +38092,20 @@ function updateViewerPreference(preferences, key, value) {
37146
38092
  function shouldLoopContinuously(input) {
37147
38093
  return Boolean(input.loopContinuously) || input.showType === 'kiosk';
37148
38094
  }
38095
+ /**
38096
+ * PowerPoint's "On Mouse Click" advance gate. An on-slide click / tap / swipe
38097
+ * advances the show only when the current slide's transition allows it: a slide
38098
+ * whose transition sets `advanceOnClick` to false is advanced solely by timings
38099
+ * or by explicit navigation (keyboard, on-screen next/prev buttons). An
38100
+ * undefined flag defaults to allowed, preserving the historical
38101
+ * click-to-advance behaviour.
38102
+ *
38103
+ * Only the click / tap / swipe advance may consult this. Keyboard, on-screen
38104
+ * next/prev buttons and timed auto-advance must never be gated by it.
38105
+ */
38106
+ function isClickAdvanceAllowed(slide) {
38107
+ return slide?.transition?.advanceOnClick !== false;
38108
+ }
37149
38109
  function applyRehearsalTimings(slides, timings) {
37150
38110
  return slides.map((slide, index) => {
37151
38111
  const advanceAfterMs = timings[index];
@@ -67977,6 +68937,16 @@ function hasExitedFullscreen(doc) {
67977
68937
  return !d?.fullscreenElement;
67978
68938
  }
67979
68939
 
68940
+ /**
68941
+ * Whether a click/tap/swipe advance must be swallowed instead of moving to the
68942
+ * next slide. A click still steps the current slide's remaining animation
68943
+ * builds, so this only blocks once they are exhausted (`playbackComplete`) and
68944
+ * only when the slide's transition sets advanceOnClick to false. Keyboard and
68945
+ * the on-screen next/prev buttons never consult this.
68946
+ */
68947
+ function shouldBlockClickAdvance(playbackComplete, slide) {
68948
+ return playbackComplete && !isClickAdvanceAllowed(slide);
68949
+ }
67980
68950
  /**
67981
68951
  * Clamp `index` to the valid range [0, count - 1].
67982
68952
  * Returns 0 when `count` is 0 to avoid -1 states.
@@ -68764,8 +69734,16 @@ class PresentationOverlayComponent {
68764
69734
  getScale: () => 1,
68765
69735
  callbacks: {
68766
69736
  onSwipe: (direction) => {
68767
- // direction 1 = swipe right (previous), -1 = swipe left (next).
68768
- this.navigate(direction === 1 ? 'prev' : 'next');
69737
+ // direction 1 = swipe right (previous), -1 = swipe left (next). A
69738
+ // swipe is PowerPoint's on-click advance, so the forward case is
69739
+ // gated by the current slide's advanceOnClick flag; a backward
69740
+ // swipe is explicit navigation and never gated.
69741
+ if (direction === 1) {
69742
+ this.navigate('prev');
69743
+ }
69744
+ else {
69745
+ this.advanceFromClick();
69746
+ }
68769
69747
  },
68770
69748
  },
68771
69749
  });
@@ -68876,6 +69854,19 @@ class PresentationOverlayComponent {
68876
69854
  if (this.annotations.tool() !== 'none') {
68877
69855
  return;
68878
69856
  }
69857
+ this.advanceFromClick();
69858
+ }
69859
+ /**
69860
+ * Click/tap/swipe advance. Like every forward step it first reveals the
69861
+ * current slide's next animation build; only once the builds are exhausted
69862
+ * does it advance the slide, and then only when the slide's transition allows
69863
+ * click-advance (advanceOnClick !== false). Keyboard and the on-screen
69864
+ * next/prev buttons call navigate() directly and are never gated.
69865
+ */
69866
+ advanceFromClick() {
69867
+ if (shouldBlockClickAdvance(this.playback.isComplete(), this.currentSlide())) {
69868
+ return;
69869
+ }
68879
69870
  this.navigate('next');
68880
69871
  }
68881
69872
  /** Toggle an annotation tool (clicking the active one disarms it). */
@@ -73953,7 +74944,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
73953
74944
  }], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }] } });
73954
74945
 
73955
74946
  // Generated by scripts/inline-shared.mjs from package.json. Do not edit.
73956
- const PPTX_ANGULAR_VIEWER_VERSION = "1.31.2";
74947
+ const PPTX_ANGULAR_VIEWER_VERSION = "1.31.4";
73957
74948
 
73958
74949
  /**
73959
74950
  * account-page.component.ts: File > Account content.
@@ -100780,5 +101771,5 @@ function cn(...values) {
100780
101771
  * Generated bundle index. Do not edit.
100781
101772
  */
100782
101773
 
100783
- export { ALIGN_OPTIONS, AUDIENCE_HASH, AUDIENCE_NONCE_KEY, AVATAR_COLOR_SWATCHES, AccessibilityPanelComponent, AccessibilityService, AccountPageComponent, ActionSettingsPanelComponent, AdvancedChartEditorComponent, AnimationAuthorPanelComponent, AnimationPanelComponent, AnimationPlaybackService, AutosaveService, BroadcastDialogComponent, CHART_EDITOR_STYLES, CURSOR_PALETTE, CanvasFitService, ChartAxisOptionsComponent, ChartAxisStyleOptionsComponent, ChartComboTypeOptionsComponent, ChartDataEditorComponent, ChartDataLabelOptionsComponent, ChartDatapointOptionsComponent, ChartDisplayOptionsComponent, ChartElementViewComponent, ChartErrorBarOptionsComponent, ChartMarkerOptionsComponent, ChartPartSelectionService, ChartPrimitivesComponent, ChartRendererComponent, ChartTrendlineOptionsComponent, CollaborationCursorsComponent, CollaborationService, ColorChangedImageComponent, CommentsPanelComponent, CommentsService, ComparePanelComponent, ConnectorRendererComponent, ConnectorTextOverlayComponent, CustomShowsComponent, DATA_TABLE_HEADER_H, DATA_TABLE_KEY_W, DATA_TABLE_PADDING, DATA_TABLE_ROW_H, DEFAULT_BOUNDS, DEFAULT_BROADCAST_SERVER_URL, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_COLOR_SCHEME, DEFAULT_FILL_COLOR, DEFAULT_LAYOUT, DEFAULT_PALETTE$1 as DEFAULT_PALETTE, DEFAULT_PRINT_SETTINGS, DEFAULT_SLIDE_BACKGROUND, DEFAULT_STROKE_COLOR, DEFAULT_STYLE, DEFAULT_TABLE_ROW_HEIGHT, DEFAULT_TEXT_COLOR$1 as DEFAULT_TEXT_COLOR, DEFAULT_VIEWER_PROFILE, DIRECTIONAL_PRESETS, DIRECTION_OPTIONS, DocumentPropertiesCardComponent, EMBEDDED_FONTS_STYLE_ID, EMPHASIS_PRESETS, ENTRANCE_PRESETS, TEMPLATES as EQUATION_TEMPLATES, EXIT_PRESETS, EditorContextMenuComponent, EditorHistory, EditorStateService, EditorToolbarComponent, EffectsPanelComponent, ElementRendererComponent, EmbeddedFontsService, EncryptedFileDialogComponent, EquationEditorDialogComponent, EquationRendererComponent, EquationTemplateGalleryComponent, ExportProgressModalComponent, ExportService, FieldContextService, FindBarComponent, FindReplaceBarComponent, FollowModeBarComponent, FontEmbeddingListComponent, FontEmbeddingPanelComponent, GALLERY_THEME_PRESETS, GradientPickerComponent, HANDOUT_OPTIONS, HeaderFooterDialogComponent, HyperlinkDialogComponent, ImagePropertiesPanelComponent, InkDrawingService, InkRendererComponent, InsertSmartArtDialogComponent, InspectorPaneHeaderComponent, InspectorPanelComponent, IsMobileService, KeepAnnotationsDialogComponent, LOCALE_CATALOG, LONG_PRESS_DURATION_MS, LONG_PRESS_MOVE_TOLERANCE_PX, LoadContentService, LocalPresencePublisher, MAX_ZOOM_SCALE, MIN_ZOOM_SCALE, MediaPreviewComponent, MediaPropertiesPanelComponent, MediaRendererComponent, MediaTrimTimelineComponent, MobileBottomBarComponent, MobileMenuSheetComponent, MobilePresenterViewComponent, MobileSheetComponent, MobileSlidesSheetComponent, MobileToolbarComponent, ModalDialogComponent, Model3DRendererComponent, NotesHandoutCardComponent, NotesPanelComponent, NotesToolbarComponent, OleRendererComponent, POWER_POINT_VIEWER_PROVIDERS, PRESENTER_CHANNEL_NAME, PRESENTER_MSG_ORIGIN, PasswordProtectionDialogComponent, PasswordStrengthMeterComponent, PowerPointViewerComponent, PresentationAnnotationOverlayComponent, PresentationAnnotationsService, PresentationOverlayComponent, PresentationPropertiesPanelComponent, PresentationSettingsCardComponent, PresentationSubtitleBarComponent, PresentationTransitionOverlayComponent, PresenterViewComponent, PresenterWindowService, PrintDialogComponent, PrintService, PrintSettingsPanelComponent, PropertiesDialogComponent, REPEAT_MODE_OPTIONS, RESIZE_HANDLES, RULER_THICKNESS, RemoteSelectionOverlayComponent, RibbonAnimationsSectionComponent, RibbonArrangeSectionComponent, RibbonColorPopoverComponent, RibbonComponent, RibbonDesignSectionComponent, RibbonDrawSectionComponent, RibbonDrawingGroupComponent, RibbonEditingSectionComponent, RibbonFileSectionComponent, RibbonFontControlsComponent, RibbonHomeSectionComponent, RibbonInsertFieldsComponent, RibbonInsertSectionComponent, RibbonParagraphControlsComponent, RibbonPrimaryRowComponent, RibbonReviewSectionComponent, RibbonSlideshowSectionComponent, RibbonTransitionsSectionComponent, RibbonViewSectionComponent, RulerGuidesService, SEQUENCE_OPTIONS, SEVERITY_GROUPS, SEVERITY_LABELS, SHORTCUT_REFERENCE_ITEMS, SLIDE_PX_PER_INCH, SLIDE_TRANSITION_KEYFRAMES, DEFAULT_PALETTE as SMARTART_DEFAULT_PALETTE, PALETTES$1 as SMARTART_PALETTES, SMART_ART_COLOR_SCHEMES, SMART_ART_STYLE_OPTIONS, SUB_ITEM_LABEL, SVG_WARP_PRESETS, SWIPE_MAX_VERTICAL_PX, SWIPE_THRESHOLD_PX, SelectionPaneComponent, SetUpSlideShowDialogComponent, SettingsAppearanceTabComponent, SettingsDialogComponent, SettingsLanguageTabComponent, ShareDialogComponent, ShortcutPanelComponent, ShowOptionsFieldsetComponent, ShowSlidesFieldsetComponent, SignatureStrippedDialogComponent, SignaturesPanelComponent, SignaturesService, SlideCanvasComponent, SlideDefaultInspectorComponent, SlideDiffChangesComponent, SlideDiffRowComponent, SlideDiffThumbnailsComponent, SlideSizeCardComponent, SlideSorterOverlayComponent, SlideThemeOverridePanelComponent, SlidesPanelComponent, SmartArt3DRendererComponent, SmartArt3DService, SmartArtPreviewComponent, SmartArtPropertiesComponent, SmartArtRendererComponent, StatusBarComponent, TABLE_STRUCTURE_TOGGLES, TEXT_DIRECTION_OPTIONS$1 as TEXT_DIRECTION_OPTIONS, THEME_CATALOG, TIMING_CURVE_OPTIONS, TRIGGER_OPTIONS, TYPE_LABELS, TableCellAdvancedFillComponent, TableCellFormattingComponent, TableDataEditorComponent, TablePropertiesComponent, TableRendererComponent, TableResizeOverlayComponent, TableSelectionService, TextAdvancedPanelComponent, ThemeEditorFieldsComponent, ThemeGalleryComponent, ThemeSelectorCardComponent, TitleBarComponent, VALIGN_OPTIONS, VIEWER_THEME, VersionHistoryPanelComponent, ViewerCanvasEditingService, ViewerCollabCursorService, ViewerCollaborationSessionService, ViewerCompareService, ViewerCustomShowsService, ViewerDialogsService, ViewerDocumentPropertiesService, ViewerExportService, ViewerExtraDialogsComponent, ViewerFileIOService, ViewerFindReplaceService, ViewerFormatPainterService, ViewerInspectorPanelService, ViewerKeyboardService, ViewerMobileSheetService, ViewerPresentationModeService, ViewerThemeGalleryService, ViewerTouchGesturesService, ViewerZoomService, WEBM_MIME_CANDIDATES, WriteBackScheduler, ZoomNavigationService, ZoomRendererComponent, ZoomTargetService, addCategory, addCommentToList, addGradientStopPatch, addItem, addSeries, addSubItem, advanceStep, alignPatch, animationFor, annotationMapToInkInserts, applyAcceptedDiff, applyAnimationPreset, applyFindReplacements, applyFormatToElement, applyMove, applyResize, applyTableStylePreset, asMediaElement, assignUserColor, attachTouchGestures, beginNodeEdit, boolFromEvent, bringForward, bringToFront, buildBarActions, buildBroadcastConfig, buildBroadcastViewerUrl, buildCategoryLabels, buildCellParagraphs, buildChartViewModel, buildChromeStyle, buildClearHyperlinkPatch, buildClickGroups, buildColStyles, buildCollaborationConfig, buildComboViewModel, buildCssGradientFromShapeStyle, buildDuotoneFilter, buildDuotoneFilterId, buildEmbeddedFontStyles, buildEquationElement, buildEquationSegment, buildFallbackViewModel, buildFontFaceRule, buildGradientFillCss, buildGridlinesAndLabels, buildHyperlinkPatch, buildInkContainerStyle, buildInkStrokes, buildLegend, buildModel3DContainerStyle, buildModel3DViewModel, buildOleActionModel, buildOleInfoRows, buildPatternFillCss, buildPrintHtmlDocument as buildPrintDocument, buildPropertiesPatch, buildRegionMapViewModel, buildSaveSlides, buildShareUrl, buildSmartArtInsertElement, buildSmartArtNodes, buildStockViewModel, buildSurfaceViewModel, buildTableViewModel, buildTreemapViewModel, buildTrimFragment, buildWaterfallViewModel, buildZeroLine, buildZoomContainerStyle, buildZoomViewModel, bulletIndentPx, canAddTopLevelNode, canRemoveTopLevelNode, canStartBroadcast, canStartShare, canUseClipboard, captionDisplayText, cellRunStyle, cellStyleToStyleMap, cellTdStyle, changeCountLabel, changeIcon, characterSpacingPatch, checkFontAvailable, clampCursorPosition, clampGifDimensions, clampIndex, clampNotesFontSize, clampScale, clampStep, clearAllLocalViewerData, clearAudienceContent, cn, collectAccessibilityIssues, collectElementText, collectSlideText, collectUsedFontFamilies, columnWidthStyle, commitNodeText, computeAlign, computeAxisTitlePrimitives, computeBarRects, computeBubbleRadius, computeCornerHandle, computeDataTablePrimitives, computeDistribute, computeDrawingViewBox, computeErrorBarPrimitives, computeHandleBoxes, computeHandoutLayout, computeIsMobile, computeIsTablet, computeLinePoints, computeLinearRegression, computePageCount, computePieLayout, computePieSlicePath, computePieSlices, computePlotLayout, computeRSquared, computeRadarPoints, computeScatterDots, computeSelectionBoxes, computeSingleSelected, computeSlideIndices, computeSnap, computeStackedBarRects, computeStackedValueRange, computeTextLines, computeTimerProgress, computeTrendlinePrimitives, computeValueRange, convertOmmlToMathMl, copyFormatFromElement, countAccessibilityIssues, countAnnotationStrokes, createCustomShow, createSwipeDismissDrag, createWebrtcBundle, createWebsocketBundle, cssObjectToStyleMap, currentColorScheme, currentLayout, currentStyle, defaultCssVars, defaultRadius, defaultThemeColors, deleteElementsByIds, deleteVersion as deleteRecoveryVersion, demoteNode, deriveModel3DBlobUrl, derivePresenceList, describeSmartArtBounds, disableGlowPatch, disableInnerShadowPatch, disableOuterShadowPatch, disableReflectionPatch, disableSoftEdgePatch, duplicateElementById, durationOf, effectsStateOf, enableGlowPatch, enableInnerShadowPatch, enableOuterShadowPatch, enableReflectionPatch, enableSoftEdgePatch, encodeGif, estimatePageCount, evenColumnWidths, evenRowHeights, exitPresentationFullscreen, extractPathPoints, eyedropperAvailable, fillColorOf, findInSlides, findOwningSlideIndex, findSlideIndexByElementId, fitPolynomial, fitZoom, fontMimeForFormat, fontSizeOf, formatAutoNumber, formatAxisValue, formatBytes, formatCursorLabel, formatElapsed, formatFileSize, formatPropertyDate, formatTime, fpsToFrameIntervalMs, generateBroadcastRoomId, generateCommentId, generateCustomShowId, generatePressureCircles, generateRulerTicks, getClrChangeParams, getContainerStyle, getDuotoneFilterDef, getImageSrc, getLocalStorageUsageSummary, getOleAriaLabel, getOleBadgeLabel, getOleDisplayName, getOleDownloadFileName, getOleTypeColor, getOleTypeLabel, getPasswordStrength, getPatternSvg, getPlaceholderStyle, getVersions as getRecoveryVersions, getResolvedShapeClipPath, getResolvedShapeClipPathFor, getShapeFillStrokeStyle, getSlideBackgroundStyle, getSlideTransitionAnimations, getSmartArtNodeBounds, getSpeechRecognitionCtor, getTextBlockStyle, getTextWarp, getTouchDistance, getWarpCategory, getWarpPath, gradientStateFromStyle, gradientStateOf, gradientStatePatch, gridColumns, groupElements, groupIssuesBySeverity, hasAnimation, hasCopyableFormat, hasExistingLink, hasExitedFullscreen, hasGradientFill, hasPressureVariation, headerLabel, inkViewBox, insertColumn, insertRow, interpolateWidth, isAudienceTab, isBold, isBrowserOpenableMime, isChildNode, isElementInteractive, isInjectableUrl, isItalic, isPpactionUrl, isPresenterMessage, isSigned, isTextElement, isUnderline, isUrlSafe, isValidRoomId, isViewportBackgroundPressTarget, isZoomActivationKey, issueTrackKey, issueTypeLabel, keyToLabel, latexToMathml, linePointsToSvgString, lineSpacingPatch, loadAudienceContent, mergeCaptionResults, mergeDown, mergeRight, mergeSelection, moveElementBy, moveNodeDown, moveNodeUp, msToFrameDelayCs, narrowToCircle, narrowToPolygon, narrowToRect, newChartElement, newEquationElement, newPresetShapeElement, newShapeElement, newSmartArtElement, newTableElement, newTextElement, nextVisibleIndex, nodeBold, nodeEditBox, nodeFillColor, nodeFontColor, nodeIdFromKey, nodeItalic, nodeStyle, normalizeFontFormat, normalizeSlidesPerPage, normalizeValue, numFromEvent, ommlToMathml, ooxmlDashToCssBorderStyle, openNativeEyeDropper, overallStatus, paletteColor, parseAudienceNonce, parseNodeTextarea, partitionSlides, patchChartData, patchChartStyle, patchTableData, patchTextStyle, pendingElementStyles, pickColorByClickFallback, pickSupportedMimeType, planGifFrames, planVideoSegments, pointsToSvgPathD, presenceToCursors, presetByLayout, presetsForCategory, pressuresToWidths, prevVisibleIndex, projectDrawingShapes, promoteNode, provideViewerTheme, radarAngle, radarRingPoints, recordWebm, redistributeColumnWidth, removeAnimation, removeCategory, removeColumn, removeCommentFromList, removeElementAnimation, removeGradientStopPatch, removeNode, removeRow, removeSeries, renderToCanvas, reorderAnimationDown, reorderAnimationUp, replaceInSlides, replaceMatch, requestPresentationFullscreen, resizeElement, resolveCaptionTracks, resolveChartKind, resolveFontVariant, resolveHyperlinkHref, resolveInteractiveElementId, resolveMediaSrc, resolveOleType, resolveParagraphBullet, resolvePresenterNotes, resolveProfileInitial, resolveRegionCode, resolvePalette as resolveSmartArtPalette, resolveThemeCatalogEntry, resolveTransitionDuration, revealedElementStyles, routeOrthogonalConnector, rowStyle, sampleColorFromSlide, sanitizeColor, sanitizeSlideIndex, sanitizeUserName, saveViewerProfile, scanAvailableFonts, searchSlides, seedBroadcastFields, seedHyperlinkDraft, seedPropertiesDraft, seedShareFields, segmentFrameCount, selectValue$2 as selectValue, sendBackward, sendToBack, sequentialColorScale, serializeWriteBack, seriesColor, setAnimationEmphasis, setAnimationEntrance, setAnimationExit, setAxis, setAxisLogScale, setAxisTitleStyle, setCategoryLabel, setCellText, setColorScheme, setDataLabels, setDataPointExplosion, setDataPointFill, setDataPointLabel, setDelay, setDirection, setDuration, setElementPosition, setGridlineStyle, setLayout, setLegend, setNodeStyle, setNodeText, setRepeatCount, setRepeatMode, setSequence, setSeriesChartType, setSeriesColor, setSeriesErrorBars, setSeriesMarker, setSeriesName, setSeriesTrendline, setSeriesValue, setStyle, setTimingCurve, setTitle, setTrigger, setTriggerShapeId, shapeStylePatch, sheetAfterNavigate, shouldUseSvgWarp, showDirectionPicker, showsTemplateAffordance, signatureCountLabel, signatureKey, signatureTimestamp, signerName, statusLabel as slideDiffStatusLabel, slideNumberOf, smartArtNodes, paletteColour as smartArtPaletteColour, snapToGridStep, splitCursorCell, splitMergedCell, statusKind, statusLabel$1 as statusLabel, storeAudienceContent, stringFromEvent$5 as stringFromEvent, strokeColorOf, strokeToInkElement, styleShadowFilter, textAdvancedPatch, textAdvancedStateFromStyle, textAdvancedStateOf, textColorOf, textDirectionPatch, textStyleOf, textStylePatch, themeStyle, themeToCssVars, thumbnailHeight, thumbnailZoom, toggleCommentResolvedInList, toggleNodeBold, toggleNodeItalic, toggleSheet, topLevelNodeCount, transformSelectedTextCase, translationsEn, ungroupElements, updateElementById, updateGlowPatch, updateGradientStopPatch, updateInnerShadowPatch, updateOuterShadowPatch, updateReflectionPatch, vAlignPatch, validatePassword, validatePrintSettings, validateRoomId, valueToY, vermilionDarkColors, vermilionDarkTheme, vermilionLightColors, vermilionLightTheme, vermilionRadius, waypointsToPathD, worstStatus, zoomTargetSlideIndex };
101774
+ export { ALIGN_OPTIONS, AUDIENCE_HASH, AUDIENCE_NONCE_KEY, AVATAR_COLOR_SWATCHES, AccessibilityPanelComponent, AccessibilityService, AccountPageComponent, ActionSettingsPanelComponent, AdvancedChartEditorComponent, AnimationAuthorPanelComponent, AnimationPanelComponent, AnimationPlaybackService, AutosaveService, BroadcastDialogComponent, CHART_EDITOR_STYLES, CURSOR_PALETTE, CanvasFitService, ChartAxisOptionsComponent, ChartAxisStyleOptionsComponent, ChartComboTypeOptionsComponent, ChartDataEditorComponent, ChartDataLabelOptionsComponent, ChartDatapointOptionsComponent, ChartDisplayOptionsComponent, ChartElementViewComponent, ChartErrorBarOptionsComponent, ChartMarkerOptionsComponent, ChartPartSelectionService, ChartPrimitivesComponent, ChartRendererComponent, ChartTrendlineOptionsComponent, CollaborationCursorsComponent, CollaborationService, ColorChangedImageComponent, CommentsPanelComponent, CommentsService, ComparePanelComponent, ConnectorRendererComponent, ConnectorTextOverlayComponent, CustomShowsComponent, DATA_TABLE_HEADER_H, DATA_TABLE_KEY_W, DATA_TABLE_PADDING, DATA_TABLE_ROW_H, DEFAULT_BOUNDS, DEFAULT_BROADCAST_SERVER_URL, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_COLOR_SCHEME, DEFAULT_FILL_COLOR, DEFAULT_LAYOUT, DEFAULT_PALETTE$1 as DEFAULT_PALETTE, DEFAULT_PRINT_SETTINGS, DEFAULT_SLIDE_BACKGROUND, DEFAULT_STROKE_COLOR, DEFAULT_STYLE, DEFAULT_TABLE_ROW_HEIGHT, DEFAULT_TEXT_COLOR$1 as DEFAULT_TEXT_COLOR, DEFAULT_VIEWER_PROFILE, DIRECTIONAL_PRESETS, DIRECTION_OPTIONS, DocumentPropertiesCardComponent, EMBEDDED_FONTS_STYLE_ID, EMPHASIS_PRESETS, ENTRANCE_PRESETS, TEMPLATES as EQUATION_TEMPLATES, EXIT_PRESETS, EditorContextMenuComponent, EditorHistory, EditorStateService, EditorToolbarComponent, EffectsPanelComponent, ElementRendererComponent, EmbeddedFontsService, EncryptedFileDialogComponent, EquationEditorDialogComponent, EquationRendererComponent, EquationTemplateGalleryComponent, ExportProgressModalComponent, ExportService, FieldContextService, FindBarComponent, FindReplaceBarComponent, FollowModeBarComponent, FontEmbeddingListComponent, FontEmbeddingPanelComponent, GALLERY_THEME_PRESETS, GradientPickerComponent, HANDOUT_OPTIONS, HeaderFooterDialogComponent, HyperlinkDialogComponent, ImagePropertiesPanelComponent, InkDrawingService, InkRendererComponent, InsertSmartArtDialogComponent, InspectorPaneHeaderComponent, InspectorPanelComponent, IsMobileService, KeepAnnotationsDialogComponent, LOCALE_CATALOG, LONG_PRESS_DURATION_MS, LONG_PRESS_MOVE_TOLERANCE_PX, LoadContentService, LocalPresencePublisher, MAX_ZOOM_SCALE, MIN_ZOOM_SCALE, MediaPreviewComponent, MediaPropertiesPanelComponent, MediaRendererComponent, MediaTrimTimelineComponent, MobileBottomBarComponent, MobileMenuSheetComponent, MobilePresenterViewComponent, MobileSheetComponent, MobileSlidesSheetComponent, MobileToolbarComponent, ModalDialogComponent, Model3DRendererComponent, NotesHandoutCardComponent, NotesPanelComponent, NotesToolbarComponent, OleRendererComponent, POWER_POINT_VIEWER_PROVIDERS, PRESENTER_CHANNEL_NAME, PRESENTER_MSG_ORIGIN, PasswordProtectionDialogComponent, PasswordStrengthMeterComponent, PowerPointViewerComponent, PresentationAnnotationOverlayComponent, PresentationAnnotationsService, PresentationOverlayComponent, PresentationPropertiesPanelComponent, PresentationSettingsCardComponent, PresentationSubtitleBarComponent, PresentationTransitionOverlayComponent, PresenterViewComponent, PresenterWindowService, PrintDialogComponent, PrintService, PrintSettingsPanelComponent, PropertiesDialogComponent, REPEAT_MODE_OPTIONS, RESIZE_HANDLES, RULER_THICKNESS, RemoteSelectionOverlayComponent, RibbonAnimationsSectionComponent, RibbonArrangeSectionComponent, RibbonColorPopoverComponent, RibbonComponent, RibbonDesignSectionComponent, RibbonDrawSectionComponent, RibbonDrawingGroupComponent, RibbonEditingSectionComponent, RibbonFileSectionComponent, RibbonFontControlsComponent, RibbonHomeSectionComponent, RibbonInsertFieldsComponent, RibbonInsertSectionComponent, RibbonParagraphControlsComponent, RibbonPrimaryRowComponent, RibbonReviewSectionComponent, RibbonSlideshowSectionComponent, RibbonTransitionsSectionComponent, RibbonViewSectionComponent, RulerGuidesService, SEQUENCE_OPTIONS, SEVERITY_GROUPS, SEVERITY_LABELS, SHORTCUT_REFERENCE_ITEMS, SLIDE_PX_PER_INCH, SLIDE_TRANSITION_KEYFRAMES, DEFAULT_PALETTE as SMARTART_DEFAULT_PALETTE, PALETTES$1 as SMARTART_PALETTES, SMART_ART_COLOR_SCHEMES, SMART_ART_STYLE_OPTIONS, SUB_ITEM_LABEL, SVG_WARP_PRESETS, SWIPE_MAX_VERTICAL_PX, SWIPE_THRESHOLD_PX, SelectionPaneComponent, SetUpSlideShowDialogComponent, SettingsAppearanceTabComponent, SettingsDialogComponent, SettingsLanguageTabComponent, ShareDialogComponent, ShortcutPanelComponent, ShowOptionsFieldsetComponent, ShowSlidesFieldsetComponent, SignatureStrippedDialogComponent, SignaturesPanelComponent, SignaturesService, SlideCanvasComponent, SlideDefaultInspectorComponent, SlideDiffChangesComponent, SlideDiffRowComponent, SlideDiffThumbnailsComponent, SlideSizeCardComponent, SlideSorterOverlayComponent, SlideThemeOverridePanelComponent, SlidesPanelComponent, SmartArt3DRendererComponent, SmartArt3DService, SmartArtPreviewComponent, SmartArtPropertiesComponent, SmartArtRendererComponent, StatusBarComponent, TABLE_STRUCTURE_TOGGLES, TEXT_DIRECTION_OPTIONS$1 as TEXT_DIRECTION_OPTIONS, THEME_CATALOG, TIMING_CURVE_OPTIONS, TRIGGER_OPTIONS, TYPE_LABELS, TableCellAdvancedFillComponent, TableCellFormattingComponent, TableDataEditorComponent, TablePropertiesComponent, TableRendererComponent, TableResizeOverlayComponent, TableSelectionService, TextAdvancedPanelComponent, ThemeEditorFieldsComponent, ThemeGalleryComponent, ThemeSelectorCardComponent, TitleBarComponent, VALIGN_OPTIONS, VIEWER_THEME, VersionHistoryPanelComponent, ViewerCanvasEditingService, ViewerCollabCursorService, ViewerCollaborationSessionService, ViewerCompareService, ViewerCustomShowsService, ViewerDialogsService, ViewerDocumentPropertiesService, ViewerExportService, ViewerExtraDialogsComponent, ViewerFileIOService, ViewerFindReplaceService, ViewerFormatPainterService, ViewerInspectorPanelService, ViewerKeyboardService, ViewerMobileSheetService, ViewerPresentationModeService, ViewerThemeGalleryService, ViewerTouchGesturesService, ViewerZoomService, WEBM_MIME_CANDIDATES, WriteBackScheduler, ZoomNavigationService, ZoomRendererComponent, ZoomTargetService, addCategory, addCommentToList, addGradientStopPatch, addItem, addSeries, addSubItem, advanceStep, alignPatch, animationFor, annotationMapToInkInserts, applyAcceptedDiff, applyAnimationPreset, applyFindReplacements, applyFormatToElement, applyMove, applyResize, applyTableStylePreset, asMediaElement, assignUserColor, attachTouchGestures, beginNodeEdit, boolFromEvent, bringForward, bringToFront, buildBarActions, buildBroadcastConfig, buildBroadcastViewerUrl, buildCategoryLabels, buildCellParagraphs, buildChartViewModel, buildChromeStyle, buildClearHyperlinkPatch, buildClickGroups, buildColStyles, buildCollaborationConfig, buildComboViewModel, buildCssGradientFromShapeStyle, buildDuotoneFilter, buildDuotoneFilterId, buildEmbeddedFontStyles, buildEquationElement, buildEquationSegment, buildFallbackViewModel, buildFontFaceRule, buildGradientFillCss, buildGridlinesAndLabels, buildHyperlinkPatch, buildInkContainerStyle, buildInkStrokes, buildLegend, buildModel3DContainerStyle, buildModel3DViewModel, buildOleActionModel, buildOleInfoRows, buildPatternFillCss, buildPrintHtmlDocument as buildPrintDocument, buildPropertiesPatch, buildRegionMapViewModel, buildSaveSlides, buildShareUrl, buildSmartArtInsertElement, buildSmartArtNodes, buildStockViewModel, buildSurfaceViewModel, buildTableViewModel, buildTreemapViewModel, buildTrimFragment, buildWaterfallViewModel, buildZeroLine, buildZoomContainerStyle, buildZoomViewModel, bulletIndentPx, canAddTopLevelNode, canRemoveTopLevelNode, canStartBroadcast, canStartShare, canUseClipboard, captionDisplayText, cellRunStyle, cellStyleToStyleMap, cellTdStyle, changeCountLabel, changeIcon, characterSpacingPatch, checkFontAvailable, clampCursorPosition, clampGifDimensions, clampIndex, clampNotesFontSize, clampScale, clampStep, clearAllLocalViewerData, clearAudienceContent, cn, collectAccessibilityIssues, collectElementText, collectSlideText, collectUsedFontFamilies, columnWidthStyle, commitNodeText, computeAlign, computeAxisTitlePrimitives, computeBarRects, computeBubbleRadius, computeCornerHandle, computeDataTablePrimitives, computeDistribute, computeDrawingViewBox, computeErrorBarPrimitives, computeHandleBoxes, computeHandoutLayout, computeIsMobile, computeIsTablet, computeLinePoints, computeLinearRegression, computePageCount, computePieLayout, computePieSlicePath, computePieSlices, computePlotLayout, computeRSquared, computeRadarPoints, computeScatterDots, computeSelectionBoxes, computeSingleSelected, computeSlideIndices, computeSnap, computeStackedBarRects, computeStackedValueRange, computeTextLines, computeTimerProgress, computeTrendlinePrimitives, computeValueRange, convertOmmlToMathMl, copyFormatFromElement, countAccessibilityIssues, countAnnotationStrokes, createCustomShow, createSwipeDismissDrag, createWebrtcBundle, createWebsocketBundle, cssObjectToStyleMap, currentColorScheme, currentLayout, currentStyle, defaultCssVars, defaultRadius, defaultThemeColors, deleteElementsByIds, deleteVersion as deleteRecoveryVersion, demoteNode, deriveModel3DBlobUrl, derivePresenceList, describeSmartArtBounds, disableGlowPatch, disableInnerShadowPatch, disableOuterShadowPatch, disableReflectionPatch, disableSoftEdgePatch, duplicateElementById, durationOf, effectsStateOf, enableGlowPatch, enableInnerShadowPatch, enableOuterShadowPatch, enableReflectionPatch, enableSoftEdgePatch, encodeGif, estimatePageCount, evenColumnWidths, evenRowHeights, exitPresentationFullscreen, extractPathPoints, eyedropperAvailable, fillColorOf, findInSlides, findOwningSlideIndex, findSlideIndexByElementId, fitPolynomial, fitZoom, fontMimeForFormat, fontSizeOf, formatAutoNumber, formatAxisValue, formatBytes, formatCursorLabel, formatElapsed, formatFileSize, formatPropertyDate, formatTime, fpsToFrameIntervalMs, generateBroadcastRoomId, generateCommentId, generateCustomShowId, generatePressureCircles, generateRulerTicks, getClrChangeParams, getContainerStyle, getDuotoneFilterDef, getImageSrc, getLocalStorageUsageSummary, getOleAriaLabel, getOleBadgeLabel, getOleDisplayName, getOleDownloadFileName, getOleTypeColor, getOleTypeLabel, getPasswordStrength, getPatternSvg, getPlaceholderStyle, getVersions as getRecoveryVersions, getResolvedShapeClipPath, getResolvedShapeClipPathFor, getShapeFillStrokeStyle, getSlideBackgroundStyle, getSlideTransitionAnimations, getSmartArtNodeBounds, getSpeechRecognitionCtor, getTextBlockStyle, getTextWarp, getTouchDistance, getWarpCategory, getWarpPath, gradientStateFromStyle, gradientStateOf, gradientStatePatch, gridColumns, groupElements, groupIssuesBySeverity, hasAnimation, hasCopyableFormat, hasExistingLink, hasExitedFullscreen, hasGradientFill, hasPressureVariation, headerLabel, inkViewBox, insertColumn, insertRow, interpolateWidth, isAudienceTab, isBold, isBrowserOpenableMime, isChildNode, isElementInteractive, isInjectableUrl, isItalic, isPpactionUrl, isPresenterMessage, isSigned, isTextElement, isUnderline, isUrlSafe, isValidRoomId, isViewportBackgroundPressTarget, isZoomActivationKey, issueTrackKey, issueTypeLabel, keyToLabel, latexToMathml, linePointsToSvgString, lineSpacingPatch, loadAudienceContent, mergeCaptionResults, mergeDown, mergeRight, mergeSelection, moveElementBy, moveNodeDown, moveNodeUp, msToFrameDelayCs, narrowToCircle, narrowToPolygon, narrowToRect, newChartElement, newEquationElement, newPresetShapeElement, newShapeElement, newSmartArtElement, newTableElement, newTextElement, nextVisibleIndex, nodeBold, nodeEditBox, nodeFillColor, nodeFontColor, nodeIdFromKey, nodeItalic, nodeStyle, normalizeFontFormat, normalizeSlidesPerPage, normalizeValue, numFromEvent, ommlToMathml, ooxmlDashToCssBorderStyle, openNativeEyeDropper, overallStatus, paletteColor, parseAudienceNonce, parseNodeTextarea, partitionSlides, patchChartData, patchChartStyle, patchTableData, patchTextStyle, pendingElementStyles, pickColorByClickFallback, pickSupportedMimeType, planGifFrames, planVideoSegments, pointsToSvgPathD, presenceToCursors, presetByLayout, presetsForCategory, pressuresToWidths, prevVisibleIndex, projectDrawingShapes, promoteNode, provideViewerTheme, radarAngle, radarRingPoints, recordWebm, redistributeColumnWidth, removeAnimation, removeCategory, removeColumn, removeCommentFromList, removeElementAnimation, removeGradientStopPatch, removeNode, removeRow, removeSeries, renderToCanvas, reorderAnimationDown, reorderAnimationUp, replaceInSlides, replaceMatch, requestPresentationFullscreen, resizeElement, resolveCaptionTracks, resolveChartKind, resolveFontVariant, resolveHyperlinkHref, resolveInteractiveElementId, resolveMediaSrc, resolveOleType, resolveParagraphBullet, resolvePresenterNotes, resolveProfileInitial, resolveRegionCode, resolvePalette as resolveSmartArtPalette, resolveThemeCatalogEntry, resolveTransitionDuration, revealedElementStyles, routeOrthogonalConnector, rowStyle, sampleColorFromSlide, sanitizeColor, sanitizeSlideIndex, sanitizeUserName, saveViewerProfile, scanAvailableFonts, searchSlides, seedBroadcastFields, seedHyperlinkDraft, seedPropertiesDraft, seedShareFields, segmentFrameCount, selectValue$2 as selectValue, sendBackward, sendToBack, sequentialColorScale, serializeWriteBack, seriesColor, setAnimationEmphasis, setAnimationEntrance, setAnimationExit, setAxis, setAxisLogScale, setAxisTitleStyle, setCategoryLabel, setCellText, setColorScheme, setDataLabels, setDataPointExplosion, setDataPointFill, setDataPointLabel, setDelay, setDirection, setDuration, setElementPosition, setGridlineStyle, setLayout, setLegend, setNodeStyle, setNodeText, setRepeatCount, setRepeatMode, setSequence, setSeriesChartType, setSeriesColor, setSeriesErrorBars, setSeriesMarker, setSeriesName, setSeriesTrendline, setSeriesValue, setStyle, setTimingCurve, setTitle, setTrigger, setTriggerShapeId, shapeStylePatch, sheetAfterNavigate, shouldBlockClickAdvance, shouldUseSvgWarp, showDirectionPicker, showsTemplateAffordance, signatureCountLabel, signatureKey, signatureTimestamp, signerName, statusLabel as slideDiffStatusLabel, slideNumberOf, smartArtNodes, paletteColour as smartArtPaletteColour, snapToGridStep, splitCursorCell, splitMergedCell, statusKind, statusLabel$1 as statusLabel, storeAudienceContent, stringFromEvent$5 as stringFromEvent, strokeColorOf, strokeToInkElement, styleShadowFilter, textAdvancedPatch, textAdvancedStateFromStyle, textAdvancedStateOf, textColorOf, textDirectionPatch, textStyleOf, textStylePatch, themeStyle, themeToCssVars, thumbnailHeight, thumbnailZoom, toggleCommentResolvedInList, toggleNodeBold, toggleNodeItalic, toggleSheet, topLevelNodeCount, transformSelectedTextCase, translationsEn, ungroupElements, updateElementById, updateGlowPatch, updateGradientStopPatch, updateInnerShadowPatch, updateOuterShadowPatch, updateReflectionPatch, vAlignPatch, validatePassword, validatePrintSettings, validateRoomId, valueToY, vermilionDarkColors, vermilionDarkTheme, vermilionLightColors, vermilionLightTheme, vermilionRadius, waypointsToPathD, worstStatus, zoomTargetSlideIndex };
100784
101775
  //# sourceMappingURL=pptx-angular-viewer.mjs.map