pptx-angular-viewer 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,12 +2,12 @@ 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, DestroyRef, Injector, effect, viewChild, ElementRef, HostListener, afterNextRender, Renderer2, ViewEncapsulation, forwardRef, untracked } from '@angular/core';
4
4
  import { TranslatePipe, TranslateService, translate } from '@ngx-translate/core';
5
- import { THEME_PRESETS, guideEmuToPx, getShapeClipPath, getAdjustmentAwareClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, ooxmlGradientAngleToCssDegrees, hasShapeProperties, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, isImageLikeElement, hasTextProperties, stripXmlOrderSuffix, orderedXmlKey, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, rebuildTableStructureInRawXml, updateCellTextInRawXml, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$2, getLinkedTextBoxSegments, deriveSlideTitle, getSubstituteFontFamily, svgPathToPolygons, THEME_COLOR_SCHEME_KEYS, createBuiltinVariables, resolveCoordinate, PptxHandler, deobfuscateFont, detectFontFormat, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, PptxMarkdownConverter, ThemePresets, formatCommentTimestamp as formatCommentTimestamp$1, 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, chartDataChangeType, getOleObjectTypeLabel, addSmartArtNodeAsChild, removeSmartArtNode, promoteSmartArtNode, demoteSmartArtNode, reorderSmartArtNode, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, applyThemeOverrideToSlide, TRANSITION_VALID_DIRECTIONS, getAnimationPresetInfo } from 'pptx-viewer-core';
5
+ import { THEME_PRESETS, guideEmuToPx, getShapeClipPath, getAdjustmentAwareClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, ooxmlGradientAngleToCssDegrees, hasShapeProperties, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, isImageLikeElement, hasTextProperties, stripXmlOrderSuffix, orderedXmlKey, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, rebuildTableStructureInRawXml, updateCellTextInRawXml, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$2, getLinkedTextBoxSegments, deriveSlideTitle, getSubstituteFontFamily, svgPathToPolygons, THEME_COLOR_SCHEME_KEYS, createBuiltinVariables, resolveCoordinate, PptxHandler, deobfuscateFont, detectFontFormat, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, PptxMarkdownConverter, ThemePresets, formatCommentTimestamp as formatCommentTimestamp$1, 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, setChartDataPointMarker, setChartDataPointExplosion, chartDataChangeType, getOleObjectTypeLabel, addSmartArtNodeAsChild, removeSmartArtNode, promoteSmartArtNode, demoteSmartArtNode, reorderSmartArtNode, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, applyThemeOverrideToSlide, TRANSITION_VALID_DIRECTIONS, getAnimationPresetInfo } from 'pptx-viewer-core';
6
6
  import DOMPurify from 'dompurify';
7
7
  import { z } from 'zod';
8
8
  import * as mcp from 'pptx-viewer-mcp';
9
9
  import * as mcpSchemas from 'pptx-viewer-mcp/schemas';
10
- import { LucideX, LucideSquare, LucideCircle, LucideSlash, LucideCopy, LucideTrash2, LucideChevronsUp, LucideChevronsDown, LucideArrowUp, LucideArrowDown, LucideGroup, LucideUngroup, LucideTextAlignStart, LucideTextAlignCenter, LucideTextAlignEnd, LucideChevronUp, LucideChevronDown, LucideAlignHorizontalSpaceAround, LucideAlignVerticalSpaceAround, LucideLayers, LucideMessageSquare, LucidePlus, LucideSettings2, LucideStickyNote, LucideDownload, LucideMenu, LucidePresentation, LucideRedo, LucideShare2, LucideSparkles, LucideUndo, LucideChevronRight, LucidePenTool, LucideHighlighter, LucideEraser, LucideMousePointer2, LucideChevronLeft, LucideStar, LucidePlay, LucideMoveRight, LucideMousePointerClick, LucidePaintbrush, LucideClock3, LucidePanelRight, LucidePencil, LucideType, LucideMinus, LucideSpline, LucideArrowLeft, LucideFilePlus2, LucideFolderOpen, LucideHome, LucideInfo, LucidePrinter, LucideSave, LucideSettings, LucideUpload, LucideUserRound, LucideAArrowUp, LucideAArrowDown, LucideRemoveFormatting, LucideList, LucideListOrdered, LucideListIndentDecrease, LucideListIndentIncrease, LucideTextAlignJustify, LucideClipboardPaste, LucideFolderPlus, LucideLayoutGrid, LucideRotateCcw, LucideScissors, LucideLink, LucideImage, LucideVideo, LucideDatabase, LucidePanelLeft, LucideEllipsis, LucideEye, LucideEyeOff, LucideMonitor, LucideColumns2, LucideCheck, LucideFileDown, LucideSearch, LucideZoomIn, LucideZoomOut, LucideBug, LucideSend, LucideCrosshair, LucideGitMerge, LucidePin, LucidePinOff, LucideWrench, LucideLoaderCircle, LucideTriangleAlert, LucideShapes, LucidePalette, LucideTable, LucideLayoutTemplate, LucideChartColumn, LucideMove, LucideNavigation, LucideFilm, LucideBot, LucideUser } from '@lucide/angular';
10
+ import { LucideX, LucideSquare, LucideCircle, LucideSlash, LucideCopy, LucideTrash2, LucideChevronsUp, LucideChevronsDown, LucideArrowUp, LucideArrowDown, LucideGroup, LucideUngroup, LucideTextAlignStart, LucideTextAlignCenter, LucideTextAlignEnd, LucideChevronUp, LucideChevronDown, LucideAlignHorizontalSpaceAround, LucideAlignVerticalSpaceAround, LucideLayers, LucideMessageSquare, LucidePlus, LucideSettings2, LucideStickyNote, LucideDownload, LucideMenu, LucidePresentation, LucideRedo, LucideShare2, LucideSparkles, LucideUndo, LucideChevronRight, LucidePenTool, LucideHighlighter, LucideEraser, LucideMousePointer2, LucideChevronLeft, LucideStar, LucideMoveRight, LucidePlay, LucideMousePointerClick, LucidePaintbrush, LucideClock3, LucidePanelRight, LucidePencil, LucideType, LucideMinus, LucideSpline, LucideArrowLeft, LucideFilePlus2, LucideFolderOpen, LucideHome, LucideInfo, LucidePrinter, LucideSave, LucideSettings, LucideUpload, LucideUserRound, LucideAArrowUp, LucideAArrowDown, LucideRemoveFormatting, LucideList, LucideListOrdered, LucideListIndentDecrease, LucideListIndentIncrease, LucideTextAlignJustify, LucideClipboardPaste, LucideFolderPlus, LucideLayoutGrid, LucideRotateCcw, LucideScissors, LucideLink, LucideImage, LucideVideo, LucideDatabase, LucidePanelLeft, LucideEllipsis, LucideEye, LucideEyeOff, LucideMonitor, LucideColumns2, LucideCheck, LucideFileDown, LucideSearch, LucideZoomIn, LucideZoomOut, LucideBug, LucideSend, LucideCrosshair, LucideGitMerge, LucidePin, LucidePinOff, LucideWrench, LucideLoaderCircle, LucideTriangleAlert, LucideShapes, LucidePalette, LucideTable, LucideLayoutTemplate, LucideChartColumn, LucideMove, LucideNavigation, LucideFilm, LucideBot, LucideUser } from '@lucide/angular';
11
11
  import { XMLParser } from 'fast-xml-parser';
12
12
  import JSZip from 'jszip';
13
13
  import { DomSanitizer } from '@angular/platform-browser';
@@ -7284,6 +7284,30 @@ function resolveDataPointExplosion(series, pointIndex) {
7284
7284
  const point = findDataPoint(series, pointIndex);
7285
7285
  return point?.explosion ?? series.explosion ?? 0;
7286
7286
  }
7287
+ /**
7288
+ * Resolve the marker actually drawn for one data point.
7289
+ *
7290
+ * OOXML lets a `c:dPt` carry its own `c:marker`, which overrides the series
7291
+ * `c:ser/c:marker` for that point alone: the classic use is highlighting a
7292
+ * single outlier with a bigger star while the rest of the line keeps its small
7293
+ * circles. Every field falls back independently, matching PowerPoint: a point
7294
+ * marker that sets only `c:symbol` keeps the series size and fill.
7295
+ *
7296
+ * This exists because the per-point marker was a write-only feature. The core
7297
+ * parser read `c:dPt/c:marker`, the save writer round-tripped it, and three
7298
+ * bindings shipped an inspector control for it, but the renderer only ever
7299
+ * looked at `series.marker`, so editing a point marker changed the file and
7300
+ * left the canvas untouched. Resolving it here fixes every binding at once,
7301
+ * since they all paint the same shared view model.
7302
+ */
7303
+ function resolveDataPointMarker(series, pointIndex) {
7304
+ const point = findDataPoint(series, pointIndex)?.marker;
7305
+ return {
7306
+ symbol: point?.symbol ?? series.marker?.symbol,
7307
+ size: point?.size ?? series.marker?.size,
7308
+ fill: point?.spPr?.fillColor ?? series.marker?.spPr?.fillColor,
7309
+ };
7310
+ }
7287
7311
 
7288
7312
  /**
7289
7313
  * chart-sparkline.ts — framework-agnostic inline mini-chart (sparkline) SVG
@@ -8619,14 +8643,22 @@ function buildMarkerPrimitive(input) {
8619
8643
  /**
8620
8644
  * Build the marker for a data point (honouring `marker.symbol`/`size`) and push
8621
8645
  * it, unless the symbol is `none`. Shared by the line / area / scatter builders.
8646
+ *
8647
+ * `pointIndex` is the SOURCE index (the `c:idx` a `c:dPt` is keyed by), not the
8648
+ * display position, because a chart with hidden/filtered categories renders
8649
+ * fewer points than the series declares and the override must still land on the
8650
+ * point the author picked.
8622
8651
  */
8623
- function pushMarker(out, series, cx, cy, fill, defaultRadius, part, opacity) {
8652
+ function pushMarker(out, series, pointIndex, cx, cy, fill, defaultRadius, part, opacity) {
8653
+ const marker = resolveDataPointMarker(series, pointIndex);
8624
8654
  const m = buildMarkerPrimitive({
8625
- symbol: series.marker?.symbol,
8626
- size: series.marker?.size,
8655
+ symbol: marker.symbol,
8656
+ size: marker.size,
8627
8657
  cx,
8628
8658
  cy,
8629
- fill,
8659
+ // A marker fill (series or per-point) wins over the plot fill, which is
8660
+ // what the line/area body is drawn with.
8661
+ fill: marker.fill ?? fill,
8630
8662
  defaultRadius,
8631
8663
  part,
8632
8664
  });
@@ -8703,7 +8735,7 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
8703
8735
  }
8704
8736
  const idx = sourceIndices[displayIndex] ?? displayIndex;
8705
8737
  const part = { role: 'dataPoint', seriesIndex: si, pointIndex: idx };
8706
- pushMarker(primitives, series, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2.5, part);
8738
+ pushMarker(primitives, series, idx, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2.5, part);
8707
8739
  });
8708
8740
  if (showLabels) {
8709
8741
  displayValues.forEach((val, displayIndex) => {
@@ -8767,7 +8799,7 @@ function buildAreas(chartData, catCount, layout, range, sourceIndices, xPosition
8767
8799
  pts.forEach((pt, displayIndex) => {
8768
8800
  const idx = sourceIndices[displayIndex] ?? displayIndex;
8769
8801
  const part = { role: 'dataPoint', seriesIndex: si, pointIndex: idx };
8770
- pushMarker(primitives, series, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2, part);
8802
+ pushMarker(primitives, series, idx, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2, part);
8771
8803
  });
8772
8804
  if (showLabels) {
8773
8805
  displayValues.forEach((val, displayIndex) => {
@@ -8803,7 +8835,7 @@ function buildScatter(chartData, layout, range) {
8803
8835
  const dots = computeScatterDots(series.values, maxXIndex, layout, range, xValues);
8804
8836
  dots.forEach((dot, vi) => {
8805
8837
  const part = { role: 'dataPoint', seriesIndex: si, pointIndex: vi };
8806
- pushMarker(primitives, series, dot.cx, dot.cy, resolveDataPointFill(series, vi, c) ?? c, 4, part, 0.85);
8838
+ pushMarker(primitives, series, vi, dot.cx, dot.cy, resolveDataPointFill(series, vi, c) ?? c, 4, part, 0.85);
8807
8839
  });
8808
8840
  if (showLabels) {
8809
8841
  series.values.forEach((val, vi) => {
@@ -14591,7 +14623,7 @@ const DIRECTIONAL_PRESETS = new Set([
14591
14623
  // Defaults for a freshly-created animation entry
14592
14624
  // ==========================================================================
14593
14625
  const DEFAULT_DURATION_MS$1 = 500;
14594
- const DEFAULT_TRIGGER = 'onClick';
14626
+ const DEFAULT_TRIGGER$1 = 'onClick';
14595
14627
  // ==========================================================================
14596
14628
  // Readers
14597
14629
  // ==========================================================================
@@ -14604,11 +14636,15 @@ function animationFor(slideAnimations, elementId) {
14604
14636
  }
14605
14637
  /**
14606
14638
  * Returns `true` when the element has at least one active effect (entrance,
14607
- * exit, or emphasis) in the slide's animation list.
14639
+ * exit, emphasis, or a motion path) in the slide's animation list.
14640
+ *
14641
+ * The motion path counts because it is driven by the same entry's timing
14642
+ * fields: without it, a motion-path-only element would hide its own
14643
+ * duration / delay / trigger controls.
14608
14644
  */
14609
14645
  function hasAnimation(slideAnimations, elementId) {
14610
14646
  const entry = animationFor(slideAnimations, elementId);
14611
- return Boolean(entry && (entry.entrance || entry.exit || entry.emphasis));
14647
+ return Boolean(entry && (entry.entrance || entry.exit || entry.emphasis || entry.motionPath));
14612
14648
  }
14613
14649
  /**
14614
14650
  * Returns `true` when the active animation entry has a preset that supports
@@ -14644,7 +14680,7 @@ function upsert(anims, elementId, updater) {
14644
14680
  elementId,
14645
14681
  durationMs: DEFAULT_DURATION_MS$1,
14646
14682
  order: anims.length,
14647
- trigger: DEFAULT_TRIGGER,
14683
+ trigger: DEFAULT_TRIGGER$1,
14648
14684
  };
14649
14685
  const updated = updater(created);
14650
14686
  if (updated === null) {
@@ -14656,7 +14692,9 @@ function setEffect(anims, elementId, group, preset) {
14656
14692
  const value = preset === 'none' ? undefined : preset;
14657
14693
  return upsert(anims, elementId, (cur) => {
14658
14694
  const next = { ...cur, [group]: value };
14659
- if (!next.entrance && !next.exit && !next.emphasis) {
14695
+ // A motion path keeps the entry alive: clearing the entrance preset must
14696
+ // not silently delete the path the user drew on the canvas.
14697
+ if (!next.entrance && !next.exit && !next.emphasis && !next.motionPath) {
14660
14698
  return null;
14661
14699
  }
14662
14700
  return next;
@@ -14785,7 +14823,7 @@ function applyAnimationPreset(animations, elementId, group, preset) {
14785
14823
  [group]: preset,
14786
14824
  durationMs: DEFAULT_DURATION_MS$1,
14787
14825
  order: animations.length,
14788
- trigger: DEFAULT_TRIGGER,
14826
+ trigger: DEFAULT_TRIGGER$1,
14789
14827
  },
14790
14828
  ];
14791
14829
  }
@@ -16026,6 +16064,23 @@ function applyFlyDirection(effect, subtype) {
16026
16064
  // ==========================================================================
16027
16065
  // Dynamic keyframe generation (motion path / rotation / scale / colour)
16028
16066
  // ==========================================================================
16067
+ /**
16068
+ * Express one motion-path coordinate as a CSS length measured against the SLIDE.
16069
+ *
16070
+ * OOXML motion-path numbers are fractions of the slide, but a CSS
16071
+ * `translate(%)` resolves against the ELEMENT's own box, so every parsed path
16072
+ * used to under-travel by the ratio between the two (a small shape barely
16073
+ * moved). The offset is therefore emitted as `calc(var(--pptx-slide-w) * f)`;
16074
+ * a binding sets those custom properties on its slide stage, and the fallback
16075
+ * is the default canvas size so an unset stage still travels a sane distance.
16076
+ *
16077
+ * @param percent - Sampled coordinate in percent-of-slide units (path * 100).
16078
+ * @param axis - `w` for the horizontal offset, `h` for the vertical one.
16079
+ */
16080
+ function slideOffset(percent, axis) {
16081
+ const fallback = axis === 'w' ? '1280px' : '720px';
16082
+ return `calc(var(--pptx-slide-${axis}, ${fallback}) * ${(percent / 100).toFixed(4)})`;
16083
+ }
16029
16084
  /**
16030
16085
  * Build a dynamic CSS `@keyframes` block for motion path, rotation, scale, or
16031
16086
  * colour animations that don't map to a static effect preset. Uses the
@@ -16043,7 +16098,7 @@ function buildDynamicKeyframes(anim, uid) {
16043
16098
  const kfLines = [];
16044
16099
  for (let i = 0; i < points.length; i++) {
16045
16100
  const pct = Math.round((i / (points.length - 1)) * 100);
16046
- kfLines.push(`\t${pct}% { transform: translate(${points[i].x.toFixed(2)}%, ${points[i].y.toFixed(2)}%); }`);
16101
+ kfLines.push(`\t${pct}% { transform: translate(${slideOffset(points[i].x, 'w')}, ${slideOffset(points[i].y, 'h')}); }`);
16047
16102
  }
16048
16103
  return {
16049
16104
  keyframeName: name,
@@ -16102,8 +16157,8 @@ function buildDynamicKeyframe(anim, uid) {
16102
16157
  const lines = [];
16103
16158
  for (let i = 0; i < points.length; i++) {
16104
16159
  const pct = Math.round((i / (points.length - 1)) * 100);
16105
- const tx = points[i].x.toFixed(2);
16106
- const ty = points[i].y.toFixed(2);
16160
+ const tx = slideOffset(points[i].x, 'w');
16161
+ const ty = slideOffset(points[i].y, 'h');
16107
16162
  if (anim.motionPathRotateAuto) {
16108
16163
  // Compute the tangent angle at this point using the direction
16109
16164
  // to the next point (or from the previous point for the last one).
@@ -16112,10 +16167,10 @@ function buildDynamicKeyframe(anim, uid) {
16112
16167
  const dx = i < points.length - 1 ? next.x - points[i].x : points[i].x - prev.x;
16113
16168
  const dy = i < points.length - 1 ? next.y - points[i].y : points[i].y - prev.y;
16114
16169
  const angleDeg = Math.atan2(dy, dx) * (180 / Math.PI);
16115
- lines.push(`\t${pct}% { transform: translate(${tx}%, ${ty}%) rotate(${angleDeg.toFixed(2)}deg); }`);
16170
+ lines.push(`\t${pct}% { transform: translate(${tx}, ${ty}) rotate(${angleDeg.toFixed(2)}deg); }`);
16116
16171
  }
16117
16172
  else {
16118
- lines.push(`\t${pct}% { transform: translate(${tx}%, ${ty}%); }`);
16173
+ lines.push(`\t${pct}% { transform: translate(${tx}, ${ty}); }`);
16119
16174
  }
16120
16175
  }
16121
16176
  return {
@@ -17877,6 +17932,662 @@ class TimelineEngine {
17877
17932
  }
17878
17933
  }
17879
17934
 
17935
+ /**
17936
+ * `animation-preview` — pure preview-descriptor construction for the editor's
17937
+ * animation panel: map an editor preset (+ direction / timing) to a CSS
17938
+ * keyframe name, definition and shorthand. The DOM player that injects the
17939
+ * `<style>` and toggles the element animation stays in each binding.
17940
+ *
17941
+ * @module render/animation-preview
17942
+ */
17943
+ // ==========================================================================
17944
+ // Preview keyframes generation
17945
+ // ==========================================================================
17946
+ /** Map editor-level animation presets to the internal effect names used by
17947
+ * the keyframe definition table. */
17948
+ const PRESET_TO_EFFECT = {
17949
+ fadeIn: 'fadeIn',
17950
+ flyIn: 'flyInBottom',
17951
+ zoomIn: 'zoomIn',
17952
+ fadeOut: 'fadeOut',
17953
+ flyOut: 'flyOutBottom',
17954
+ zoomOut: 'zoomOut',
17955
+ spin: 'spin',
17956
+ pulse: 'pulse',
17957
+ colorWave: 'colorWave',
17958
+ bounce: 'bounce',
17959
+ flash: 'flash',
17960
+ };
17961
+ /** Direction-aware fly-in/out effect name overrides. */
17962
+ const DIRECTION_FLY_MAP = {
17963
+ fromLeft: { flyIn: 'flyInLeft', flyOut: 'flyOutLeft' },
17964
+ fromRight: { flyIn: 'flyInRight', flyOut: 'flyOutRight' },
17965
+ fromTop: { flyIn: 'flyInTop', flyOut: 'flyOutTop' },
17966
+ fromBottom: { flyIn: 'flyInBottom', flyOut: 'flyOutBottom' },
17967
+ fromTopLeft: { flyIn: 'flyInTop', flyOut: 'flyOutTop' },
17968
+ fromTopRight: { flyIn: 'flyInTop', flyOut: 'flyOutTop' },
17969
+ fromBottomLeft: { flyIn: 'flyInBottom', flyOut: 'flyOutBottom' },
17970
+ fromBottomRight: { flyIn: 'flyInBottom', flyOut: 'flyOutBottom' },
17971
+ };
17972
+ /**
17973
+ * Resolve the CSS @keyframes effect name for a given preset and direction.
17974
+ */
17975
+ function resolvePreviewEffect(preset, direction) {
17976
+ if ((preset === 'flyIn' || preset === 'flyOut') && direction) {
17977
+ const dirMap = DIRECTION_FLY_MAP[direction];
17978
+ if (dirMap) {
17979
+ return preset === 'flyIn' ? dirMap.flyIn : dirMap.flyOut;
17980
+ }
17981
+ }
17982
+ return PRESET_TO_EFFECT[preset];
17983
+ }
17984
+ /**
17985
+ * Map a timing curve name to a CSS easing string.
17986
+ * Supports the standard OOXML timing curves plus cubic-bezier extraction.
17987
+ */
17988
+ function timingCurveToCss(curve, cubicBezierValues) {
17989
+ if (cubicBezierValues) {
17990
+ // Validate cubic-bezier format: "x1,y1,x2,y2"
17991
+ const parts = cubicBezierValues.split(',').map((s) => s.trim());
17992
+ if (parts.length === 4 && parts.every((p) => !Number.isNaN(Number(p)))) {
17993
+ return `cubic-bezier(${parts.join(', ')})`;
17994
+ }
17995
+ }
17996
+ switch (curve) {
17997
+ case 'ease':
17998
+ return 'ease';
17999
+ case 'ease-in':
18000
+ return 'ease-in';
18001
+ case 'ease-out':
18002
+ return 'ease-out';
18003
+ case 'linear':
18004
+ return 'linear';
18005
+ default:
18006
+ return 'ease';
18007
+ }
18008
+ }
18009
+ /**
18010
+ * Build a preview animation descriptor for a given preset.
18011
+ *
18012
+ * Returns `undefined` if the preset doesn't have a known effect.
18013
+ */
18014
+ function buildPreviewAnimation(preset, options) {
18015
+ if (preset === 'none') {
18016
+ return undefined;
18017
+ }
18018
+ const effectName = resolvePreviewEffect(preset, options?.direction);
18019
+ if (!effectName) {
18020
+ return undefined;
18021
+ }
18022
+ const keyframeName = `pptx-${effectName}`;
18023
+ const keyframesCss = getEffectKeyframes(effectName);
18024
+ if (!keyframesCss) {
18025
+ return undefined;
18026
+ }
18027
+ const duration = options?.durationMs ?? 600;
18028
+ const easing = timingCurveToCss(options?.timingCurve, options?.cubicBezier);
18029
+ return {
18030
+ keyframeName,
18031
+ keyframesCss,
18032
+ cssAnimation: `${keyframeName} ${duration}ms ${easing} 0ms 1 normal both`,
18033
+ durationMs: duration,
18034
+ };
18035
+ }
18036
+ // ==========================================================================
18037
+ // Timing curve extraction from OOXML bezier values
18038
+ // ==========================================================================
18039
+ /**
18040
+ * Parse OOXML timing curve bezier values from `a:cTn/a:timing/a:curve`.
18041
+ *
18042
+ * OOXML stores bezier control points as attributes:
18043
+ * - `x1`, `y1` — first control point (0..100000 range)
18044
+ * - `x2`, `y2` — second control point (0..100000 range)
18045
+ *
18046
+ * Returns a CSS `cubic-bezier()` string, or undefined if not parseable.
18047
+ */
18048
+ function parseOoxmlBezierCurve(x1, y1, x2, y2) {
18049
+ if (x1 === undefined || y1 === undefined || x2 === undefined || y2 === undefined) {
18050
+ return undefined;
18051
+ }
18052
+ // OOXML values are in 0..100000 range, CSS cubic-bezier uses 0..1
18053
+ const cx1 = Math.max(0, Math.min(1, x1 / 100000));
18054
+ const cy1 = y1 / 100000; // y can exceed 0..1 range for overshoot
18055
+ const cx2 = Math.max(0, Math.min(1, x2 / 100000));
18056
+ const cy2 = y2 / 100000;
18057
+ return `${cx1.toFixed(4)},${cy1.toFixed(4)},${cx2.toFixed(4)},${cy2.toFixed(4)}`;
18058
+ }
18059
+
18060
+ /**
18061
+ * `motion-path-geometry`: turn an OOXML motion path into the things the
18062
+ * authoring UI needs - an SVG outline to draw over the canvas, the draggable
18063
+ * start/end handles, an edited path string after a drag, and the `@keyframes`
18064
+ * block that plays the motion.
18065
+ *
18066
+ * WHY this is separate from `animation-motion-path`: that module samples a path
18067
+ * into waypoints for the native-timeline playback engine and expresses them as
18068
+ * percentages of the element box. Authoring needs the *slide* space PowerPoint
18069
+ * actually stores (`@path` numbers are fractions of the slide, measured from
18070
+ * the element centre), plus the ability to write a modified path back out. Both
18071
+ * share the sampler; only this module knows about pixels and editing.
18072
+ *
18073
+ * @module render/motion-path-geometry
18074
+ */
18075
+ /** Commands this module can rewrite. Anything else makes a path read-only. */
18076
+ const EDITABLE = new Set(['M', 'L', 'C', 'Z', 'm', 'l', 'c', 'z']);
18077
+ /** Coordinate count per command letter. */
18078
+ const ARITY = { m: 2, l: 2, c: 6, z: 0 };
18079
+ /**
18080
+ * Tokenise a path into segments. Returns `undefined` when the path contains a
18081
+ * command this module cannot faithfully rewrite (arcs, quadratics, PowerPoint's
18082
+ * trailing `E` marker), so callers can fall back to read-only behaviour instead
18083
+ * of silently corrupting the geometry.
18084
+ */
18085
+ function scanPath(path) {
18086
+ const tokens = path.match(/[A-Za-z]|-?\d*\.?\d+(?:[eE][-+]?\d+)?/gu);
18087
+ if (!tokens) {
18088
+ return undefined;
18089
+ }
18090
+ const segments = [];
18091
+ let i = 0;
18092
+ let cmd = '';
18093
+ while (i < tokens.length) {
18094
+ if (/^[A-Za-z]$/u.test(tokens[i])) {
18095
+ cmd = tokens[i];
18096
+ i++;
18097
+ if (!EDITABLE.has(cmd)) {
18098
+ return undefined;
18099
+ }
18100
+ if (cmd === 'Z' || cmd === 'z') {
18101
+ segments.push({ cmd, coords: [] });
18102
+ }
18103
+ continue;
18104
+ }
18105
+ const arity = ARITY[cmd.toLowerCase()];
18106
+ if (!arity) {
18107
+ return undefined;
18108
+ }
18109
+ const coords = [];
18110
+ for (let n = 0; n < arity; n++) {
18111
+ const value = Number(tokens[i++]);
18112
+ if (!Number.isFinite(value)) {
18113
+ return undefined;
18114
+ }
18115
+ coords.push(value);
18116
+ }
18117
+ segments.push({ cmd, coords });
18118
+ // A repeated coordinate run after `M` is an implicit lineto, as in SVG.
18119
+ if (cmd === 'M') {
18120
+ cmd = 'L';
18121
+ }
18122
+ else if (cmd === 'm') {
18123
+ cmd = 'l';
18124
+ }
18125
+ }
18126
+ return segments.length > 0 ? segments : undefined;
18127
+ }
18128
+ /** Render segments back to a path string, trimming float noise. */
18129
+ function formatPath(segments) {
18130
+ return segments
18131
+ .map((segment) => segment.coords.length === 0
18132
+ ? segment.cmd
18133
+ : `${segment.cmd} ${segment.coords.map((n) => trimNumber(n)).join(' ')}`)
18134
+ .join(' ');
18135
+ }
18136
+ /** Format a coordinate with at most 4 decimals and no trailing zeros. */
18137
+ function trimNumber(value) {
18138
+ return String(Number(value.toFixed(4)));
18139
+ }
18140
+ /**
18141
+ * Sample the path into points expressed as slide fractions relative to the
18142
+ * element centre (the same space the path string uses).
18143
+ */
18144
+ function motionPathFractionPoints(path) {
18145
+ // `parseMotionPathPoints` returns percentages; authoring works in fractions.
18146
+ return parseMotionPathPoints(path).map((point) => ({ x: point.x / 100, y: point.y / 100 }));
18147
+ }
18148
+ /** Convert a fraction-space point to slide pixels within `frame`. */
18149
+ function toPixels(point, frame) {
18150
+ return {
18151
+ x: frame.originX + point.x * frame.slideWidth,
18152
+ y: frame.originY + point.y * frame.slideHeight,
18153
+ };
18154
+ }
18155
+ /**
18156
+ * Build an SVG `d` attribute (slide pixels) tracing the motion the element will
18157
+ * make, so the canvas can draw the path where the shape will travel.
18158
+ */
18159
+ function motionPathToSvgD(path, frame) {
18160
+ const points = motionPathFractionPoints(path).map((point) => toPixels(point, frame));
18161
+ if (points.length === 0) {
18162
+ return '';
18163
+ }
18164
+ const head = `M ${trimNumber(frame.originX)} ${trimNumber(frame.originY)}`;
18165
+ const rest = points.map((point) => `L ${trimNumber(point.x)} ${trimNumber(point.y)}`);
18166
+ return [head, ...rest].join(' ');
18167
+ }
18168
+ /** The path's final waypoint in slide pixels (the draggable end handle). */
18169
+ function motionPathEndPixel(path, frame) {
18170
+ const points = motionPathFractionPoints(path);
18171
+ // Indexed rather than `.at(-1)`: Angular vendors this file and compiles it
18172
+ // against an ES2021 lib, where `Array.prototype.at` does not exist.
18173
+ const last = points.length > 0 ? points[points.length - 1] : undefined;
18174
+ return last ? toPixels(last, frame) : { x: frame.originX, y: frame.originY };
18175
+ }
18176
+ /** The path's final waypoint in slide fractions. */
18177
+ function motionPathEndFraction(path) {
18178
+ // Indexed rather than `.at(-1)`: see the note in {@link motionPathEndPixel}.
18179
+ const points = motionPathFractionPoints(path);
18180
+ return points.length > 0 ? points[points.length - 1] : { x: 0, y: 0 };
18181
+ }
18182
+ /**
18183
+ * Move the path's end point to `(x, y)` (slide fractions), returning a new path
18184
+ * string. The last drawn command's endpoint is rewritten; a bezier's control
18185
+ * handles shift with it so the curve keeps its shape instead of kinking.
18186
+ * Returns the input unchanged for closed or unparseable paths, which have no
18187
+ * meaningful free end.
18188
+ */
18189
+ function setMotionPathEnd(path, x, y) {
18190
+ const segments = scanPath(path);
18191
+ if (!segments) {
18192
+ return path;
18193
+ }
18194
+ // Reverse scan rather than `findLastIndex`: Angular vendors this file and
18195
+ // compiles it against an ES2021 lib, where that method does not exist.
18196
+ let lastIndex = -1;
18197
+ for (let i = segments.length - 1; i >= 0; i--) {
18198
+ if (segments[i].coords.length > 0) {
18199
+ lastIndex = i;
18200
+ break;
18201
+ }
18202
+ }
18203
+ if (lastIndex < 0 || segments.some((segment) => segment.cmd === 'Z' || segment.cmd === 'z')) {
18204
+ return path;
18205
+ }
18206
+ const last = segments[lastIndex];
18207
+ if (last.cmd === last.cmd.toLowerCase()) {
18208
+ // Relative commands would need the running cursor; not worth the risk.
18209
+ return path;
18210
+ }
18211
+ const coords = [...last.coords];
18212
+ const endX = coords[coords.length - 2];
18213
+ const endY = coords[coords.length - 1];
18214
+ const dx = x - endX;
18215
+ const dy = y - endY;
18216
+ coords[coords.length - 2] = x;
18217
+ coords[coords.length - 1] = y;
18218
+ if (last.cmd === 'C') {
18219
+ // Drag the trailing control point along so the tangent is preserved.
18220
+ coords[2] += dx;
18221
+ coords[3] += dy;
18222
+ }
18223
+ const next = segments.map((segment, index) => index === lastIndex ? { ...last, coords } : segment);
18224
+ return formatPath(next);
18225
+ }
18226
+ /**
18227
+ * Shift every absolute coordinate by `(dx, dy)` slide fractions: the "drag the
18228
+ * whole path" gesture. Relative commands are deltas and stay untouched.
18229
+ */
18230
+ function translateMotionPath(path, dx, dy) {
18231
+ const segments = scanPath(path);
18232
+ if (!segments) {
18233
+ return path;
18234
+ }
18235
+ const moved = segments.map((segment) => {
18236
+ if (segment.cmd === segment.cmd.toLowerCase() || segment.coords.length === 0) {
18237
+ return segment;
18238
+ }
18239
+ return {
18240
+ cmd: segment.cmd,
18241
+ coords: segment.coords.map((value, index) => (index % 2 === 0 ? value + dx : value + dy)),
18242
+ };
18243
+ });
18244
+ return formatPath(moved);
18245
+ }
18246
+ /** Whether a path can be edited by {@link setMotionPathEnd} / drag handles. */
18247
+ function isEditableMotionPath(path) {
18248
+ const segments = scanPath(path);
18249
+ if (!segments) {
18250
+ return false;
18251
+ }
18252
+ return !segments.some((segment) => segment.cmd === 'Z' || segment.cmd === 'z');
18253
+ }
18254
+ /**
18255
+ * Build the `@keyframes` block that plays a motion path.
18256
+ *
18257
+ * WHY pixels and not percentages: a CSS `translate(%)` resolves against the
18258
+ * ELEMENT's own box, while the path is a fraction of the SLIDE. A 25%-of-slide
18259
+ * move on a small shape would otherwise travel a fraction of the intended
18260
+ * distance, so the frame's slide size is baked in here.
18261
+ */
18262
+ function buildMotionPathKeyframes(args) {
18263
+ const points = motionPathFractionPoints(args.path);
18264
+ if (points.length < 2) {
18265
+ return undefined;
18266
+ }
18267
+ const frames = points.map((point, index) => {
18268
+ const percent = Math.round((index / (points.length - 1)) * 100);
18269
+ const x = trimNumber(point.x * args.slideWidth);
18270
+ const y = trimNumber(point.y * args.slideHeight);
18271
+ return `\t${percent}% { transform: translate(${x}px, ${y}px); }`;
18272
+ });
18273
+ return {
18274
+ keyframeName: args.keyframeName,
18275
+ css: `@keyframes ${args.keyframeName} {\n${frames.join('\n')}\n}`,
18276
+ };
18277
+ }
18278
+
18279
+ /**
18280
+ * `motion-path-presets`: the authoring catalogue of PowerPoint motion-path
18281
+ * effects (Lines, Arcs, Turns, Shapes, Loops).
18282
+ *
18283
+ * WHY a path string and not a preset id: OOXML carries a motion path as free
18284
+ * form geometry in `p:animMotion/@path`; the integer `presetID` on a `path`
18285
+ * class node is informational and is NOT how PowerPoint reconstructs the
18286
+ * curve. So a preset here is a *named path string*, and applying one simply
18287
+ * writes that string onto the element's animation. Anything the user then
18288
+ * drags stays representable, and a deck authored elsewhere round-trips through
18289
+ * the same field.
18290
+ *
18291
+ * Coordinate space: the numbers are fractions of the SLIDE box, measured from
18292
+ * the animated element's own centre (`pathEditMode="relative"`), which is what
18293
+ * PowerPoint emits. `0.25 0` means "a quarter of the slide width to the right
18294
+ * of where the shape sits". Keeping the catalogue in the wire units means no
18295
+ * conversion happens on apply or save, only on render.
18296
+ *
18297
+ * The shapes/loops use x fractions of 0.125 against y fractions of 0.2222 so
18298
+ * they read as visually round on the 16:9 canvas the viewer renders at
18299
+ * (0.125 * 1280 === 0.2222 * 720 === 160 px).
18300
+ *
18301
+ * @module render/motion-path-presets
18302
+ */
18303
+ /** Bezier handle length for a circular quadrant (4/3 * tan(pi/8)). */
18304
+ const K = 0.5523;
18305
+ /** Half-extents used by the closed shapes, equal in rendered pixels on 16:9. */
18306
+ const RX = 0.125;
18307
+ const RY = 0.2222;
18308
+ const CIRCLE = [
18309
+ 'M 0 0',
18310
+ `C ${-K * RX} 0 ${-RX} ${-RY + K * RY} ${-RX} ${-RY}`,
18311
+ `C ${-RX} ${-RY - K * RY} ${-K * RX} ${-2 * RY} 0 ${-2 * RY}`,
18312
+ `C ${K * RX} ${-2 * RY} ${RX} ${-RY - K * RY} ${RX} ${-RY}`,
18313
+ `C ${RX} ${-RY + K * RY} ${K * RX} 0 0 0`,
18314
+ ]
18315
+ .join(' ')
18316
+ .replace(/(\d\.\d{4})\d+/gu, '$1');
18317
+ /**
18318
+ * Every motion-path preset, grouped by family in gallery order. Lines travel a
18319
+ * quarter of the slide, matching the distance PowerPoint's own line presets use
18320
+ * for a shape dropped near the middle of a slide.
18321
+ */
18322
+ const MOTION_PATH_PRESETS = [
18323
+ // -- Lines ---------------------------------------------------------------
18324
+ { id: 'lineRight', family: 'lines', path: 'M 0 0 L 0.25 0' },
18325
+ { id: 'lineLeft', family: 'lines', path: 'M 0 0 L -0.25 0' },
18326
+ { id: 'lineDown', family: 'lines', path: 'M 0 0 L 0 0.25' },
18327
+ { id: 'lineUp', family: 'lines', path: 'M 0 0 L 0 -0.25' },
18328
+ { id: 'lineDiagonalUpRight', family: 'lines', path: 'M 0 0 L 0.25 -0.25' },
18329
+ { id: 'lineDiagonalDownRight', family: 'lines', path: 'M 0 0 L 0.25 0.25' },
18330
+ { id: 'lineDiagonalUpLeft', family: 'lines', path: 'M 0 0 L -0.25 -0.25' },
18331
+ { id: 'lineDiagonalDownLeft', family: 'lines', path: 'M 0 0 L -0.25 0.25' },
18332
+ // -- Arcs ----------------------------------------------------------------
18333
+ {
18334
+ id: 'arcUp',
18335
+ family: 'arcs',
18336
+ path: 'M 0 0 C 0 -0.1227 0.0559 -0.2222 0.125 -0.2222 C 0.1941 -0.2222 0.25 -0.1227 0.25 0',
18337
+ },
18338
+ {
18339
+ id: 'arcDown',
18340
+ family: 'arcs',
18341
+ path: 'M 0 0 C 0 0.1227 0.0559 0.2222 0.125 0.2222 C 0.1941 0.2222 0.25 0.1227 0.25 0',
18342
+ },
18343
+ {
18344
+ id: 'arcRight',
18345
+ family: 'arcs',
18346
+ path: 'M 0 0 C 0.069 0 0.125 0.0995 0.125 0.2222 C 0.125 0.3449 0.069 0.4444 0 0.4444',
18347
+ },
18348
+ {
18349
+ id: 'arcLeft',
18350
+ family: 'arcs',
18351
+ path: 'M 0 0 C -0.069 0 -0.125 0.0995 -0.125 0.2222 C -0.125 0.3449 -0.069 0.4444 0 0.4444',
18352
+ },
18353
+ // -- Turns ---------------------------------------------------------------
18354
+ { id: 'turnUp', family: 'turns', path: 'M 0 0 L 0.25 0 L 0.25 -0.25' },
18355
+ { id: 'turnDown', family: 'turns', path: 'M 0 0 L 0.25 0 L 0.25 0.25' },
18356
+ { id: 'turnRight', family: 'turns', path: 'M 0 0 L 0 0.25 L 0.25 0.25' },
18357
+ { id: 'turnLeft', family: 'turns', path: 'M 0 0 L 0 0.25 L -0.25 0.25' },
18358
+ {
18359
+ id: 'sCurve',
18360
+ family: 'turns',
18361
+ path: 'M 0 0 C 0.0833 -0.1111 0.1667 0.1111 0.25 0',
18362
+ },
18363
+ {
18364
+ id: 'zigzag',
18365
+ family: 'turns',
18366
+ path: 'M 0 0 L 0.0625 -0.1111 L 0.125 0 L 0.1875 -0.1111 L 0.25 0',
18367
+ },
18368
+ // -- Shapes --------------------------------------------------------------
18369
+ { id: 'circle', family: 'shapes', path: CIRCLE },
18370
+ { id: 'square', family: 'shapes', path: 'M 0 0 L 0.125 0 L 0.125 -0.2222 L 0 -0.2222 Z' },
18371
+ { id: 'triangle', family: 'shapes', path: 'M 0 0 L 0.0625 -0.2222 L 0.125 0 Z' },
18372
+ {
18373
+ id: 'diamond',
18374
+ family: 'shapes',
18375
+ path: 'M 0 0 L 0.0625 -0.1111 L 0.125 0 L 0.0625 0.1111 Z',
18376
+ },
18377
+ {
18378
+ id: 'hexagon',
18379
+ family: 'shapes',
18380
+ path: 'M 0 0 L 0.0417 -0.1111 L 0.125 -0.1111 L 0.1667 0 L 0.125 0.1111 L 0.0417 0.1111 Z',
18381
+ },
18382
+ // -- Loops ---------------------------------------------------------------
18383
+ {
18384
+ id: 'loopDeLoop',
18385
+ family: 'loops',
18386
+ path: 'M 0 0 C 0.0625 0 0.0972 -0.2222 0.1667 -0.2222 C 0.2361 -0.2222 0.2708 0 0.1667 0 L 0.3333 0',
18387
+ },
18388
+ {
18389
+ id: 'figureEight',
18390
+ family: 'loops',
18391
+ path: 'M 0 0 C -0.0833 0 -0.0833 -0.2222 0 -0.2222 C 0.0833 -0.2222 0.0833 0 0.1667 0 C 0.25 0 0.25 -0.2222 0.1667 -0.2222 C 0.0833 -0.2222 0.0833 0 0 0',
18392
+ },
18393
+ {
18394
+ id: 'spiral',
18395
+ family: 'loops',
18396
+ path: 'M 0 0 C 0.0417 0 0.0417 -0.0741 0 -0.0741 C -0.0625 -0.0741 -0.0625 -0.1852 0 -0.1852 C 0.0833 -0.1852 0.0833 -0.3333 0 -0.3333',
18397
+ },
18398
+ ];
18399
+ /** The five families in gallery order, for building grouped UI. */
18400
+ const MOTION_PATH_FAMILIES = [
18401
+ 'lines',
18402
+ 'arcs',
18403
+ 'turns',
18404
+ 'shapes',
18405
+ 'loops',
18406
+ ];
18407
+ /** i18n key for a family heading (`Lines`, `Arcs`, ...). */
18408
+ function motionPathFamilyLabelKey(family) {
18409
+ return `pptx.animation.motionPath.family.${family}`;
18410
+ }
18411
+ /** i18n key for a preset's label (`Right`, `Arc Up`, ...). */
18412
+ function motionPathPresetLabelKey(presetId) {
18413
+ return `pptx.animation.motionPath.preset.${presetId}`;
18414
+ }
18415
+ /** Look a preset up by id. */
18416
+ function motionPathPresetById(presetId) {
18417
+ return MOTION_PATH_PRESETS.find((preset) => preset.id === presetId);
18418
+ }
18419
+ /** Every preset in one family, in catalogue order. */
18420
+ function motionPathPresetsByFamily(family) {
18421
+ return MOTION_PATH_PRESETS.filter((preset) => preset.family === family);
18422
+ }
18423
+ /**
18424
+ * Reverse lookup: the preset id whose path string matches `path`, or
18425
+ * `undefined` for a hand-dragged (custom) path. Lets the panel name the applied
18426
+ * effect instead of showing raw path data.
18427
+ */
18428
+ function motionPathPresetIdForPath(path) {
18429
+ if (!path) {
18430
+ return undefined;
18431
+ }
18432
+ const normalized = path.trim().replace(/\s+/gu, ' ');
18433
+ return MOTION_PATH_PRESETS.find((preset) => preset.path.trim().replace(/\s+/gu, ' ') === normalized)?.id;
18434
+ }
18435
+ /** The path applied by the ribbon's one-click "Path Animation" command. */
18436
+ const DEFAULT_MOTION_PATH_PRESET_ID = 'lineRight';
18437
+
18438
+ /**
18439
+ * `motion-path-authoring`: apply, edit, remove and preview a motion path on the
18440
+ * editor's slide-level `animations[]` model.
18441
+ *
18442
+ * WHY a bucket of its own: `PptxElementAnimation` models entrance / emphasis /
18443
+ * exit as named presets, but a motion path is geometry, not a preset name. It
18444
+ * therefore lives in the sibling `motionPath` field (which core already
18445
+ * serialises to a real `p:animMotion` node AND preserves in the editor
18446
+ * extension), and the same entry's `trigger` / `durationMs` / `delayMs` /
18447
+ * `order` drive it exactly as they drive the other three buckets. That is why
18448
+ * these helpers upsert the SAME entry rather than adding a second one: one
18449
+ * element, one timeline row, whichever effects it carries.
18450
+ *
18451
+ * Pure: no DOM, no framework. Bindings own the gallery, the canvas overlay and
18452
+ * the `<style>` injection.
18453
+ *
18454
+ * @module render/motion-path-authoring
18455
+ */
18456
+ /** Duration PowerPoint gives a freshly applied motion path. */
18457
+ const DEFAULT_MOTION_PATH_DURATION_MS = 2000;
18458
+ /** Trigger a freshly applied motion path gets, matching the other buckets. */
18459
+ const DEFAULT_TRIGGER = 'onClick';
18460
+ /** Read the motion path applied to an element, if any. */
18461
+ function motionPathFor(animations, elementId) {
18462
+ return animations.find((animation) => animation.elementId === elementId)?.motionPath;
18463
+ }
18464
+ /** Whether the element currently carries a motion path. */
18465
+ function hasMotionPath(animations, elementId) {
18466
+ return Boolean(motionPathFor(animations, elementId));
18467
+ }
18468
+ /**
18469
+ * Set (or replace) the raw motion path on an element, creating the animation
18470
+ * entry when the element had none. `pathEditMode` is pinned to `relative`
18471
+ * because the catalogue's coordinates are offsets from the element centre,
18472
+ * which is what a `relative` path means on the wire.
18473
+ */
18474
+ function setMotionPath(animations, elementId, path) {
18475
+ const index = animations.findIndex((animation) => animation.elementId === elementId);
18476
+ if (index >= 0) {
18477
+ return animations.map((animation, i) => i === index
18478
+ ? {
18479
+ ...animation,
18480
+ motionPath: path,
18481
+ motionPathEditMode: animation.motionPathEditMode ?? 'relative',
18482
+ durationMs: animation.durationMs ?? DEFAULT_MOTION_PATH_DURATION_MS,
18483
+ }
18484
+ : animation);
18485
+ }
18486
+ return [
18487
+ ...animations,
18488
+ {
18489
+ elementId,
18490
+ motionPath: path,
18491
+ motionPathEditMode: 'relative',
18492
+ durationMs: DEFAULT_MOTION_PATH_DURATION_MS,
18493
+ order: animations.length,
18494
+ trigger: DEFAULT_TRIGGER,
18495
+ },
18496
+ ];
18497
+ }
18498
+ /**
18499
+ * Apply a catalogue preset by id. Unknown ids leave the list untouched so a
18500
+ * stale UI id can never blank an existing path.
18501
+ */
18502
+ function applyMotionPathPreset(animations, elementId, presetId) {
18503
+ const preset = motionPathPresetById(presetId);
18504
+ if (!preset) {
18505
+ return [...animations];
18506
+ }
18507
+ return setMotionPath(animations, elementId, preset.path);
18508
+ }
18509
+ /**
18510
+ * Clear the motion path. The whole entry is dropped when nothing else is left
18511
+ * on it, so removing the only effect does not leave an empty timeline row
18512
+ * behind (the same rule the preset setters follow).
18513
+ */
18514
+ function clearMotionPath(animations, elementId) {
18515
+ const index = animations.findIndex((animation) => animation.elementId === elementId);
18516
+ if (index < 0) {
18517
+ return [...animations];
18518
+ }
18519
+ const current = animations[index];
18520
+ const stillHasEffect = Boolean(current.entrance || current.emphasis || current.exit);
18521
+ if (!stillHasEffect) {
18522
+ return animations
18523
+ .filter((animation) => animation.elementId !== elementId)
18524
+ .map((animation, i) => ({ ...animation, order: i }));
18525
+ }
18526
+ return animations.map((animation, i) => i === index
18527
+ ? {
18528
+ ...animation,
18529
+ motionPath: undefined,
18530
+ motionPathEditMode: undefined,
18531
+ motionPtsTypes: undefined,
18532
+ }
18533
+ : animation);
18534
+ }
18535
+ /**
18536
+ * Project the editor's motion paths onto the native-timeline model so a path
18537
+ * applied in this session plays in the slide show BEFORE the deck is saved.
18538
+ *
18539
+ * WHY it is needed: the slide-show engine reads `slide.nativeAnimations`, which
18540
+ * is only regenerated from `slide.animations` when the file is written. Without
18541
+ * this bridge a user would apply a path, press play, see nothing, and only get
18542
+ * the motion after a save + reload round trip.
18543
+ *
18544
+ * Any element already carrying a native motion path is skipped, so a reloaded
18545
+ * deck (where the same path exists in BOTH models) never plays it twice.
18546
+ */
18547
+ function motionPathNativeAnimations(slide) {
18548
+ const native = slide.nativeAnimations ?? [];
18549
+ const alreadyNative = new Set(native.filter((animation) => animation.motionPath).map((animation) => animation.targetId));
18550
+ return (slide.animations ?? [])
18551
+ .filter((animation) => Boolean(animation.motionPath) && !alreadyNative.has(animation.elementId))
18552
+ .map((animation) => ({
18553
+ targetId: animation.elementId,
18554
+ trigger: animation.trigger ?? DEFAULT_TRIGGER,
18555
+ triggerShapeId: animation.triggerShapeId,
18556
+ presetClass: 'path',
18557
+ durationMs: animation.durationMs ?? DEFAULT_MOTION_PATH_DURATION_MS,
18558
+ delayMs: animation.delayMs ?? 0,
18559
+ motionPath: animation.motionPath,
18560
+ motionPathEditMode: animation.motionPathEditMode ?? 'relative',
18561
+ }));
18562
+ }
18563
+ /**
18564
+ * Build the preview descriptor that plays a motion path on the canvas: the
18565
+ * `@keyframes` block plus the `animation` shorthand a binding assigns to the
18566
+ * element. Mirrors {@link buildPreviewAnimation} for the preset buckets so a
18567
+ * binding's existing preview player only needs one extra branch.
18568
+ */
18569
+ function buildMotionPathPreview(args) {
18570
+ const keyframeName = `pptx-motion-preview-${args.uid ?? 0}`;
18571
+ const keyframes = buildMotionPathKeyframes({
18572
+ path: args.path,
18573
+ slideWidth: args.slideWidth,
18574
+ slideHeight: args.slideHeight,
18575
+ keyframeName,
18576
+ });
18577
+ if (!keyframes) {
18578
+ return undefined;
18579
+ }
18580
+ const durationMs = args.durationMs ?? DEFAULT_MOTION_PATH_DURATION_MS;
18581
+ const delayMs = args.delayMs ?? 0;
18582
+ const easing = timingCurveToCss(args.timingCurve);
18583
+ return {
18584
+ keyframeName,
18585
+ keyframesCss: keyframes.css,
18586
+ cssAnimation: `${keyframeName} ${durationMs}ms ${easing} ${delayMs}ms 1 normal both`,
18587
+ durationMs,
18588
+ };
18589
+ }
18590
+
17880
18591
  /**
17881
18592
  * `presentation-animation-controller`: a pure, framework-agnostic controller
17882
18593
  * that encapsulates the native-animation (`p:timing`) element playback a binding
@@ -17954,7 +18665,10 @@ class PresentationAnimationController {
17954
18665
  * id list (base element ids + text-build sub-element ids) to track.
17955
18666
  */
17956
18667
  static fromSlide(slide) {
17957
- const nativeAnims = slide.nativeAnimations ?? [];
18668
+ // Motion paths authored in this session live on the editor model until the
18669
+ // deck is saved; project them in so pressing play right after applying one
18670
+ // actually moves the shape.
18671
+ const nativeAnims = [...(slide.nativeAnimations ?? []), ...motionPathNativeAnimations(slide)];
17958
18672
  const segmentCounts = buildSegmentCounts(slide, nativeAnims);
17959
18673
  const expandedAnims = segmentCounts.size > 0 ? expandTextBuildAnimations(nativeAnims, segmentCounts) : nativeAnims;
17960
18674
  const engine = TimelineEngine.fromAnimations(expandedAnims);
@@ -18208,131 +18922,6 @@ function executeMediaCommandInDom(command, frameRoot) {
18208
18922
  return runMediaCommand(command, (targetId) => findMediaElementByElementId(targetId, frameRoot?.() ?? null));
18209
18923
  }
18210
18924
 
18211
- /**
18212
- * `animation-preview` — pure preview-descriptor construction for the editor's
18213
- * animation panel: map an editor preset (+ direction / timing) to a CSS
18214
- * keyframe name, definition and shorthand. The DOM player that injects the
18215
- * `<style>` and toggles the element animation stays in each binding.
18216
- *
18217
- * @module render/animation-preview
18218
- */
18219
- // ==========================================================================
18220
- // Preview keyframes generation
18221
- // ==========================================================================
18222
- /** Map editor-level animation presets to the internal effect names used by
18223
- * the keyframe definition table. */
18224
- const PRESET_TO_EFFECT = {
18225
- fadeIn: 'fadeIn',
18226
- flyIn: 'flyInBottom',
18227
- zoomIn: 'zoomIn',
18228
- fadeOut: 'fadeOut',
18229
- flyOut: 'flyOutBottom',
18230
- zoomOut: 'zoomOut',
18231
- spin: 'spin',
18232
- pulse: 'pulse',
18233
- colorWave: 'colorWave',
18234
- bounce: 'bounce',
18235
- flash: 'flash',
18236
- };
18237
- /** Direction-aware fly-in/out effect name overrides. */
18238
- const DIRECTION_FLY_MAP = {
18239
- fromLeft: { flyIn: 'flyInLeft', flyOut: 'flyOutLeft' },
18240
- fromRight: { flyIn: 'flyInRight', flyOut: 'flyOutRight' },
18241
- fromTop: { flyIn: 'flyInTop', flyOut: 'flyOutTop' },
18242
- fromBottom: { flyIn: 'flyInBottom', flyOut: 'flyOutBottom' },
18243
- fromTopLeft: { flyIn: 'flyInTop', flyOut: 'flyOutTop' },
18244
- fromTopRight: { flyIn: 'flyInTop', flyOut: 'flyOutTop' },
18245
- fromBottomLeft: { flyIn: 'flyInBottom', flyOut: 'flyOutBottom' },
18246
- fromBottomRight: { flyIn: 'flyInBottom', flyOut: 'flyOutBottom' },
18247
- };
18248
- /**
18249
- * Resolve the CSS @keyframes effect name for a given preset and direction.
18250
- */
18251
- function resolvePreviewEffect(preset, direction) {
18252
- if ((preset === 'flyIn' || preset === 'flyOut') && direction) {
18253
- const dirMap = DIRECTION_FLY_MAP[direction];
18254
- if (dirMap) {
18255
- return preset === 'flyIn' ? dirMap.flyIn : dirMap.flyOut;
18256
- }
18257
- }
18258
- return PRESET_TO_EFFECT[preset];
18259
- }
18260
- /**
18261
- * Map a timing curve name to a CSS easing string.
18262
- * Supports the standard OOXML timing curves plus cubic-bezier extraction.
18263
- */
18264
- function timingCurveToCss(curve, cubicBezierValues) {
18265
- if (cubicBezierValues) {
18266
- // Validate cubic-bezier format: "x1,y1,x2,y2"
18267
- const parts = cubicBezierValues.split(',').map((s) => s.trim());
18268
- if (parts.length === 4 && parts.every((p) => !Number.isNaN(Number(p)))) {
18269
- return `cubic-bezier(${parts.join(', ')})`;
18270
- }
18271
- }
18272
- switch (curve) {
18273
- case 'ease':
18274
- return 'ease';
18275
- case 'ease-in':
18276
- return 'ease-in';
18277
- case 'ease-out':
18278
- return 'ease-out';
18279
- case 'linear':
18280
- return 'linear';
18281
- default:
18282
- return 'ease';
18283
- }
18284
- }
18285
- /**
18286
- * Build a preview animation descriptor for a given preset.
18287
- *
18288
- * Returns `undefined` if the preset doesn't have a known effect.
18289
- */
18290
- function buildPreviewAnimation(preset, options) {
18291
- if (preset === 'none') {
18292
- return undefined;
18293
- }
18294
- const effectName = resolvePreviewEffect(preset, options?.direction);
18295
- if (!effectName) {
18296
- return undefined;
18297
- }
18298
- const keyframeName = `pptx-${effectName}`;
18299
- const keyframesCss = getEffectKeyframes(effectName);
18300
- if (!keyframesCss) {
18301
- return undefined;
18302
- }
18303
- const duration = options?.durationMs ?? 600;
18304
- const easing = timingCurveToCss(options?.timingCurve, options?.cubicBezier);
18305
- return {
18306
- keyframeName,
18307
- keyframesCss,
18308
- cssAnimation: `${keyframeName} ${duration}ms ${easing} 0ms 1 normal both`,
18309
- durationMs: duration,
18310
- };
18311
- }
18312
- // ==========================================================================
18313
- // Timing curve extraction from OOXML bezier values
18314
- // ==========================================================================
18315
- /**
18316
- * Parse OOXML timing curve bezier values from `a:cTn/a:timing/a:curve`.
18317
- *
18318
- * OOXML stores bezier control points as attributes:
18319
- * - `x1`, `y1` — first control point (0..100000 range)
18320
- * - `x2`, `y2` — second control point (0..100000 range)
18321
- *
18322
- * Returns a CSS `cubic-bezier()` string, or undefined if not parseable.
18323
- */
18324
- function parseOoxmlBezierCurve(x1, y1, x2, y2) {
18325
- if (x1 === undefined || y1 === undefined || x2 === undefined || y2 === undefined) {
18326
- return undefined;
18327
- }
18328
- // OOXML values are in 0..100000 range, CSS cubic-bezier uses 0..1
18329
- const cx1 = Math.max(0, Math.min(1, x1 / 100000));
18330
- const cy1 = y1 / 100000; // y can exceed 0..1 range for overshoot
18331
- const cx2 = Math.max(0, Math.min(1, x2 / 100000));
18332
- const cy2 = y2 / 100000;
18333
- return `${cx1.toFixed(4)},${cy1.toFixed(4)},${cx2.toFixed(4)},${cy2.toFixed(4)}`;
18334
- }
18335
-
18336
18925
  /**
18337
18926
  * Camera-preset → CSS perspective/rotation mapping (framework-agnostic).
18338
18927
  *
@@ -24756,7 +25345,7 @@ const DEBOUNCE_MS = 600;
24756
25345
  const EXPANDED_MAX_HEIGHT = 200;
24757
25346
  const PX_TO_PT = 0.75;
24758
25347
  const MAX_INDENT_LEVEL = 4;
24759
- const INDENT_PX = 24;
25348
+ const INDENT_PX$1 = 24;
24760
25349
  /* ------------------------------------------------------------------ */
24761
25350
  /* Utility helpers */
24762
25351
  /* ------------------------------------------------------------------ */
@@ -24821,7 +25410,7 @@ function buildParagraph(segments) {
24821
25410
  }
24822
25411
  }
24823
25412
  if (first?.style.paragraphMarginLeft) {
24824
- indentLevel = Math.min(MAX_INDENT_LEVEL, Math.max(0, Math.round(first.style.paragraphMarginLeft / INDENT_PX)));
25413
+ indentLevel = Math.min(MAX_INDENT_LEVEL, Math.max(0, Math.round(first.style.paragraphMarginLeft / INDENT_PX$1)));
24825
25414
  }
24826
25415
  return {
24827
25416
  segments: segments.length > 0 ? segments : [{ text: '', style: {} }],
@@ -24867,7 +25456,7 @@ function paragraphsToSegments(paragraphs) {
24867
25456
  para.segments.forEach((seg, sIdx) => {
24868
25457
  const updated = { ...seg, style: { ...seg.style } };
24869
25458
  if (para.indentLevel > 0) {
24870
- updated.style.paragraphMarginLeft = para.indentLevel * INDENT_PX;
25459
+ updated.style.paragraphMarginLeft = para.indentLevel * INDENT_PX$1;
24871
25460
  }
24872
25461
  else {
24873
25462
  delete updated.style.paragraphMarginLeft;
@@ -25240,7 +25829,7 @@ function parseSegmentsFromRichEditor(root) {
25240
25829
  if (paraIndent > 0) {
25241
25830
  segments[i].style = {
25242
25831
  ...segments[i].style,
25243
- paragraphMarginLeft: paraIndent * INDENT_PX,
25832
+ paragraphMarginLeft: paraIndent * INDENT_PX$1,
25244
25833
  };
25245
25834
  }
25246
25835
  break;
@@ -25311,7 +25900,7 @@ function segmentsToEditorHtml(segments) {
25311
25900
  if (para.indentLevel > 0) {
25312
25901
  attrs.push(`data-indent-level="${para.indentLevel}"`);
25313
25902
  }
25314
- const indent = para.indentLevel * INDENT_PX;
25903
+ const indent = para.indentLevel * INDENT_PX$1;
25315
25904
  const divStyles = [];
25316
25905
  if (indent > 0) {
25317
25906
  divStyles.push(`padding-left:${indent}px`);
@@ -41268,6 +41857,740 @@ function clampIndex$1(index, slideCount) {
41268
41857
  return Math.min(Math.max(Math.trunc(index), 0), slideCount - 1);
41269
41858
  }
41270
41859
 
41860
+ /**
41861
+ * PowerPoint's Outline view, as a framework-agnostic model of the deck's text.
41862
+ *
41863
+ * Outline view shows a deck as an indented text document: one line per slide
41864
+ * title at the far left, that slide's body text indented beneath it. It is the
41865
+ * fastest way to draft or restructure a deck, and no binding shipped it.
41866
+ *
41867
+ * This module owns the READ half (deck -> rows) plus the paragraph read/write
41868
+ * primitives the edit half builds on. `outline-view-edit` owns the WRITE half.
41869
+ * Both are pure: the bindings own the markup and the event listeners, and own
41870
+ * no rule about what a row is or what Tab does.
41871
+ *
41872
+ * Three decisions are worth stating up front, because they are the ones a
41873
+ * reader will otherwise assume were oversights:
41874
+ *
41875
+ * 1. **Placeholders first, then a fallback.** PowerPoint's outline shows only
41876
+ * placeholder text, and free text boxes never appear. Applying that alone
41877
+ * here would blank the outline for the many decks (and every deck this repo
41878
+ * hand-authors as a fixture) that carry no `<p:ph>` at all, which reads as a
41879
+ * broken feature rather than as fidelity. So placeholders win when present,
41880
+ * and a slide with none falls back to its text-bearing elements in document
41881
+ * order. See {@link resolveSlideOutlineElements}.
41882
+ * 2. **A slide always contributes at least one row.** A slide with no title
41883
+ * placeholder, or no text whatsoever, still gets a title row (with a null
41884
+ * `elementId`). Without it the outline silently hides slides, and hiding a
41885
+ * slide from the view whose entire job is to show the deck's structure is
41886
+ * the worst failure this feature has.
41887
+ * 3. **A row is one (element, paragraph) pair**, keyed by ids rather than by
41888
+ * position, so a key survives an edit that inserts a slide above it.
41889
+ *
41890
+ * @module render/outline-view
41891
+ */
41892
+ // ---------------------------------------------------------------------------
41893
+ // DOM contract
41894
+ // ---------------------------------------------------------------------------
41895
+ /**
41896
+ * Marks the outline-view root in every binding.
41897
+ *
41898
+ * A neutral data attribute rather than a class or a test id, for the same
41899
+ * reason `READING_VIEW_ATTR` is one: `e2e/` addresses all five viewers through
41900
+ * one selector, and a class name is a styling decision each binding may make
41901
+ * differently.
41902
+ */
41903
+ const OUTLINE_VIEW_ATTR = 'data-pptx-outline-view';
41904
+ /** Marks one editable outline line. Its value is the row's {@link OutlineRow.key}. */
41905
+ const OUTLINE_ROW_ATTR = 'data-pptx-outline-row';
41906
+ /** Marks a row's slide number, so a binding can group rows per slide. */
41907
+ const OUTLINE_SLIDE_ATTR = 'data-pptx-outline-slide';
41908
+ /** Marks a row's outline level, so styling and tests read it without parsing CSS. */
41909
+ const OUTLINE_LEVEL_ATTR = 'data-pptx-outline-level';
41910
+ /** Highest `a:pPr/@lvl` OOXML allows (ECMA-376 CT_TextParagraphProperties). */
41911
+ const MAX_PARAGRAPH_LEVEL = 8;
41912
+ /** Placeholder types PowerPoint treats as the slide's title. */
41913
+ const TITLE_PH_TYPES = new Set(['title', 'ctrtitle']);
41914
+ /**
41915
+ * Placeholder types that never belong in an outline.
41916
+ *
41917
+ * Header / footer / date / slide-number repeat on every slide, so listing them
41918
+ * would bury the actual content under four rows of chrome per slide.
41919
+ */
41920
+ const CHROME_PH_TYPES = new Set(['ftr', 'hdr', 'dt', 'sldnum']);
41921
+ /** Name given to a title element the outline creates for a slide that lacked one. */
41922
+ const OUTLINE_TITLE_ELEMENT_NAME = 'Title';
41923
+ /**
41924
+ * Read an element's placeholder type from its preserved raw XML.
41925
+ *
41926
+ * Returns `'body'` for a `<p:ph>` that declares no `@type`: the schema default
41927
+ * for `CT_Placeholder/@type` is `body`, and a content placeholder authored as
41928
+ * `<p:ph idx="1"/>` is extremely common. Treating it as "not a placeholder"
41929
+ * dropped the body of most real decks from the outline.
41930
+ */
41931
+ function placeholderType(element) {
41932
+ const ph = element.rawXml?.['p:nvSpPr']?.['p:nvPr']?.['p:ph'];
41933
+ if (!ph) {
41934
+ return undefined;
41935
+ }
41936
+ const type = typeof ph['@_type'] === 'string' ? ph['@_type'].trim().toLowerCase() : '';
41937
+ return type.length > 0 ? type : 'body';
41938
+ }
41939
+ /** Whether an element can carry outline text at all. */
41940
+ function isTextBearing(element) {
41941
+ return hasTextProperties(element);
41942
+ }
41943
+ /** Plain text of an element, used only to decide whether it is worth a row. */
41944
+ function elementHasText(element) {
41945
+ if (!hasTextProperties(element)) {
41946
+ return false;
41947
+ }
41948
+ if (typeof element.text === 'string' && element.text.trim().length > 0) {
41949
+ return true;
41950
+ }
41951
+ return (element.textSegments ?? []).some((segment) => segment.text.trim().length > 0);
41952
+ }
41953
+ /**
41954
+ * Decide which of a slide's elements the outline reads.
41955
+ *
41956
+ * Placeholder-bearing slides resolve exactly as PowerPoint does: the
41957
+ * title/ctrTitle placeholder titles the slide and the remaining content
41958
+ * placeholders supply the body, with header/footer/date/slide-number chrome
41959
+ * excluded. A slide with no usable placeholder falls back to its text-bearing
41960
+ * elements in document order, first as the title and the rest as body: see the
41961
+ * module docstring for why the strict rule alone was not shippable here.
41962
+ *
41963
+ * A slide that has never had a title also matches an element NAMED `Title`,
41964
+ * which is what {@link OUTLINE_TITLE_ELEMENT_NAME} stamps on the element the
41965
+ * outline creates when someone types into an empty title row. Without that the
41966
+ * newly created title would be re-read as body text on the very next render.
41967
+ */
41968
+ function resolveSlideOutlineElements(slide) {
41969
+ const elements = (slide.elements ?? []).filter(isTextBearing);
41970
+ const placeholderTitle = elements.find((element) => {
41971
+ const type = placeholderType(element);
41972
+ return type !== undefined && TITLE_PH_TYPES.has(type);
41973
+ });
41974
+ const placeholderBody = elements.filter((element) => {
41975
+ const type = placeholderType(element);
41976
+ return type !== undefined && !TITLE_PH_TYPES.has(type) && !CHROME_PH_TYPES.has(type);
41977
+ });
41978
+ if (placeholderTitle || placeholderBody.length > 0) {
41979
+ return { title: placeholderTitle ?? null, body: placeholderBody };
41980
+ }
41981
+ const named = elements.find((element) => (element.name ?? '').trim().toLowerCase() === OUTLINE_TITLE_ELEMENT_NAME.toLowerCase());
41982
+ const withText = elements.filter(elementHasText);
41983
+ const title = named ?? withText[0] ?? null;
41984
+ return { title, body: withText.filter((element) => element !== title) };
41985
+ }
41986
+ /**
41987
+ * Whether a segment is the display-only bullet marker core inserts on load.
41988
+ *
41989
+ * Mirrors the save writer's own `isRenderedBulletMarker`: the marker is not
41990
+ * authored text, it is a rendering convenience, and letting it into an outline
41991
+ * row would show "• Item" in the editable line and then write the glyph back
41992
+ * into the deck as literal characters on the next keystroke.
41993
+ */
41994
+ function isBulletMarker(segment) {
41995
+ const bullet = segment.bulletInfo;
41996
+ if (!bullet) {
41997
+ return false;
41998
+ }
41999
+ const marker = bullet.char
42000
+ ? `${bullet.char} `
42001
+ : bullet.imageRelId || bullet.imageDataUrl
42002
+ ? '\u{1F4CE} '
42003
+ : bullet.autoNumType
42004
+ ? undefined
42005
+ : '• ';
42006
+ return marker ? segment.text === marker : bullet.paragraphIndex !== undefined;
42007
+ }
42008
+ function isParagraphSeparator(segment) {
42009
+ return Boolean(segment.isParagraphBreak) || (segment.text === '\n' && !segment.isLineBreak);
42010
+ }
42011
+ /**
42012
+ * Split an element's segments into paragraph groups.
42013
+ *
42014
+ * Falls back to splitting `element.text` when the element carries no segments
42015
+ * (SDK-built and freshly inserted elements), so every downstream helper can
42016
+ * assume groups exist and none of them has to special-case a bare string.
42017
+ */
42018
+ function groupElementParagraphs(element) {
42019
+ const withText = hasTextProperties(element) ? element : undefined;
42020
+ const segments = withText?.textSegments;
42021
+ if (!segments || segments.length === 0) {
42022
+ const style = { ...(withText?.textStyle ?? {}) };
42023
+ return (withText?.text ?? '').split('\n').map((text) => ({
42024
+ content: [{ text, style: { ...style } }],
42025
+ }));
42026
+ }
42027
+ const groups = [{ content: [] }];
42028
+ for (const segment of segments) {
42029
+ if (isParagraphSeparator(segment)) {
42030
+ groups.push({ content: [] });
42031
+ continue;
42032
+ }
42033
+ const current = groups[groups.length - 1];
42034
+ if (current.content.length === 0 && current.marker === undefined && isBulletMarker(segment)) {
42035
+ current.marker = segment;
42036
+ continue;
42037
+ }
42038
+ current.content.push(segment);
42039
+ }
42040
+ return groups;
42041
+ }
42042
+ /**
42043
+ * Plain text of one paragraph group.
42044
+ *
42045
+ * A soft line break (`a:br`) becomes a single space rather than a newline: an
42046
+ * outline row is one line, and emitting "\n" here would make the row's text
42047
+ * indistinguishable from a paragraph break and silently convert the soft break
42048
+ * into a hard one the first time the row was written back.
42049
+ */
42050
+ function paragraphGroupText(group) {
42051
+ return group.content.map((segment) => (segment.isLineBreak ? ' ' : segment.text)).join('');
42052
+ }
42053
+ /** Raw `a:pPr/@lvl` of a paragraph group, read from its first segment. */
42054
+ function paragraphGroupLevel(group) {
42055
+ const first = group.marker ?? group.content[0];
42056
+ const level = first?.paragraphLevel;
42057
+ return typeof level === 'number' && level > 0 ? Math.min(level, MAX_PARAGRAPH_LEVEL) : 0;
42058
+ }
42059
+ /**
42060
+ * Read an element's paragraphs as the outline sees them.
42061
+ *
42062
+ * Trailing empty paragraphs are dropped, matching `buildParagraphs`: both the
42063
+ * load path and the edit-remap path leave a separator behind, and showing an
42064
+ * always-blank last row under every block would double the outline's length
42065
+ * with lines the deck never draws.
42066
+ */
42067
+ function readElementParagraphs(element) {
42068
+ const groups = groupElementParagraphs(element);
42069
+ const paragraphs = groups.map((group) => ({
42070
+ text: paragraphGroupText(group),
42071
+ level: paragraphGroupLevel(group),
42072
+ }));
42073
+ let last = paragraphs.length - 1;
42074
+ while (last >= 0 && paragraphs[last].text.length === 0) {
42075
+ last -= 1;
42076
+ }
42077
+ return paragraphs.slice(0, last + 1);
42078
+ }
42079
+ // ---------------------------------------------------------------------------
42080
+ // Rows
42081
+ // ---------------------------------------------------------------------------
42082
+ /** Build a row key from the parts that identify it. */
42083
+ function outlineRowKey(slideId, elementId, paragraphIndex) {
42084
+ return `${slideId}|${elementId ?? 'new-title'}|${paragraphIndex}`;
42085
+ }
42086
+ function pushElementRows(rows, element, kind, slide, slideIndex) {
42087
+ for (const [paragraphIndex, paragraph] of readElementParagraphs(element).entries()) {
42088
+ rows.push({
42089
+ key: outlineRowKey(slide.id, element.id, paragraphIndex),
42090
+ slideIndex,
42091
+ slideId: slide.id,
42092
+ kind,
42093
+ elementId: element.id,
42094
+ paragraphIndex,
42095
+ // A title always sits in the leftmost column; body text is pushed one
42096
+ // column right of it, so authored level 0 reads as outline level 1.
42097
+ level: kind === 'title' ? 0 : paragraph.level + 1,
42098
+ text: paragraph.text,
42099
+ });
42100
+ }
42101
+ }
42102
+ /**
42103
+ * Build the whole deck's outline.
42104
+ *
42105
+ * Every slide contributes at least one row even when it holds no text: see the
42106
+ * module docstring, decision 2.
42107
+ */
42108
+ function buildOutline(slides) {
42109
+ const rows = [];
42110
+ for (const [slideIndex, slide] of slides.entries()) {
42111
+ const { title, body } = resolveSlideOutlineElements(slide);
42112
+ const before = rows.length;
42113
+ if (title) {
42114
+ pushElementRows(rows, title, 'title', slide, slideIndex);
42115
+ }
42116
+ if (rows.length === before) {
42117
+ // No title element, or one whose text is entirely empty: emit the
42118
+ // synthetic row so the slide is still visible and still typeable.
42119
+ rows.push({
42120
+ key: outlineRowKey(slide.id, title?.id ?? null, 0),
42121
+ slideIndex,
42122
+ slideId: slide.id,
42123
+ kind: 'title',
42124
+ elementId: title?.id ?? null,
42125
+ paragraphIndex: 0,
42126
+ level: 0,
42127
+ text: '',
42128
+ });
42129
+ }
42130
+ for (const element of body) {
42131
+ pushElementRows(rows, element, 'body', slide, slideIndex);
42132
+ }
42133
+ }
42134
+ return rows;
42135
+ }
42136
+ /** Find a row by key, or `undefined`. */
42137
+ function findOutlineRow(rows, key) {
42138
+ return rows.find((row) => row.key === key);
42139
+ }
42140
+
42141
+ /** MIME type bindings should use when writing the payload as a custom clipboard format. */
42142
+ const ELEMENT_CLIPBOARD_MIME_TYPE = 'application/x-pptx-viewer-elements+json';
42143
+ /** Marker identifying a serialized element-clipboard payload. */
42144
+ const ELEMENT_CLIPBOARD_MARKER = 'pptx-viewer/elements';
42145
+ /** Current codec version; {@link deserializeElementClipboard} rejects others. */
42146
+ const ELEMENT_CLIPBOARD_VERSION = 1;
42147
+ /** Standard paste/duplicate cascade offset in px (applied to both axes). */
42148
+ const PASTE_OFFSET_PX = 20;
42149
+ /** Generate a unique element id (`el-<timestamp>-<random>`). */
42150
+ function generateElementId() {
42151
+ return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
42152
+ }
42153
+ /**
42154
+ * Build the id for a pasted / duplicated clone so that it routes to the same
42155
+ * store it is inserted into.
42156
+ *
42157
+ * In edit-template mode the clone lands in the template store, so it must keep
42158
+ * a template (`master-` / `layout-`) prefix; otherwise later edits (which route
42159
+ * by id prefix) would target the wrong store and be lost. Outside template mode
42160
+ * a normal id is generated.
42161
+ */
42162
+ function makeCloneId(intoTemplate, sourceId) {
42163
+ if (!intoTemplate) {
42164
+ return generateElementId();
42165
+ }
42166
+ if (sourceId.startsWith('master-')) {
42167
+ return `master-${generateElementId()}`;
42168
+ }
42169
+ return `layout-${generateElementId()}`;
42170
+ }
42171
+ /**
42172
+ * Snapshot a copied/cut element into the in-memory clipboard payload.
42173
+ * Deep-clones so later canvas edits never mutate the clipboard content.
42174
+ */
42175
+ function buildElementClipboardPayload(element, isTemplate) {
42176
+ return { element: structuredClone(element), isTemplate };
42177
+ }
42178
+ /**
42179
+ * Produce the clone inserted by paste/duplicate: a deep copy with a fresh
42180
+ * (template-prefix aware) id and the standard cascade offset applied.
42181
+ */
42182
+ function cloneElementForPaste(element, options = {}) {
42183
+ const clone = structuredClone(element);
42184
+ clone.id = makeCloneId(options.intoTemplate ?? false, element.id);
42185
+ clone.x += options.offsetX ?? PASTE_OFFSET_PX;
42186
+ clone.y += options.offsetY ?? PASTE_OFFSET_PX;
42187
+ return clone;
42188
+ }
42189
+ /** Clone every element in decoded clipboard data for insertion (fresh ids + offset). */
42190
+ function prepareElementsForPaste(data, options = {}) {
42191
+ return data.elements.map((element) => cloneElementForPaste(element, options));
42192
+ }
42193
+ /** Tag used to mark base64-encoded binary fields inside the JSON payload. */
42194
+ const BINARY_TAG = '__pptxU8';
42195
+ function uint8ToBase64(bytes) {
42196
+ let binary = '';
42197
+ for (let i = 0; i < bytes.length; i++) {
42198
+ binary += String.fromCharCode(bytes[i]);
42199
+ }
42200
+ return btoa(binary);
42201
+ }
42202
+ function base64ToUint8(base64) {
42203
+ const binary = atob(base64);
42204
+ const bytes = new Uint8Array(binary.length);
42205
+ for (let i = 0; i < binary.length; i++) {
42206
+ bytes[i] = binary.charCodeAt(i);
42207
+ }
42208
+ return bytes;
42209
+ }
42210
+ /**
42211
+ * Serialize elements to the marked, versioned JSON clipboard string.
42212
+ * `Uint8Array` fields anywhere in the element tree (embedded workbooks, raw
42213
+ * part bytes) are base64-tagged so they survive the text round trip.
42214
+ */
42215
+ function serializeElementClipboard(elements, isTemplate = false) {
42216
+ return JSON.stringify({
42217
+ marker: ELEMENT_CLIPBOARD_MARKER,
42218
+ version: ELEMENT_CLIPBOARD_VERSION,
42219
+ isTemplate,
42220
+ elements,
42221
+ }, (_key, value) => {
42222
+ if (value instanceof Uint8Array) {
42223
+ return { [BINARY_TAG]: uint8ToBase64(value) };
42224
+ }
42225
+ return value;
42226
+ });
42227
+ }
42228
+ function isElementLike(value) {
42229
+ if (typeof value !== 'object' || value === null) {
42230
+ return false;
42231
+ }
42232
+ const candidate = value;
42233
+ return (typeof candidate.id === 'string' &&
42234
+ typeof candidate.type === 'string' &&
42235
+ typeof candidate.x === 'number' &&
42236
+ typeof candidate.y === 'number' &&
42237
+ typeof candidate.width === 'number' &&
42238
+ typeof candidate.height === 'number');
42239
+ }
42240
+ /**
42241
+ * Decode a clipboard string produced by {@link serializeElementClipboard}.
42242
+ * Returns `null` for anything else (foreign clipboard text, malformed JSON,
42243
+ * wrong marker/version, or structurally invalid elements), so callers can fall
42244
+ * back to their plain-text paste path.
42245
+ */
42246
+ function deserializeElementClipboard(text) {
42247
+ let parsed;
42248
+ try {
42249
+ parsed = JSON.parse(text, (_key, value) => {
42250
+ if (typeof value === 'object' && value !== null) {
42251
+ const tagged = value[BINARY_TAG];
42252
+ if (typeof tagged === 'string') {
42253
+ return base64ToUint8(tagged);
42254
+ }
42255
+ }
42256
+ return value;
42257
+ });
42258
+ }
42259
+ catch {
42260
+ return null;
42261
+ }
42262
+ if (typeof parsed !== 'object' || parsed === null) {
42263
+ return null;
42264
+ }
42265
+ const candidate = parsed;
42266
+ if (candidate.marker !== ELEMENT_CLIPBOARD_MARKER ||
42267
+ candidate.version !== ELEMENT_CLIPBOARD_VERSION ||
42268
+ !Array.isArray(candidate.elements) ||
42269
+ candidate.elements.length === 0 ||
42270
+ !candidate.elements.every(isElementLike)) {
42271
+ return null;
42272
+ }
42273
+ return {
42274
+ elements: candidate.elements,
42275
+ isTemplate: candidate.isTemplate === true,
42276
+ };
42277
+ }
42278
+
42279
+ /**
42280
+ * The WRITE half of Outline view: turn a keystroke into a new deck.
42281
+ *
42282
+ * Pure and total: `applyOutlineEdit` takes the slides it is given and returns
42283
+ * new slides, never mutating the input. That shape is deliberate. Every binding
42284
+ * already has a history mechanism that watches its slide state, so handing back
42285
+ * a fresh `PptxSlide[]` makes undo/redo work through the machinery each binding
42286
+ * already has instead of asking five of them to grow an outline-specific
42287
+ * history path.
42288
+ *
42289
+ * What is intentionally NOT implemented, so nobody reads its absence as a bug:
42290
+ *
42291
+ * **Tab on a title row does nothing.** In PowerPoint, demoting a title merges
42292
+ * that slide into the one above it and promoting a top-level bullet splits a
42293
+ * new slide out. Both are safe there because PowerPoint's outline owns the
42294
+ * whole slide. Here a slide carries arbitrary elements (images, charts, tables,
42295
+ * ink) that the outline cannot see, so merging two slides would silently
42296
+ * destroy content the user never had on screen. Levels move within a body,
42297
+ * slides are created by Enter on a title, and nothing in this view deletes a
42298
+ * slide.
42299
+ *
42300
+ * @module render/outline-view-edit
42301
+ */
42302
+ /**
42303
+ * Translate a key press inside an outline row into an edit.
42304
+ *
42305
+ * Both of PowerPoint's spellings are honoured: bare Tab / Shift+Tab, and the
42306
+ * Alt+Shift+Arrow chords that work anywhere in PowerPoint's outline. Anything
42307
+ * with Ctrl or Meta is left alone so the browser keeps its own shortcuts, and
42308
+ * so Ctrl+Z reaches the binding's undo rather than being eaten here.
42309
+ */
42310
+ function mapOutlineKey(input, rowKey) {
42311
+ const none = { edit: null, preventDefault: false };
42312
+ if (input.ctrlKey || input.metaKey) {
42313
+ return none;
42314
+ }
42315
+ if (input.key === 'Tab' && !input.altKey) {
42316
+ return {
42317
+ edit: { type: 'indent', key: rowKey, delta: input.shiftKey ? -1 : 1 },
42318
+ preventDefault: true,
42319
+ };
42320
+ }
42321
+ if (input.altKey && input.shiftKey && (input.key === 'ArrowRight' || input.key === 'ArrowLeft')) {
42322
+ return {
42323
+ edit: { type: 'indent', key: rowKey, delta: input.key === 'ArrowRight' ? 1 : -1 },
42324
+ preventDefault: true,
42325
+ };
42326
+ }
42327
+ if (input.key === 'Enter' && !input.shiftKey && !input.altKey) {
42328
+ return { edit: { type: 'insertAfter', key: rowKey }, preventDefault: true };
42329
+ }
42330
+ return none;
42331
+ }
42332
+ // ---------------------------------------------------------------------------
42333
+ // Paragraph writing
42334
+ // ---------------------------------------------------------------------------
42335
+ /** Clone a segment list, stamping (or clearing) the paragraph's `a:pPr/@lvl`. */
42336
+ function withLevel(segments, level) {
42337
+ return segments.map((segment, index) => {
42338
+ if (index > 0) {
42339
+ return segment;
42340
+ }
42341
+ const next = { ...segment };
42342
+ if (level > 0) {
42343
+ next.paragraphLevel = Math.min(level, MAX_PARAGRAPH_LEVEL);
42344
+ }
42345
+ else {
42346
+ delete next.paragraphLevel;
42347
+ }
42348
+ return next;
42349
+ });
42350
+ }
42351
+ /**
42352
+ * Rebuild one paragraph's segments for `text`.
42353
+ *
42354
+ * An UNCHANGED paragraph is reused verbatim rather than remapped. That is what
42355
+ * keeps equations, fields, hyperlinks and soft line breaks intact while someone
42356
+ * types on a different line: remapping redistributes characters across runs,
42357
+ * which is exactly right for the line being edited and pure loss for every
42358
+ * other line in the same text body.
42359
+ */
42360
+ function rebuildParagraph(group, donor, text, elementStyle) {
42361
+ if (group && paragraphGroupText(group) === text) {
42362
+ return [...(group.marker ? [group.marker] : []), ...group.content];
42363
+ }
42364
+ const source = group ?? donor;
42365
+ const marker = source?.marker ? [source.marker] : [];
42366
+ const content = remapTextToSegments(text, source?.content, elementStyle);
42367
+ return [...marker, ...content];
42368
+ }
42369
+ /**
42370
+ * Write paragraphs back onto an element, returning a new element.
42371
+ *
42372
+ * `element.text` is rewritten from the row text, which means it no longer
42373
+ * carries the bullet glyphs core prefixes on load. That is correct rather than
42374
+ * lossy: `textSegments` is what both the renderer and the save writer read when
42375
+ * it exists, and the glyph is re-derived from the paragraph's own bullet
42376
+ * properties, which travel on the preserved marker segment.
42377
+ */
42378
+ function writeElementParagraphs(element, paragraphs) {
42379
+ const groups = groupElementParagraphs(element);
42380
+ const donor = groups[groups.length - 1];
42381
+ const style = hasTextProperties(element) ? element.textStyle : undefined;
42382
+ const segments = [];
42383
+ for (const [index, paragraph] of paragraphs.entries()) {
42384
+ if (index > 0) {
42385
+ const previous = segments[segments.length - 1];
42386
+ segments.push({
42387
+ text: '\n',
42388
+ style: { ...(previous?.style ?? style ?? {}) },
42389
+ isParagraphBreak: true,
42390
+ });
42391
+ }
42392
+ segments.push(...withLevel(rebuildParagraph(groups[index], donor, paragraph.text, style), paragraph.level));
42393
+ }
42394
+ return {
42395
+ ...element,
42396
+ text: paragraphs.map((paragraph) => paragraph.text).join('\n'),
42397
+ textSegments: segments,
42398
+ };
42399
+ }
42400
+ // ---------------------------------------------------------------------------
42401
+ // Element / slide creation
42402
+ // ---------------------------------------------------------------------------
42403
+ const DEFAULT_CANVAS = { width: 960, height: 540 };
42404
+ /**
42405
+ * Build the title element for a slide that had none.
42406
+ *
42407
+ * Created WITHOUT `rawXml`: the save writer only synthesises a well-formed
42408
+ * `<p:sp>` for elements that carry no raw XML, so handing it a fabricated
42409
+ * `<p:ph>` stub would short-circuit that and emit a shape with no `spPr` and no
42410
+ * `txBody`. The element is identified as the title by its NAME instead, which
42411
+ * `resolveSlideOutlineElements` matches.
42412
+ */
42413
+ function createTitleElement(text, options) {
42414
+ const canvas = options.canvas ?? DEFAULT_CANVAS;
42415
+ return {
42416
+ type: 'text',
42417
+ id: options.idGenerator ? options.idGenerator() : generateElementId(),
42418
+ name: OUTLINE_TITLE_ELEMENT_NAME,
42419
+ x: Math.round(canvas.width * 0.06),
42420
+ y: Math.round(canvas.height * 0.08),
42421
+ width: Math.round(canvas.width * 0.88),
42422
+ height: Math.round(canvas.height * 0.17),
42423
+ text,
42424
+ textStyle: { fontSize: 36, bold: true },
42425
+ textSegments: [{ text, style: { fontSize: 36, bold: true } }],
42426
+ };
42427
+ }
42428
+ /** Renumber slides so `slideNumber` still matches document order after an insert. */
42429
+ function renumber(slides) {
42430
+ return slides.map((slide, index) => ({ ...slide, slideNumber: index + 1 }));
42431
+ }
42432
+ // ---------------------------------------------------------------------------
42433
+ // Apply
42434
+ // ---------------------------------------------------------------------------
42435
+ function unchanged(slides, row) {
42436
+ return {
42437
+ slides,
42438
+ changed: false,
42439
+ focusKey: row?.key ?? null,
42440
+ activeSlideIndex: row?.slideIndex ?? 0,
42441
+ };
42442
+ }
42443
+ /** Replace one element on one slide, returning fresh slide + deck arrays. */
42444
+ function replaceElement(slides, slideIndex, elementId, next) {
42445
+ return slides.map((slide, index) => index === slideIndex
42446
+ ? {
42447
+ ...slide,
42448
+ elements: (slide.elements ?? []).map((element) => element.id === elementId ? next : element),
42449
+ }
42450
+ : slide);
42451
+ }
42452
+ function editParagraphs(slides, row, mutate) {
42453
+ const slide = slides[row.slideIndex];
42454
+ const element = (slide?.elements ?? []).find((candidate) => candidate.id === row.elementId);
42455
+ if (!element) {
42456
+ return null;
42457
+ }
42458
+ const paragraphs = readElementParagraphs(element);
42459
+ // A row can address the paragraph one past the end: an element whose text is
42460
+ // entirely empty reads as zero paragraphs but still owns row 0.
42461
+ while (paragraphs.length <= row.paragraphIndex) {
42462
+ paragraphs.push({ text: '', level: 0 });
42463
+ }
42464
+ const next = mutate(paragraphs);
42465
+ if (!next) {
42466
+ return null;
42467
+ }
42468
+ return {
42469
+ slides: replaceElement(slides, row.slideIndex, element.id, writeElementParagraphs(element, next)),
42470
+ changed: true,
42471
+ focusKey: row.key,
42472
+ activeSlideIndex: row.slideIndex,
42473
+ };
42474
+ }
42475
+ /**
42476
+ * An outline row is one LINE. Newlines a paste drags in are folded to spaces
42477
+ * rather than split into extra paragraphs, because splitting here would leave
42478
+ * the caller's row keys addressing paragraphs that had silently moved.
42479
+ */
42480
+ function oneLine(text) {
42481
+ return text.replace(/[\r\n]+/g, ' ');
42482
+ }
42483
+ function applySetText(slides, row, raw, options) {
42484
+ const text = oneLine(raw);
42485
+ if (row.elementId === null) {
42486
+ if (text.length === 0) {
42487
+ return unchanged([...slides], row);
42488
+ }
42489
+ const element = createTitleElement(text, options);
42490
+ // Prepended, not appended: the fallback resolver reads the FIRST
42491
+ // text-bearing element as the title, and a slide reaching this branch has
42492
+ // no placeholder to anchor that decision to.
42493
+ const next = slides.map((slide, index) => index === row.slideIndex
42494
+ ? { ...slide, elements: [element, ...(slide.elements ?? [])] }
42495
+ : slide);
42496
+ return {
42497
+ slides: next,
42498
+ changed: true,
42499
+ focusKey: outlineRowKey(row.slideId, element.id, 0),
42500
+ activeSlideIndex: row.slideIndex,
42501
+ };
42502
+ }
42503
+ const result = editParagraphs(slides, row, (paragraphs) => {
42504
+ if (paragraphs[row.paragraphIndex].text === text) {
42505
+ return null;
42506
+ }
42507
+ const next = [...paragraphs];
42508
+ next[row.paragraphIndex] = { ...next[row.paragraphIndex], text };
42509
+ return next;
42510
+ });
42511
+ return result ?? unchanged([...slides], row);
42512
+ }
42513
+ function applyIndent(slides, row, delta) {
42514
+ // Titles are the outline's leftmost column and there is nothing to promote
42515
+ // them into: see the module docstring for why demoting one does not merge
42516
+ // slides here.
42517
+ if (row.kind !== 'body' || row.elementId === null || delta === 0) {
42518
+ return unchanged([...slides], row);
42519
+ }
42520
+ const result = editParagraphs(slides, row, (paragraphs) => {
42521
+ const current = paragraphs[row.paragraphIndex];
42522
+ const level = Math.min(Math.max(current.level + (delta > 0 ? 1 : -1), 0), MAX_PARAGRAPH_LEVEL);
42523
+ if (level === current.level) {
42524
+ return null;
42525
+ }
42526
+ const next = [...paragraphs];
42527
+ next[row.paragraphIndex] = { ...current, level };
42528
+ return next;
42529
+ });
42530
+ return result ?? unchanged([...slides], row);
42531
+ }
42532
+ function applyInsertAfter(slides, row, options) {
42533
+ if (row.kind === 'title') {
42534
+ // PowerPoint's defining outline gesture: Enter on a title starts a slide.
42535
+ const slide = {
42536
+ id: makeSlideId(options.idGenerator),
42537
+ rId: '',
42538
+ slideNumber: row.slideIndex + 2,
42539
+ elements: [],
42540
+ };
42541
+ const next = renumber([
42542
+ ...slides.slice(0, row.slideIndex + 1),
42543
+ slide,
42544
+ ...slides.slice(row.slideIndex + 1),
42545
+ ]);
42546
+ return {
42547
+ slides: next,
42548
+ changed: true,
42549
+ focusKey: outlineRowKey(slide.id, null, 0),
42550
+ activeSlideIndex: row.slideIndex + 1,
42551
+ };
42552
+ }
42553
+ const result = editParagraphs(slides, row, (paragraphs) => {
42554
+ const next = [...paragraphs];
42555
+ next.splice(row.paragraphIndex + 1, 0, {
42556
+ text: '',
42557
+ level: paragraphs[row.paragraphIndex].level,
42558
+ });
42559
+ return next;
42560
+ });
42561
+ if (!result) {
42562
+ return unchanged([...slides], row);
42563
+ }
42564
+ return {
42565
+ ...result,
42566
+ focusKey: outlineRowKey(row.slideId, row.elementId, row.paragraphIndex + 1),
42567
+ };
42568
+ }
42569
+ /**
42570
+ * Apply one outline edit to a deck.
42571
+ *
42572
+ * Always returns a result, never throws: a key that no longer resolves (the row
42573
+ * was removed by a concurrent collaborator, or the deck reloaded under the
42574
+ * pane) yields `changed: false` rather than an exception thrown at someone who
42575
+ * was only typing.
42576
+ */
42577
+ function applyOutlineEdit(slides, edit, options = {}) {
42578
+ const row = findOutlineRow(buildOutline(slides), edit.key);
42579
+ if (!row) {
42580
+ return unchanged([...slides]);
42581
+ }
42582
+ switch (edit.type) {
42583
+ case 'setText':
42584
+ return applySetText(slides, row, edit.text, options);
42585
+ case 'indent':
42586
+ return applyIndent(slides, row, edit.delta);
42587
+ case 'insertAfter':
42588
+ return applyInsertAfter(slides, row, options);
42589
+ default:
42590
+ return unchanged([...slides], row);
42591
+ }
42592
+ }
42593
+
41271
42594
  /**
41272
42595
  * The editor keymap: one source of truth for "what does this key do".
41273
42596
  *
@@ -45493,144 +46816,6 @@ const COMMON_FONTS = [
45493
46816
  'Verdana',
45494
46817
  ];
45495
46818
 
45496
- /** MIME type bindings should use when writing the payload as a custom clipboard format. */
45497
- const ELEMENT_CLIPBOARD_MIME_TYPE = 'application/x-pptx-viewer-elements+json';
45498
- /** Marker identifying a serialized element-clipboard payload. */
45499
- const ELEMENT_CLIPBOARD_MARKER = 'pptx-viewer/elements';
45500
- /** Current codec version; {@link deserializeElementClipboard} rejects others. */
45501
- const ELEMENT_CLIPBOARD_VERSION = 1;
45502
- /** Standard paste/duplicate cascade offset in px (applied to both axes). */
45503
- const PASTE_OFFSET_PX = 20;
45504
- /** Generate a unique element id (`el-<timestamp>-<random>`). */
45505
- function generateElementId() {
45506
- return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
45507
- }
45508
- /**
45509
- * Build the id for a pasted / duplicated clone so that it routes to the same
45510
- * store it is inserted into.
45511
- *
45512
- * In edit-template mode the clone lands in the template store, so it must keep
45513
- * a template (`master-` / `layout-`) prefix; otherwise later edits (which route
45514
- * by id prefix) would target the wrong store and be lost. Outside template mode
45515
- * a normal id is generated.
45516
- */
45517
- function makeCloneId(intoTemplate, sourceId) {
45518
- if (!intoTemplate) {
45519
- return generateElementId();
45520
- }
45521
- if (sourceId.startsWith('master-')) {
45522
- return `master-${generateElementId()}`;
45523
- }
45524
- return `layout-${generateElementId()}`;
45525
- }
45526
- /**
45527
- * Snapshot a copied/cut element into the in-memory clipboard payload.
45528
- * Deep-clones so later canvas edits never mutate the clipboard content.
45529
- */
45530
- function buildElementClipboardPayload(element, isTemplate) {
45531
- return { element: structuredClone(element), isTemplate };
45532
- }
45533
- /**
45534
- * Produce the clone inserted by paste/duplicate: a deep copy with a fresh
45535
- * (template-prefix aware) id and the standard cascade offset applied.
45536
- */
45537
- function cloneElementForPaste(element, options = {}) {
45538
- const clone = structuredClone(element);
45539
- clone.id = makeCloneId(options.intoTemplate ?? false, element.id);
45540
- clone.x += options.offsetX ?? PASTE_OFFSET_PX;
45541
- clone.y += options.offsetY ?? PASTE_OFFSET_PX;
45542
- return clone;
45543
- }
45544
- /** Clone every element in decoded clipboard data for insertion (fresh ids + offset). */
45545
- function prepareElementsForPaste(data, options = {}) {
45546
- return data.elements.map((element) => cloneElementForPaste(element, options));
45547
- }
45548
- /** Tag used to mark base64-encoded binary fields inside the JSON payload. */
45549
- const BINARY_TAG = '__pptxU8';
45550
- function uint8ToBase64(bytes) {
45551
- let binary = '';
45552
- for (let i = 0; i < bytes.length; i++) {
45553
- binary += String.fromCharCode(bytes[i]);
45554
- }
45555
- return btoa(binary);
45556
- }
45557
- function base64ToUint8(base64) {
45558
- const binary = atob(base64);
45559
- const bytes = new Uint8Array(binary.length);
45560
- for (let i = 0; i < binary.length; i++) {
45561
- bytes[i] = binary.charCodeAt(i);
45562
- }
45563
- return bytes;
45564
- }
45565
- /**
45566
- * Serialize elements to the marked, versioned JSON clipboard string.
45567
- * `Uint8Array` fields anywhere in the element tree (embedded workbooks, raw
45568
- * part bytes) are base64-tagged so they survive the text round trip.
45569
- */
45570
- function serializeElementClipboard(elements, isTemplate = false) {
45571
- return JSON.stringify({
45572
- marker: ELEMENT_CLIPBOARD_MARKER,
45573
- version: ELEMENT_CLIPBOARD_VERSION,
45574
- isTemplate,
45575
- elements,
45576
- }, (_key, value) => {
45577
- if (value instanceof Uint8Array) {
45578
- return { [BINARY_TAG]: uint8ToBase64(value) };
45579
- }
45580
- return value;
45581
- });
45582
- }
45583
- function isElementLike(value) {
45584
- if (typeof value !== 'object' || value === null) {
45585
- return false;
45586
- }
45587
- const candidate = value;
45588
- return (typeof candidate.id === 'string' &&
45589
- typeof candidate.type === 'string' &&
45590
- typeof candidate.x === 'number' &&
45591
- typeof candidate.y === 'number' &&
45592
- typeof candidate.width === 'number' &&
45593
- typeof candidate.height === 'number');
45594
- }
45595
- /**
45596
- * Decode a clipboard string produced by {@link serializeElementClipboard}.
45597
- * Returns `null` for anything else (foreign clipboard text, malformed JSON,
45598
- * wrong marker/version, or structurally invalid elements), so callers can fall
45599
- * back to their plain-text paste path.
45600
- */
45601
- function deserializeElementClipboard(text) {
45602
- let parsed;
45603
- try {
45604
- parsed = JSON.parse(text, (_key, value) => {
45605
- if (typeof value === 'object' && value !== null) {
45606
- const tagged = value[BINARY_TAG];
45607
- if (typeof tagged === 'string') {
45608
- return base64ToUint8(tagged);
45609
- }
45610
- }
45611
- return value;
45612
- });
45613
- }
45614
- catch {
45615
- return null;
45616
- }
45617
- if (typeof parsed !== 'object' || parsed === null) {
45618
- return null;
45619
- }
45620
- const candidate = parsed;
45621
- if (candidate.marker !== ELEMENT_CLIPBOARD_MARKER ||
45622
- candidate.version !== ELEMENT_CLIPBOARD_VERSION ||
45623
- !Array.isArray(candidate.elements) ||
45624
- candidate.elements.length === 0 ||
45625
- !candidate.elements.every(isElementLike)) {
45626
- return null;
45627
- }
45628
- return {
45629
- elements: candidate.elements,
45630
- isTemplate: candidate.isTemplate === true,
45631
- };
45632
- }
45633
-
45634
46819
  /** Immutably replace one slide's inherited element list. */
45635
46820
  function setTemplateElements(map, slideId, elements) {
45636
46821
  return { ...map, [slideId]: elements };
@@ -53251,6 +54436,48 @@ const translationsEn = {
53251
54436
  'pptx.animation.sequence.byParagraph': 'By Paragraph',
53252
54437
  'pptx.animation.sequence.byWord': 'By Word',
53253
54438
  'pptx.animation.sequence.byLetter': 'By Letter',
54439
+ // Motion-path authoring: the gallery families/presets, the panel row, and
54440
+ // the on-canvas path editor handles.
54441
+ 'pptx.animation.motionPath': 'Motion Paths',
54442
+ 'pptx.animation.motionPath.family.lines': 'Lines',
54443
+ 'pptx.animation.motionPath.family.arcs': 'Arcs',
54444
+ 'pptx.animation.motionPath.family.turns': 'Turns',
54445
+ 'pptx.animation.motionPath.family.shapes': 'Shapes',
54446
+ 'pptx.animation.motionPath.family.loops': 'Loops',
54447
+ 'pptx.animation.motionPath.preset.lineRight': 'Right',
54448
+ 'pptx.animation.motionPath.preset.lineLeft': 'Left',
54449
+ 'pptx.animation.motionPath.preset.lineDown': 'Down',
54450
+ 'pptx.animation.motionPath.preset.lineUp': 'Up',
54451
+ 'pptx.animation.motionPath.preset.lineDiagonalUpRight': 'Diagonal Up Right',
54452
+ 'pptx.animation.motionPath.preset.lineDiagonalDownRight': 'Diagonal Down Right',
54453
+ 'pptx.animation.motionPath.preset.lineDiagonalUpLeft': 'Diagonal Up Left',
54454
+ 'pptx.animation.motionPath.preset.lineDiagonalDownLeft': 'Diagonal Down Left',
54455
+ 'pptx.animation.motionPath.preset.arcUp': 'Arc Up',
54456
+ 'pptx.animation.motionPath.preset.arcDown': 'Arc Down',
54457
+ 'pptx.animation.motionPath.preset.arcRight': 'Arc Right',
54458
+ 'pptx.animation.motionPath.preset.arcLeft': 'Arc Left',
54459
+ 'pptx.animation.motionPath.preset.turnUp': 'Turn Up',
54460
+ 'pptx.animation.motionPath.preset.turnDown': 'Turn Down',
54461
+ 'pptx.animation.motionPath.preset.turnRight': 'Turn Right',
54462
+ 'pptx.animation.motionPath.preset.turnLeft': 'Turn Left',
54463
+ 'pptx.animation.motionPath.preset.sCurve': 'S Curve',
54464
+ 'pptx.animation.motionPath.preset.zigzag': 'Zigzag',
54465
+ 'pptx.animation.motionPath.preset.circle': 'Circle',
54466
+ 'pptx.animation.motionPath.preset.square': 'Square',
54467
+ 'pptx.animation.motionPath.preset.triangle': 'Triangle',
54468
+ 'pptx.animation.motionPath.preset.diamond': 'Diamond',
54469
+ 'pptx.animation.motionPath.preset.hexagon': 'Hexagon',
54470
+ 'pptx.animation.motionPath.preset.loopDeLoop': 'Loop de Loop',
54471
+ 'pptx.animation.motionPath.preset.figureEight': 'Figure 8',
54472
+ 'pptx.animation.motionPath.preset.spiral': 'Spiral',
54473
+ 'pptx.animation.motionPath.label': 'Motion Path',
54474
+ 'pptx.animation.motionPath.custom': 'Custom Path',
54475
+ 'pptx.animation.motionPath.none': 'No motion path',
54476
+ 'pptx.animation.motionPath.remove': 'Remove motion path',
54477
+ 'pptx.animation.motionPath.editHint': 'Drag the end point on the slide to retarget the path',
54478
+ 'pptx.animation.motionPath.endHandle': 'Motion path end point',
54479
+ 'pptx.animation.motionPath.overlay': 'Motion path preview',
54480
+ 'pptx.animations.motionPathGalleryAria': 'Motion Paths: Lines, Arcs, Turns, Shapes, and Loops',
53254
54481
  // --- 2026-07-03 missing-key sweep: keys used by Vue/Angular with no
53255
54482
  // existing dictionary entry (React has 0 such gaps and is the ground-
53256
54483
  // truth reference these were derived from). Appended flat rather than
@@ -53408,6 +54635,11 @@ const translationsEn = {
53408
54635
  'pptx.view.masterViews': 'Master Views',
53409
54636
  'pptx.view.normal': 'Normal',
53410
54637
  'pptx.view.presentationViews': 'Presentation Views',
54638
+ 'pptx.view.outlineView': 'Outline View',
54639
+ 'pptx.view.outlineViewTooltip': 'Outline view: edit the deck as indented text',
54640
+ 'pptx.outline.titleLine': 'Slide title',
54641
+ 'pptx.outline.bodyLine': 'Outline line',
54642
+ 'pptx.outline.hint': 'Tab and Shift+Tab change the level. Enter adds a line; on a title it adds a slide.',
53411
54643
  'pptx.view.readingView': 'Reading View',
53412
54644
  'pptx.view.selection': 'Selection',
53413
54645
  'pptx.view.slideMasterTooltip': 'Edit slide masters and layouts',
@@ -56270,7 +57502,7 @@ function createLocalStorageBackend(namespace) {
56270
57502
  /** Try IndexedDB first; fall back to localStorage on any failure. */
56271
57503
  async function resolveBackend(dbName, namespace) {
56272
57504
  try {
56273
- const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-BqhL79Dq.mjs');
57505
+ const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-DmCC3jY4.mjs');
56274
57506
  const db = await openChatDb(dbName);
56275
57507
  return createIdbBackend(db);
56276
57508
  }
@@ -74602,6 +75834,11 @@ class SlideCanvasComponent {
74602
75834
  position: 'relative',
74603
75835
  overflow: 'hidden',
74604
75836
  'box-shadow': '0 10px 40px rgba(0, 0, 0, 0.35)',
75837
+ // Motion-path keyframes translate by a fraction of the SLIDE, not of the
75838
+ // animated element's own box, so the stage publishes its size for those
75839
+ // `calc(var(--pptx-slide-w) * f)` offsets to resolve against.
75840
+ '--pptx-slide-w': `${size.width}px`,
75841
+ '--pptx-slide-h': `${size.height}px`,
74605
75842
  // Resolved slide background: image → gradient → pattern → solid colour.
74606
75843
  // A stacked overlay layer (the morph departing slide) opts out entirely
74607
75844
  // and stays see-through, so it cannot occlude the stage beneath it.
@@ -77582,6 +78819,249 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
77582
78819
  `, styles: [":host{display:block}.pptx-ng-mtoolbar{position:relative;z-index:20;display:flex;align-items:center;gap:.25rem;padding:.25rem .5rem;min-height:52px;background:color-mix(in srgb,var(--pptx-secondary, #1f2937) 50%,transparent);border-bottom:1px solid var(--pptx-border, #374151);padding-top:max(env(safe-area-inset-top,0px),.25rem)}.pptx-ng-mtoolbar-spacer{flex:1 1 auto}.pptx-ng-mtoolbar-btn{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;border:none;border-radius:.375rem;background:transparent;color:color-mix(in srgb,var(--pptx-foreground, #f3f4f6) 80%,transparent);cursor:pointer;touch-action:manipulation;transition:background .12s,transform .08s;-webkit-tap-highlight-color:transparent}.pptx-ng-mtoolbar-btn:active:not([disabled]){transform:scale(.92)}.pptx-ng-mtoolbar-btn:hover:not([disabled]){background:color-mix(in srgb,var(--pptx-accent, #1f2937) 60%,transparent)}.pptx-ng-mtoolbar-btn.is-active{color:var(--pptx-primary, #6366f1)}.pptx-ng-mtoolbar-btn[disabled]{opacity:.35;cursor:not-allowed}.pptx-ng-mtoolbar-present{color:var(--pptx-primary, #6366f1)}.pptx-ng-mtoolbar-share{padding:0 .75rem;background:var(--pptx-primary, #6366f1);color:var(--pptx-primary-foreground, #ffffff)}.pptx-ng-mtoolbar-share:hover:not([disabled]){background:color-mix(in srgb,var(--pptx-primary, #6366f1) 90%,transparent)}.pptx-ng-mtoolbar-icon{width:1.25rem;height:1.25rem;flex-shrink:0}.pptx-ng-mtoolbar-icon.is-sm{width:1rem;height:1rem}\n"] }]
77583
78820
  }], propDecorators: { canUndo: [{ type: i0.Input, args: [{ isSignal: true, alias: "canUndo", required: false }] }], canRedo: [{ type: i0.Input, args: [{ isSignal: true, alias: "canRedo", required: false }] }], canPresent: [{ type: i0.Input, args: [{ isSignal: true, alias: "canPresent", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], menuOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuOpen", required: false }] }], aiEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiEnabled", required: false }] }], aiPanelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiPanelOpen", required: false }] }], hiddenActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenActions", required: false }] }], toggleMenu: [{ type: i0.Output, args: ["toggleMenu"] }], toggleAiPanel: [{ type: i0.Output, args: ["toggleAiPanel"] }], undo: [{ type: i0.Output, args: ["undo"] }], redo: [{ type: i0.Output, args: ["redo"] }], share: [{ type: i0.Output, args: ["share"] }], save: [{ type: i0.Output, args: ["save"] }], present: [{ type: i0.Output, args: ["present"] }] } });
77584
78821
 
78822
+ /**
78823
+ * motion-path-overlay.component.ts: draws the selected element's motion path on
78824
+ * the slide stage and lets the user drag its end point.
78825
+ *
78826
+ * Selector: `pptx-motion-path-overlay`
78827
+ *
78828
+ * WHY it is a stage-level sibling and not part of the element's own adorners: a
78829
+ * motion path routinely extends far outside the shape's bounding box, and the
78830
+ * element wrapper carries the shape's rotation / flip transform, which would
78831
+ * skew the path. Projected into the scaled stage (like the collaboration
78832
+ * overlays) it shares the stage's UNSCALED slide-pixel space, so the stage's
78833
+ * own `transform: scale()` applies the on-screen zoom exactly once and the only
78834
+ * zoom maths left is converting a pointer delta back into slide pixels.
78835
+ *
78836
+ * The DOM contract (`data-pptx-motion-path-overlay`,
78837
+ * `data-pptx-motion-path-handle="end"`) is deliberately neutral and identical
78838
+ * in every binding, so `e2e/` can address one viewer the same way it addresses
78839
+ * the next.
78840
+ *
78841
+ * Reference binding: packages/react/src/viewer/components/canvas/MotionPathOverlay.tsx
78842
+ *
78843
+ * @module viewer/motion-path-overlay
78844
+ */
78845
+ /** Slide size assumed when the stage cannot be measured (detached overlay). */
78846
+ const FALLBACK_CANVAS = { width: 1280, height: 720 };
78847
+ class MotionPathOverlayComponent {
78848
+ /** The selected element; its centre is the path origin. */
78849
+ element = input(null, /* @ts-ignore */
78850
+ ...(ngDevMode ? [{ debugName: "element" }] : /* istanbul ignore next */ []));
78851
+ /** The active slide's animations; the path is read off the matching entry. */
78852
+ animations = input([], /* @ts-ignore */
78853
+ ...(ngDevMode ? [{ debugName: "animations" }] : /* istanbul ignore next */ []));
78854
+ /** Stage size in slide pixels: the unit the path fractions scale by. */
78855
+ canvasSize = input(FALLBACK_CANVAS, /* @ts-ignore */
78856
+ ...(ngDevMode ? [{ debugName: "canvasSize" }] : /* istanbul ignore next */ []));
78857
+ /** Whether the deck is editable; a read-only deck draws no overlay at all. */
78858
+ canEdit = input(false, /* @ts-ignore */
78859
+ ...(ngDevMode ? [{ debugName: "canEdit" }] : /* istanbul ignore next */ []));
78860
+ /** Emits an edited path (drag of the end handle) for the host to commit. */
78861
+ pathChange = output();
78862
+ overlayRef = viewChild('overlay', /* @ts-ignore */
78863
+ ...(ngDevMode ? [{ debugName: "overlayRef" }] : /* istanbul ignore next */ []));
78864
+ drag = null;
78865
+ /** Guards a divide-by-zero when a host passes an unmeasured stage. */
78866
+ size = computed(() => {
78867
+ const size = this.canvasSize();
78868
+ return size.width > 0 && size.height > 0 ? size : FALLBACK_CANVAS;
78869
+ }, /* @ts-ignore */
78870
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
78871
+ /** The path applied to the selected element, or `''` when there is none. */
78872
+ path = computed(() => {
78873
+ const element = this.element();
78874
+ if (!element || !this.canEdit()) {
78875
+ return '';
78876
+ }
78877
+ return motionPathFor(this.animations(), element.id) ?? '';
78878
+ }, /* @ts-ignore */
78879
+ ...(ngDevMode ? [{ debugName: "path" }] : /* istanbul ignore next */ []));
78880
+ /** Slide-pixel frame the path fractions are measured against. */
78881
+ frame = computed(() => {
78882
+ const element = this.element();
78883
+ const size = this.size();
78884
+ return {
78885
+ originX: element ? element.x + element.width / 2 : 0,
78886
+ originY: element ? element.y + element.height / 2 : 0,
78887
+ slideWidth: size.width,
78888
+ slideHeight: size.height,
78889
+ };
78890
+ }, /* @ts-ignore */
78891
+ ...(ngDevMode ? [{ debugName: "frame" }] : /* istanbul ignore next */ []));
78892
+ /** The `d` attribute of the drawn path; empty when nothing is drawable. */
78893
+ pathD = computed(() => {
78894
+ const path = this.path();
78895
+ return path ? motionPathToSvgD(path, this.frame()) : '';
78896
+ }, /* @ts-ignore */
78897
+ ...(ngDevMode ? [{ debugName: "pathD" }] : /* istanbul ignore next */ []));
78898
+ /** Where the end handle sits, in slide pixels. */
78899
+ endPoint = computed(() => motionPathEndPixel(this.path(), this.frame()), /* @ts-ignore */
78900
+ ...(ngDevMode ? [{ debugName: "endPoint" }] : /* istanbul ignore next */ []));
78901
+ /** A closed shape has no free end, so its handle stays inert. */
78902
+ editable = computed(() => this.canEdit() && isEditableMotionPath(this.path()), /* @ts-ignore */
78903
+ ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
78904
+ onHandlePointerDown(event) {
78905
+ if (!this.editable()) {
78906
+ return;
78907
+ }
78908
+ event.stopPropagation();
78909
+ event.preventDefault();
78910
+ capturePointer(event);
78911
+ this.drag = { pointerId: event.pointerId, clientX: event.clientX, clientY: event.clientY };
78912
+ }
78913
+ onHandlePointerMove(event) {
78914
+ const drag = this.drag;
78915
+ if (!drag || drag.pointerId !== event.pointerId) {
78916
+ return;
78917
+ }
78918
+ event.stopPropagation();
78919
+ const path = this.path();
78920
+ const frame = this.frame();
78921
+ const scale = this.stageScale();
78922
+ const end = this.endPoint();
78923
+ const nextX = (end.x + (event.clientX - drag.clientX) / scale - frame.originX) / frame.slideWidth;
78924
+ const nextY = (end.y + (event.clientY - drag.clientY) / scale - frame.originY) / frame.slideHeight;
78925
+ const next = setMotionPathEnd(path, nextX, nextY);
78926
+ if (next === path) {
78927
+ return;
78928
+ }
78929
+ // Re-anchor to the pointer so the next move measures from where the path
78930
+ // actually ended up, not from where the gesture began.
78931
+ this.drag = { pointerId: event.pointerId, clientX: event.clientX, clientY: event.clientY };
78932
+ this.pathChange.emit(next);
78933
+ }
78934
+ onHandlePointerUp(event) {
78935
+ if (this.drag?.pointerId !== event.pointerId) {
78936
+ return;
78937
+ }
78938
+ releasePointer(event);
78939
+ this.drag = null;
78940
+ }
78941
+ /**
78942
+ * The stage's on-screen scale, measured rather than plumbed.
78943
+ *
78944
+ * The overlay is rendered INSIDE the scaled stage at slide-pixel size, so the
78945
+ * ratio between its rendered width and the slide width is exactly the scale
78946
+ * the stage transform applied (auto-fit folded with the user's zoom). Reading
78947
+ * it here avoids threading a value the canvas owns privately, per instance,
78948
+ * through the whole viewer.
78949
+ */
78950
+ stageScale() {
78951
+ const svg = this.overlayRef()?.nativeElement;
78952
+ if (!svg) {
78953
+ return 1;
78954
+ }
78955
+ const rendered = svg.getBoundingClientRect().width;
78956
+ return rendered > 0 ? rendered / this.size().width : 1;
78957
+ }
78958
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MotionPathOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
78959
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: MotionPathOverlayComponent, isStandalone: true, selector: "pptx-motion-path-overlay", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: false, transformFunction: null }, animations: { classPropertyName: "animations", publicName: "animations", isSignal: true, isRequired: false, transformFunction: null }, canvasSize: { classPropertyName: "canvasSize", publicName: "canvasSize", isSignal: true, isRequired: false, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pathChange: "pathChange" }, viewQueries: [{ propertyName: "overlayRef", first: true, predicate: ["overlay"], descendants: true, isSignal: true }], ngImport: i0, template: `
78960
+ @if (pathD(); as d) {
78961
+ <svg
78962
+ #overlay
78963
+ class="pptx-ng-motion-path-overlay"
78964
+ data-pptx-motion-path-overlay="true"
78965
+ role="img"
78966
+ [attr.aria-label]="'pptx.animation.motionPath.overlay' | translate"
78967
+ [attr.width]="size().width"
78968
+ [attr.height]="size().height"
78969
+ >
78970
+ <path
78971
+ [attr.d]="d"
78972
+ fill="none"
78973
+ stroke="#0ea5e9"
78974
+ stroke-width="2"
78975
+ stroke-dasharray="6 4"
78976
+ vector-effect="non-scaling-stroke"
78977
+ />
78978
+ <circle
78979
+ [attr.cx]="frame().originX"
78980
+ [attr.cy]="frame().originY"
78981
+ r="5"
78982
+ fill="#0ea5e9"
78983
+ opacity="0.55"
78984
+ />
78985
+ <circle
78986
+ data-pptx-motion-path-handle="end"
78987
+ [class.is-editable]="editable()"
78988
+ [attr.cx]="endPoint().x"
78989
+ [attr.cy]="endPoint().y"
78990
+ r="7"
78991
+ fill="#ffffff"
78992
+ stroke="#0ea5e9"
78993
+ stroke-width="2"
78994
+ [attr.aria-label]="'pptx.animation.motionPath.endHandle' | translate"
78995
+ (pointerdown)="onHandlePointerDown($event)"
78996
+ (pointermove)="onHandlePointerMove($event)"
78997
+ (pointerup)="onHandlePointerUp($event)"
78998
+ (pointercancel)="onHandlePointerUp($event)"
78999
+ />
79000
+ </svg>
79001
+ }
79002
+ `, isInline: true, styles: [":host{position:absolute;inset:0;pointer-events:none;overflow:visible}.pptx-ng-motion-path-overlay{position:absolute;top:0;left:0;z-index:45;pointer-events:none;overflow:visible}circle.is-editable{pointer-events:auto;cursor:move}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
79003
+ }
79004
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MotionPathOverlayComponent, decorators: [{
79005
+ type: Component,
79006
+ args: [{ selector: 'pptx-motion-path-overlay', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe], template: `
79007
+ @if (pathD(); as d) {
79008
+ <svg
79009
+ #overlay
79010
+ class="pptx-ng-motion-path-overlay"
79011
+ data-pptx-motion-path-overlay="true"
79012
+ role="img"
79013
+ [attr.aria-label]="'pptx.animation.motionPath.overlay' | translate"
79014
+ [attr.width]="size().width"
79015
+ [attr.height]="size().height"
79016
+ >
79017
+ <path
79018
+ [attr.d]="d"
79019
+ fill="none"
79020
+ stroke="#0ea5e9"
79021
+ stroke-width="2"
79022
+ stroke-dasharray="6 4"
79023
+ vector-effect="non-scaling-stroke"
79024
+ />
79025
+ <circle
79026
+ [attr.cx]="frame().originX"
79027
+ [attr.cy]="frame().originY"
79028
+ r="5"
79029
+ fill="#0ea5e9"
79030
+ opacity="0.55"
79031
+ />
79032
+ <circle
79033
+ data-pptx-motion-path-handle="end"
79034
+ [class.is-editable]="editable()"
79035
+ [attr.cx]="endPoint().x"
79036
+ [attr.cy]="endPoint().y"
79037
+ r="7"
79038
+ fill="#ffffff"
79039
+ stroke="#0ea5e9"
79040
+ stroke-width="2"
79041
+ [attr.aria-label]="'pptx.animation.motionPath.endHandle' | translate"
79042
+ (pointerdown)="onHandlePointerDown($event)"
79043
+ (pointermove)="onHandlePointerMove($event)"
79044
+ (pointerup)="onHandlePointerUp($event)"
79045
+ (pointercancel)="onHandlePointerUp($event)"
79046
+ />
79047
+ </svg>
79048
+ }
79049
+ `, styles: [":host{position:absolute;inset:0;pointer-events:none;overflow:visible}.pptx-ng-motion-path-overlay{position:absolute;top:0;left:0;z-index:45;pointer-events:none;overflow:visible}circle.is-editable{pointer-events:auto;cursor:move}\n"] }]
79050
+ }], propDecorators: { element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: false }] }], animations: [{ type: i0.Input, args: [{ isSignal: true, alias: "animations", required: false }] }], canvasSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "canvasSize", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], pathChange: [{ type: i0.Output, args: ["pathChange"] }], overlayRef: [{ type: i0.ViewChild, args: ['overlay', { isSignal: true }] }] } });
79051
+ /** Capture the pointer on the handle so a fast drag cannot outrun it. */
79052
+ function capturePointer(event) {
79053
+ const target = event.target;
79054
+ if (target instanceof Element && typeof target.setPointerCapture === 'function') {
79055
+ target.setPointerCapture(event.pointerId);
79056
+ }
79057
+ }
79058
+ function releasePointer(event) {
79059
+ const target = event.target;
79060
+ if (target instanceof Element && typeof target.releasePointerCapture === 'function') {
79061
+ target.releasePointerCapture(event.pointerId);
79062
+ }
79063
+ }
79064
+
77585
79065
  /**
77586
79066
  * notes-toolbar.component.ts: formatting toolbar for the rich speaker-notes
77587
79067
  * editor. Private child of {@link NotesPanelComponent} (not exported from the
@@ -78246,6 +79726,315 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
78246
79726
  `, styles: [":host{display:block}.pptx-ng-notes-panel{display:flex;flex-direction:column;border-top:1px solid var(--pptx-border, rgba(0, 0, 0, .1));background:var(--pptx-background, #ffffff);color:var(--pptx-foreground, #111827)}.pptx-ng-notes-header{display:flex;width:100%;align-items:center;justify-content:space-between;padding:.5rem .75rem;border:none;background:transparent;font-size:.8125rem;font-weight:600;color:var(--pptx-muted-foreground, #6b7280);cursor:pointer}.pptx-ng-notes-header:hover{color:var(--pptx-foreground, #111827)}.pptx-ng-notes-body{padding:0 .75rem .75rem}.pptx-ng-notes-rich,.pptx-ng-notes-textarea{box-sizing:border-box;width:100%;min-height:5rem;padding:.5rem;border:1px solid rgba(0,0,0,.15);border-radius:.375rem;background:#00000008;font:inherit;font-size:.8125rem;line-height:1.5;color:#111827}.pptx-ng-notes-rich{overflow:auto;resize:vertical}.pptx-ng-notes-textarea{resize:vertical}.pptx-ng-notes-rich:focus,.pptx-ng-notes-textarea:focus{outline:none;border-color:#6366f1;box-shadow:0 0 0 1px #6366f14d}.pptx-ng-notes-textarea:disabled{cursor:not-allowed;opacity:.6}\n"] }]
78247
79727
  }], ctorParameters: () => [], propDecorators: { slide: [{ type: i0.Input, args: [{ isSignal: true, alias: "slide", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], update: [{ type: i0.Output, args: ["update"] }], notesToggle: [{ type: i0.Output, args: ["notesToggle"] }], richEditor: [{ type: i0.ViewChild, args: ['richEditor', { isSignal: true }] }], textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }] } });
78248
79728
 
79729
+ /**
79730
+ * outline-view-overlay.styles.ts: CSS for PowerPoint's Outline view.
79731
+ *
79732
+ * Lifted out of the component (as `reading-view-overlay.styles.ts` was) so that
79733
+ * file stays a template plus its wiring and keeps under this repo's 300-LOC
79734
+ * cap. A plain exported const still compiles AOT: ng-packagr statically
79735
+ * evaluates the imported string.
79736
+ *
79737
+ * @module angular-viewer/outline-view-overlay.styles
79738
+ */
79739
+ /**
79740
+ * Styles for `pptx-outline-view-overlay`.
79741
+ *
79742
+ * `position: fixed; inset: 0` fills the browser WINDOW without the Fullscreen
79743
+ * API, matching the sibling reading view and slide sorter overlays.
79744
+ */
79745
+ const OUTLINE_VIEW_OVERLAY_STYLES = `
79746
+ .pptx-ng-outline-root {
79747
+ position: fixed;
79748
+ inset: 0;
79749
+ z-index: 1300;
79750
+ display: flex;
79751
+ flex-direction: column;
79752
+ background: #171717;
79753
+ color: #f5f5f5;
79754
+ }
79755
+
79756
+ .pptx-ng-outline-bar {
79757
+ display: flex;
79758
+ align-items: center;
79759
+ gap: 0.75rem;
79760
+ padding: 0.5rem 1rem;
79761
+ border-bottom: 1px solid rgb(255 255 255 / 0.1);
79762
+ }
79763
+
79764
+ .pptx-ng-outline-title {
79765
+ font-size: 0.875rem;
79766
+ font-weight: 600;
79767
+ }
79768
+
79769
+ .pptx-ng-outline-hint {
79770
+ flex: 1 1 auto;
79771
+ overflow: hidden;
79772
+ font-size: 0.6875rem;
79773
+ color: rgb(255 255 255 / 0.5);
79774
+ text-overflow: ellipsis;
79775
+ white-space: nowrap;
79776
+ }
79777
+
79778
+ .pptx-ng-outline-btn {
79779
+ display: inline-flex;
79780
+ align-items: center;
79781
+ justify-content: center;
79782
+ width: 2rem;
79783
+ height: 2rem;
79784
+ padding: 0;
79785
+ color: rgb(255 255 255 / 0.8);
79786
+ background: transparent;
79787
+ border: 0;
79788
+ border-radius: 0.25rem;
79789
+ cursor: pointer;
79790
+ }
79791
+
79792
+ .pptx-ng-outline-btn:hover {
79793
+ color: #ffffff;
79794
+ background: rgb(255 255 255 / 0.15);
79795
+ }
79796
+
79797
+ .pptx-ng-outline-rows {
79798
+ flex: 1 1 auto;
79799
+ min-height: 0;
79800
+ overflow: auto;
79801
+ padding: 0.75rem 1rem;
79802
+ }
79803
+
79804
+ .pptx-ng-outline-row {
79805
+ display: flex;
79806
+ align-items: center;
79807
+ gap: 0.5rem;
79808
+ padding: 0.125rem 0;
79809
+ }
79810
+
79811
+ .pptx-ng-outline-number {
79812
+ width: 1.5rem;
79813
+ flex: 0 0 auto;
79814
+ font-size: 0.625rem;
79815
+ font-variant-numeric: tabular-nums;
79816
+ color: rgb(255 255 255 / 0.4);
79817
+ text-align: right;
79818
+ }
79819
+
79820
+ .pptx-ng-outline-input {
79821
+ width: 100%;
79822
+ padding: 0.125rem 0.25rem;
79823
+ font-size: 0.8125rem;
79824
+ color: rgb(255 255 255 / 0.8);
79825
+ background: transparent;
79826
+ border: 0;
79827
+ border-radius: 0.25rem;
79828
+ outline: none;
79829
+ }
79830
+
79831
+ .pptx-ng-outline-input.is-title {
79832
+ font-size: 0.875rem;
79833
+ font-weight: 600;
79834
+ color: #f5f5f5;
79835
+ }
79836
+
79837
+ .pptx-ng-outline-input:focus {
79838
+ background: rgb(255 255 255 / 0.1);
79839
+ }
79840
+ `;
79841
+
79842
+ /**
79843
+ * outline-view-overlay.component.ts: PowerPoint's Outline view, Angular binding.
79844
+ *
79845
+ * The deck as an editable indented text document: one row per slide title at
79846
+ * the left margin, that slide's body lines stepped in beneath it. Typing edits
79847
+ * the slide, Tab and Shift+Tab change a line's outline level, and Enter on a
79848
+ * title starts a new slide.
79849
+ *
79850
+ * Every rule (what a row is, what Tab does, which edit produces a new slide)
79851
+ * comes from `render/outline-view` and `render/outline-view-edit` in
79852
+ * `pptx-viewer-shared`, so the five bindings cannot drift apart. Nothing in
79853
+ * this file decides what an outline gesture means.
79854
+ *
79855
+ * The deck it edits is the EDITABLE one, never the merged display deck: Angular
79856
+ * partitions each slide's inherited master/layout elements into a separate
79857
+ * store, and committing a merged deck back would bake that whole template layer
79858
+ * into every slide's own elements.
79859
+ *
79860
+ * Reference binding: packages/react/src/viewer/components/OutlineViewOverlay.tsx
79861
+ */
79862
+ /** Indent per outline level, in pixels. Level 0 (a title) sits flush left. */
79863
+ const INDENT_PX = 22;
79864
+ class OutlineViewOverlayComponent {
79865
+ /** The editable (template-free) deck. See the class docstring for why. */
79866
+ slides = input([], /* @ts-ignore */
79867
+ ...(ngDevMode ? [{ debugName: "slides" }] : /* istanbul ignore next */ []));
79868
+ canvasSize = input.required(/* @ts-ignore */
79869
+ ...(ngDevMode ? [{ debugName: "canvasSize" }] : /* istanbul ignore next */ []));
79870
+ canEdit = input(false, /* @ts-ignore */
79871
+ ...(ngDevMode ? [{ debugName: "canEdit" }] : /* istanbul ignore next */ []));
79872
+ /** Emits the deck after an edit, so the host records ONE undo entry. */
79873
+ commit = output();
79874
+ closed = output();
79875
+ INDENT_PX = INDENT_PX;
79876
+ rows = computed(() => buildOutline(this.slides()), /* @ts-ignore */
79877
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
79878
+ rowsHost = viewChild('rowsHost', /* @ts-ignore */
79879
+ ...(ngDevMode ? [{ debugName: "rowsHost" }] : /* istanbul ignore next */ []));
79880
+ onInput(event, key) {
79881
+ const target = event.target;
79882
+ this.run({ type: 'setText', key, text: target?.value ?? '' });
79883
+ }
79884
+ onRowKeyDown(event, key) {
79885
+ const { edit, preventDefault } = mapOutlineKey(event, key);
79886
+ if (preventDefault) {
79887
+ // Tab would otherwise walk out of the outline entirely, and Enter would
79888
+ // submit a surrounding form on a host page that has one.
79889
+ event.preventDefault();
79890
+ }
79891
+ if (edit) {
79892
+ this.run(edit);
79893
+ }
79894
+ }
79895
+ run(edit) {
79896
+ if (!this.canEdit()) {
79897
+ return;
79898
+ }
79899
+ const result = applyOutlineEdit(this.slides(), edit, { canvas: this.canvasSize() });
79900
+ if (!result.changed) {
79901
+ return;
79902
+ }
79903
+ this.commit.emit({ slides: result.slides, activeSlideIndex: result.activeSlideIndex });
79904
+ this.focusRow(result.focusKey);
79905
+ }
79906
+ /**
79907
+ * Restore the caret after the host has re-fed the new deck.
79908
+ *
79909
+ * Deferred to a microtask because the row that should take focus may not
79910
+ * exist yet: a new slide's title row only enters the DOM once change
79911
+ * detection has run over the committed deck.
79912
+ */
79913
+ focusRow(key) {
79914
+ if (!key) {
79915
+ return;
79916
+ }
79917
+ queueMicrotask(() => {
79918
+ const selector = `[${OUTLINE_ROW_ATTR}="${CSS.escape(key)}"]`;
79919
+ const target = this.rowsHost()?.nativeElement.querySelector(selector);
79920
+ if (target && document.activeElement !== target) {
79921
+ target.focus();
79922
+ target.setSelectionRange(target.value.length, target.value.length);
79923
+ }
79924
+ });
79925
+ }
79926
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OutlineViewOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
79927
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: OutlineViewOverlayComponent, isStandalone: true, selector: "pptx-outline-view-overlay", inputs: { slides: { classPropertyName: "slides", publicName: "slides", isSignal: true, isRequired: false, transformFunction: null }, canvasSize: { classPropertyName: "canvasSize", publicName: "canvasSize", isSignal: true, isRequired: true, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", closed: "closed" }, viewQueries: [{ propertyName: "rowsHost", first: true, predicate: ["rowsHost"], descendants: true, isSignal: true }], ngImport: i0, template: `
79928
+ <div
79929
+ class="pptx-ng-outline-root"
79930
+ [attr.data-pptx-outline-view]="'true'"
79931
+ role="region"
79932
+ [attr.aria-label]="'pptx.view.outlineView' | translate"
79933
+ >
79934
+ <div class="pptx-ng-outline-bar">
79935
+ <span class="pptx-ng-outline-title">{{ 'pptx.view.outlineView' | translate }}</span>
79936
+ <span class="pptx-ng-outline-hint">{{ 'pptx.outline.hint' | translate }}</span>
79937
+ <button
79938
+ type="button"
79939
+ class="pptx-ng-outline-btn"
79940
+ [attr.aria-label]="'pptx.statusBar.normalView' | translate"
79941
+ [title]="'pptx.statusBar.normalView' | translate"
79942
+ (click)="closed.emit()"
79943
+ >
79944
+ <svg lucideX class="h-4 w-4"></svg>
79945
+ </button>
79946
+ </div>
79947
+
79948
+ <div #rowsHost class="pptx-ng-outline-rows">
79949
+ @for (row of rows(); track row.key) {
79950
+ <div class="pptx-ng-outline-row" [style.padding-left.px]="row.level * INDENT_PX">
79951
+ <!--
79952
+ The slide number is drawn only on a slide's first row, which is
79953
+ always its title row, so the outline reads as a list of slides
79954
+ rather than as one undifferentiated wall of lines.
79955
+ -->
79956
+ <span class="pptx-ng-outline-number">{{
79957
+ row.kind === 'title' ? row.slideIndex + 1 : ''
79958
+ }}</span>
79959
+ <input
79960
+ type="text"
79961
+ class="pptx-ng-outline-input"
79962
+ [class.is-title]="row.kind === 'title'"
79963
+ [value]="row.text"
79964
+ [readOnly]="!canEdit()"
79965
+ [attr.data-pptx-outline-row]="row.key"
79966
+ [attr.data-pptx-outline-slide]="row.slideIndex + 1"
79967
+ [attr.data-pptx-outline-level]="row.level"
79968
+ [attr.aria-label]="
79969
+ (row.kind === 'title' ? 'pptx.outline.titleLine' : 'pptx.outline.bodyLine')
79970
+ | translate
79971
+ "
79972
+ (input)="onInput($event, row.key)"
79973
+ (keydown)="onRowKeyDown($event, row.key)"
79974
+ />
79975
+ </div>
79976
+ }
79977
+ </div>
79978
+ </div>
79979
+ `, isInline: true, styles: [".pptx-ng-outline-root{position:fixed;inset:0;z-index:1300;display:flex;flex-direction:column;background:#171717;color:#f5f5f5}.pptx-ng-outline-bar{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-bottom:1px solid rgb(255 255 255 / .1)}.pptx-ng-outline-title{font-size:.875rem;font-weight:600}.pptx-ng-outline-hint{flex:1 1 auto;overflow:hidden;font-size:.6875rem;color:#ffffff80;text-overflow:ellipsis;white-space:nowrap}.pptx-ng-outline-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;color:#fffc;background:transparent;border:0;border-radius:.25rem;cursor:pointer}.pptx-ng-outline-btn:hover{color:#fff;background:#ffffff26}.pptx-ng-outline-rows{flex:1 1 auto;min-height:0;overflow:auto;padding:.75rem 1rem}.pptx-ng-outline-row{display:flex;align-items:center;gap:.5rem;padding:.125rem 0}.pptx-ng-outline-number{width:1.5rem;flex:0 0 auto;font-size:.625rem;font-variant-numeric:tabular-nums;color:#fff6;text-align:right}.pptx-ng-outline-input{width:100%;padding:.125rem .25rem;font-size:.8125rem;color:#fffc;background:transparent;border:0;border-radius:.25rem;outline:none}.pptx-ng-outline-input.is-title{font-size:.875rem;font-weight:600;color:#f5f5f5}.pptx-ng-outline-input:focus{background:#ffffff1a}\n"], dependencies: [{ kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
79980
+ }
79981
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OutlineViewOverlayComponent, decorators: [{
79982
+ type: Component,
79983
+ args: [{ selector: 'pptx-outline-view-overlay', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe, LucideX], template: `
79984
+ <div
79985
+ class="pptx-ng-outline-root"
79986
+ [attr.data-pptx-outline-view]="'true'"
79987
+ role="region"
79988
+ [attr.aria-label]="'pptx.view.outlineView' | translate"
79989
+ >
79990
+ <div class="pptx-ng-outline-bar">
79991
+ <span class="pptx-ng-outline-title">{{ 'pptx.view.outlineView' | translate }}</span>
79992
+ <span class="pptx-ng-outline-hint">{{ 'pptx.outline.hint' | translate }}</span>
79993
+ <button
79994
+ type="button"
79995
+ class="pptx-ng-outline-btn"
79996
+ [attr.aria-label]="'pptx.statusBar.normalView' | translate"
79997
+ [title]="'pptx.statusBar.normalView' | translate"
79998
+ (click)="closed.emit()"
79999
+ >
80000
+ <svg lucideX class="h-4 w-4"></svg>
80001
+ </button>
80002
+ </div>
80003
+
80004
+ <div #rowsHost class="pptx-ng-outline-rows">
80005
+ @for (row of rows(); track row.key) {
80006
+ <div class="pptx-ng-outline-row" [style.padding-left.px]="row.level * INDENT_PX">
80007
+ <!--
80008
+ The slide number is drawn only on a slide's first row, which is
80009
+ always its title row, so the outline reads as a list of slides
80010
+ rather than as one undifferentiated wall of lines.
80011
+ -->
80012
+ <span class="pptx-ng-outline-number">{{
80013
+ row.kind === 'title' ? row.slideIndex + 1 : ''
80014
+ }}</span>
80015
+ <input
80016
+ type="text"
80017
+ class="pptx-ng-outline-input"
80018
+ [class.is-title]="row.kind === 'title'"
80019
+ [value]="row.text"
80020
+ [readOnly]="!canEdit()"
80021
+ [attr.data-pptx-outline-row]="row.key"
80022
+ [attr.data-pptx-outline-slide]="row.slideIndex + 1"
80023
+ [attr.data-pptx-outline-level]="row.level"
80024
+ [attr.aria-label]="
80025
+ (row.kind === 'title' ? 'pptx.outline.titleLine' : 'pptx.outline.bodyLine')
80026
+ | translate
80027
+ "
80028
+ (input)="onInput($event, row.key)"
80029
+ (keydown)="onRowKeyDown($event, row.key)"
80030
+ />
80031
+ </div>
80032
+ }
80033
+ </div>
80034
+ </div>
80035
+ `, styles: [".pptx-ng-outline-root{position:fixed;inset:0;z-index:1300;display:flex;flex-direction:column;background:#171717;color:#f5f5f5}.pptx-ng-outline-bar{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-bottom:1px solid rgb(255 255 255 / .1)}.pptx-ng-outline-title{font-size:.875rem;font-weight:600}.pptx-ng-outline-hint{flex:1 1 auto;overflow:hidden;font-size:.6875rem;color:#ffffff80;text-overflow:ellipsis;white-space:nowrap}.pptx-ng-outline-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;color:#fffc;background:transparent;border:0;border-radius:.25rem;cursor:pointer}.pptx-ng-outline-btn:hover{color:#fff;background:#ffffff26}.pptx-ng-outline-rows{flex:1 1 auto;min-height:0;overflow:auto;padding:.75rem 1rem}.pptx-ng-outline-row{display:flex;align-items:center;gap:.5rem;padding:.125rem 0}.pptx-ng-outline-number{width:1.5rem;flex:0 0 auto;font-size:.625rem;font-variant-numeric:tabular-nums;color:#fff6;text-align:right}.pptx-ng-outline-input{width:100%;padding:.125rem .25rem;font-size:.8125rem;color:#fffc;background:transparent;border:0;border-radius:.25rem;outline:none}.pptx-ng-outline-input.is-title{font-size:.875rem;font-weight:600;color:#f5f5f5}.pptx-ng-outline-input:focus{background:#ffffff1a}\n"] }]
80036
+ }], propDecorators: { slides: [{ type: i0.Input, args: [{ isSignal: true, alias: "slides", required: false }] }], canvasSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "canvasSize", required: true }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], commit: [{ type: i0.Output, args: ["commit"] }], closed: [{ type: i0.Output, args: ["closed"] }], rowsHost: [{ type: i0.ViewChild, args: ['rowsHost', { isSignal: true }] }] } });
80037
+
78249
80038
  /**
78250
80039
  * A drop-in replacement for `html2canvas(element, options)` that first
78251
80040
  * resolves any oklch / oklab / lch / lab / color() values in the cloned
@@ -83587,6 +85376,13 @@ class PresentationOverlayComponent {
83587
85376
  width: `${size.width * z}px`,
83588
85377
  height: `${size.height * z}px`,
83589
85378
  transform: 'translate(-50%, -50%)',
85379
+ // Motion-path keyframes translate by a fraction of the SLIDE (see
85380
+ // `slideOffset` in the shared timeline helpers), so the presentation
85381
+ // stage publishes the slide size the same way the editing stage does.
85382
+ // Without it every parsed path falls back to the 1280x720 default and a
85383
+ // deck authored at another size under-travels.
85384
+ '--pptx-slide-w': `${size.width}px`,
85385
+ '--pptx-slide-h': `${size.height}px`,
83590
85386
  };
83591
85387
  }, /* @ts-ignore */
83592
85388
  ...(ngDevMode ? [{ debugName: "stageContainerStyle" }] : /* istanbul ignore next */ []));
@@ -86936,6 +88732,121 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
86936
88732
  }]
86937
88733
  }], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], addAnimation: [{ type: i0.Output, args: ["addAnimation"] }] } });
86938
88734
 
88735
+ /**
88736
+ * ribbon-motion-path-gallery.component.ts: the Animations tab's motion-path
88737
+ * gallery (PowerPoint's Lines / Arcs / Turns / Shapes / Loops families).
88738
+ *
88739
+ * Its own component rather than more markup inside
88740
+ * {@link RibbonAnimationsSectionComponent} for the same reason
88741
+ * {@link RibbonAnimationGalleryComponent} is: the catalogue is thirty-odd
88742
+ * buttons, which would push that file past this repo's 300-LOC cap on its own.
88743
+ *
88744
+ * WHY it is a SIBLING of the entrance/emphasis/exit gallery and not a fourth
88745
+ * column of it: a motion path is not one of those three buckets. It is
88746
+ * geometry that coexists with them on the SAME animation entry, so folding it
88747
+ * into the preset columns would imply a mutually exclusive choice the model
88748
+ * does not make (an element can fade in AND travel a path).
88749
+ *
88750
+ * Every path is a real <button> carrying its translated name as both `title`
88751
+ * and visible text, because `e2e/ribbon-control-inventory.spec.ts` inventories
88752
+ * each tab by accessible name and diffs every binding against React: a gallery
88753
+ * hidden behind a hover menu, or one whose buttons are named by an icon only,
88754
+ * is a gallery a screen-reader user (and that spec) does not have. The columns
88755
+ * come from the shared catalogue rather than a hand-written list so a preset
88756
+ * added there needs no follow-up in five bindings.
88757
+ */
88758
+ /**
88759
+ * The gallery's columns, in the shared catalogue's own (ribbon) order.
88760
+ *
88761
+ * Built once at module load: the catalogue is static, so recomputing it per
88762
+ * change-detection pass would allocate thirty view models for nothing.
88763
+ */
88764
+ const MOTION_PATH_COLUMNS = MOTION_PATH_FAMILIES.map((family) => ({
88765
+ family,
88766
+ labelKey: motionPathFamilyLabelKey(family),
88767
+ presets: motionPathPresetsByFamily(family).map((preset) => ({
88768
+ id: preset.id,
88769
+ labelKey: motionPathPresetLabelKey(preset.id),
88770
+ })),
88771
+ }));
88772
+ class RibbonMotionPathGalleryComponent {
88773
+ /** True when the deck is not editable or nothing is selected. */
88774
+ disabled = input(true, /* @ts-ignore */
88775
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
88776
+ /** Emits the catalogue preset id of the pressed path. */
88777
+ applyMotionPath = output();
88778
+ columns = MOTION_PATH_COLUMNS;
88779
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonMotionPathGalleryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
88780
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: RibbonMotionPathGalleryComponent, isStandalone: true, selector: "pptx-ribbon-motion-path-gallery", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { applyMotionPath: "applyMotionPath" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
88781
+ <div
88782
+ role="group"
88783
+ class="flex max-h-[62px] items-start gap-2 overflow-y-auto rounded-sm border border-border/60 bg-muted/30 px-1.5 py-1"
88784
+ [attr.aria-label]="'pptx.animations.motionPathGalleryAria' | translate"
88785
+ >
88786
+ @for (column of columns; track column.family) {
88787
+ <div class="flex flex-col gap-0.5">
88788
+ <span class="text-[9px] font-semibold leading-3 text-muted-foreground">
88789
+ {{ column.labelKey | translate }}
88790
+ </span>
88791
+ <div class="flex max-w-[150px] flex-wrap gap-0.5">
88792
+ @for (preset of column.presets; track preset.id) {
88793
+ <button
88794
+ type="button"
88795
+ class="inline-flex items-center gap-0.5 rounded-sm px-1 py-0.5 text-[9px] leading-3 text-foreground transition-colors hover:bg-accent disabled:opacity-35"
88796
+ [disabled]="disabled()"
88797
+ [title]="preset.labelKey | translate"
88798
+ (click)="applyMotionPath.emit(preset.id)"
88799
+ >
88800
+ <svg lucideMoveRight aria-hidden="true" class="h-2.5 w-2.5 text-sky-500"></svg>
88801
+ <span class="whitespace-nowrap">{{ preset.labelKey | translate }}</span>
88802
+ </button>
88803
+ }
88804
+ </div>
88805
+ </div>
88806
+ }
88807
+ </div>
88808
+ `, isInline: true, dependencies: [{ kind: "component", type: LucideMoveRight, selector: "svg[lucideMoveRight]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
88809
+ }
88810
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonMotionPathGalleryComponent, decorators: [{
88811
+ type: Component,
88812
+ args: [{
88813
+ selector: 'pptx-ribbon-motion-path-gallery',
88814
+ standalone: true,
88815
+ changeDetection: ChangeDetectionStrategy.OnPush,
88816
+ host: { class: 'contents' },
88817
+ imports: [TranslatePipe, LucideMoveRight],
88818
+ template: `
88819
+ <div
88820
+ role="group"
88821
+ class="flex max-h-[62px] items-start gap-2 overflow-y-auto rounded-sm border border-border/60 bg-muted/30 px-1.5 py-1"
88822
+ [attr.aria-label]="'pptx.animations.motionPathGalleryAria' | translate"
88823
+ >
88824
+ @for (column of columns; track column.family) {
88825
+ <div class="flex flex-col gap-0.5">
88826
+ <span class="text-[9px] font-semibold leading-3 text-muted-foreground">
88827
+ {{ column.labelKey | translate }}
88828
+ </span>
88829
+ <div class="flex max-w-[150px] flex-wrap gap-0.5">
88830
+ @for (preset of column.presets; track preset.id) {
88831
+ <button
88832
+ type="button"
88833
+ class="inline-flex items-center gap-0.5 rounded-sm px-1 py-0.5 text-[9px] leading-3 text-foreground transition-colors hover:bg-accent disabled:opacity-35"
88834
+ [disabled]="disabled()"
88835
+ [title]="preset.labelKey | translate"
88836
+ (click)="applyMotionPath.emit(preset.id)"
88837
+ >
88838
+ <svg lucideMoveRight aria-hidden="true" class="h-2.5 w-2.5 text-sky-500"></svg>
88839
+ <span class="whitespace-nowrap">{{ preset.labelKey | translate }}</span>
88840
+ </button>
88841
+ }
88842
+ </div>
88843
+ </div>
88844
+ }
88845
+ </div>
88846
+ `,
88847
+ }]
88848
+ }], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], applyMotionPath: [{ type: i0.Output, args: ["applyMotionPath"] }] } });
88849
+
86939
88850
  /**
86940
88851
  * ribbon-animations-section.component.ts: the Animations ribbon tab (Preview,
86941
88852
  * the entrance/emphasis/exit gallery, the Advanced Animation group, and the
@@ -86969,10 +88880,36 @@ class RibbonAnimationsSectionComponent {
86969
88880
  canAuthor() {
86970
88881
  return canAuthorAnimation(this.canEdit(), this.hasSel());
86971
88882
  }
88883
+ /** The path the one-click "Path Animation" command applies. */
88884
+ defaultMotionPathPresetId = DEFAULT_MOTION_PATH_PRESET_ID;
86972
88885
  /** Apply the preset a gallery button asked for. */
86973
88886
  onGalleryPick(pick) {
86974
88887
  this.addAnimation(pick.preset, pick.group);
86975
88888
  }
88889
+ /**
88890
+ * Apply a catalogue motion path to the selected element.
88891
+ *
88892
+ * Separate from {@link addAnimation} because a path is written to the
88893
+ * `motionPath` field of the SAME animation entry the preset buckets use, not
88894
+ * to one of those buckets: applying a path must not wipe an entrance the
88895
+ * element already carries, and clearing that entrance later must not wipe
88896
+ * the path. The shared helper owns both rules.
88897
+ */
88898
+ applyMotionPath(presetId) {
88899
+ if (!this.canEdit()) {
88900
+ return;
88901
+ }
88902
+ const el = this.selectedElement();
88903
+ if (!el) {
88904
+ return;
88905
+ }
88906
+ const slide = this.editor.slides()[this.slideIndex()];
88907
+ if (!slide) {
88908
+ return;
88909
+ }
88910
+ const updated = applyMotionPathPreset(slide.animations ?? [], el.id, presetId);
88911
+ this.editor.updateSlide(this.slideIndex(), { animations: updated });
88912
+ }
86976
88913
  /**
86977
88914
  * Add an animation preset to the selected element on the active slide.
86978
88915
  * Delegates to the immutable helpers in animation-author-helpers.ts and
@@ -87038,6 +88975,28 @@ class RibbonAnimationsSectionComponent {
87038
88975
  (addAnimation)="onGalleryPick($event)"
87039
88976
  />
87040
88977
  <span class="pptx-rb-sep"></span>
88978
+ <!--
88979
+ Motion Paths: its own captioned group (a path is geometry that coexists
88980
+ with the preset buckets, not a fourth bucket of them), capped at React's
88981
+ own 420px. Uncapped, five 150px family columns claim ~800px of a
88982
+ single-row ribbon and squeeze the Advanced Animation pills beside them
88983
+ until their labels overlap.
88984
+ -->
88985
+ <section
88986
+ class="relative flex max-w-[420px] shrink items-start gap-1 overflow-hidden pb-3"
88987
+ [attr.aria-label]="'pptx.animation.motionPath' | translate"
88988
+ >
88989
+ <pptx-ribbon-motion-path-gallery
88990
+ [disabled]="!canAuthor()"
88991
+ (applyMotionPath)="applyMotionPath($event)"
88992
+ />
88993
+ <span
88994
+ class="pointer-events-none absolute inset-x-1 bottom-0 truncate text-center text-[9px] leading-3 text-muted-foreground"
88995
+ >
88996
+ {{ 'pptx.animation.motionPath' | translate }}
88997
+ </span>
88998
+ </section>
88999
+ <span class="pptx-rb-sep"></span>
87041
89000
  <!-- Advanced Animation -->
87042
89001
  <button
87043
89002
  type="button"
@@ -87048,11 +89007,15 @@ class RibbonAnimationsSectionComponent {
87048
89007
  <svg lucideStar class="h-4 w-4 text-red-500"></svg>
87049
89008
  {{ 'pptx.animations.exitEffects' | translate }}
87050
89009
  </button>
89010
+ <!--
89011
+ One-click default path (Lines: Right). It used to apply a Fly In
89012
+ entrance, which is not a path at all.
89013
+ -->
87051
89014
  <button
87052
89015
  type="button"
87053
89016
  class="pptx-rb-pill"
87054
89017
  [disabled]="!canAuthor()"
87055
- (click)="addAnimation('flyIn', 'entrance')"
89018
+ (click)="applyMotionPath(defaultMotionPathPresetId)"
87056
89019
  >
87057
89020
  <svg lucideMoveRight class="h-4 w-4"></svg>
87058
89021
  {{ 'pptx.animations.pathAnimation' | translate }}
@@ -87128,7 +89091,7 @@ class RibbonAnimationsSectionComponent {
87128
89091
  disabled
87129
89092
  />
87130
89093
  </div>
87131
- `, isInline: true, dependencies: [{ kind: "component", type: LucidePlay, selector: "svg[lucidePlay]" }, { kind: "component", type: LucideSparkles, selector: "svg[lucideSparkles], svg[lucideStars]" }, { kind: "component", type: LucideStar, selector: "svg[lucideStar]" }, { kind: "component", type: LucideMoveRight, selector: "svg[lucideMoveRight]" }, { kind: "component", type: LucideMousePointerClick, selector: "svg[lucideMousePointerClick]" }, { kind: "component", type: LucidePaintbrush, selector: "svg[lucidePaintbrush]" }, { kind: "component", type: LucideClock3, selector: "svg[lucideClock3]" }, { kind: "component", type: LucideTrash2, selector: "svg[lucideTrash2]" }, { kind: "component", type: LucidePanelRight, selector: "svg[lucidePanelRight]" }, { kind: "component", type: RibbonAnimationGalleryComponent, selector: "pptx-ribbon-animation-gallery", inputs: ["disabled"], outputs: ["addAnimation"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
89094
+ `, isInline: true, dependencies: [{ kind: "component", type: LucidePlay, selector: "svg[lucidePlay]" }, { kind: "component", type: LucideSparkles, selector: "svg[lucideSparkles], svg[lucideStars]" }, { kind: "component", type: LucideStar, selector: "svg[lucideStar]" }, { kind: "component", type: LucideMoveRight, selector: "svg[lucideMoveRight]" }, { kind: "component", type: LucideMousePointerClick, selector: "svg[lucideMousePointerClick]" }, { kind: "component", type: LucidePaintbrush, selector: "svg[lucidePaintbrush]" }, { kind: "component", type: LucideClock3, selector: "svg[lucideClock3]" }, { kind: "component", type: LucideTrash2, selector: "svg[lucideTrash2]" }, { kind: "component", type: LucidePanelRight, selector: "svg[lucidePanelRight]" }, { kind: "component", type: RibbonAnimationGalleryComponent, selector: "pptx-ribbon-animation-gallery", inputs: ["disabled"], outputs: ["addAnimation"] }, { kind: "component", type: RibbonMotionPathGalleryComponent, selector: "pptx-ribbon-motion-path-gallery", inputs: ["disabled"], outputs: ["applyMotionPath"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
87132
89095
  }
87133
89096
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonAnimationsSectionComponent, decorators: [{
87134
89097
  type: Component,
@@ -87149,6 +89112,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
87149
89112
  LucideTrash2,
87150
89113
  LucidePanelRight,
87151
89114
  RibbonAnimationGalleryComponent,
89115
+ RibbonMotionPathGalleryComponent,
87152
89116
  ],
87153
89117
  template: `
87154
89118
  <!-- Preview: plays presentation from this slide; no element-only preview API yet -->
@@ -87168,6 +89132,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
87168
89132
  (addAnimation)="onGalleryPick($event)"
87169
89133
  />
87170
89134
  <span class="pptx-rb-sep"></span>
89135
+ <!--
89136
+ Motion Paths: its own captioned group (a path is geometry that coexists
89137
+ with the preset buckets, not a fourth bucket of them), capped at React's
89138
+ own 420px. Uncapped, five 150px family columns claim ~800px of a
89139
+ single-row ribbon and squeeze the Advanced Animation pills beside them
89140
+ until their labels overlap.
89141
+ -->
89142
+ <section
89143
+ class="relative flex max-w-[420px] shrink items-start gap-1 overflow-hidden pb-3"
89144
+ [attr.aria-label]="'pptx.animation.motionPath' | translate"
89145
+ >
89146
+ <pptx-ribbon-motion-path-gallery
89147
+ [disabled]="!canAuthor()"
89148
+ (applyMotionPath)="applyMotionPath($event)"
89149
+ />
89150
+ <span
89151
+ class="pointer-events-none absolute inset-x-1 bottom-0 truncate text-center text-[9px] leading-3 text-muted-foreground"
89152
+ >
89153
+ {{ 'pptx.animation.motionPath' | translate }}
89154
+ </span>
89155
+ </section>
89156
+ <span class="pptx-rb-sep"></span>
87171
89157
  <!-- Advanced Animation -->
87172
89158
  <button
87173
89159
  type="button"
@@ -87178,11 +89164,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
87178
89164
  <svg lucideStar class="h-4 w-4 text-red-500"></svg>
87179
89165
  {{ 'pptx.animations.exitEffects' | translate }}
87180
89166
  </button>
89167
+ <!--
89168
+ One-click default path (Lines: Right). It used to apply a Fly In
89169
+ entrance, which is not a path at all.
89170
+ -->
87181
89171
  <button
87182
89172
  type="button"
87183
89173
  class="pptx-rb-pill"
87184
89174
  [disabled]="!canAuthor()"
87185
- (click)="addAnimation('flyIn', 'entrance')"
89175
+ (click)="applyMotionPath(defaultMotionPathPresetId)"
87186
89176
  >
87187
89177
  <svg lucideMoveRight class="h-4 w-4"></svg>
87188
89178
  {{ 'pptx.animations.pathAnimation' | translate }}
@@ -88484,6 +90474,8 @@ class RibbonViewSectionComponent {
88484
90474
  openSorter = output();
88485
90475
  /** Enter Reading View: the deck full-window, NOT the fullscreen slide show. */
88486
90476
  openReadingView = output();
90477
+ /** Enter Outline view: the deck as editable indented text. */
90478
+ openOutlineView = output();
88487
90479
  openMasterView = output();
88488
90480
  toggleGrid = output();
88489
90481
  toggleRulers = output();
@@ -88495,7 +90487,7 @@ class RibbonViewSectionComponent {
88495
90487
  zoomToFit = output();
88496
90488
  toggleEyedropper = output();
88497
90489
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonViewSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
88498
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: RibbonViewSectionComponent, isStandalone: true, selector: "pptx-ribbon-view-section", inputs: { canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, showRulers: { classPropertyName: "showRulers", publicName: "showRulers", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, snapToGrid: { classPropertyName: "snapToGrid", publicName: "snapToGrid", isSignal: true, isRequired: false, transformFunction: null }, snapToShape: { classPropertyName: "snapToShape", publicName: "snapToShape", isSignal: true, isRequired: false, transformFunction: null }, eyedropperActive: { classPropertyName: "eyedropperActive", publicName: "eyedropperActive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openSorter: "openSorter", openReadingView: "openReadingView", openMasterView: "openMasterView", toggleGrid: "toggleGrid", toggleRulers: "toggleRulers", toggleGuides: "toggleGuides", toggleSelectionPane: "toggleSelectionPane", toggleSnapToGrid: "toggleSnapToGrid", toggleSnapToShape: "toggleSnapToShape", addGuide: "addGuide", zoomToFit: "zoomToFit", toggleEyedropper: "toggleEyedropper" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
90490
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: RibbonViewSectionComponent, isStandalone: true, selector: "pptx-ribbon-view-section", inputs: { canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, showRulers: { classPropertyName: "showRulers", publicName: "showRulers", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, snapToGrid: { classPropertyName: "snapToGrid", publicName: "snapToGrid", isSignal: true, isRequired: false, transformFunction: null }, snapToShape: { classPropertyName: "snapToShape", publicName: "snapToShape", isSignal: true, isRequired: false, transformFunction: null }, eyedropperActive: { classPropertyName: "eyedropperActive", publicName: "eyedropperActive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openSorter: "openSorter", openReadingView: "openReadingView", openOutlineView: "openOutlineView", openMasterView: "openMasterView", toggleGrid: "toggleGrid", toggleRulers: "toggleRulers", toggleGuides: "toggleGuides", toggleSelectionPane: "toggleSelectionPane", toggleSnapToGrid: "toggleSnapToGrid", toggleSnapToShape: "toggleSnapToShape", addGuide: "addGuide", zoomToFit: "zoomToFit", toggleEyedropper: "toggleEyedropper" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
88499
90491
  <!-- Presentation views -->
88500
90492
  <button type="button" class="pptx-rb-pill" [title]="'pptx.view.normal' | translate">
88501
90493
  {{ 'pptx.view.normal' | translate }}
@@ -88508,6 +90500,14 @@ class RibbonViewSectionComponent {
88508
90500
  >
88509
90501
  {{ 'pptx.slideSorter.title' | translate }}
88510
90502
  </button>
90503
+ <button
90504
+ type="button"
90505
+ class="pptx-rb-pill"
90506
+ [title]="'pptx.view.outlineViewTooltip' | translate"
90507
+ (click)="openOutlineView.emit()"
90508
+ >
90509
+ {{ 'pptx.view.outlineView' | translate }}
90510
+ </button>
88511
90511
  <button
88512
90512
  type="button"
88513
90513
  class="pptx-rb-pill"
@@ -88669,6 +90669,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
88669
90669
  >
88670
90670
  {{ 'pptx.slideSorter.title' | translate }}
88671
90671
  </button>
90672
+ <button
90673
+ type="button"
90674
+ class="pptx-rb-pill"
90675
+ [title]="'pptx.view.outlineViewTooltip' | translate"
90676
+ (click)="openOutlineView.emit()"
90677
+ >
90678
+ {{ 'pptx.view.outlineView' | translate }}
90679
+ </button>
88672
90680
  <button
88673
90681
  type="button"
88674
90682
  class="pptx-rb-pill"
@@ -88809,7 +90817,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
88809
90817
  </button>
88810
90818
  `,
88811
90819
  }]
88812
- }], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], eyedropperActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropperActive", required: false }] }], openSorter: [{ type: i0.Output, args: ["openSorter"] }], openReadingView: [{ type: i0.Output, args: ["openReadingView"] }], openMasterView: [{ type: i0.Output, args: ["openMasterView"] }], toggleGrid: [{ type: i0.Output, args: ["toggleGrid"] }], toggleRulers: [{ type: i0.Output, args: ["toggleRulers"] }], toggleGuides: [{ type: i0.Output, args: ["toggleGuides"] }], toggleSelectionPane: [{ type: i0.Output, args: ["toggleSelectionPane"] }], toggleSnapToGrid: [{ type: i0.Output, args: ["toggleSnapToGrid"] }], toggleSnapToShape: [{ type: i0.Output, args: ["toggleSnapToShape"] }], addGuide: [{ type: i0.Output, args: ["addGuide"] }], zoomToFit: [{ type: i0.Output, args: ["zoomToFit"] }], toggleEyedropper: [{ type: i0.Output, args: ["toggleEyedropper"] }] } });
90820
+ }], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], eyedropperActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropperActive", required: false }] }], openSorter: [{ type: i0.Output, args: ["openSorter"] }], openReadingView: [{ type: i0.Output, args: ["openReadingView"] }], openOutlineView: [{ type: i0.Output, args: ["openOutlineView"] }], openMasterView: [{ type: i0.Output, args: ["openMasterView"] }], toggleGrid: [{ type: i0.Output, args: ["toggleGrid"] }], toggleRulers: [{ type: i0.Output, args: ["toggleRulers"] }], toggleGuides: [{ type: i0.Output, args: ["toggleGuides"] }], toggleSelectionPane: [{ type: i0.Output, args: ["toggleSelectionPane"] }], toggleSnapToGrid: [{ type: i0.Output, args: ["toggleSnapToGrid"] }], toggleSnapToShape: [{ type: i0.Output, args: ["toggleSnapToShape"] }], addGuide: [{ type: i0.Output, args: ["addGuide"] }], zoomToFit: [{ type: i0.Output, args: ["zoomToFit"] }], toggleEyedropper: [{ type: i0.Output, args: ["toggleEyedropper"] }] } });
88813
90821
 
88814
90822
  /**
88815
90823
  * ribbon-content-secondary.component.ts: the ribbon's Slide Show/Review/View/
@@ -88880,6 +90888,7 @@ class RibbonContentSecondaryComponent {
88880
90888
  openSorter = output();
88881
90889
  /** View tab > Reading View: the deck full-window, not the slide show. */
88882
90890
  openReadingView = output();
90891
+ openOutlineView = output();
88883
90892
  openMasterView = output();
88884
90893
  toggleNotes = output();
88885
90894
  toggleInspector = output();
@@ -88918,7 +90927,7 @@ class RibbonContentSecondaryComponent {
88918
90927
  this.drawToolChange.emit(state);
88919
90928
  }
88920
90929
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonContentSecondaryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
88921
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: RibbonContentSecondaryComponent, isStandalone: true, selector: "pptx-ribbon-content-secondary", inputs: { activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: true, transformFunction: null }, slideIndex: { classPropertyName: "slideIndex", publicName: "slideIndex", isSignal: true, isRequired: false, transformFunction: null }, slideCount: { classPropertyName: "slideCount", publicName: "slideCount", isSignal: true, isRequired: false, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, selectedElement: { classPropertyName: "selectedElement", publicName: "selectedElement", isSignal: true, isRequired: false, transformFunction: null }, showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, showRulers: { classPropertyName: "showRulers", publicName: "showRulers", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, snapToGrid: { classPropertyName: "snapToGrid", publicName: "snapToGrid", isSignal: true, isRequired: false, transformFunction: null }, snapToShape: { classPropertyName: "snapToShape", publicName: "snapToShape", isSignal: true, isRequired: false, transformFunction: null }, eyedropperActive: { classPropertyName: "eyedropperActive", publicName: "eyedropperActive", isSignal: true, isRequired: false, transformFunction: null }, themeGalleryOpen: { classPropertyName: "themeGalleryOpen", publicName: "themeGalleryOpen", isSignal: true, isRequired: false, transformFunction: null }, spellCheckEnabled: { classPropertyName: "spellCheckEnabled", publicName: "spellCheckEnabled", isSignal: true, isRequired: false, transformFunction: null }, showSubtitles: { classPropertyName: "showSubtitles", publicName: "showSubtitles", isSignal: true, isRequired: false, transformFunction: null }, hiddenActions: { classPropertyName: "hiddenActions", publicName: "hiddenActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { present: "present", presenter: "presenter", record: "record", presentFromBeginning: "presentFromBeginning", rehearseTimings: "rehearseTimings", toggleSubtitles: "toggleSubtitles", openSubtitleSettings: "openSubtitleSettings", recordFromBeginning: "recordFromBeginning", recordFromCurrent: "recordFromCurrent", spellCheckChange: "spellCheckChange", broadcast: "broadcast", info: "info", print: "print", comments: "comments", a11y: "a11y", link: "link", openSorter: "openSorter", openReadingView: "openReadingView", openMasterView: "openMasterView", toggleNotes: "toggleNotes", toggleInspector: "toggleInspector", drawToolChange: "drawToolChange", toggleThemeGallery: "toggleThemeGallery", toggleGrid: "toggleGrid", toggleRulers: "toggleRulers", toggleGuides: "toggleGuides", toggleSelectionPane: "toggleSelectionPane", openCustomShows: "openCustomShows", toggleSnapToGrid: "toggleSnapToGrid", toggleSnapToShape: "toggleSnapToShape", addGuide: "addGuide", zoomToFit: "zoomToFit", toggleEyedropper: "toggleEyedropper", openSetUpSlideShow: "openSetUpSlideShow", openCompare: "openCompare", openShortcuts: "openShortcuts", openSettings: "openSettings" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
90930
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: RibbonContentSecondaryComponent, isStandalone: true, selector: "pptx-ribbon-content-secondary", inputs: { activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: true, transformFunction: null }, slideIndex: { classPropertyName: "slideIndex", publicName: "slideIndex", isSignal: true, isRequired: false, transformFunction: null }, slideCount: { classPropertyName: "slideCount", publicName: "slideCount", isSignal: true, isRequired: false, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, selectedElement: { classPropertyName: "selectedElement", publicName: "selectedElement", isSignal: true, isRequired: false, transformFunction: null }, showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, showRulers: { classPropertyName: "showRulers", publicName: "showRulers", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, snapToGrid: { classPropertyName: "snapToGrid", publicName: "snapToGrid", isSignal: true, isRequired: false, transformFunction: null }, snapToShape: { classPropertyName: "snapToShape", publicName: "snapToShape", isSignal: true, isRequired: false, transformFunction: null }, eyedropperActive: { classPropertyName: "eyedropperActive", publicName: "eyedropperActive", isSignal: true, isRequired: false, transformFunction: null }, themeGalleryOpen: { classPropertyName: "themeGalleryOpen", publicName: "themeGalleryOpen", isSignal: true, isRequired: false, transformFunction: null }, spellCheckEnabled: { classPropertyName: "spellCheckEnabled", publicName: "spellCheckEnabled", isSignal: true, isRequired: false, transformFunction: null }, showSubtitles: { classPropertyName: "showSubtitles", publicName: "showSubtitles", isSignal: true, isRequired: false, transformFunction: null }, hiddenActions: { classPropertyName: "hiddenActions", publicName: "hiddenActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { present: "present", presenter: "presenter", record: "record", presentFromBeginning: "presentFromBeginning", rehearseTimings: "rehearseTimings", toggleSubtitles: "toggleSubtitles", openSubtitleSettings: "openSubtitleSettings", recordFromBeginning: "recordFromBeginning", recordFromCurrent: "recordFromCurrent", spellCheckChange: "spellCheckChange", broadcast: "broadcast", info: "info", print: "print", comments: "comments", a11y: "a11y", link: "link", openSorter: "openSorter", openReadingView: "openReadingView", openOutlineView: "openOutlineView", openMasterView: "openMasterView", toggleNotes: "toggleNotes", toggleInspector: "toggleInspector", drawToolChange: "drawToolChange", toggleThemeGallery: "toggleThemeGallery", toggleGrid: "toggleGrid", toggleRulers: "toggleRulers", toggleGuides: "toggleGuides", toggleSelectionPane: "toggleSelectionPane", openCustomShows: "openCustomShows", toggleSnapToGrid: "toggleSnapToGrid", toggleSnapToShape: "toggleSnapToShape", addGuide: "addGuide", zoomToFit: "zoomToFit", toggleEyedropper: "toggleEyedropper", openSetUpSlideShow: "openSetUpSlideShow", openCompare: "openCompare", openShortcuts: "openShortcuts", openSettings: "openSettings" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
88922
90931
  @switch (activeTab()) {
88923
90932
  @case ('slideShow') {
88924
90933
  <pptx-ribbon-slideshow-section
@@ -88960,6 +90969,7 @@ class RibbonContentSecondaryComponent {
88960
90969
  [eyedropperActive]="eyedropperActive()"
88961
90970
  (openSorter)="openSorter.emit()"
88962
90971
  (openReadingView)="openReadingView.emit()"
90972
+ (openOutlineView)="openOutlineView.emit()"
88963
90973
  (openMasterView)="openMasterView.emit()"
88964
90974
  (toggleGrid)="toggleGrid.emit()"
88965
90975
  (toggleRulers)="toggleRulers.emit()"
@@ -89026,7 +91036,7 @@ class RibbonContentSecondaryComponent {
89026
91036
  />
89027
91037
  }
89028
91038
  }
89029
- `, isInline: true, dependencies: [{ kind: "component", type: RibbonSlideshowSectionComponent, selector: "pptx-ribbon-slideshow-section", inputs: ["slideCount", "showSubtitles", "hiddenActions"], outputs: ["presentFromBeginning", "presentFromCurrent", "presenter", "broadcast", "openCustomShows", "openSetUpSlideShow", "rehearseTimings", "record", "toggleSubtitles", "openSubtitleSettings"] }, { kind: "component", type: RibbonReviewSectionComponent, selector: "pptx-ribbon-review-section", inputs: ["spellCheckEnabled", "canEdit"], outputs: ["comments", "spellCheckChange", "a11y", "openCompare", "language", "link"] }, { kind: "component", type: RibbonViewSectionComponent, selector: "pptx-ribbon-view-section", inputs: ["canEdit", "showGrid", "showRulers", "showGuides", "snapToGrid", "snapToShape", "eyedropperActive"], outputs: ["openSorter", "openReadingView", "openMasterView", "toggleGrid", "toggleRulers", "toggleGuides", "toggleSelectionPane", "toggleSnapToGrid", "toggleSnapToShape", "addGuide", "zoomToFit", "toggleEyedropper"] }, { kind: "component", type: RibbonDrawSectionComponent, selector: "pptx-ribbon-draw-section", inputs: ["activeTool", "drawingColor", "drawingWidth"], outputs: ["drawToolChange"] }, { kind: "component", type: RibbonDesignSectionComponent, selector: "pptx-ribbon-design-section", inputs: ["themeGalleryOpen"], outputs: ["toggleThemeGallery", "info", "toggleInspector"] }, { kind: "component", type: RibbonTransitionsSectionComponent, selector: "pptx-ribbon-transitions-section", inputs: ["slideIndex", "selectedTransition", "transitionDurationSec"], outputs: ["present", "toggleInspector", "transitionChange", "durationChange"] }, { kind: "component", type: RibbonAnimationsSectionComponent, selector: "pptx-ribbon-animations-section", inputs: ["slideIndex", "selectedElement", "canEdit"], outputs: ["present", "toggleInspector"] }, { kind: "component", type: RibbonRecordSectionComponent, selector: "pptx-ribbon-record-section", outputs: ["recordFromBeginning", "recordFromCurrent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
91039
+ `, isInline: true, dependencies: [{ kind: "component", type: RibbonSlideshowSectionComponent, selector: "pptx-ribbon-slideshow-section", inputs: ["slideCount", "showSubtitles", "hiddenActions"], outputs: ["presentFromBeginning", "presentFromCurrent", "presenter", "broadcast", "openCustomShows", "openSetUpSlideShow", "rehearseTimings", "record", "toggleSubtitles", "openSubtitleSettings"] }, { kind: "component", type: RibbonReviewSectionComponent, selector: "pptx-ribbon-review-section", inputs: ["spellCheckEnabled", "canEdit"], outputs: ["comments", "spellCheckChange", "a11y", "openCompare", "language", "link"] }, { kind: "component", type: RibbonViewSectionComponent, selector: "pptx-ribbon-view-section", inputs: ["canEdit", "showGrid", "showRulers", "showGuides", "snapToGrid", "snapToShape", "eyedropperActive"], outputs: ["openSorter", "openReadingView", "openOutlineView", "openMasterView", "toggleGrid", "toggleRulers", "toggleGuides", "toggleSelectionPane", "toggleSnapToGrid", "toggleSnapToShape", "addGuide", "zoomToFit", "toggleEyedropper"] }, { kind: "component", type: RibbonDrawSectionComponent, selector: "pptx-ribbon-draw-section", inputs: ["activeTool", "drawingColor", "drawingWidth"], outputs: ["drawToolChange"] }, { kind: "component", type: RibbonDesignSectionComponent, selector: "pptx-ribbon-design-section", inputs: ["themeGalleryOpen"], outputs: ["toggleThemeGallery", "info", "toggleInspector"] }, { kind: "component", type: RibbonTransitionsSectionComponent, selector: "pptx-ribbon-transitions-section", inputs: ["slideIndex", "selectedTransition", "transitionDurationSec"], outputs: ["present", "toggleInspector", "transitionChange", "durationChange"] }, { kind: "component", type: RibbonAnimationsSectionComponent, selector: "pptx-ribbon-animations-section", inputs: ["slideIndex", "selectedElement", "canEdit"], outputs: ["present", "toggleInspector"] }, { kind: "component", type: RibbonRecordSectionComponent, selector: "pptx-ribbon-record-section", outputs: ["recordFromBeginning", "recordFromCurrent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
89030
91040
  }
89031
91041
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonContentSecondaryComponent, decorators: [{
89032
91042
  type: Component,
@@ -89088,6 +91098,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
89088
91098
  [eyedropperActive]="eyedropperActive()"
89089
91099
  (openSorter)="openSorter.emit()"
89090
91100
  (openReadingView)="openReadingView.emit()"
91101
+ (openOutlineView)="openOutlineView.emit()"
89091
91102
  (openMasterView)="openMasterView.emit()"
89092
91103
  (toggleGrid)="toggleGrid.emit()"
89093
91104
  (toggleRulers)="toggleRulers.emit()"
@@ -89156,7 +91167,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
89156
91167
  }
89157
91168
  `,
89158
91169
  }]
89159
- }], propDecorators: { activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: true }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], slideCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideCount", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], eyedropperActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropperActive", required: false }] }], themeGalleryOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeGalleryOpen", required: false }] }], spellCheckEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellCheckEnabled", required: false }] }], showSubtitles: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSubtitles", required: false }] }], hiddenActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenActions", required: false }] }], present: [{ type: i0.Output, args: ["present"] }], presenter: [{ type: i0.Output, args: ["presenter"] }], record: [{ type: i0.Output, args: ["record"] }], presentFromBeginning: [{ type: i0.Output, args: ["presentFromBeginning"] }], rehearseTimings: [{ type: i0.Output, args: ["rehearseTimings"] }], toggleSubtitles: [{ type: i0.Output, args: ["toggleSubtitles"] }], openSubtitleSettings: [{ type: i0.Output, args: ["openSubtitleSettings"] }], recordFromBeginning: [{ type: i0.Output, args: ["recordFromBeginning"] }], recordFromCurrent: [{ type: i0.Output, args: ["recordFromCurrent"] }], spellCheckChange: [{ type: i0.Output, args: ["spellCheckChange"] }], broadcast: [{ type: i0.Output, args: ["broadcast"] }], info: [{ type: i0.Output, args: ["info"] }], print: [{ type: i0.Output, args: ["print"] }], comments: [{ type: i0.Output, args: ["comments"] }], a11y: [{ type: i0.Output, args: ["a11y"] }], link: [{ type: i0.Output, args: ["link"] }], openSorter: [{ type: i0.Output, args: ["openSorter"] }], openReadingView: [{ type: i0.Output, args: ["openReadingView"] }], openMasterView: [{ type: i0.Output, args: ["openMasterView"] }], toggleNotes: [{ type: i0.Output, args: ["toggleNotes"] }], toggleInspector: [{ type: i0.Output, args: ["toggleInspector"] }], drawToolChange: [{ type: i0.Output, args: ["drawToolChange"] }], toggleThemeGallery: [{ type: i0.Output, args: ["toggleThemeGallery"] }], toggleGrid: [{ type: i0.Output, args: ["toggleGrid"] }], toggleRulers: [{ type: i0.Output, args: ["toggleRulers"] }], toggleGuides: [{ type: i0.Output, args: ["toggleGuides"] }], toggleSelectionPane: [{ type: i0.Output, args: ["toggleSelectionPane"] }], openCustomShows: [{ type: i0.Output, args: ["openCustomShows"] }], toggleSnapToGrid: [{ type: i0.Output, args: ["toggleSnapToGrid"] }], toggleSnapToShape: [{ type: i0.Output, args: ["toggleSnapToShape"] }], addGuide: [{ type: i0.Output, args: ["addGuide"] }], zoomToFit: [{ type: i0.Output, args: ["zoomToFit"] }], toggleEyedropper: [{ type: i0.Output, args: ["toggleEyedropper"] }], openSetUpSlideShow: [{ type: i0.Output, args: ["openSetUpSlideShow"] }], openCompare: [{ type: i0.Output, args: ["openCompare"] }], openShortcuts: [{ type: i0.Output, args: ["openShortcuts"] }], openSettings: [{ type: i0.Output, args: ["openSettings"] }] } });
91170
+ }], propDecorators: { activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: true }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], slideCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideCount", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], eyedropperActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropperActive", required: false }] }], themeGalleryOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeGalleryOpen", required: false }] }], spellCheckEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellCheckEnabled", required: false }] }], showSubtitles: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSubtitles", required: false }] }], hiddenActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenActions", required: false }] }], present: [{ type: i0.Output, args: ["present"] }], presenter: [{ type: i0.Output, args: ["presenter"] }], record: [{ type: i0.Output, args: ["record"] }], presentFromBeginning: [{ type: i0.Output, args: ["presentFromBeginning"] }], rehearseTimings: [{ type: i0.Output, args: ["rehearseTimings"] }], toggleSubtitles: [{ type: i0.Output, args: ["toggleSubtitles"] }], openSubtitleSettings: [{ type: i0.Output, args: ["openSubtitleSettings"] }], recordFromBeginning: [{ type: i0.Output, args: ["recordFromBeginning"] }], recordFromCurrent: [{ type: i0.Output, args: ["recordFromCurrent"] }], spellCheckChange: [{ type: i0.Output, args: ["spellCheckChange"] }], broadcast: [{ type: i0.Output, args: ["broadcast"] }], info: [{ type: i0.Output, args: ["info"] }], print: [{ type: i0.Output, args: ["print"] }], comments: [{ type: i0.Output, args: ["comments"] }], a11y: [{ type: i0.Output, args: ["a11y"] }], link: [{ type: i0.Output, args: ["link"] }], openSorter: [{ type: i0.Output, args: ["openSorter"] }], openReadingView: [{ type: i0.Output, args: ["openReadingView"] }], openOutlineView: [{ type: i0.Output, args: ["openOutlineView"] }], openMasterView: [{ type: i0.Output, args: ["openMasterView"] }], toggleNotes: [{ type: i0.Output, args: ["toggleNotes"] }], toggleInspector: [{ type: i0.Output, args: ["toggleInspector"] }], drawToolChange: [{ type: i0.Output, args: ["drawToolChange"] }], toggleThemeGallery: [{ type: i0.Output, args: ["toggleThemeGallery"] }], toggleGrid: [{ type: i0.Output, args: ["toggleGrid"] }], toggleRulers: [{ type: i0.Output, args: ["toggleRulers"] }], toggleGuides: [{ type: i0.Output, args: ["toggleGuides"] }], toggleSelectionPane: [{ type: i0.Output, args: ["toggleSelectionPane"] }], openCustomShows: [{ type: i0.Output, args: ["openCustomShows"] }], toggleSnapToGrid: [{ type: i0.Output, args: ["toggleSnapToGrid"] }], toggleSnapToShape: [{ type: i0.Output, args: ["toggleSnapToShape"] }], addGuide: [{ type: i0.Output, args: ["addGuide"] }], zoomToFit: [{ type: i0.Output, args: ["zoomToFit"] }], toggleEyedropper: [{ type: i0.Output, args: ["toggleEyedropper"] }], openSetUpSlideShow: [{ type: i0.Output, args: ["openSetUpSlideShow"] }], openCompare: [{ type: i0.Output, args: ["openCompare"] }], openShortcuts: [{ type: i0.Output, args: ["openShortcuts"] }], openSettings: [{ type: i0.Output, args: ["openSettings"] }] } });
89160
91171
 
89161
91172
  /**
89162
91173
  * ribbon-shape-extras.component.ts: the Arrange group's shape-level extras
@@ -90069,7 +92080,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
90069
92080
  }], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }] } });
90070
92081
 
90071
92082
  // Generated by scripts/inline-shared.mjs from package.json. Do not edit.
90072
- const PPTX_ANGULAR_VIEWER_VERSION = "2.9.0";
92083
+ const PPTX_ANGULAR_VIEWER_VERSION = "2.10.0";
90073
92084
 
90074
92085
  /**
90075
92086
  * account-page.component.ts: File > Account content.
@@ -94132,6 +96143,7 @@ class RibbonComponent {
94132
96143
  openSorter = output();
94133
96144
  /** View tab > Reading View: the deck full-window, not the slide show. */
94134
96145
  openReadingView = output();
96146
+ openOutlineView = output();
94135
96147
  openMasterView = output();
94136
96148
  toggleNotes = output();
94137
96149
  toggleFormatPainter = output();
@@ -94196,7 +96208,7 @@ class RibbonComponent {
94196
96208
  this.spellCheckChange.emit(enabled);
94197
96209
  }
94198
96210
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
94199
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: RibbonComponent, isStandalone: true, selector: "pptx-ribbon", inputs: { slideIndex: { classPropertyName: "slideIndex", publicName: "slideIndex", isSignal: true, isRequired: false, transformFunction: null }, slideCount: { classPropertyName: "slideCount", publicName: "slideCount", isSignal: true, isRequired: false, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, selectedElement: { classPropertyName: "selectedElement", publicName: "selectedElement", isSignal: true, isRequired: false, transformFunction: null }, zoomPercent: { classPropertyName: "zoomPercent", publicName: "zoomPercent", isSignal: true, isRequired: false, transformFunction: null }, formatPainterActive: { classPropertyName: "formatPainterActive", publicName: "formatPainterActive", isSignal: true, isRequired: false, transformFunction: null }, canActivateFormatPainter: { classPropertyName: "canActivateFormatPainter", publicName: "canActivateFormatPainter", isSignal: true, isRequired: false, transformFunction: null }, exporting: { classPropertyName: "exporting", publicName: "exporting", isSignal: true, isRequired: false, transformFunction: null }, hasMacros: { classPropertyName: "hasMacros", publicName: "hasMacros", isSignal: true, isRequired: false, transformFunction: null }, showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, showRulers: { classPropertyName: "showRulers", publicName: "showRulers", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, snapToGrid: { classPropertyName: "snapToGrid", publicName: "snapToGrid", isSignal: true, isRequired: false, transformFunction: null }, snapToShape: { classPropertyName: "snapToShape", publicName: "snapToShape", isSignal: true, isRequired: false, transformFunction: null }, eyedropperActive: { classPropertyName: "eyedropperActive", publicName: "eyedropperActive", isSignal: true, isRequired: false, transformFunction: null }, themeGalleryOpen: { classPropertyName: "themeGalleryOpen", publicName: "themeGalleryOpen", isSignal: true, isRequired: false, transformFunction: null }, sidebarCollapsed: { classPropertyName: "sidebarCollapsed", publicName: "sidebarCollapsed", isSignal: true, isRequired: false, transformFunction: null }, inspectorOpen: { classPropertyName: "inspectorOpen", publicName: "inspectorOpen", isSignal: true, isRequired: false, transformFunction: null }, commentsOpen: { classPropertyName: "commentsOpen", publicName: "commentsOpen", isSignal: true, isRequired: false, transformFunction: null }, commentCount: { classPropertyName: "commentCount", publicName: "commentCount", isSignal: true, isRequired: false, transformFunction: null }, findOpen: { classPropertyName: "findOpen", publicName: "findOpen", isSignal: true, isRequired: false, transformFunction: null }, collabConnected: { classPropertyName: "collabConnected", publicName: "collabConnected", isSignal: true, isRequired: false, transformFunction: null }, connectedCount: { classPropertyName: "connectedCount", publicName: "connectedCount", isSignal: true, isRequired: false, transformFunction: null }, spellCheckEnabled: { classPropertyName: "spellCheckEnabled", publicName: "spellCheckEnabled", isSignal: true, isRequired: false, transformFunction: null }, showSubtitles: { classPropertyName: "showSubtitles", publicName: "showSubtitles", isSignal: true, isRequired: false, transformFunction: null }, hiddenActions: { classPropertyName: "hiddenActions", publicName: "hiddenActions", isSignal: true, isRequired: false, transformFunction: null }, aiEnabled: { classPropertyName: "aiEnabled", publicName: "aiEnabled", isSignal: true, isRequired: false, transformFunction: null }, aiPanelOpen: { classPropertyName: "aiPanelOpen", publicName: "aiPanelOpen", isSignal: true, isRequired: false, transformFunction: null }, accountAuth: { classPropertyName: "accountAuth", publicName: "accountAuth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { prev: "prev", next: "next", zoomIn: "zoomIn", zoomOut: "zoomOut", zoomReset: "zoomReset", find: "find", present: "present", presenter: "presenter", record: "record", presentFromBeginning: "presentFromBeginning", rehearseTimings: "rehearseTimings", toggleSubtitles: "toggleSubtitles", openSubtitleSettings: "openSubtitleSettings", recordFromBeginning: "recordFromBeginning", recordFromCurrent: "recordFromCurrent", spellCheckChange: "spellCheckChange", share: "share", broadcast: "broadcast", openFile: "openFile", openRecentFile: "openRecentFile", createPresentation: "createPresentation", save: "save", savePpsx: "savePpsx", savePptm: "savePptm", packageForSharing: "packageForSharing", toggleSidebar: "toggleSidebar", toggleAiPanel: "toggleAiPanel", signatures: "signatures", info: "info", print: "print", comments: "comments", a11y: "a11y", link: "link", openSorter: "openSorter", openReadingView: "openReadingView", openMasterView: "openMasterView", toggleNotes: "toggleNotes", toggleFormatPainter: "toggleFormatPainter", exportPng: "exportPng", exportPdf: "exportPdf", exportGif: "exportGif", exportVideo: "exportVideo", copySlideAsImage: "copySlideAsImage", replace: "replace", toggleInspector: "toggleInspector", drawToolChange: "drawToolChange", toggleThemeGallery: "toggleThemeGallery", toggleGrid: "toggleGrid", toggleRulers: "toggleRulers", toggleGuides: "toggleGuides", toggleSelectionPane: "toggleSelectionPane", openCustomShows: "openCustomShows", toggleSnapToGrid: "toggleSnapToGrid", toggleSnapToShape: "toggleSnapToShape", addGuide: "addGuide", zoomToFit: "zoomToFit", toggleEyedropper: "toggleEyedropper", openSmartArtDialog: "openSmartArtDialog", openEquationDialog: "openEquationDialog", openSetUpSlideShow: "openSetUpSlideShow", openCompare: "openCompare", openPassword: "openPassword", openFontEmbedding: "openFontEmbedding", openVersionHistory: "openVersionHistory", openShortcuts: "openShortcuts", openSettings: "openSettings" }, ngImport: i0, template: `
96211
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: RibbonComponent, isStandalone: true, selector: "pptx-ribbon", inputs: { slideIndex: { classPropertyName: "slideIndex", publicName: "slideIndex", isSignal: true, isRequired: false, transformFunction: null }, slideCount: { classPropertyName: "slideCount", publicName: "slideCount", isSignal: true, isRequired: false, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, selectedElement: { classPropertyName: "selectedElement", publicName: "selectedElement", isSignal: true, isRequired: false, transformFunction: null }, zoomPercent: { classPropertyName: "zoomPercent", publicName: "zoomPercent", isSignal: true, isRequired: false, transformFunction: null }, formatPainterActive: { classPropertyName: "formatPainterActive", publicName: "formatPainterActive", isSignal: true, isRequired: false, transformFunction: null }, canActivateFormatPainter: { classPropertyName: "canActivateFormatPainter", publicName: "canActivateFormatPainter", isSignal: true, isRequired: false, transformFunction: null }, exporting: { classPropertyName: "exporting", publicName: "exporting", isSignal: true, isRequired: false, transformFunction: null }, hasMacros: { classPropertyName: "hasMacros", publicName: "hasMacros", isSignal: true, isRequired: false, transformFunction: null }, showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, showRulers: { classPropertyName: "showRulers", publicName: "showRulers", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, snapToGrid: { classPropertyName: "snapToGrid", publicName: "snapToGrid", isSignal: true, isRequired: false, transformFunction: null }, snapToShape: { classPropertyName: "snapToShape", publicName: "snapToShape", isSignal: true, isRequired: false, transformFunction: null }, eyedropperActive: { classPropertyName: "eyedropperActive", publicName: "eyedropperActive", isSignal: true, isRequired: false, transformFunction: null }, themeGalleryOpen: { classPropertyName: "themeGalleryOpen", publicName: "themeGalleryOpen", isSignal: true, isRequired: false, transformFunction: null }, sidebarCollapsed: { classPropertyName: "sidebarCollapsed", publicName: "sidebarCollapsed", isSignal: true, isRequired: false, transformFunction: null }, inspectorOpen: { classPropertyName: "inspectorOpen", publicName: "inspectorOpen", isSignal: true, isRequired: false, transformFunction: null }, commentsOpen: { classPropertyName: "commentsOpen", publicName: "commentsOpen", isSignal: true, isRequired: false, transformFunction: null }, commentCount: { classPropertyName: "commentCount", publicName: "commentCount", isSignal: true, isRequired: false, transformFunction: null }, findOpen: { classPropertyName: "findOpen", publicName: "findOpen", isSignal: true, isRequired: false, transformFunction: null }, collabConnected: { classPropertyName: "collabConnected", publicName: "collabConnected", isSignal: true, isRequired: false, transformFunction: null }, connectedCount: { classPropertyName: "connectedCount", publicName: "connectedCount", isSignal: true, isRequired: false, transformFunction: null }, spellCheckEnabled: { classPropertyName: "spellCheckEnabled", publicName: "spellCheckEnabled", isSignal: true, isRequired: false, transformFunction: null }, showSubtitles: { classPropertyName: "showSubtitles", publicName: "showSubtitles", isSignal: true, isRequired: false, transformFunction: null }, hiddenActions: { classPropertyName: "hiddenActions", publicName: "hiddenActions", isSignal: true, isRequired: false, transformFunction: null }, aiEnabled: { classPropertyName: "aiEnabled", publicName: "aiEnabled", isSignal: true, isRequired: false, transformFunction: null }, aiPanelOpen: { classPropertyName: "aiPanelOpen", publicName: "aiPanelOpen", isSignal: true, isRequired: false, transformFunction: null }, accountAuth: { classPropertyName: "accountAuth", publicName: "accountAuth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { prev: "prev", next: "next", zoomIn: "zoomIn", zoomOut: "zoomOut", zoomReset: "zoomReset", find: "find", present: "present", presenter: "presenter", record: "record", presentFromBeginning: "presentFromBeginning", rehearseTimings: "rehearseTimings", toggleSubtitles: "toggleSubtitles", openSubtitleSettings: "openSubtitleSettings", recordFromBeginning: "recordFromBeginning", recordFromCurrent: "recordFromCurrent", spellCheckChange: "spellCheckChange", share: "share", broadcast: "broadcast", openFile: "openFile", openRecentFile: "openRecentFile", createPresentation: "createPresentation", save: "save", savePpsx: "savePpsx", savePptm: "savePptm", packageForSharing: "packageForSharing", toggleSidebar: "toggleSidebar", toggleAiPanel: "toggleAiPanel", signatures: "signatures", info: "info", print: "print", comments: "comments", a11y: "a11y", link: "link", openSorter: "openSorter", openReadingView: "openReadingView", openOutlineView: "openOutlineView", openMasterView: "openMasterView", toggleNotes: "toggleNotes", toggleFormatPainter: "toggleFormatPainter", exportPng: "exportPng", exportPdf: "exportPdf", exportGif: "exportGif", exportVideo: "exportVideo", copySlideAsImage: "copySlideAsImage", replace: "replace", toggleInspector: "toggleInspector", drawToolChange: "drawToolChange", toggleThemeGallery: "toggleThemeGallery", toggleGrid: "toggleGrid", toggleRulers: "toggleRulers", toggleGuides: "toggleGuides", toggleSelectionPane: "toggleSelectionPane", openCustomShows: "openCustomShows", toggleSnapToGrid: "toggleSnapToGrid", toggleSnapToShape: "toggleSnapToShape", addGuide: "addGuide", zoomToFit: "zoomToFit", toggleEyedropper: "toggleEyedropper", openSmartArtDialog: "openSmartArtDialog", openEquationDialog: "openEquationDialog", openSetUpSlideShow: "openSetUpSlideShow", openCompare: "openCompare", openPassword: "openPassword", openFontEmbedding: "openFontEmbedding", openVersionHistory: "openVersionHistory", openShortcuts: "openShortcuts", openSettings: "openSettings" }, ngImport: i0, template: `
94200
96212
  <div
94201
96213
  role="toolbar"
94202
96214
  [attr.aria-label]="'pptx.toolbar.presentationToolbarAria' | translate"
@@ -94321,6 +96333,7 @@ class RibbonComponent {
94321
96333
  (link)="link.emit()"
94322
96334
  (openSorter)="openSorter.emit()"
94323
96335
  (openReadingView)="openReadingView.emit()"
96336
+ (openOutlineView)="openOutlineView.emit()"
94324
96337
  (openMasterView)="openMasterView.emit()"
94325
96338
  (toggleNotes)="toggleNotes.emit()"
94326
96339
  (toggleInspector)="toggleInspector.emit()"
@@ -94343,7 +96356,7 @@ class RibbonComponent {
94343
96356
  />
94344
96357
  </div>
94345
96358
  </div>
94346
- `, isInline: true, dependencies: [{ kind: "component", type: RibbonPrimaryRowComponent, selector: "pptx-ribbon-primary-row", inputs: ["slideCount", "sidebarCollapsed", "inspectorOpen", "commentsOpen", "commentCount", "hiddenActions", "aiEnabled", "aiPanelOpen"], outputs: ["toggleSidebar", "toggleAiPanel", "toggleComments", "present", "presenter", "broadcast", "openCustomShows", "toggleInspector", "openSettings", "exportPng", "exportPdf", "exportGif", "exportVideo", "print", "info", "a11y", "save"] }, { kind: "component", type: RibbonTabListComponent, selector: "pptx-ribbon-tab-list", inputs: ["activeTab", "canEdit", "collabConnected", "connectedCount", "ribbonExpanded", "hiddenActions"], outputs: ["selectTab", "record", "share", "toggleRibbonExpanded"] }, { kind: "component", type: RibbonContentComponent, selector: "pptx-ribbon-content", inputs: ["activeTab", "slideIndex", "slideCount", "canEdit", "selectedElement", "formatPainterActive", "canActivateFormatPainter", "exporting", "hasMacros", "hiddenActions", "accountAuth"], outputs: ["selectTab", "find", "share", "openFile", "openRecentFile", "createPresentation", "save", "savePpsx", "savePptm", "packageForSharing", "signatures", "info", "print", "toggleFormatPainter", "exportPng", "exportPdf", "exportGif", "exportVideo", "copySlideAsImage", "replace", "openSmartArtDialog", "openEquationDialog", "link", "openPassword", "openFontEmbedding", "openVersionHistory", "openSettings"] }, { kind: "component", type: RibbonContentSecondaryComponent, selector: "pptx-ribbon-content-secondary", inputs: ["activeTab", "slideIndex", "slideCount", "canEdit", "selectedElement", "showGrid", "showRulers", "showGuides", "snapToGrid", "snapToShape", "eyedropperActive", "themeGalleryOpen", "spellCheckEnabled", "showSubtitles", "hiddenActions"], outputs: ["present", "presenter", "record", "presentFromBeginning", "rehearseTimings", "toggleSubtitles", "openSubtitleSettings", "recordFromBeginning", "recordFromCurrent", "spellCheckChange", "broadcast", "info", "print", "comments", "a11y", "link", "openSorter", "openReadingView", "openMasterView", "toggleNotes", "toggleInspector", "drawToolChange", "toggleThemeGallery", "toggleGrid", "toggleRulers", "toggleGuides", "toggleSelectionPane", "openCustomShows", "toggleSnapToGrid", "toggleSnapToShape", "addGuide", "zoomToFit", "toggleEyedropper", "openSetUpSlideShow", "openCompare", "openShortcuts", "openSettings"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
96359
+ `, isInline: true, dependencies: [{ kind: "component", type: RibbonPrimaryRowComponent, selector: "pptx-ribbon-primary-row", inputs: ["slideCount", "sidebarCollapsed", "inspectorOpen", "commentsOpen", "commentCount", "hiddenActions", "aiEnabled", "aiPanelOpen"], outputs: ["toggleSidebar", "toggleAiPanel", "toggleComments", "present", "presenter", "broadcast", "openCustomShows", "toggleInspector", "openSettings", "exportPng", "exportPdf", "exportGif", "exportVideo", "print", "info", "a11y", "save"] }, { kind: "component", type: RibbonTabListComponent, selector: "pptx-ribbon-tab-list", inputs: ["activeTab", "canEdit", "collabConnected", "connectedCount", "ribbonExpanded", "hiddenActions"], outputs: ["selectTab", "record", "share", "toggleRibbonExpanded"] }, { kind: "component", type: RibbonContentComponent, selector: "pptx-ribbon-content", inputs: ["activeTab", "slideIndex", "slideCount", "canEdit", "selectedElement", "formatPainterActive", "canActivateFormatPainter", "exporting", "hasMacros", "hiddenActions", "accountAuth"], outputs: ["selectTab", "find", "share", "openFile", "openRecentFile", "createPresentation", "save", "savePpsx", "savePptm", "packageForSharing", "signatures", "info", "print", "toggleFormatPainter", "exportPng", "exportPdf", "exportGif", "exportVideo", "copySlideAsImage", "replace", "openSmartArtDialog", "openEquationDialog", "link", "openPassword", "openFontEmbedding", "openVersionHistory", "openSettings"] }, { kind: "component", type: RibbonContentSecondaryComponent, selector: "pptx-ribbon-content-secondary", inputs: ["activeTab", "slideIndex", "slideCount", "canEdit", "selectedElement", "showGrid", "showRulers", "showGuides", "snapToGrid", "snapToShape", "eyedropperActive", "themeGalleryOpen", "spellCheckEnabled", "showSubtitles", "hiddenActions"], outputs: ["present", "presenter", "record", "presentFromBeginning", "rehearseTimings", "toggleSubtitles", "openSubtitleSettings", "recordFromBeginning", "recordFromCurrent", "spellCheckChange", "broadcast", "info", "print", "comments", "a11y", "link", "openSorter", "openReadingView", "openOutlineView", "openMasterView", "toggleNotes", "toggleInspector", "drawToolChange", "toggleThemeGallery", "toggleGrid", "toggleRulers", "toggleGuides", "toggleSelectionPane", "openCustomShows", "toggleSnapToGrid", "toggleSnapToShape", "addGuide", "zoomToFit", "toggleEyedropper", "openSetUpSlideShow", "openCompare", "openShortcuts", "openSettings"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
94347
96360
  }
94348
96361
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RibbonComponent, decorators: [{
94349
96362
  type: Component,
@@ -94483,6 +96496,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
94483
96496
  (link)="link.emit()"
94484
96497
  (openSorter)="openSorter.emit()"
94485
96498
  (openReadingView)="openReadingView.emit()"
96499
+ (openOutlineView)="openOutlineView.emit()"
94486
96500
  (openMasterView)="openMasterView.emit()"
94487
96501
  (toggleNotes)="toggleNotes.emit()"
94488
96502
  (toggleInspector)="toggleInspector.emit()"
@@ -94507,7 +96521,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
94507
96521
  </div>
94508
96522
  `,
94509
96523
  }]
94510
- }], propDecorators: { slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], slideCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideCount", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }], zoomPercent: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomPercent", required: false }] }], formatPainterActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatPainterActive", required: false }] }], canActivateFormatPainter: [{ type: i0.Input, args: [{ isSignal: true, alias: "canActivateFormatPainter", required: false }] }], exporting: [{ type: i0.Input, args: [{ isSignal: true, alias: "exporting", required: false }] }], hasMacros: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMacros", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], eyedropperActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropperActive", required: false }] }], themeGalleryOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeGalleryOpen", required: false }] }], sidebarCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarCollapsed", required: false }] }], inspectorOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "inspectorOpen", required: false }] }], commentsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "commentsOpen", required: false }] }], commentCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "commentCount", required: false }] }], findOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "findOpen", required: false }] }], collabConnected: [{ type: i0.Input, args: [{ isSignal: true, alias: "collabConnected", required: false }] }], connectedCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "connectedCount", required: false }] }], spellCheckEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellCheckEnabled", required: false }] }], showSubtitles: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSubtitles", required: false }] }], hiddenActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenActions", required: false }] }], aiEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiEnabled", required: false }] }], aiPanelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiPanelOpen", required: false }] }], accountAuth: [{ type: i0.Input, args: [{ isSignal: true, alias: "accountAuth", required: false }] }], prev: [{ type: i0.Output, args: ["prev"] }], next: [{ type: i0.Output, args: ["next"] }], zoomIn: [{ type: i0.Output, args: ["zoomIn"] }], zoomOut: [{ type: i0.Output, args: ["zoomOut"] }], zoomReset: [{ type: i0.Output, args: ["zoomReset"] }], find: [{ type: i0.Output, args: ["find"] }], present: [{ type: i0.Output, args: ["present"] }], presenter: [{ type: i0.Output, args: ["presenter"] }], record: [{ type: i0.Output, args: ["record"] }], presentFromBeginning: [{ type: i0.Output, args: ["presentFromBeginning"] }], rehearseTimings: [{ type: i0.Output, args: ["rehearseTimings"] }], toggleSubtitles: [{ type: i0.Output, args: ["toggleSubtitles"] }], openSubtitleSettings: [{ type: i0.Output, args: ["openSubtitleSettings"] }], recordFromBeginning: [{ type: i0.Output, args: ["recordFromBeginning"] }], recordFromCurrent: [{ type: i0.Output, args: ["recordFromCurrent"] }], spellCheckChange: [{ type: i0.Output, args: ["spellCheckChange"] }], share: [{ type: i0.Output, args: ["share"] }], broadcast: [{ type: i0.Output, args: ["broadcast"] }], openFile: [{ type: i0.Output, args: ["openFile"] }], openRecentFile: [{ type: i0.Output, args: ["openRecentFile"] }], createPresentation: [{ type: i0.Output, args: ["createPresentation"] }], save: [{ type: i0.Output, args: ["save"] }], savePpsx: [{ type: i0.Output, args: ["savePpsx"] }], savePptm: [{ type: i0.Output, args: ["savePptm"] }], packageForSharing: [{ type: i0.Output, args: ["packageForSharing"] }], toggleSidebar: [{ type: i0.Output, args: ["toggleSidebar"] }], toggleAiPanel: [{ type: i0.Output, args: ["toggleAiPanel"] }], signatures: [{ type: i0.Output, args: ["signatures"] }], info: [{ type: i0.Output, args: ["info"] }], print: [{ type: i0.Output, args: ["print"] }], comments: [{ type: i0.Output, args: ["comments"] }], a11y: [{ type: i0.Output, args: ["a11y"] }], link: [{ type: i0.Output, args: ["link"] }], openSorter: [{ type: i0.Output, args: ["openSorter"] }], openReadingView: [{ type: i0.Output, args: ["openReadingView"] }], openMasterView: [{ type: i0.Output, args: ["openMasterView"] }], toggleNotes: [{ type: i0.Output, args: ["toggleNotes"] }], toggleFormatPainter: [{ type: i0.Output, args: ["toggleFormatPainter"] }], exportPng: [{ type: i0.Output, args: ["exportPng"] }], exportPdf: [{ type: i0.Output, args: ["exportPdf"] }], exportGif: [{ type: i0.Output, args: ["exportGif"] }], exportVideo: [{ type: i0.Output, args: ["exportVideo"] }], copySlideAsImage: [{ type: i0.Output, args: ["copySlideAsImage"] }], replace: [{ type: i0.Output, args: ["replace"] }], toggleInspector: [{ type: i0.Output, args: ["toggleInspector"] }], drawToolChange: [{ type: i0.Output, args: ["drawToolChange"] }], toggleThemeGallery: [{ type: i0.Output, args: ["toggleThemeGallery"] }], toggleGrid: [{ type: i0.Output, args: ["toggleGrid"] }], toggleRulers: [{ type: i0.Output, args: ["toggleRulers"] }], toggleGuides: [{ type: i0.Output, args: ["toggleGuides"] }], toggleSelectionPane: [{ type: i0.Output, args: ["toggleSelectionPane"] }], openCustomShows: [{ type: i0.Output, args: ["openCustomShows"] }], toggleSnapToGrid: [{ type: i0.Output, args: ["toggleSnapToGrid"] }], toggleSnapToShape: [{ type: i0.Output, args: ["toggleSnapToShape"] }], addGuide: [{ type: i0.Output, args: ["addGuide"] }], zoomToFit: [{ type: i0.Output, args: ["zoomToFit"] }], toggleEyedropper: [{ type: i0.Output, args: ["toggleEyedropper"] }], openSmartArtDialog: [{ type: i0.Output, args: ["openSmartArtDialog"] }], openEquationDialog: [{ type: i0.Output, args: ["openEquationDialog"] }], openSetUpSlideShow: [{ type: i0.Output, args: ["openSetUpSlideShow"] }], openCompare: [{ type: i0.Output, args: ["openCompare"] }], openPassword: [{ type: i0.Output, args: ["openPassword"] }], openFontEmbedding: [{ type: i0.Output, args: ["openFontEmbedding"] }], openVersionHistory: [{ type: i0.Output, args: ["openVersionHistory"] }], openShortcuts: [{ type: i0.Output, args: ["openShortcuts"] }], openSettings: [{ type: i0.Output, args: ["openSettings"] }] } });
96524
+ }], propDecorators: { slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], slideCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideCount", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }], zoomPercent: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomPercent", required: false }] }], formatPainterActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatPainterActive", required: false }] }], canActivateFormatPainter: [{ type: i0.Input, args: [{ isSignal: true, alias: "canActivateFormatPainter", required: false }] }], exporting: [{ type: i0.Input, args: [{ isSignal: true, alias: "exporting", required: false }] }], hasMacros: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMacros", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], eyedropperActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropperActive", required: false }] }], themeGalleryOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeGalleryOpen", required: false }] }], sidebarCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarCollapsed", required: false }] }], inspectorOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "inspectorOpen", required: false }] }], commentsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "commentsOpen", required: false }] }], commentCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "commentCount", required: false }] }], findOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "findOpen", required: false }] }], collabConnected: [{ type: i0.Input, args: [{ isSignal: true, alias: "collabConnected", required: false }] }], connectedCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "connectedCount", required: false }] }], spellCheckEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellCheckEnabled", required: false }] }], showSubtitles: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSubtitles", required: false }] }], hiddenActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenActions", required: false }] }], aiEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiEnabled", required: false }] }], aiPanelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiPanelOpen", required: false }] }], accountAuth: [{ type: i0.Input, args: [{ isSignal: true, alias: "accountAuth", required: false }] }], prev: [{ type: i0.Output, args: ["prev"] }], next: [{ type: i0.Output, args: ["next"] }], zoomIn: [{ type: i0.Output, args: ["zoomIn"] }], zoomOut: [{ type: i0.Output, args: ["zoomOut"] }], zoomReset: [{ type: i0.Output, args: ["zoomReset"] }], find: [{ type: i0.Output, args: ["find"] }], present: [{ type: i0.Output, args: ["present"] }], presenter: [{ type: i0.Output, args: ["presenter"] }], record: [{ type: i0.Output, args: ["record"] }], presentFromBeginning: [{ type: i0.Output, args: ["presentFromBeginning"] }], rehearseTimings: [{ type: i0.Output, args: ["rehearseTimings"] }], toggleSubtitles: [{ type: i0.Output, args: ["toggleSubtitles"] }], openSubtitleSettings: [{ type: i0.Output, args: ["openSubtitleSettings"] }], recordFromBeginning: [{ type: i0.Output, args: ["recordFromBeginning"] }], recordFromCurrent: [{ type: i0.Output, args: ["recordFromCurrent"] }], spellCheckChange: [{ type: i0.Output, args: ["spellCheckChange"] }], share: [{ type: i0.Output, args: ["share"] }], broadcast: [{ type: i0.Output, args: ["broadcast"] }], openFile: [{ type: i0.Output, args: ["openFile"] }], openRecentFile: [{ type: i0.Output, args: ["openRecentFile"] }], createPresentation: [{ type: i0.Output, args: ["createPresentation"] }], save: [{ type: i0.Output, args: ["save"] }], savePpsx: [{ type: i0.Output, args: ["savePpsx"] }], savePptm: [{ type: i0.Output, args: ["savePptm"] }], packageForSharing: [{ type: i0.Output, args: ["packageForSharing"] }], toggleSidebar: [{ type: i0.Output, args: ["toggleSidebar"] }], toggleAiPanel: [{ type: i0.Output, args: ["toggleAiPanel"] }], signatures: [{ type: i0.Output, args: ["signatures"] }], info: [{ type: i0.Output, args: ["info"] }], print: [{ type: i0.Output, args: ["print"] }], comments: [{ type: i0.Output, args: ["comments"] }], a11y: [{ type: i0.Output, args: ["a11y"] }], link: [{ type: i0.Output, args: ["link"] }], openSorter: [{ type: i0.Output, args: ["openSorter"] }], openReadingView: [{ type: i0.Output, args: ["openReadingView"] }], openOutlineView: [{ type: i0.Output, args: ["openOutlineView"] }], openMasterView: [{ type: i0.Output, args: ["openMasterView"] }], toggleNotes: [{ type: i0.Output, args: ["toggleNotes"] }], toggleFormatPainter: [{ type: i0.Output, args: ["toggleFormatPainter"] }], exportPng: [{ type: i0.Output, args: ["exportPng"] }], exportPdf: [{ type: i0.Output, args: ["exportPdf"] }], exportGif: [{ type: i0.Output, args: ["exportGif"] }], exportVideo: [{ type: i0.Output, args: ["exportVideo"] }], copySlideAsImage: [{ type: i0.Output, args: ["copySlideAsImage"] }], replace: [{ type: i0.Output, args: ["replace"] }], toggleInspector: [{ type: i0.Output, args: ["toggleInspector"] }], drawToolChange: [{ type: i0.Output, args: ["drawToolChange"] }], toggleThemeGallery: [{ type: i0.Output, args: ["toggleThemeGallery"] }], toggleGrid: [{ type: i0.Output, args: ["toggleGrid"] }], toggleRulers: [{ type: i0.Output, args: ["toggleRulers"] }], toggleGuides: [{ type: i0.Output, args: ["toggleGuides"] }], toggleSelectionPane: [{ type: i0.Output, args: ["toggleSelectionPane"] }], openCustomShows: [{ type: i0.Output, args: ["openCustomShows"] }], toggleSnapToGrid: [{ type: i0.Output, args: ["toggleSnapToGrid"] }], toggleSnapToShape: [{ type: i0.Output, args: ["toggleSnapToShape"] }], addGuide: [{ type: i0.Output, args: ["addGuide"] }], zoomToFit: [{ type: i0.Output, args: ["zoomToFit"] }], toggleEyedropper: [{ type: i0.Output, args: ["toggleEyedropper"] }], openSmartArtDialog: [{ type: i0.Output, args: ["openSmartArtDialog"] }], openEquationDialog: [{ type: i0.Output, args: ["openEquationDialog"] }], openSetUpSlideShow: [{ type: i0.Output, args: ["openSetUpSlideShow"] }], openCompare: [{ type: i0.Output, args: ["openCompare"] }], openPassword: [{ type: i0.Output, args: ["openPassword"] }], openFontEmbedding: [{ type: i0.Output, args: ["openFontEmbedding"] }], openVersionHistory: [{ type: i0.Output, args: ["openVersionHistory"] }], openShortcuts: [{ type: i0.Output, args: ["openShortcuts"] }], openSettings: [{ type: i0.Output, args: ["openSettings"] }] } });
94511
96525
 
94512
96526
  /**
94513
96527
  * selection-pane.component.ts: Side panel listing all elements on the active slide.
@@ -95526,8 +97540,23 @@ function getAnimationElementLabel(element) {
95526
97540
  }
95527
97541
 
95528
97542
  let activePreview;
97543
+ /** Slide size assumed when the stage cannot be measured (detached preview). */
97544
+ const FALLBACK_SLIDE_WIDTH = 1280;
97545
+ const FALLBACK_SLIDE_HEIGHT = 720;
97546
+ /**
97547
+ * The element a preview should play on.
97548
+ *
97549
+ * The slides panel renders a thumbnail of the SAME element with the same
97550
+ * `data-element-id`, and it comes first in DOM order, so taking the first match
97551
+ * played every preview on a thumbnail the user is not looking at. For a motion
97552
+ * path that is worse than invisible: the travel is measured against the
97553
+ * thumbnail's own tiny stage, so it also plays at the wrong distance. Only the
97554
+ * interactive main canvas carries `data-pptx-viewport`, which is the neutral
97555
+ * marker every binding already agrees on.
97556
+ */
95529
97557
  function findElement(elementId) {
95530
- return [...document.querySelectorAll('[data-element-id]')].find((element) => element.dataset['elementId'] === elementId);
97558
+ const matches = [...document.querySelectorAll('[data-element-id]')].filter((element) => element.dataset['elementId'] === elementId);
97559
+ return matches.find((element) => element.closest('[data-pptx-viewport]')) ?? matches[0];
95531
97560
  }
95532
97561
  function stopAngularAnimationPreview() {
95533
97562
  if (!activePreview) {
@@ -95539,19 +97568,45 @@ function stopAngularAnimationPreview() {
95539
97568
  activePreview.styleElement.remove();
95540
97569
  activePreview = undefined;
95541
97570
  }
95542
- function previewAngularAnimation(animation) {
95543
- stopAngularAnimationPreview();
97571
+ /**
97572
+ * Pick the descriptor an animation entry should play.
97573
+ *
97574
+ * A motion path WINS over the preset buckets: it is the effect being authored
97575
+ * on the canvas at that moment, and a fade or a spin would hide the travel
97576
+ * entirely. The slide size comes from the element's offset parent (the stage)
97577
+ * because path coordinates are fractions of the SLIDE, not of the element's own
97578
+ * box, and the stage is laid out in unscaled slide pixels.
97579
+ */
97580
+ function buildAngularPreviewDescriptor(animation, element) {
97581
+ if (animation.motionPath) {
97582
+ const stage = element.offsetParent;
97583
+ return buildMotionPathPreview({
97584
+ path: animation.motionPath,
97585
+ slideWidth: stage?.offsetWidth || FALLBACK_SLIDE_WIDTH,
97586
+ slideHeight: stage?.offsetHeight || FALLBACK_SLIDE_HEIGHT,
97587
+ durationMs: animation.durationMs,
97588
+ delayMs: animation.delayMs,
97589
+ timingCurve: animation.timingCurve,
97590
+ });
97591
+ }
95544
97592
  const preset = animation.entrance ?? animation.emphasis ?? animation.exit;
95545
97593
  if (!preset || preset === 'none') {
95546
- return false;
97594
+ return undefined;
95547
97595
  }
95548
- const descriptor = buildPreviewAnimation(preset, {
97596
+ return buildPreviewAnimation(preset, {
95549
97597
  direction: animation.direction,
95550
97598
  durationMs: animation.durationMs ?? 500,
95551
97599
  timingCurve: animation.timingCurve,
95552
97600
  });
95553
- const element = descriptor ? findElement(animation.elementId) : undefined;
95554
- if (!descriptor || !element) {
97601
+ }
97602
+ function previewAngularAnimation(animation) {
97603
+ stopAngularAnimationPreview();
97604
+ const element = findElement(animation.elementId);
97605
+ if (!element) {
97606
+ return false;
97607
+ }
97608
+ const descriptor = buildAngularPreviewDescriptor(animation, element);
97609
+ if (!descriptor) {
95555
97610
  return false;
95556
97611
  }
95557
97612
  const styleElement = document.createElement('style');
@@ -95726,6 +97781,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
95726
97781
  `, styles: [".timeline{display:grid;gap:6px;padding:8px 0;border-top:1px solid var(--pptx-inspector-border, #333)}h4{margin:0;color:var(--pptx-inspector-muted, #888);font-size:10px;text-transform:uppercase}.bar{position:relative;height:22px;overflow:hidden;border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;background:var(--pptx-inspector-input-bg, #2d2d2d)}.bar span{position:absolute;top:3px;bottom:3px;min-width:2%;border-radius:2px;background:#6b9a68}.bar span.selected{outline:1px solid var(--pptx-primary, #4c9ffe)}.list{display:grid;gap:3px;max-height:160px;overflow-y:auto}.item{display:flex;align-items:center;gap:4px;min-width:0;padding:4px;border:1px solid transparent;border-radius:3px;background:var(--pptx-inspector-input-bg, #2d2d2d);cursor:grab;font-size:10px}.item.selected{border-color:var(--pptx-primary, #4c9ffe)}.item.drag-over{border-top:2px solid var(--pptx-primary, #4c9ffe)}.grip,.order{color:var(--pptx-inspector-muted, #888)}.name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.effect{color:var(--pptx-inspector-muted, #aaa)}\n"] }]
95727
97782
  }], propDecorators: { animations: [{ type: i0.Input, args: [{ isSignal: true, alias: "animations", required: true }] }], elements: [{ type: i0.Input, args: [{ isSignal: true, alias: "elements", required: false }] }], selectedElementId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElementId", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], animationsChange: [{ type: i0.Output, args: ["animationsChange"] }] } });
95728
97783
 
97784
+ /**
97785
+ * motion-path-row.component.ts: the animation panel's motion-path row.
97786
+ *
97787
+ * Selector: `pptx-motion-path-row`
97788
+ *
97789
+ * Its own component rather than more markup inside
97790
+ * {@link AnimationAuthorPanelComponent} because that file already sits just
97791
+ * under this repo's 300-LOC cap, and the row carries a catalogue-sized
97792
+ * `<select>` of its own.
97793
+ *
97794
+ * WHY a "Custom Path" option exists: dragging the end handle on the canvas
97795
+ * produces a path that no longer matches any catalogue entry. Without a slot
97796
+ * to show that in, the select would snap back to the preset the user started
97797
+ * from and misreport what will actually play. The option is therefore rendered
97798
+ * only while the applied path is unrecognised, and re-picking it is a no-op
97799
+ * (the panel refuses to translate "custom" back into some catalogue geometry).
97800
+ *
97801
+ * Reference binding: packages/react/src/viewer/components/inspector/MotionPathRow.tsx
97802
+ *
97803
+ * @module viewer/motion-path-row
97804
+ */
97805
+ /** The select value standing for "this path was hand-dragged". */
97806
+ const CUSTOM_MOTION_PATH_VALUE = 'custom';
97807
+ /** The select value standing for "no path at all". */
97808
+ const NO_MOTION_PATH_VALUE = 'none';
97809
+ /**
97810
+ * Which option the select should show for an applied path.
97811
+ *
97812
+ * Pure so the branch that used to be a template ternary can be asserted
97813
+ * directly: `none` when nothing is applied, the catalogue id when the geometry
97814
+ * is recognised, `custom` when it is not.
97815
+ */
97816
+ function motionPathSelectValue(motionPath) {
97817
+ if (!motionPath) {
97818
+ return NO_MOTION_PATH_VALUE;
97819
+ }
97820
+ return motionPathPresetIdForPath(motionPath) ?? CUSTOM_MOTION_PATH_VALUE;
97821
+ }
97822
+ class MotionPathRowComponent {
97823
+ /** The path currently applied to the selected element, if any. */
97824
+ motionPath = input(undefined, /* @ts-ignore */
97825
+ ...(ngDevMode ? [{ debugName: "motionPath" }] : /* istanbul ignore next */ []));
97826
+ /** Whether the select is live. */
97827
+ canEdit = input(true, /* @ts-ignore */
97828
+ ...(ngDevMode ? [{ debugName: "canEdit" }] : /* istanbul ignore next */ []));
97829
+ /**
97830
+ * Emits a catalogue preset id, or `'none'` to clear the path. `'custom'` is
97831
+ * never emitted: re-picking the marker changes nothing.
97832
+ */
97833
+ presetChange = output();
97834
+ columns = MOTION_PATH_COLUMNS;
97835
+ /** The option the select shows: `none`, a catalogue id, or `custom`. */
97836
+ selectedValue = computed(() => motionPathSelectValue(this.motionPath()), /* @ts-ignore */
97837
+ ...(ngDevMode ? [{ debugName: "selectedValue" }] : /* istanbul ignore next */ []));
97838
+ /** True while the applied path matches no catalogue entry. */
97839
+ isCustom = computed(() => this.selectedValue() === CUSTOM_MOTION_PATH_VALUE, /* @ts-ignore */
97840
+ ...(ngDevMode ? [{ debugName: "isCustom" }] : /* istanbul ignore next */ []));
97841
+ onSelect(event) {
97842
+ const target = event.target;
97843
+ if (!(target instanceof HTMLSelectElement)) {
97844
+ return;
97845
+ }
97846
+ // The custom marker is read-only: it describes a dragged path, and there is
97847
+ // no catalogue geometry to restore it to.
97848
+ if (target.value === CUSTOM_MOTION_PATH_VALUE) {
97849
+ return;
97850
+ }
97851
+ this.presetChange.emit(target.value);
97852
+ }
97853
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MotionPathRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
97854
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: MotionPathRowComponent, isStandalone: true, selector: "pptx-motion-path-row", inputs: { motionPath: { classPropertyName: "motionPath", publicName: "motionPath", isSignal: true, isRequired: false, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { presetChange: "presetChange" }, ngImport: i0, template: `
97855
+ <label class="pptx-ng-anim__section pptx-ng-motion-path">
97856
+ <span class="pptx-ng-anim__label">
97857
+ {{ 'pptx.animation.motionPath.label' | translate }}
97858
+ </span>
97859
+ <!--
97860
+ Selection is expressed with [selected] on each option rather than
97861
+ [value] on the select: Angular applies an element's own property
97862
+ bindings before the @for blocks below it have produced any options, so
97863
+ a [value] naming a catalogue id would be assigned to an empty select
97864
+ and silently drop back to the first entry on first render.
97865
+ -->
97866
+ <select class="pptx-ng-anim__select" [disabled]="!canEdit()" (change)="onSelect($event)">
97867
+ <option value="none" [selected]="selectedValue() === 'none'">
97868
+ {{ 'pptx.animation.motionPath.none' | translate }}
97869
+ </option>
97870
+ @if (isCustom()) {
97871
+ <option value="custom" selected>
97872
+ {{ 'pptx.animation.motionPath.custom' | translate }}
97873
+ </option>
97874
+ }
97875
+ @for (column of columns; track column.family) {
97876
+ <optgroup [label]="column.labelKey | translate">
97877
+ @for (preset of column.presets; track preset.id) {
97878
+ <option [value]="preset.id" [selected]="selectedValue() === preset.id">
97879
+ {{ preset.labelKey | translate }}
97880
+ </option>
97881
+ }
97882
+ </optgroup>
97883
+ }
97884
+ </select>
97885
+ @if (motionPath()) {
97886
+ <span class="pptx-ng-motion-path__hint">
97887
+ {{ 'pptx.animation.motionPath.editHint' | translate }}
97888
+ </span>
97889
+ }
97890
+ </label>
97891
+ `, isInline: true, styles: [".pptx-ng-motion-path{display:block}.pptx-ng-motion-path__hint{display:block;margin-top:.2rem;font-size:10px;color:var(--pptx-inspector-muted, #888)}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
97892
+ }
97893
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MotionPathRowComponent, decorators: [{
97894
+ type: Component,
97895
+ args: [{ selector: 'pptx-motion-path-row', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe], template: `
97896
+ <label class="pptx-ng-anim__section pptx-ng-motion-path">
97897
+ <span class="pptx-ng-anim__label">
97898
+ {{ 'pptx.animation.motionPath.label' | translate }}
97899
+ </span>
97900
+ <!--
97901
+ Selection is expressed with [selected] on each option rather than
97902
+ [value] on the select: Angular applies an element's own property
97903
+ bindings before the @for blocks below it have produced any options, so
97904
+ a [value] naming a catalogue id would be assigned to an empty select
97905
+ and silently drop back to the first entry on first render.
97906
+ -->
97907
+ <select class="pptx-ng-anim__select" [disabled]="!canEdit()" (change)="onSelect($event)">
97908
+ <option value="none" [selected]="selectedValue() === 'none'">
97909
+ {{ 'pptx.animation.motionPath.none' | translate }}
97910
+ </option>
97911
+ @if (isCustom()) {
97912
+ <option value="custom" selected>
97913
+ {{ 'pptx.animation.motionPath.custom' | translate }}
97914
+ </option>
97915
+ }
97916
+ @for (column of columns; track column.family) {
97917
+ <optgroup [label]="column.labelKey | translate">
97918
+ @for (preset of column.presets; track preset.id) {
97919
+ <option [value]="preset.id" [selected]="selectedValue() === preset.id">
97920
+ {{ preset.labelKey | translate }}
97921
+ </option>
97922
+ }
97923
+ </optgroup>
97924
+ }
97925
+ </select>
97926
+ @if (motionPath()) {
97927
+ <span class="pptx-ng-motion-path__hint">
97928
+ {{ 'pptx.animation.motionPath.editHint' | translate }}
97929
+ </span>
97930
+ }
97931
+ </label>
97932
+ `, styles: [".pptx-ng-motion-path{display:block}.pptx-ng-motion-path__hint{display:block;margin-top:.2rem;font-size:10px;color:var(--pptx-inspector-muted, #888)}\n"] }]
97933
+ }], propDecorators: { motionPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "motionPath", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], presetChange: [{ type: i0.Output, args: ["presetChange"] }] } });
97934
+
95729
97935
  /**
95730
97936
  * animation-author-panel.component.ts: Animation authoring inspector sub-panel.
95731
97937
  *
@@ -95881,6 +98087,22 @@ class AnimationAuthorPanelComponent {
95881
98087
  }
95882
98088
  this.emit(setAnimationEmphasis(this.animations(), this.element().id, value));
95883
98089
  }
98090
+ // ── Motion path ───────────────────────────────────────────────────────────
98091
+ /**
98092
+ * Apply or clear the element's motion path.
98093
+ *
98094
+ * The path lives on the SAME animation entry as the preset buckets, so this
98095
+ * deliberately does not go through the preset setters: applying a path must
98096
+ * leave an existing entrance alone, and clearing one must drop the entry only
98097
+ * when nothing else is left on it. Both rules live in the shared helpers.
98098
+ */
98099
+ onMotionPathChange(presetId) {
98100
+ const animations = this.animations();
98101
+ const elementId = this.element().id;
98102
+ this.emit(presetId === 'none'
98103
+ ? clearMotionPath(animations, elementId)
98104
+ : applyMotionPathPreset(animations, elementId, presetId));
98105
+ }
95884
98106
  // ── Trigger handlers ─────────────────────────────────────────────────────
95885
98107
  onTriggerChange(event) {
95886
98108
  const value = stringFromSelect(event);
@@ -96037,6 +98259,15 @@ class AnimationAuthorPanelComponent {
96037
98259
  </select>
96038
98260
  </section>
96039
98261
 
98262
+ <!-- ── Motion path: geometry, not a preset, so it gets its own row ─ -->
98263
+ <section class="pptx-ng-anim__section">
98264
+ <pptx-motion-path-row
98265
+ [motionPath]="current()?.motionPath"
98266
+ [canEdit]="canEdit()"
98267
+ (presetChange)="onMotionPathChange($event)"
98268
+ />
98269
+ </section>
98270
+
96040
98271
  <!-- ── Effect options: only shown when an animation is set ─────── -->
96041
98272
  @if (currentHasAnimation()) {
96042
98273
  <!-- ── Direction picker (directional presets only) ──────────── -->
@@ -96264,11 +98495,18 @@ class AnimationAuthorPanelComponent {
96264
98495
  }
96265
98496
  <!-- end @if (currentHasAnimation()) -->
96266
98497
  </aside>
96267
- `, isInline: true, styles: [".pptx-ng-anim{display:flex;flex-direction:column;gap:0;padding:.5rem;background:var(--pptx-inspector-bg, #1e1e1e);color:var(--pptx-inspector-fg, #e0e0e0);font-size:12px;min-width:220px;overflow-y:auto}.pptx-ng-anim__header{display:flex;align-items:center;justify-content:space-between;padding-bottom:.4rem;border-bottom:1px solid var(--pptx-inspector-border, #333);margin-bottom:.35rem}.pptx-ng-anim__title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888)}.pptx-ng-anim__header-actions{display:flex;align-items:center;gap:4px}.pptx-ng-anim__preview-btn{padding:2px 6px;border:1px solid var(--pptx-primary, #4c9ffe);border-radius:3px;background:transparent;color:var(--pptx-primary, #4c9ffe);cursor:pointer;font-size:10px}.pptx-ng-anim__subheading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);padding:.4rem 0 .2rem;border-top:1px solid var(--pptx-inspector-border, #333);margin-top:.2rem}.pptx-ng-anim__section{padding:.25rem 0;border-bottom:1px solid var(--pptx-inspector-border, #2a2a2a)}.pptx-ng-anim__section:last-child{border-bottom:none}.pptx-ng-anim__label{display:block;font-size:10px;color:var(--pptx-inspector-muted, #888);margin-bottom:.2rem}.pptx-ng-anim__select,.pptx-ng-anim__input{width:100%;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:3px 6px;font-size:12px;box-sizing:border-box}.pptx-ng-anim__select:disabled,.pptx-ng-anim__input:disabled{opacity:.5;cursor:not-allowed}.pptx-ng-anim__direction-grid{display:flex;flex-wrap:wrap;gap:.25rem}.pptx-ng-anim__dir-btn{width:30px;height:30px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center}.pptx-ng-anim__dir-btn.is-active{background:var(--pptx-inspector-active, #0078d4);border-color:var(--pptx-inspector-active, #0078d4);color:#fff}.pptx-ng-anim__dir-btn:disabled{opacity:.5;cursor:not-allowed}.pptx-ng-anim__row{display:flex;gap:.35rem}.pptx-ng-anim__order-btn{flex:1;padding:3px 6px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:11px;white-space:nowrap}.pptx-ng-anim__order-btn:hover:not(:disabled){background:var(--pptx-inspector-hover, #3a3a3a)}.pptx-ng-anim__order-btn:disabled{opacity:.5;cursor:not-allowed}.pptx-ng-anim__remove-btn{padding:2px 6px;background:transparent;border:1px solid var(--pptx-inspector-danger-border, #6b2a2a);color:var(--pptx-inspector-danger, #f47c7c);border-radius:3px;cursor:pointer;font-size:10px}.pptx-ng-anim__remove-btn:hover{background:var(--pptx-inspector-danger-hover, #4a1a1a)}@media(pointer:coarse),(max-width:640px){.pptx-ng-anim{width:100%;min-width:0;box-sizing:border-box;font-size:14px}.pptx-ng-anim__select,.pptx-ng-anim__input{min-height:40px;font-size:16px;padding:6px 8px}.pptx-ng-anim__dir-btn{width:40px;height:40px}.pptx-ng-anim__order-btn{min-height:40px;font-size:13px}}\n"], dependencies: [{ kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "component", type: LucideArrowUp, selector: "svg[lucideArrowUp]" }, { kind: "component", type: LucideArrowDown, selector: "svg[lucideArrowDown]" }, { kind: "component", type: AnimationTimelineComponent, selector: "pptx-animation-timeline", inputs: ["animations", "elements", "selectedElementId", "canEdit"], outputs: ["animationsChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
98498
+ `, isInline: true, styles: [".pptx-ng-anim{display:flex;flex-direction:column;gap:0;padding:.5rem;background:var(--pptx-inspector-bg, #1e1e1e);color:var(--pptx-inspector-fg, #e0e0e0);font-size:12px;min-width:220px;overflow-y:auto}.pptx-ng-anim__header{display:flex;align-items:center;justify-content:space-between;padding-bottom:.4rem;border-bottom:1px solid var(--pptx-inspector-border, #333);margin-bottom:.35rem}.pptx-ng-anim__title{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888)}.pptx-ng-anim__header-actions{display:flex;align-items:center;gap:4px}.pptx-ng-anim__preview-btn{padding:2px 6px;border:1px solid var(--pptx-primary, #4c9ffe);border-radius:3px;background:transparent;color:var(--pptx-primary, #4c9ffe);cursor:pointer;font-size:10px}.pptx-ng-anim__subheading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);padding:.4rem 0 .2rem;border-top:1px solid var(--pptx-inspector-border, #333);margin-top:.2rem}.pptx-ng-anim__section{padding:.25rem 0;border-bottom:1px solid var(--pptx-inspector-border, #2a2a2a)}.pptx-ng-anim__section:last-child{border-bottom:none}.pptx-ng-anim__label{display:block;font-size:10px;color:var(--pptx-inspector-muted, #888);margin-bottom:.2rem}.pptx-ng-anim__select,.pptx-ng-anim__input{width:100%;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:3px 6px;font-size:12px;box-sizing:border-box}.pptx-ng-anim__select:disabled,.pptx-ng-anim__input:disabled{opacity:.5;cursor:not-allowed}.pptx-ng-anim__direction-grid{display:flex;flex-wrap:wrap;gap:.25rem}.pptx-ng-anim__dir-btn{width:30px;height:30px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center}.pptx-ng-anim__dir-btn.is-active{background:var(--pptx-inspector-active, #0078d4);border-color:var(--pptx-inspector-active, #0078d4);color:#fff}.pptx-ng-anim__dir-btn:disabled{opacity:.5;cursor:not-allowed}.pptx-ng-anim__row{display:flex;gap:.35rem}.pptx-ng-anim__order-btn{flex:1;padding:3px 6px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:11px;white-space:nowrap}.pptx-ng-anim__order-btn:hover:not(:disabled){background:var(--pptx-inspector-hover, #3a3a3a)}.pptx-ng-anim__order-btn:disabled{opacity:.5;cursor:not-allowed}.pptx-ng-anim__remove-btn{padding:2px 6px;background:transparent;border:1px solid var(--pptx-inspector-danger-border, #6b2a2a);color:var(--pptx-inspector-danger, #f47c7c);border-radius:3px;cursor:pointer;font-size:10px}.pptx-ng-anim__remove-btn:hover{background:var(--pptx-inspector-danger-hover, #4a1a1a)}@media(pointer:coarse),(max-width:640px){.pptx-ng-anim{width:100%;min-width:0;box-sizing:border-box;font-size:14px}.pptx-ng-anim__select,.pptx-ng-anim__input{min-height:40px;font-size:16px;padding:6px 8px}.pptx-ng-anim__dir-btn{width:40px;height:40px}.pptx-ng-anim__order-btn{min-height:40px;font-size:13px}}\n"], dependencies: [{ kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "component", type: LucideArrowUp, selector: "svg[lucideArrowUp]" }, { kind: "component", type: LucideArrowDown, selector: "svg[lucideArrowDown]" }, { kind: "component", type: AnimationTimelineComponent, selector: "pptx-animation-timeline", inputs: ["animations", "elements", "selectedElementId", "canEdit"], outputs: ["animationsChange"] }, { kind: "component", type: MotionPathRowComponent, selector: "pptx-motion-path-row", inputs: ["motionPath", "canEdit"], outputs: ["presetChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
96268
98499
  }
96269
98500
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AnimationAuthorPanelComponent, decorators: [{
96270
98501
  type: Component,
96271
- args: [{ selector: 'pptx-animation-author-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe, LucideX, LucideArrowUp, LucideArrowDown, AnimationTimelineComponent], template: `
98502
+ args: [{ selector: 'pptx-animation-author-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
98503
+ TranslatePipe,
98504
+ LucideX,
98505
+ LucideArrowUp,
98506
+ LucideArrowDown,
98507
+ AnimationTimelineComponent,
98508
+ MotionPathRowComponent,
98509
+ ], template: `
96272
98510
  <aside class="pptx-ng-anim" [attr.aria-label]="'pptx.animations.propertiesLabel' | translate">
96273
98511
  <!-- ── Header ───────────────────────────────────────────────────── -->
96274
98512
  <div class="pptx-ng-anim__header">
@@ -96353,6 +98591,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
96353
98591
  </select>
96354
98592
  </section>
96355
98593
 
98594
+ <!-- ── Motion path: geometry, not a preset, so it gets its own row ─ -->
98595
+ <section class="pptx-ng-anim__section">
98596
+ <pptx-motion-path-row
98597
+ [motionPath]="current()?.motionPath"
98598
+ [canEdit]="canEdit()"
98599
+ (presetChange)="onMotionPathChange($event)"
98600
+ />
98601
+ </section>
98602
+
96356
98603
  <!-- ── Effect options: only shown when an animation is set ─────── -->
96357
98604
  @if (currentHasAnimation()) {
96358
98605
  <!-- ── Direction picker (directional presets only) ──────────── -->
@@ -97229,6 +99476,13 @@ function setSeriesChartType(element, seriesIndex, seriesType) {
97229
99476
  function setDataPointFill(element, seriesIndex, pointIndex, color) {
97230
99477
  return withClonedChart(element, (el) => setChartDataPointFill(el, seriesIndex, pointIndex, color));
97231
99478
  }
99479
+ /**
99480
+ * Set or clear a per-data-point marker override (`c:dPt/c:marker`), which
99481
+ * replaces the series marker for that one point.
99482
+ */
99483
+ function setDataPointMarker(element, seriesIndex, pointIndex, marker) {
99484
+ return withClonedChart(element, (el) => setChartDataPointMarker(el, seriesIndex, pointIndex, marker));
99485
+ }
97232
99486
  /** Set or clear a per-data-point pie/doughnut slice explosion. */
97233
99487
  function setDataPointExplosion(element, seriesIndex, pointIndex, explosion) {
97234
99488
  return withClonedChart(element, (el) => setChartDataPointExplosion(el, seriesIndex, pointIndex, explosion));
@@ -98068,6 +100322,242 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
98068
100322
  `, styles: [".pptx-chart-card{display:flex;flex-direction:column;gap:.35rem;padding:.5rem 0;border-top:1px solid var(--pptx-inspector-border, #333)}.pptx-chart-card__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-chart-card__group{display:flex;flex-direction:column;gap:.3rem}.pptx-chart-card__group--indent{margin-left:.5rem}.pptx-chart-card__subhead{font-size:11px;font-weight:600}.pptx-chart-card__row{display:flex;align-items:center;gap:.4rem;font-size:11px}.pptx-chart-card__label{flex:0 0 auto;width:5rem;color:var(--pptx-inspector-muted, #888)}.pptx-chart-card__label--wide{width:6.5rem}.pptx-chart-card__name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pptx-chart-card__check{display:flex;align-items:center;gap:.35rem;font-size:11px;cursor:pointer}.pptx-chart-card__input{flex:1 1 auto;min-width:0;box-sizing:border-box;padding:2px 4px;font-size:11px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;color:inherit;outline:none}.pptx-chart-card__input--num{flex:0 0 auto;width:4rem;text-align:right}.pptx-chart-card__input:focus{border-color:var(--pptx-inspector-active, #0078d4)}.pptx-chart-card__input:disabled{opacity:.6;cursor:not-allowed}.pptx-chart-card__color{flex:0 0 auto;width:26px;height:20px;padding:0;border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;background:transparent;cursor:pointer}.pptx-chart-card__color:disabled{opacity:.6;cursor:not-allowed}.pptx-chart-card__clear{flex:0 0 auto;padding:0 2px;font-size:12px;line-height:1;background:none;border:none;color:var(--pptx-inspector-muted, #888);cursor:pointer}.pptx-chart-card__clear:hover{color:var(--pptx-inspector-danger, #f47c7c)}.pptx-chart-card__input--num::-webkit-outer-spin-button,.pptx-chart-card__input--num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"] }]
98069
100323
  }], propDecorators: { element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: true }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], elementChange: [{ type: i0.Output, args: ["elementChange"] }] } });
98070
100324
 
100325
+ /**
100326
+ * chart-datapoint-marker-options.component.ts: Per-data-point marker overrides.
100327
+ *
100328
+ * Selector: `pptx-chart-datapoint-marker-options`
100329
+ *
100330
+ * Mirrors React's `ChartDataPointMarkerOptions.tsx`, the one advanced chart
100331
+ * control Angular had never been given: `pptx-chart-datapoint-options` covers a
100332
+ * point's fill and slice explosion, but a `c:dPt` can also carry its own
100333
+ * `c:marker`, which replaces the series marker for that point alone (the usual
100334
+ * use is calling out a single outlier on an otherwise uniform line).
100335
+ *
100336
+ * The checkbox is the presence of the override, not a value: ticking it seeds a
100337
+ * circle, clearing it removes the whole `c:dPt/c:marker`. Edits route through
100338
+ * `setDataPointMarker`, which wraps core's headless op, so the `c:dPt`
100339
+ * bookkeeping matches React, Vue, Svelte and Vanilla exactly.
100340
+ *
100341
+ * @module angular-viewer/chart-datapoint-marker-options
100342
+ */
100343
+ /** Concrete symbols only; '' is the "series default" sentinel, which the
100344
+ * presence checkbox already expresses. */
100345
+ const SYMBOL_OPTIONS = MARKER_SYMBOL_OPTIONS.filter((option) => option.value !== '');
100346
+ class ChartDatapointMarkerOptionsComponent {
100347
+ element = input.required(/* @ts-ignore */
100348
+ ...(ngDevMode ? [{ debugName: "element" }] : /* istanbul ignore next */ []));
100349
+ canEdit = input(true, /* @ts-ignore */
100350
+ ...(ngDevMode ? [{ debugName: "canEdit" }] : /* istanbul ignore next */ []));
100351
+ elementChange = output();
100352
+ symbolOptions = SYMBOL_OPTIONS;
100353
+ seriesIndex = signal(0, /* @ts-ignore */
100354
+ ...(ngDevMode ? [{ debugName: "seriesIndex" }] : /* istanbul ignore next */ []));
100355
+ series = computed(() => this.element().chartData?.series ?? [], /* @ts-ignore */
100356
+ ...(ngDevMode ? [{ debugName: "series" }] : /* istanbul ignore next */ []));
100357
+ categories = computed(() => this.element().chartData?.categories ?? [], /* @ts-ignore */
100358
+ ...(ngDevMode ? [{ debugName: "categories" }] : /* istanbul ignore next */ []));
100359
+ /** Clamp the picker: removing a series must not strand the index past the end. */
100360
+ activeIndex = computed(() => Math.min(this.seriesIndex(), Math.max(0, this.series().length - 1)), /* @ts-ignore */
100361
+ ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
100362
+ supported = computed(() => {
100363
+ const type = this.element().chartData?.chartType;
100364
+ return (type !== undefined &&
100365
+ MARKER_SUPPORTED_TYPES.has(type) &&
100366
+ this.series().length > 0 &&
100367
+ this.categories().length > 0);
100368
+ }, /* @ts-ignore */
100369
+ ...(ngDevMode ? [{ debugName: "supported" }] : /* istanbul ignore next */ []));
100370
+ markerAt(pointIndex) {
100371
+ return this.series()[this.activeIndex()]?.dataPoints?.find((p) => p.idx === pointIndex)?.marker;
100372
+ }
100373
+ onSeries(event) {
100374
+ const value = selectValue$2(event);
100375
+ if (value === null) {
100376
+ return;
100377
+ }
100378
+ const num = Number.parseInt(value, 10);
100379
+ if (Number.isFinite(num)) {
100380
+ this.seriesIndex.set(num);
100381
+ }
100382
+ }
100383
+ onToggle(pointIndex, event) {
100384
+ this.emit(pointIndex, boolFromEvent(event) ? { symbol: 'circle' } : null);
100385
+ }
100386
+ onSymbol(pointIndex, event) {
100387
+ const value = selectValue$2(event);
100388
+ if (value === null) {
100389
+ return;
100390
+ }
100391
+ this.emit(pointIndex, { symbol: value });
100392
+ }
100393
+ onSize(pointIndex, event) {
100394
+ const num = numFromEvent(event);
100395
+ this.emit(pointIndex, { size: typeof num === 'number' ? num : undefined });
100396
+ }
100397
+ onFill(pointIndex, event) {
100398
+ const value = stringFromEvent$5(event);
100399
+ if (value === null) {
100400
+ return;
100401
+ }
100402
+ this.emit(pointIndex, { fillColor: value });
100403
+ }
100404
+ emit(pointIndex, marker) {
100405
+ this.elementChange.emit(setDataPointMarker(this.element(), this.activeIndex(), pointIndex, marker));
100406
+ }
100407
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChartDatapointMarkerOptionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
100408
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChartDatapointMarkerOptionsComponent, isStandalone: true, selector: "pptx-chart-datapoint-marker-options", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: true, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { elementChange: "elementChange" }, ngImport: i0, template: `
100409
+ @if (supported()) {
100410
+ <section class="pptx-chart-card" [attr.aria-label]="'pptx.chart.pointMarkers' | translate">
100411
+ <h4 class="pptx-chart-card__heading">{{ 'pptx.chart.pointMarkers' | translate }}</h4>
100412
+
100413
+ @if (series().length > 1) {
100414
+ <label class="pptx-chart-card__row">
100415
+ <span class="pptx-chart-card__label">{{ 'pptx.chart.series' | translate }}</span>
100416
+ <select
100417
+ class="pptx-chart-card__input"
100418
+ [disabled]="!canEdit()"
100419
+ [value]="activeIndex()"
100420
+ (change)="onSeries($event)"
100421
+ >
100422
+ @for (s of series(); track $index; let i = $index) {
100423
+ <option [value]="i">{{ s.name }}</option>
100424
+ }
100425
+ </select>
100426
+ </label>
100427
+ }
100428
+
100429
+ <div class="pptx-chart-card__group">
100430
+ @for (cat of categories(); track $index; let ci = $index) {
100431
+ <div class="pptx-chart-card__row">
100432
+ <span class="pptx-chart-card__name" [title]="cat">{{ cat }}</span>
100433
+ <label class="pptx-chart-card__check">
100434
+ <input
100435
+ type="checkbox"
100436
+ [disabled]="!canEdit()"
100437
+ [checked]="markerAt(ci) !== undefined"
100438
+ (change)="onToggle(ci, $event)"
100439
+ />
100440
+ {{ 'pptx.chart.markerOverride' | translate }}
100441
+ </label>
100442
+ </div>
100443
+
100444
+ @if (markerAt(ci); as marker) {
100445
+ <div class="pptx-chart-card__row pptx-chart-card__group--indent">
100446
+ <select
100447
+ class="pptx-chart-card__input"
100448
+ [disabled]="!canEdit()"
100449
+ [value]="marker.symbol"
100450
+ (change)="onSymbol(ci, $event)"
100451
+ >
100452
+ @for (opt of symbolOptions; track opt.value) {
100453
+ <option [value]="opt.value">{{ opt.labelKey | translate }}</option>
100454
+ }
100455
+ </select>
100456
+ <input
100457
+ type="number"
100458
+ min="1"
100459
+ max="20"
100460
+ class="pptx-chart-card__input pptx-chart-card__input--num"
100461
+ [title]="'pptx.chart.markerSize' | translate"
100462
+ [placeholder]="'pptx.chart.auto' | translate"
100463
+ [disabled]="!canEdit()"
100464
+ [value]="marker.size ?? ''"
100465
+ (change)="onSize(ci, $event)"
100466
+ />
100467
+ <input
100468
+ type="color"
100469
+ class="pptx-chart-card__color"
100470
+ [title]="'pptx.chart.markerFill' | translate"
100471
+ [disabled]="!canEdit()"
100472
+ [value]="marker.spPr?.fillColor ?? '#4472c4'"
100473
+ (input)="onFill(ci, $event)"
100474
+ />
100475
+ </div>
100476
+ }
100477
+ }
100478
+ </div>
100479
+ </section>
100480
+ }
100481
+ `, isInline: true, styles: [".pptx-chart-card{display:flex;flex-direction:column;gap:.35rem;padding:.5rem 0;border-top:1px solid var(--pptx-inspector-border, #333)}.pptx-chart-card__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-chart-card__group{display:flex;flex-direction:column;gap:.3rem}.pptx-chart-card__group--indent{margin-left:.5rem}.pptx-chart-card__subhead{font-size:11px;font-weight:600}.pptx-chart-card__row{display:flex;align-items:center;gap:.4rem;font-size:11px}.pptx-chart-card__label{flex:0 0 auto;width:5rem;color:var(--pptx-inspector-muted, #888)}.pptx-chart-card__label--wide{width:6.5rem}.pptx-chart-card__name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pptx-chart-card__check{display:flex;align-items:center;gap:.35rem;font-size:11px;cursor:pointer}.pptx-chart-card__input{flex:1 1 auto;min-width:0;box-sizing:border-box;padding:2px 4px;font-size:11px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;color:inherit;outline:none}.pptx-chart-card__input--num{flex:0 0 auto;width:4rem;text-align:right}.pptx-chart-card__input:focus{border-color:var(--pptx-inspector-active, #0078d4)}.pptx-chart-card__input:disabled{opacity:.6;cursor:not-allowed}.pptx-chart-card__color{flex:0 0 auto;width:26px;height:20px;padding:0;border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;background:transparent;cursor:pointer}.pptx-chart-card__color:disabled{opacity:.6;cursor:not-allowed}.pptx-chart-card__clear{flex:0 0 auto;padding:0 2px;font-size:12px;line-height:1;background:none;border:none;color:var(--pptx-inspector-muted, #888);cursor:pointer}.pptx-chart-card__clear:hover{color:var(--pptx-inspector-danger, #f47c7c)}.pptx-chart-card__input--num::-webkit-outer-spin-button,.pptx-chart-card__input--num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
100482
+ }
100483
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChartDatapointMarkerOptionsComponent, decorators: [{
100484
+ type: Component,
100485
+ args: [{ selector: 'pptx-chart-datapoint-marker-options', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe], template: `
100486
+ @if (supported()) {
100487
+ <section class="pptx-chart-card" [attr.aria-label]="'pptx.chart.pointMarkers' | translate">
100488
+ <h4 class="pptx-chart-card__heading">{{ 'pptx.chart.pointMarkers' | translate }}</h4>
100489
+
100490
+ @if (series().length > 1) {
100491
+ <label class="pptx-chart-card__row">
100492
+ <span class="pptx-chart-card__label">{{ 'pptx.chart.series' | translate }}</span>
100493
+ <select
100494
+ class="pptx-chart-card__input"
100495
+ [disabled]="!canEdit()"
100496
+ [value]="activeIndex()"
100497
+ (change)="onSeries($event)"
100498
+ >
100499
+ @for (s of series(); track $index; let i = $index) {
100500
+ <option [value]="i">{{ s.name }}</option>
100501
+ }
100502
+ </select>
100503
+ </label>
100504
+ }
100505
+
100506
+ <div class="pptx-chart-card__group">
100507
+ @for (cat of categories(); track $index; let ci = $index) {
100508
+ <div class="pptx-chart-card__row">
100509
+ <span class="pptx-chart-card__name" [title]="cat">{{ cat }}</span>
100510
+ <label class="pptx-chart-card__check">
100511
+ <input
100512
+ type="checkbox"
100513
+ [disabled]="!canEdit()"
100514
+ [checked]="markerAt(ci) !== undefined"
100515
+ (change)="onToggle(ci, $event)"
100516
+ />
100517
+ {{ 'pptx.chart.markerOverride' | translate }}
100518
+ </label>
100519
+ </div>
100520
+
100521
+ @if (markerAt(ci); as marker) {
100522
+ <div class="pptx-chart-card__row pptx-chart-card__group--indent">
100523
+ <select
100524
+ class="pptx-chart-card__input"
100525
+ [disabled]="!canEdit()"
100526
+ [value]="marker.symbol"
100527
+ (change)="onSymbol(ci, $event)"
100528
+ >
100529
+ @for (opt of symbolOptions; track opt.value) {
100530
+ <option [value]="opt.value">{{ opt.labelKey | translate }}</option>
100531
+ }
100532
+ </select>
100533
+ <input
100534
+ type="number"
100535
+ min="1"
100536
+ max="20"
100537
+ class="pptx-chart-card__input pptx-chart-card__input--num"
100538
+ [title]="'pptx.chart.markerSize' | translate"
100539
+ [placeholder]="'pptx.chart.auto' | translate"
100540
+ [disabled]="!canEdit()"
100541
+ [value]="marker.size ?? ''"
100542
+ (change)="onSize(ci, $event)"
100543
+ />
100544
+ <input
100545
+ type="color"
100546
+ class="pptx-chart-card__color"
100547
+ [title]="'pptx.chart.markerFill' | translate"
100548
+ [disabled]="!canEdit()"
100549
+ [value]="marker.spPr?.fillColor ?? '#4472c4'"
100550
+ (input)="onFill(ci, $event)"
100551
+ />
100552
+ </div>
100553
+ }
100554
+ }
100555
+ </div>
100556
+ </section>
100557
+ }
100558
+ `, styles: [".pptx-chart-card{display:flex;flex-direction:column;gap:.35rem;padding:.5rem 0;border-top:1px solid var(--pptx-inspector-border, #333)}.pptx-chart-card__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-chart-card__group{display:flex;flex-direction:column;gap:.3rem}.pptx-chart-card__group--indent{margin-left:.5rem}.pptx-chart-card__subhead{font-size:11px;font-weight:600}.pptx-chart-card__row{display:flex;align-items:center;gap:.4rem;font-size:11px}.pptx-chart-card__label{flex:0 0 auto;width:5rem;color:var(--pptx-inspector-muted, #888)}.pptx-chart-card__label--wide{width:6.5rem}.pptx-chart-card__name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pptx-chart-card__check{display:flex;align-items:center;gap:.35rem;font-size:11px;cursor:pointer}.pptx-chart-card__input{flex:1 1 auto;min-width:0;box-sizing:border-box;padding:2px 4px;font-size:11px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;color:inherit;outline:none}.pptx-chart-card__input--num{flex:0 0 auto;width:4rem;text-align:right}.pptx-chart-card__input:focus{border-color:var(--pptx-inspector-active, #0078d4)}.pptx-chart-card__input:disabled{opacity:.6;cursor:not-allowed}.pptx-chart-card__color{flex:0 0 auto;width:26px;height:20px;padding:0;border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;background:transparent;cursor:pointer}.pptx-chart-card__color:disabled{opacity:.6;cursor:not-allowed}.pptx-chart-card__clear{flex:0 0 auto;padding:0 2px;font-size:12px;line-height:1;background:none;border:none;color:var(--pptx-inspector-muted, #888);cursor:pointer}.pptx-chart-card__clear:hover{color:var(--pptx-inspector-danger, #f47c7c)}.pptx-chart-card__input--num::-webkit-outer-spin-button,.pptx-chart-card__input--num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"] }]
100559
+ }], propDecorators: { element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: true }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], elementChange: [{ type: i0.Output, args: ["elementChange"] }] } });
100560
+
98071
100561
  /**
98072
100562
  * chart-datapoint-options.component.ts: Per-data-point fill + slice explosion.
98073
100563
  *
@@ -98979,6 +101469,11 @@ class AdvancedChartEditorComponent {
98979
101469
  [canEdit]="canEdit()"
98980
101470
  (elementChange)="elementChange.emit($event)"
98981
101471
  />
101472
+ <pptx-chart-datapoint-marker-options
101473
+ [element]="element()"
101474
+ [canEdit]="canEdit()"
101475
+ (elementChange)="elementChange.emit($event)"
101476
+ />
98982
101477
  <pptx-chart-trendline-options
98983
101478
  [element]="element()"
98984
101479
  [canEdit]="canEdit()"
@@ -98990,7 +101485,7 @@ class AdvancedChartEditorComponent {
98990
101485
  (elementChange)="elementChange.emit($event)"
98991
101486
  />
98992
101487
  </div>
98993
- `, isInline: true, styles: [".pptx-advanced-chart{display:flex;flex-direction:column}\n"], dependencies: [{ kind: "component", type: ChartDisplayOptionsComponent, selector: "pptx-chart-display-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDataLabelOptionsComponent, selector: "pptx-chart-data-label-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartAxisOptionsComponent, selector: "pptx-chart-axis-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartAxisStyleOptionsComponent, selector: "pptx-chart-axis-style-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartMarkerOptionsComponent, selector: "pptx-chart-marker-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartComboTypeOptionsComponent, selector: "pptx-chart-combo-type-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDatapointOptionsComponent, selector: "pptx-chart-datapoint-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartTrendlineOptionsComponent, selector: "pptx-chart-trendline-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartErrorBarOptionsComponent, selector: "pptx-chart-error-bar-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
101488
+ `, isInline: true, styles: [".pptx-advanced-chart{display:flex;flex-direction:column}\n"], dependencies: [{ kind: "component", type: ChartDisplayOptionsComponent, selector: "pptx-chart-display-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDataLabelOptionsComponent, selector: "pptx-chart-data-label-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartAxisOptionsComponent, selector: "pptx-chart-axis-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartAxisStyleOptionsComponent, selector: "pptx-chart-axis-style-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartMarkerOptionsComponent, selector: "pptx-chart-marker-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartComboTypeOptionsComponent, selector: "pptx-chart-combo-type-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDatapointOptionsComponent, selector: "pptx-chart-datapoint-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDatapointMarkerOptionsComponent, selector: "pptx-chart-datapoint-marker-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartTrendlineOptionsComponent, selector: "pptx-chart-trendline-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartErrorBarOptionsComponent, selector: "pptx-chart-error-bar-options", inputs: ["element", "canEdit"], outputs: ["elementChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
98994
101489
  }
98995
101490
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AdvancedChartEditorComponent, decorators: [{
98996
101491
  type: Component,
@@ -99002,6 +101497,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
99002
101497
  ChartMarkerOptionsComponent,
99003
101498
  ChartComboTypeOptionsComponent,
99004
101499
  ChartDatapointOptionsComponent,
101500
+ ChartDatapointMarkerOptionsComponent,
99005
101501
  ChartTrendlineOptionsComponent,
99006
101502
  ChartErrorBarOptionsComponent,
99007
101503
  ], template: `
@@ -99041,6 +101537,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
99041
101537
  [canEdit]="canEdit()"
99042
101538
  (elementChange)="elementChange.emit($event)"
99043
101539
  />
101540
+ <pptx-chart-datapoint-marker-options
101541
+ [element]="element()"
101542
+ [canEdit]="canEdit()"
101543
+ (elementChange)="elementChange.emit($event)"
101544
+ />
99044
101545
  <pptx-chart-trendline-options
99045
101546
  [element]="element()"
99046
101547
  [canEdit]="canEdit()"
@@ -116440,6 +118941,8 @@ class PowerPointViewerComponent {
116440
118941
  */
116441
118942
  showReadingView = signal(false, /* @ts-ignore */
116442
118943
  ...(ngDevMode ? [{ debugName: "showReadingView" }] : /* istanbul ignore next */ []));
118944
+ showOutlineView = signal(false, /* @ts-ignore */
118945
+ ...(ngDevMode ? [{ debugName: "showOutlineView" }] : /* istanbul ignore next */ []));
116443
118946
  /** Full-canvas master editor visibility and active target. */
116444
118947
  showMasterView = signal(false, /* @ts-ignore */
116445
118948
  ...(ngDevMode ? [{ debugName: "showMasterView" }] : /* istanbul ignore next */ []));
@@ -117111,6 +119614,17 @@ class PowerPointViewerComponent {
117111
119614
  this.showReadingView.set(false);
117112
119615
  this.goTo(slideIndex);
117113
119616
  }
119617
+ /**
119618
+ * Commit an outline edit as ONE undoable entry.
119619
+ *
119620
+ * `applyReplacement` rather than `setSlides`: the latter resets the undo
119621
+ * stack and the selection, which would make every keystroke in the outline
119622
+ * throw away the user's history.
119623
+ */
119624
+ onOutlineCommit(commit) {
119625
+ this.editor.applyReplacement(commit.slides);
119626
+ this.goTo(commit.activeSlideIndex);
119627
+ }
117114
119628
  goPrev() {
117115
119629
  this.goTo(this.activeSlideIndex() - 1);
117116
119630
  }
@@ -117260,6 +119774,23 @@ class PowerPointViewerComponent {
117260
119774
  }
117261
119775
  this.canvasEditing.onElementSelect(event);
117262
119776
  }
119777
+ /**
119778
+ * Commit a motion path dragged on the canvas.
119779
+ *
119780
+ * `setMotionPath` rather than a preset apply: the dragged geometry no longer
119781
+ * matches any catalogue entry, and it is written onto the SAME animation
119782
+ * entry the preset buckets use, so the element's entrance survives the edit.
119783
+ */
119784
+ onMotionPathChange(path) {
119785
+ const element = this.selectedElement();
119786
+ const slide = this.activeSlide();
119787
+ if (!this.canEdit() || !element || !slide) {
119788
+ return;
119789
+ }
119790
+ this.editor.updateSlide(this.activeSlideIndex(), {
119791
+ animations: setMotionPath(slide.animations ?? [], element.id, path),
119792
+ });
119793
+ }
117263
119794
  /** Context-menu "Ask AI about this": scope + open the assistant, empty composer. */
117264
119795
  onContextMenuAskAi() {
117265
119796
  this.aiPanelStore.askAboutSelection();
@@ -117730,6 +120261,7 @@ class PowerPointViewerComponent {
117730
120261
  (link)="docProperties.showHyperlink.set(true)"
117731
120262
  (openSorter)="showSorter.set(true)"
117732
120263
  (openReadingView)="showReadingView.set(true)"
120264
+ (openOutlineView)="showOutlineView.set(true)"
117733
120265
  (toggleNotes)="mobileSheetSvc.toggleNotes()"
117734
120266
  (toggleFormatPainter)="formatPainter.toggle()"
117735
120267
  (exportPng)="xport.exportPng()"
@@ -117881,6 +120413,19 @@ class PowerPointViewerComponent {
117881
120413
  [activeSlideIndex]="activeSlideIndex()"
117882
120414
  />
117883
120415
  }
120416
+ <!--
120417
+ Motion path of the selected element: projected for the same
120418
+ reason the collaboration overlays are, and authored in raw
120419
+ slide coordinates. It draws nothing unless the deck is
120420
+ editable and the selection actually carries a path.
120421
+ -->
120422
+ <pptx-motion-path-overlay
120423
+ [element]="selectedElement()"
120424
+ [animations]="activeSlide()?.animations ?? []"
120425
+ [canvasSize]="loader.canvasSize()"
120426
+ [canEdit]="canEdit()"
120427
+ (pathChange)="onMotionPathChange($event)"
120428
+ />
117884
120429
  </pptx-slide-canvas>
117885
120430
  @if (collab.active() && collab.presence().length > 0) {
117886
120431
  <div class="pptx-ng-collab-follow">
@@ -118085,6 +120630,21 @@ class PowerPointViewerComponent {
118085
120630
  />
118086
120631
  }
118087
120632
 
120633
+ @if (showOutlineView()) {
120634
+ <!--
120635
+ The EDITABLE deck, not the merged display deck: the merged one has
120636
+ each slide's inherited master/layout elements folded in, and
120637
+ committing that back would bake the template layer into the slides.
120638
+ -->
120639
+ <pptx-outline-view-overlay
120640
+ [slides]="editor.slides()"
120641
+ [canvasSize]="loader.canvasSize()"
120642
+ [canEdit]="canEdit()"
120643
+ (commit)="onOutlineCommit($event)"
120644
+ (closed)="showOutlineView.set(false)"
120645
+ />
120646
+ }
120647
+
118088
120648
  @if (showReadingView()) {
118089
120649
  <!--
118090
120650
  The merged deck, so each slide carries its own inherited template
@@ -118391,7 +120951,7 @@ class PowerPointViewerComponent {
118391
120951
  />
118392
120952
  }
118393
120953
  </div>
118394
- `, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: SlideCanvasComponent, selector: "pptx-slide-canvas", inputs: ["slide", "canvasSize", "mediaDataUrls", "zoom", "editable", "showGrid", "showRulers", "rulerUnit", "showGuides", "snapToGrid", "snapToShape", "guideCommand", "spellCheck", "snapToGuides", "autoFit", "transparentBackground", "interactive", "presenting", "selectedIds", "editingId", "editTemplateMode", "templateElements", "aiHighlights", "aiActive", "aiActiveSlideIndex", "aiChangeBatch", "aiPickMode", "drawTool", "drawColor", "drawWidth"], outputs: ["elementSelect", "backgroundClick", "transformStart", "transformUpdate", "contextMenu", "textEditStart", "textCommit", "textInput", "textCancel", "textFormat", "rotateUpdate", "marqueeSelect", "inkStrokeComplete", "eraserHit", "cellCommit", "tableChange"] }, { kind: "component", type: PresentationOverlayComponent, selector: "pptx-presentation-overlay", inputs: ["slides", "canvasSize", "mediaDataUrls", "startIndex", "showWithAnimation", "useTimings", "subtitlesVisible", "sessionEnded"], outputs: ["indexChange", "closed", "subtitlesChange", "annotationsExit"] }, { kind: "component", type: PresenterViewComponent, selector: "pptx-presenter-view", inputs: ["slides", "currentSlideIndex", "canvasSize", "templateElements", "mediaDataUrls", "presentationStartTime", "isAudienceWindowOpen"], outputs: ["movePresentationSlide", "exit", "openAudienceWindow", "closeAudienceWindow", "navigateToSlide"] }, { kind: "component", type: MobilePresenterViewComponent, selector: "pptx-mobile-presenter-view", inputs: ["slides", "currentSlideIndex", "canvasSize", "templateElements", "mediaDataUrls", "presentationStartTime"], outputs: ["movePresentationSlide", "exit"] }, { kind: "component", type: SlideSorterOverlayComponent, selector: "pptx-slide-sorter-overlay", inputs: ["slides", "canvasSize", "mediaDataUrls", "activeIndex"], outputs: ["select", "closed"] }, { kind: "component", type: ReadingViewOverlayComponent, selector: "pptx-reading-view-overlay", inputs: ["slides", "canvasSize", "mediaDataUrls", "activeSlideIndex"], outputs: ["exit"] }, { kind: "component", type: SlideDefaultInspectorComponent, selector: "pptx-slide-default-inspector", inputs: ["slideIndex", "canEdit", "selectedElement", "comments"], outputs: ["commentAdd", "commentRemove", "commentResolve"] }, { kind: "component", type: FindBarComponent, selector: "pptx-find-bar", inputs: ["slides"], outputs: ["navigate", "closed"] }, { kind: "component", type: FindReplaceBarComponent, selector: "pptx-find-replace-bar", inputs: ["matchCount", "matchIndex"], outputs: ["find", "navigate", "replaceOne", "replaceAll", "close"] }, { kind: "component", type: SlidesPanelComponent, selector: "pptx-slides-panel", inputs: ["canvasSize", "mediaDataUrls", "activeIndex"], outputs: ["select"] }, { kind: "component", type: StatusBarComponent, selector: "pptx-status-bar", inputs: ["slideIndex", "slideCount", "canEdit", "dirty", "autosaveStatus", "notesOpen", "zoomPercent", "sorterActive", "presenting", "hiddenActions"], outputs: ["toggleNotes", "normalView", "openSorter", "slideShow", "zoomIn", "zoomOut", "zoomReset"] }, { kind: "component", type: EditorContextMenuComponent, selector: "pptx-editor-context-menu", inputs: ["x", "y", "slideIndex", "showAiActions"], outputs: ["closed", "askAi", "fixAi", "editHyperlink", "addComment"] }, { kind: "component", type: ExportProgressModalComponent, selector: "pptx-export-progress-modal", inputs: ["open", "title", "progress", "statusMessage"], outputs: ["cancel"] }, { kind: "component", type: CommentsPanelComponent, selector: "pptx-comments-panel", inputs: ["comments", "authorName"], outputs: ["add", "remove", "resolve"] }, { kind: "component", type: SignaturesPanelComponent, selector: "pptx-signatures-panel", inputs: ["signatures"] }, { kind: "component", type: AccessibilityPanelComponent, selector: "pptx-accessibility-panel", inputs: ["issues"], outputs: ["selectSlide"] }, { kind: "component", type: CollaborationCursorsComponent, selector: "pptx-collaboration-cursors", inputs: ["cursors", "zoom"] }, { kind: "component", type: RemoteSelectionOverlayComponent, selector: "pptx-remote-selection-overlay", inputs: ["presences", "elements", "activeSlideIndex", "zoom"] }, { kind: "component", type: FollowModeBarComponent, selector: "pptx-follow-mode-bar", inputs: ["presences", "followedClientId"], outputs: ["follow"] }, { kind: "component", type: PropertiesDialogComponent, selector: "pptx-properties-dialog", inputs: ["open", "properties"], outputs: ["save", "close"] }, { kind: "component", type: HyperlinkDialogComponent, selector: "pptx-hyperlink-dialog", inputs: ["open", "element"], outputs: ["save", "close"] }, { kind: "component", type: PrintDialogComponent, selector: "pptx-print-dialog", inputs: ["slides", "activeSlideIndex", "defaultSlidesPerPage", "defaultFrameSlides"], outputs: ["print", "cancel"] }, { kind: "component", type: ShareDialogComponent, selector: "pptx-share-dialog", inputs: ["open", "defaults", "active", "connected", "userCount", "shareUrl", "p2p"], outputs: ["start", "stop", "close"] }, { kind: "component", type: BroadcastDialogComponent, selector: "pptx-broadcast-dialog", inputs: ["open", "defaults", "active", "connected", "viewerCount", "viewerUrl", "p2p"], outputs: ["start", "stop", "close"] }, { kind: "component", type: MobileBottomBarComponent, selector: "pptx-mobile-bottom-bar", inputs: ["slideCount", "commentCount", "activeSheet"], outputs: ["openSlides", "insert", "openFormat", "openComments", "notes"] }, { kind: "component", type: MobileMenuSheetComponent, selector: "pptx-mobile-menu-sheet", inputs: ["open", "slideCount", "exporting", "showNotes", "canEdit", "hiddenActions"], outputs: ["closed", "openFind", "openSorter", "toggleNotes", "insertText", "present", "openFile", "savePptx", "exportPng", "exportPdf", "exportGif", "exportVideo", "print"] }, { kind: "component", type: MobileSlidesSheetComponent, selector: "pptx-mobile-slides-sheet", inputs: ["open", "slides", "canvasSize", "mediaDataUrls", "activeIndex"], outputs: ["closed", "jumpToSlide"] }, { kind: "component", type: MobileToolbarComponent, selector: "pptx-mobile-toolbar", inputs: ["canUndo", "canRedo", "canPresent", "canEdit", "menuOpen", "aiEnabled", "aiPanelOpen", "hiddenActions"], outputs: ["toggleMenu", "toggleAiPanel", "undo", "redo", "share", "save", "present"] }, { kind: "component", type: MasterViewCanvasComponent, selector: "pptx-master-view-canvas", inputs: ["tab", "slideMasters", "activeMasterIndex", "activeLayoutIndex", "notesMaster", "handoutMaster", "canvasSize", "notesCanvasSize", "mediaDataUrls", "editable"], outputs: ["notesMasterChange", "handoutMasterChange"] }, { kind: "component", type: MasterViewSidebarComponent, selector: "pptx-master-view-sidebar", inputs: ["tab", "slideMasters", "notesMaster", "handoutMaster", "activeMasterIndex", "activeLayoutIndex", "handoutSlidesPerPage"], outputs: ["tabChange", "selectMaster", "selectLayout", "slidesPerPageChange", "backgroundChange", "close"] }, { kind: "component", type: NotesPanelComponent, selector: "pptx-notes-panel", inputs: ["slide", "expanded"], outputs: ["update", "notesToggle"] }, { kind: "component", type: RibbonComponent, selector: "pptx-ribbon", inputs: ["slideIndex", "slideCount", "canEdit", "selectedElement", "zoomPercent", "formatPainterActive", "canActivateFormatPainter", "exporting", "hasMacros", "showGrid", "showRulers", "showGuides", "snapToGrid", "snapToShape", "eyedropperActive", "themeGalleryOpen", "sidebarCollapsed", "inspectorOpen", "commentsOpen", "commentCount", "findOpen", "collabConnected", "connectedCount", "spellCheckEnabled", "showSubtitles", "hiddenActions", "aiEnabled", "aiPanelOpen", "accountAuth"], outputs: ["prev", "next", "zoomIn", "zoomOut", "zoomReset", "find", "present", "presenter", "record", "presentFromBeginning", "rehearseTimings", "toggleSubtitles", "openSubtitleSettings", "recordFromBeginning", "recordFromCurrent", "spellCheckChange", "share", "broadcast", "openFile", "openRecentFile", "createPresentation", "save", "savePpsx", "savePptm", "packageForSharing", "toggleSidebar", "toggleAiPanel", "signatures", "info", "print", "comments", "a11y", "link", "openSorter", "openReadingView", "openMasterView", "toggleNotes", "toggleFormatPainter", "exportPng", "exportPdf", "exportGif", "exportVideo", "copySlideAsImage", "replace", "toggleInspector", "drawToolChange", "toggleThemeGallery", "toggleGrid", "toggleRulers", "toggleGuides", "toggleSelectionPane", "openCustomShows", "toggleSnapToGrid", "toggleSnapToShape", "addGuide", "zoomToFit", "toggleEyedropper", "openSmartArtDialog", "openEquationDialog", "openSetUpSlideShow", "openCompare", "openPassword", "openFontEmbedding", "openVersionHistory", "openShortcuts", "openSettings"] }, { kind: "component", type: TitleBarComponent, selector: "pptx-title-bar", inputs: ["canEdit", "fileName", "isDirty", "autosaveStatus", "autosaveEnabled", "canUndo", "canRedo", "undoLabel", "redoLabel", "findReplaceOpen", "hiddenActions", "quickAccess"], outputs: ["toggleAutosave", "save", "undo", "redo", "quickCommand", "toggleFindReplace", "commandSearch"] }, { kind: "component", type: ThemeGalleryComponent, selector: "pptx-theme-gallery", inputs: ["open", "activeName", "theme"], outputs: ["applyTheme", "applyCustomTheme", "close"] }, { kind: "component", type: SelectionPaneComponent, selector: "pptx-selection-pane", inputs: ["elements", "selectedIds"], outputs: ["selectElement", "bringForward", "sendBackward", "toggleHidden"] }, { kind: "component", type: CustomShowsComponent, selector: "pptx-custom-shows", inputs: ["open", "slides", "customShows", "activeCustomShowId"], outputs: ["create", "remove", "update", "setActive", "close"] }, { kind: "component", type: InsertSmartArtDialogComponent, selector: "pptx-insert-smart-art-dialog", inputs: ["open"], outputs: ["close", "insert"] }, { kind: "component", type: ViewerExtraDialogsComponent, selector: "pptx-viewer-extra-dialogs", inputs: ["activeSlideIndex", "selectedElementId", "filePath", "customShows", "themeKey", "availableThemes", "localeCode", "availableLocales", "aiExportVisible"], outputs: ["restoreContent", "themeKeySelect", "localeSelect"] }, { kind: "component", type: RehearseTimingsComponent, selector: "pptx-rehearse-timings", inputs: ["summary", "paused", "slideStartedAt", "presentationStartedAt", "timings"], outputs: ["togglePause", "save", "discard"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
120954
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: SlideCanvasComponent, selector: "pptx-slide-canvas", inputs: ["slide", "canvasSize", "mediaDataUrls", "zoom", "editable", "showGrid", "showRulers", "rulerUnit", "showGuides", "snapToGrid", "snapToShape", "guideCommand", "spellCheck", "snapToGuides", "autoFit", "transparentBackground", "interactive", "presenting", "selectedIds", "editingId", "editTemplateMode", "templateElements", "aiHighlights", "aiActive", "aiActiveSlideIndex", "aiChangeBatch", "aiPickMode", "drawTool", "drawColor", "drawWidth"], outputs: ["elementSelect", "backgroundClick", "transformStart", "transformUpdate", "contextMenu", "textEditStart", "textCommit", "textInput", "textCancel", "textFormat", "rotateUpdate", "marqueeSelect", "inkStrokeComplete", "eraserHit", "cellCommit", "tableChange"] }, { kind: "component", type: PresentationOverlayComponent, selector: "pptx-presentation-overlay", inputs: ["slides", "canvasSize", "mediaDataUrls", "startIndex", "showWithAnimation", "useTimings", "subtitlesVisible", "sessionEnded"], outputs: ["indexChange", "closed", "subtitlesChange", "annotationsExit"] }, { kind: "component", type: PresenterViewComponent, selector: "pptx-presenter-view", inputs: ["slides", "currentSlideIndex", "canvasSize", "templateElements", "mediaDataUrls", "presentationStartTime", "isAudienceWindowOpen"], outputs: ["movePresentationSlide", "exit", "openAudienceWindow", "closeAudienceWindow", "navigateToSlide"] }, { kind: "component", type: MobilePresenterViewComponent, selector: "pptx-mobile-presenter-view", inputs: ["slides", "currentSlideIndex", "canvasSize", "templateElements", "mediaDataUrls", "presentationStartTime"], outputs: ["movePresentationSlide", "exit"] }, { kind: "component", type: SlideSorterOverlayComponent, selector: "pptx-slide-sorter-overlay", inputs: ["slides", "canvasSize", "mediaDataUrls", "activeIndex"], outputs: ["select", "closed"] }, { kind: "component", type: OutlineViewOverlayComponent, selector: "pptx-outline-view-overlay", inputs: ["slides", "canvasSize", "canEdit"], outputs: ["commit", "closed"] }, { kind: "component", type: ReadingViewOverlayComponent, selector: "pptx-reading-view-overlay", inputs: ["slides", "canvasSize", "mediaDataUrls", "activeSlideIndex"], outputs: ["exit"] }, { kind: "component", type: SlideDefaultInspectorComponent, selector: "pptx-slide-default-inspector", inputs: ["slideIndex", "canEdit", "selectedElement", "comments"], outputs: ["commentAdd", "commentRemove", "commentResolve"] }, { kind: "component", type: FindBarComponent, selector: "pptx-find-bar", inputs: ["slides"], outputs: ["navigate", "closed"] }, { kind: "component", type: FindReplaceBarComponent, selector: "pptx-find-replace-bar", inputs: ["matchCount", "matchIndex"], outputs: ["find", "navigate", "replaceOne", "replaceAll", "close"] }, { kind: "component", type: SlidesPanelComponent, selector: "pptx-slides-panel", inputs: ["canvasSize", "mediaDataUrls", "activeIndex"], outputs: ["select"] }, { kind: "component", type: StatusBarComponent, selector: "pptx-status-bar", inputs: ["slideIndex", "slideCount", "canEdit", "dirty", "autosaveStatus", "notesOpen", "zoomPercent", "sorterActive", "presenting", "hiddenActions"], outputs: ["toggleNotes", "normalView", "openSorter", "slideShow", "zoomIn", "zoomOut", "zoomReset"] }, { kind: "component", type: EditorContextMenuComponent, selector: "pptx-editor-context-menu", inputs: ["x", "y", "slideIndex", "showAiActions"], outputs: ["closed", "askAi", "fixAi", "editHyperlink", "addComment"] }, { kind: "component", type: ExportProgressModalComponent, selector: "pptx-export-progress-modal", inputs: ["open", "title", "progress", "statusMessage"], outputs: ["cancel"] }, { kind: "component", type: CommentsPanelComponent, selector: "pptx-comments-panel", inputs: ["comments", "authorName"], outputs: ["add", "remove", "resolve"] }, { kind: "component", type: SignaturesPanelComponent, selector: "pptx-signatures-panel", inputs: ["signatures"] }, { kind: "component", type: AccessibilityPanelComponent, selector: "pptx-accessibility-panel", inputs: ["issues"], outputs: ["selectSlide"] }, { kind: "component", type: CollaborationCursorsComponent, selector: "pptx-collaboration-cursors", inputs: ["cursors", "zoom"] }, { kind: "component", type: RemoteSelectionOverlayComponent, selector: "pptx-remote-selection-overlay", inputs: ["presences", "elements", "activeSlideIndex", "zoom"] }, { kind: "component", type: MotionPathOverlayComponent, selector: "pptx-motion-path-overlay", inputs: ["element", "animations", "canvasSize", "canEdit"], outputs: ["pathChange"] }, { kind: "component", type: FollowModeBarComponent, selector: "pptx-follow-mode-bar", inputs: ["presences", "followedClientId"], outputs: ["follow"] }, { kind: "component", type: PropertiesDialogComponent, selector: "pptx-properties-dialog", inputs: ["open", "properties"], outputs: ["save", "close"] }, { kind: "component", type: HyperlinkDialogComponent, selector: "pptx-hyperlink-dialog", inputs: ["open", "element"], outputs: ["save", "close"] }, { kind: "component", type: PrintDialogComponent, selector: "pptx-print-dialog", inputs: ["slides", "activeSlideIndex", "defaultSlidesPerPage", "defaultFrameSlides"], outputs: ["print", "cancel"] }, { kind: "component", type: ShareDialogComponent, selector: "pptx-share-dialog", inputs: ["open", "defaults", "active", "connected", "userCount", "shareUrl", "p2p"], outputs: ["start", "stop", "close"] }, { kind: "component", type: BroadcastDialogComponent, selector: "pptx-broadcast-dialog", inputs: ["open", "defaults", "active", "connected", "viewerCount", "viewerUrl", "p2p"], outputs: ["start", "stop", "close"] }, { kind: "component", type: MobileBottomBarComponent, selector: "pptx-mobile-bottom-bar", inputs: ["slideCount", "commentCount", "activeSheet"], outputs: ["openSlides", "insert", "openFormat", "openComments", "notes"] }, { kind: "component", type: MobileMenuSheetComponent, selector: "pptx-mobile-menu-sheet", inputs: ["open", "slideCount", "exporting", "showNotes", "canEdit", "hiddenActions"], outputs: ["closed", "openFind", "openSorter", "toggleNotes", "insertText", "present", "openFile", "savePptx", "exportPng", "exportPdf", "exportGif", "exportVideo", "print"] }, { kind: "component", type: MobileSlidesSheetComponent, selector: "pptx-mobile-slides-sheet", inputs: ["open", "slides", "canvasSize", "mediaDataUrls", "activeIndex"], outputs: ["closed", "jumpToSlide"] }, { kind: "component", type: MobileToolbarComponent, selector: "pptx-mobile-toolbar", inputs: ["canUndo", "canRedo", "canPresent", "canEdit", "menuOpen", "aiEnabled", "aiPanelOpen", "hiddenActions"], outputs: ["toggleMenu", "toggleAiPanel", "undo", "redo", "share", "save", "present"] }, { kind: "component", type: MasterViewCanvasComponent, selector: "pptx-master-view-canvas", inputs: ["tab", "slideMasters", "activeMasterIndex", "activeLayoutIndex", "notesMaster", "handoutMaster", "canvasSize", "notesCanvasSize", "mediaDataUrls", "editable"], outputs: ["notesMasterChange", "handoutMasterChange"] }, { kind: "component", type: MasterViewSidebarComponent, selector: "pptx-master-view-sidebar", inputs: ["tab", "slideMasters", "notesMaster", "handoutMaster", "activeMasterIndex", "activeLayoutIndex", "handoutSlidesPerPage"], outputs: ["tabChange", "selectMaster", "selectLayout", "slidesPerPageChange", "backgroundChange", "close"] }, { kind: "component", type: NotesPanelComponent, selector: "pptx-notes-panel", inputs: ["slide", "expanded"], outputs: ["update", "notesToggle"] }, { kind: "component", type: RibbonComponent, selector: "pptx-ribbon", inputs: ["slideIndex", "slideCount", "canEdit", "selectedElement", "zoomPercent", "formatPainterActive", "canActivateFormatPainter", "exporting", "hasMacros", "showGrid", "showRulers", "showGuides", "snapToGrid", "snapToShape", "eyedropperActive", "themeGalleryOpen", "sidebarCollapsed", "inspectorOpen", "commentsOpen", "commentCount", "findOpen", "collabConnected", "connectedCount", "spellCheckEnabled", "showSubtitles", "hiddenActions", "aiEnabled", "aiPanelOpen", "accountAuth"], outputs: ["prev", "next", "zoomIn", "zoomOut", "zoomReset", "find", "present", "presenter", "record", "presentFromBeginning", "rehearseTimings", "toggleSubtitles", "openSubtitleSettings", "recordFromBeginning", "recordFromCurrent", "spellCheckChange", "share", "broadcast", "openFile", "openRecentFile", "createPresentation", "save", "savePpsx", "savePptm", "packageForSharing", "toggleSidebar", "toggleAiPanel", "signatures", "info", "print", "comments", "a11y", "link", "openSorter", "openReadingView", "openOutlineView", "openMasterView", "toggleNotes", "toggleFormatPainter", "exportPng", "exportPdf", "exportGif", "exportVideo", "copySlideAsImage", "replace", "toggleInspector", "drawToolChange", "toggleThemeGallery", "toggleGrid", "toggleRulers", "toggleGuides", "toggleSelectionPane", "openCustomShows", "toggleSnapToGrid", "toggleSnapToShape", "addGuide", "zoomToFit", "toggleEyedropper", "openSmartArtDialog", "openEquationDialog", "openSetUpSlideShow", "openCompare", "openPassword", "openFontEmbedding", "openVersionHistory", "openShortcuts", "openSettings"] }, { kind: "component", type: TitleBarComponent, selector: "pptx-title-bar", inputs: ["canEdit", "fileName", "isDirty", "autosaveStatus", "autosaveEnabled", "canUndo", "canRedo", "undoLabel", "redoLabel", "findReplaceOpen", "hiddenActions", "quickAccess"], outputs: ["toggleAutosave", "save", "undo", "redo", "quickCommand", "toggleFindReplace", "commandSearch"] }, { kind: "component", type: ThemeGalleryComponent, selector: "pptx-theme-gallery", inputs: ["open", "activeName", "theme"], outputs: ["applyTheme", "applyCustomTheme", "close"] }, { kind: "component", type: SelectionPaneComponent, selector: "pptx-selection-pane", inputs: ["elements", "selectedIds"], outputs: ["selectElement", "bringForward", "sendBackward", "toggleHidden"] }, { kind: "component", type: CustomShowsComponent, selector: "pptx-custom-shows", inputs: ["open", "slides", "customShows", "activeCustomShowId"], outputs: ["create", "remove", "update", "setActive", "close"] }, { kind: "component", type: InsertSmartArtDialogComponent, selector: "pptx-insert-smart-art-dialog", inputs: ["open"], outputs: ["close", "insert"] }, { kind: "component", type: ViewerExtraDialogsComponent, selector: "pptx-viewer-extra-dialogs", inputs: ["activeSlideIndex", "selectedElementId", "filePath", "customShows", "themeKey", "availableThemes", "localeCode", "availableLocales", "aiExportVisible"], outputs: ["restoreContent", "themeKeySelect", "localeSelect"] }, { kind: "component", type: RehearseTimingsComponent, selector: "pptx-rehearse-timings", inputs: ["summary", "paused", "slideStartedAt", "presentationStartedAt", "timings"], outputs: ["togglePause", "save", "discard"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
118395
120955
  Promise.resolve().then(function () { return aiChatPanel_component; }).then(m => m.AiChatPanelComponent)]] });
118396
120956
  }
118397
120957
  i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.8", ngImport: i0, type: PowerPointViewerComponent, resolveDeferredDeps: () => [/* @ts-ignore */
@@ -118410,6 +120970,7 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.8", ng
118410
120970
  PresenterViewComponent,
118411
120971
  MobilePresenterViewComponent,
118412
120972
  SlideSorterOverlayComponent,
120973
+ OutlineViewOverlayComponent,
118413
120974
  ReadingViewOverlayComponent,
118414
120975
  SlideDefaultInspectorComponent,
118415
120976
  FindBarComponent,
@@ -118424,6 +120985,7 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.8", ng
118424
120985
  AccessibilityPanelComponent,
118425
120986
  CollaborationCursorsComponent,
118426
120987
  RemoteSelectionOverlayComponent,
120988
+ MotionPathOverlayComponent,
118427
120989
  FollowModeBarComponent,
118428
120990
  PropertiesDialogComponent,
118429
120991
  HyperlinkDialogComponent,
@@ -118546,6 +121108,7 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.8", ng
118546
121108
  (link)="docProperties.showHyperlink.set(true)"
118547
121109
  (openSorter)="showSorter.set(true)"
118548
121110
  (openReadingView)="showReadingView.set(true)"
121111
+ (openOutlineView)="showOutlineView.set(true)"
118549
121112
  (toggleNotes)="mobileSheetSvc.toggleNotes()"
118550
121113
  (toggleFormatPainter)="formatPainter.toggle()"
118551
121114
  (exportPng)="xport.exportPng()"
@@ -118697,6 +121260,19 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.8", ng
118697
121260
  [activeSlideIndex]="activeSlideIndex()"
118698
121261
  />
118699
121262
  }
121263
+ <!--
121264
+ Motion path of the selected element: projected for the same
121265
+ reason the collaboration overlays are, and authored in raw
121266
+ slide coordinates. It draws nothing unless the deck is
121267
+ editable and the selection actually carries a path.
121268
+ -->
121269
+ <pptx-motion-path-overlay
121270
+ [element]="selectedElement()"
121271
+ [animations]="activeSlide()?.animations ?? []"
121272
+ [canvasSize]="loader.canvasSize()"
121273
+ [canEdit]="canEdit()"
121274
+ (pathChange)="onMotionPathChange($event)"
121275
+ />
118700
121276
  </pptx-slide-canvas>
118701
121277
  @if (collab.active() && collab.presence().length > 0) {
118702
121278
  <div class="pptx-ng-collab-follow">
@@ -118901,6 +121477,21 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.8", ng
118901
121477
  />
118902
121478
  }
118903
121479
 
121480
+ @if (showOutlineView()) {
121481
+ <!--
121482
+ The EDITABLE deck, not the merged display deck: the merged one has
121483
+ each slide's inherited master/layout elements folded in, and
121484
+ committing that back would bake the template layer into the slides.
121485
+ -->
121486
+ <pptx-outline-view-overlay
121487
+ [slides]="editor.slides()"
121488
+ [canvasSize]="loader.canvasSize()"
121489
+ [canEdit]="canEdit()"
121490
+ (commit)="onOutlineCommit($event)"
121491
+ (closed)="showOutlineView.set(false)"
121492
+ />
121493
+ }
121494
+
118904
121495
  @if (showReadingView()) {
118905
121496
  <!--
118906
121497
  The merged deck, so each slide carries its own inherited template
@@ -121100,5 +123691,5 @@ function cn(...values) {
121100
123691
  * Generated bundle index. Do not edit.
121101
123692
  */
121102
123693
 
121103
- export { DATA_TABLE_HEADER_H as $, ALIGN_OPTIONS as A, BroadcastDialogComponent as B, CHART_EDITOR_STYLES as C, CanvasFitService as D, ChartAxisOptionsComponent as E, ChartAxisStyleOptionsComponent as F, ChartComboTypeOptionsComponent as G, ChartDataEditorComponent as H, ChartDataLabelOptionsComponent as I, ChartDatapointOptionsComponent as J, ChartDisplayOptionsComponent as K, ChartElementViewComponent as L, ChartErrorBarOptionsComponent as M, ChartMarkerOptionsComponent as N, ChartPartSelectionService as O, ChartPrimitivesComponent as P, ChartRendererComponent as Q, ChartTrendlineOptionsComponent as R, CollaborationCursorsComponent as S, CollaborationService as T, ColorChangedImageComponent as U, CommentsPanelComponent as V, CommentsService as W, ComparePanelComponent as X, ConnectorRendererComponent as Y, ConnectorTextOverlayComponent as Z, CustomShowsComponent as _, ANIMATION_PRESET_CATEGORIES as a, MAX_ZOOM_SCALE as a$, DATA_TABLE_KEY_W as a0, DATA_TABLE_PADDING as a1, DATA_TABLE_ROW_H as a2, DEFAULT_BOUNDS as a3, DEFAULT_BROADCAST_SERVER_URL as a4, DEFAULT_CANVAS_HEIGHT as a5, DEFAULT_CANVAS_WIDTH as a6, DEFAULT_COLOR_SCHEME as a7, DEFAULT_FILL_COLOR as a8, DEFAULT_LAYOUT as a9, EquationTemplateGalleryComponent as aA, ExportProgressModalComponent as aB, ExportService as aC, FieldContextService as aD, FindBarComponent as aE, FindReplaceBarComponent as aF, FollowModeBarComponent as aG, FontEmbeddingListComponent as aH, FontEmbeddingPanelComponent as aI, GALLERY_THEME_PRESETS as aJ, GradientPickerComponent as aK, HANDOUT_OPTIONS as aL, HeaderFooterDialogComponent as aM, HyperlinkDialogComponent as aN, ImagePropertiesPanelComponent as aO, InkDrawingService as aP, InkRendererComponent as aQ, InsertSmartArtDialogComponent as aR, InspectorPaneHeaderComponent as aS, InspectorPanelComponent as aT, IsMobileService as aU, KeepAnnotationsDialogComponent as aV, LOCALE_CATALOG as aW, LONG_PRESS_DURATION_MS as aX, LONG_PRESS_MOVE_TOLERANCE_PX as aY, LoadContentService as aZ, LocalPresencePublisher as a_, DEFAULT_PALETTE$1 as aa, DEFAULT_PRINT_SETTINGS as ab, DEFAULT_SLIDE_BACKGROUND as ac, DEFAULT_STROKE_COLOR as ad, DEFAULT_STYLE as ae, DEFAULT_TABLE_ROW_HEIGHT as af, DEFAULT_TEXT_COLOR$1 as ag, DEFAULT_VIEWER_PROFILE as ah, DIRECTIONAL_PRESETS as ai, DIRECTION_OPTIONS as aj, DocumentPropertiesCardComponent as ak, EMBEDDED_FONTS_STYLE_ID as al, EMPHASIS_PRESETS as am, ENTRANCE_PRESETS as an, TEMPLATES as ao, EXIT_PRESETS as ap, EditorContextMenuComponent as aq, EditorHistory as ar, EditorStateService as as, EditorToolbarComponent as at, EffectsPanelComponent as au, ElementRendererComponent as av, EmbeddedFontsService as aw, EncryptedFileDialogComponent as ax, EquationEditorDialogComponent as ay, EquationRendererComponent as az, AUDIENCE_HASH as b, RibbonSlideshowSectionComponent as b$, MIN_ZOOM_SCALE as b0, MediaPreviewComponent as b1, MediaPropertiesPanelComponent as b2, MediaRendererComponent as b3, MediaTrimTimelineComponent as b4, MobileBottomBarComponent as b5, MobileMenuSheetComponent as b6, MobilePresenterViewComponent as b7, MobileSheetComponent as b8, MobileSlidesSheetComponent as b9, PrintSettingsPanelComponent as bA, PropertiesDialogComponent as bB, REPEAT_MODE_OPTIONS as bC, RESIZE_HANDLES as bD, RULER_FONT_SIZE as bE, RULER_THICKNESS as bF, ReadingViewOverlayComponent as bG, RemoteSelectionOverlayComponent as bH, RibbonAnimationGalleryComponent as bI, RibbonAnimationsSectionComponent as bJ, RibbonArrangeSectionComponent as bK, RibbonColorPopoverComponent as bL, RibbonComponent as bM, RibbonDesignSectionComponent as bN, RibbonDrawSectionComponent as bO, RibbonDrawingGroupComponent as bP, RibbonEditingSectionComponent as bQ, RibbonFileSectionComponent as bR, RibbonFontControlsComponent as bS, RibbonHomeSectionComponent as bT, RibbonHyperlinkButtonComponent as bU, RibbonInsertFieldsComponent as bV, RibbonInsertSectionComponent as bW, RibbonParagraphControlsComponent as bX, RibbonPrimaryRowComponent as bY, RibbonReviewSectionComponent as bZ, RibbonShapeExtrasComponent as b_, MobileToolbarComponent as ba, ModalDialogComponent as bb, Model3DRendererComponent as bc, NotesHandoutCardComponent as bd, NotesPanelComponent as be, NotesToolbarComponent as bf, OleRendererComponent as bg, POWER_POINT_VIEWER_PROVIDERS as bh, PRESENTER_CHANNEL_NAME as bi, PRESENTER_MSG_ORIGIN as bj, PX_PER_CM as bk, PX_PER_INCH as bl, PasswordProtectionDialogComponent as bm, PasswordStrengthMeterComponent as bn, PowerPointViewerComponent as bo, PresentationAnnotationOverlayComponent as bp, PresentationAnnotationsService as bq, PresentationOverlayComponent as br, PresentationPropertiesPanelComponent as bs, PresentationSettingsCardComponent as bt, PresentationSubtitleBarComponent as bu, PresentationTransitionOverlayComponent as bv, PresenterViewComponent as bw, PresenterWindowService as bx, PrintDialogComponent as by, PrintService as bz, AUDIENCE_NONCE_KEY as c, TextAdvancedPanelComponent as c$, RibbonTransitionsSectionComponent as c0, RibbonViewSectionComponent as c1, RulerGuidesService as c2, SEQUENCE_OPTIONS as c3, SEVERITY_GROUPS as c4, SEVERITY_LABELS as c5, SHORTCUT_REFERENCE_ITEMS as c6, SLIDE_TRANSITION_KEYFRAMES as c7, DEFAULT_PALETTE as c8, PALETTES$1 as c9, SlideThemeOverridePanelComponent as cA, SlideTransitionCardComponent as cB, SlidesPanelComponent as cC, SmartArt3DRendererComponent as cD, SmartArt3DService as cE, SmartArtPreviewComponent as cF, SmartArtPropertiesComponent as cG, SmartArtRendererComponent as cH, StatusBarComponent as cI, TABLE_STRUCTURE_TOGGLES as cJ, TEXT_3D_BOTTOM_BEVEL_KEYS as cK, TEXT_3D_TOP_BEVEL_KEYS as cL, TEXT_DIRECTION_OPTIONS$1 as cM, THEME_CATALOG as cN, TIMING_CURVE_OPTIONS as cO, TRIGGER_OPTIONS as cP, TYPE_LABELS as cQ, TableCellAdvancedFillComponent as cR, TableCellFormattingComponent as cS, TableDataEditorComponent as cT, TablePropertiesComponent as cU, TableRendererComponent as cV, TableResizeOverlayComponent as cW, TableSelectionService as cX, TagsCardComponent as cY, Text3DBevelSectionComponent as cZ, Text3DPanelComponent as c_, SMART_ART_COLOR_SCHEMES as ca, SMART_ART_STYLE_OPTIONS as cb, SUB_ITEM_LABEL as cc, SVG_WARP_PRESETS as cd, SWIPE_MAX_VERTICAL_PX as ce, SWIPE_THRESHOLD_PX as cf, SelectionPaneComponent as cg, SetUpSlideShowDialogComponent as ch, SettingsAppearanceTabComponent as ci, SettingsDialogComponent as cj, SettingsLanguageTabComponent as ck, ShareDialogComponent as cl, ShortcutPanelComponent as cm, ShowOptionsFieldsetComponent as cn, ShowSlidesFieldsetComponent as co, SignatureStrippedDialogComponent as cp, SignaturesPanelComponent as cq, SignaturesService as cr, SlideBackgroundCardComponent as cs, SlideCanvasComponent as ct, SlideDefaultInspectorComponent as cu, SlideDiffChangesComponent as cv, SlideDiffRowComponent as cw, SlideDiffThumbnailsComponent as cx, SlideSizeCardComponent as cy, SlideSorterOverlayComponent as cz, AVATAR_COLOR_SWATCHES as d, buildBroadcastViewerUrl as d$, ThemeEditorFieldsComponent as d0, ThemeGalleryComponent as d1, ThemeSelectorCardComponent as d2, TitleBarComponent as d3, TransitionDirectionPickerComponent as d4, TransitionPreviewComponent as d5, VALIGN_OPTIONS as d6, VIEWER_THEME as d7, VersionHistoryPanelComponent as d8, ViewerCanvasEditingService as d9, addGradientStopPatch as dA, addItem as dB, addSeries as dC, addSubItem as dD, advanceStep as dE, aiToggleVisible as dF, alignPatch as dG, animationFor as dH, animationPresetLabelKey as dI, annotationMapToInkInserts as dJ, applyAcceptedDiff as dK, applyAnimationPreset as dL, applyFindReplacements as dM, applyFormatToElement as dN, applyMove as dO, applyResize as dP, applyTableStylePreset as dQ, asMediaElement as dR, assignUserColor as dS, attachTouchGestures as dT, beginNodeEdit as dU, bevelSizePatch as dV, boolFromEvent as dW, bringForward as dX, bringToFront as dY, buildBarActions as dZ, buildBroadcastConfig as d_, ViewerCollabCursorService as da, ViewerCollaborationSessionService as db, ViewerCompareService as dc, ViewerCustomShowsService as dd, ViewerDialogsService as de, ViewerDocumentPropertiesService as df, ViewerExportService as dg, ViewerExtraDialogsComponent as dh, ViewerFileIOService as di, ViewerFindReplaceService as dj, ViewerFormatPainterService as dk, ViewerInspectorPanelService as dl, ViewerKeyboardService as dm, ViewerMobileSheetService as dn, ViewerPresentationModeService as dp, ViewerThemeGalleryService as dq, ViewerTouchGesturesService as dr, ViewerZoomService as ds, WEBM_MIME_CANDIDATES as dt, WriteBackScheduler as du, ZoomNavigationService as dv, ZoomRendererComponent as dw, ZoomTargetService as dx, addCategory as dy, addCommentToList as dz, AccessibilityPanelComponent as e, clampCursorPosition as e$, buildCategoryLabels as e0, buildCellParagraphs as e1, buildChartViewModel as e2, buildChatLogExport as e3, buildChatLogMarkdown as e4, buildChromeStyle as e5, buildClearHyperlinkPatch as e6, buildClickGroups as e7, buildColStyles as e8, buildCollaborationConfig as e9, buildSmartArtNodes as eA, buildStockViewModel as eB, buildSurfaceViewModel as eC, buildTableViewModel as eD, buildTreemapViewModel as eE, buildTrimFragment as eF, buildWaterfallViewModel as eG, buildZeroLine as eH, buildZoomContainerStyle as eI, buildZoomViewModel as eJ, bulletIndentPx as eK, canAddTopLevelNode as eL, canGroupSelection as eM, canRemoveTopLevelNode as eN, canSetStrokeWidth as eO, canStartBroadcast as eP, canStartShare as eQ, canUngroupSelection as eR, canUseClipboard as eS, captionDisplayText as eT, cellRunStyle as eU, cellStyleToStyleMap as eV, cellTdStyle as eW, changeCountLabel as eX, changeIcon as eY, characterSpacingPatch as eZ, checkFontAvailable as e_, buildComboViewModel as ea, buildCssGradientFromShapeStyle as eb, buildDuotoneFilter as ec, buildDuotoneFilterId as ed, buildEmbeddedFontStyles as ee, buildEquationElement as ef, buildEquationSegment as eg, buildFallbackViewModel as eh, buildFontFaceRule as ei, buildGradientFillCss as ej, buildGridlinesAndLabels as ek, buildHyperlinkPatch as el, buildInkContainerStyle as em, buildInkStrokes as en, buildLegend as eo, buildModel3DContainerStyle as ep, buildModel3DViewModel as eq, buildOleActionModel as er, buildOleInfoRows as es, buildPatternFillCss as et, buildPrintHtmlDocument as eu, buildPropertiesPatch as ev, buildRegionMapViewModel as ew, buildSaveSlides as ex, buildShareUrl as ey, buildSmartArtInsertElement as ez, AccessibilityService as f, defaultRadius as f$, clampGifDimensions as f0, clampIndex as f1, clampNotesFontSize as f2, clampScale as f3, clampStep as f4, clearAllLocalViewerData as f5, clearAudienceContent as f6, cn as f7, collectAccessibilityIssues as f8, collectElementText as f9, computeRSquared as fA, computeRadarPoints as fB, computeScatterDots as fC, computeSelectionBoxes as fD, computeSingleSelected as fE, computeSlideIndices as fF, computeSnap as fG, computeStackedBarRects as fH, computeStackedValueRange as fI, computeTextLines as fJ, computeTimerProgress as fK, computeTrendlinePrimitives as fL, computeValueRange as fM, convertOmmlToMathMl as fN, copyFormatFromElement as fO, countAccessibilityIssues as fP, countAnnotationStrokes as fQ, createAngularAiBridge as fR, createCustomShow as fS, createSwipeDismissDrag as fT, createWebrtcBundle as fU, createWebsocketBundle as fV, cssObjectToStyleMap as fW, currentColorScheme as fX, currentLayout as fY, currentStyle as fZ, defaultCssVars as f_, collectSlideText as fa, collectStoredChats as fb, collectUsedFontFamilies as fc, columnWidthStyle as fd, commitNodeText as fe, computeAlign as ff, computeAxisTitlePrimitives as fg, computeBarRects as fh, computeBubbleRadius as fi, computeCornerHandle as fj, computeDataTablePrimitives as fk, computeDistribute as fl, computeDrawingViewBox as fm, computeErrorBarPrimitives as fn, computeFocusTargets as fo, computeHandleBoxes as fp, computeHandoutLayout as fq, computeIsMobile as fr, computeIsTablet as fs, computeLinePoints as ft, computeLinearRegression as fu, computePageCount as fv, computePieLayout as fw, computePieSlicePath as fx, computePieSlices as fy, computePlotLayout as fz, AccountPageComponent as g, getPatternSvg as g$, defaultThemeColors as g0, deleteElementsByIds as g1, deleteVersion as g2, demoteNode as g3, deriveModel3DBlobUrl as g4, derivePresenceList as g5, describeSmartArtBounds as g6, disableGlowPatch as g7, disableInnerShadowPatch as g8, disableOuterShadowPatch as g9, fontSizeOf as gA, formatAutoNumber as gB, formatAxisValue as gC, formatBytes as gD, formatCursorLabel as gE, formatElapsed as gF, formatFileSize as gG, formatPropertyDate as gH, formatTime as gI, fpsToFrameIntervalMs as gJ, generateBroadcastRoomId as gK, generateCommentId as gL, generateCustomShowId as gM, generatePressureCircles as gN, generateTicks as gO, getClrChangeParams as gP, getContainerStyle as gQ, getDuotoneFilterDef as gR, getImageSrc as gS, getLocalStorageUsageSummary as gT, getOleAriaLabel as gU, getOleBadgeLabel as gV, getOleDisplayName as gW, getOleDownloadFileName as gX, getOleTypeColor as gY, getOleTypeLabel as gZ, getPasswordStrength as g_, disableReflectionPatch as ga, disableSoftEdgePatch as gb, duplicateElementById as gc, durationOf as gd, effectsStateOf as ge, enableGlowPatch as gf, enableInnerShadowPatch as gg, enableOuterShadowPatch as gh, enableReflectionPatch as gi, enableSoftEdgePatch as gj, encodeGif as gk, estimatePageCount as gl, evenColumnWidths as gm, evenRowHeights as gn, exitPresentationFullscreen as go, exportAiChatLogs as gp, extractPathPoints as gq, eyedropperAvailable as gr, fillColorOf as gs, findInSlides as gt, findOwningSlideIndex as gu, findSlideIndexByElementId as gv, fitPolynomial as gw, fitZoom as gx, focusTargetChips as gy, fontMimeForFormat as gz, ActionSettingsPanelComponent as h, moveNodeUp as h$, getPlaceholderStyle as h0, getVersions as h1, getResolvedShapeClipPath as h2, getResolvedShapeClipPathFor as h3, getShapeFillStrokeStyle as h4, getSlideBackgroundStyle as h5, getSlideTransitionAnimations as h6, getSmartArtNodeBounds as h7, getSpeechRecognitionCtor as h8, getTextBlockStyle as h9, isChildNode as hA, isElementInteractive as hB, isInjectableUrl as hC, isItalic as hD, isPpactionUrl as hE, isPresenterMessage as hF, isSigned as hG, isTextElement as hH, isTwoTableFocus as hI, isUnderline as hJ, isUrlSafe as hK, isValidRoomId as hL, isViewportBackgroundPressTarget as hM, isZoomActivationKey as hN, issueTrackKey as hO, issueTypeLabel as hP, keyToLabel as hQ, latexToMathml as hR, linePointsToSvgString as hS, lineSpacingPatch as hT, loadAudienceContent as hU, mergeCaptionResults as hV, mergeDown as hW, mergeRight as hX, mergeSelection as hY, moveElementBy as hZ, moveNodeDown as h_, getTextWarp as ha, getTouchDistance as hb, getWarpCategory as hc, getWarpPath as hd, gradientStateFromStyle as he, gradientStateOf as hf, gradientStatePatch as hg, gridColumns as hh, groupElements as hi, groupIssuesBySeverity as hj, hasAnimation as hk, hasCopyableFormat as hl, hasExistingLink as hm, hasExitedFullscreen as hn, hasGradientFill as ho, hasPressureVariation as hp, hasVisibleSlideAfter as hq, headerLabel as hr, imageDimensions as hs, inkViewBox as ht, insertTableElementColumn as hu, insertTableElementRow as hv, interpolateWidth as hw, isAudienceTab as hx, isBold as hy, isBrowserOpenableMime as hz, AdvancedChartEditorComponent as i, removeNode as i$, msToFrameDelayCs as i0, narrowToCircle as i1, narrowToPolygon as i2, narrowToRect as i3, newChartElement as i4, newEquationElement as i5, newPresetShapeElement as i6, newShapeElement as i7, newSmartArtElement as i8, newTableElement as i9, patchTextStyle as iA, pendingElementStyles as iB, pickColorByClickFallback as iC, pickFile as iD, pickSupportedMimeType as iE, planGifFrames as iF, planVideoSegments as iG, pointsToSvgPathD as iH, presenceToCursors as iI, presetByLayout as iJ, presetsForCategory as iK, pressuresToWidths as iL, prevVisibleIndex as iM, projectDrawingShapes as iN, promoteNode as iO, provideViewerTheme as iP, radarAngle as iQ, radarRingPoints as iR, readAsDataUrl as iS, recordWebm as iT, redistributeColumnWidth as iU, removeAnimation as iV, removeCategory as iW, removeTableElementColumn as iX, removeCommentFromList as iY, removeElementAnimation as iZ, removeGradientStopPatch as i_, newTextElement as ia, nextVisibleIndex as ib, nodeBold as ic, nodeEditBox as id, nodeFillColor as ie, nodeFontColor as ig, nodeIdFromKey as ih, nodeItalic as ii, nodeStyle as ij, normalizeFontFormat as ik, normalizeSlidesPerPage as il, normalizeValue as im, numFromEvent as io, ommlToMathml as ip, ooxmlDashToCssBorderStyle as iq, openNativeEyeDropper as ir, overallStatus as is, paletteColor as it, parseAudienceNonce as iu, parseNodeTextarea as iv, partitionSlides as iw, patchChartData as ix, patchChartStyle as iy, patchTableData as iz, AiChangeOverlayComponent as j, setDuration as j$, removeTableElementRow as j0, removeSeries as j1, renderToCanvas as j2, reorderAnimationDown as j3, reorderAnimationUp as j4, replaceInSlides as j5, replaceMatch as j6, requestPresentationFullscreen as j7, resizeElement as j8, resolveCaptionTracks as j9, searchSlides as jA, seedBroadcastFields as jB, seedHyperlinkDraft as jC, seedPropertiesDraft as jD, seedShareFields as jE, segmentFrameCount as jF, selectValue$2 as jG, sendBackward as jH, sendToBack as jI, sequentialColorScale as jJ, serializeWriteBack as jK, seriesColor as jL, setAnimationEmphasis as jM, setAnimationEntrance as jN, setAnimationExit as jO, setAxis as jP, setAxisLogScale as jQ, setAxisTitleStyle as jR, setCategoryLabel as jS, setCellText as jT, setColorScheme as jU, setDataLabels as jV, setDataPointExplosion as jW, setDataPointFill as jX, setDataPointLabel as jY, setDelay as jZ, setDirection as j_, resolveChartKind as ja, resolveFontVariant as jb, resolveHyperlinkHref as jc, resolveInteractiveElementId as jd, resolveMediaSrc as je, resolveOleType as jf, resolveParagraphBullet as jg, resolvePresenterNotes as jh, resolveProfileInitial as ji, resolveRegionCode as jj, resolveSlideAutoAdvanceMs as jk, resolvePalette as jl, resolveThemeCatalogEntry as jm, resolveTransitionDuration as jn, revealedElementStyles as jo, routeOrthogonalConnector as jp, rowStyle as jq, rulerDragToGuidePosition as jr, rulerHighlight as js, rulerStripTicks as jt, sampleColorFromSlide as ju, sanitizeColor as jv, sanitizeSlideIndex as jw, sanitizeUserName as jx, saveViewerProfile as jy, scanAvailableFonts as jz, AiChatPanelComponent as k, translationsEn as k$, setElementPosition as k0, setGridlineStyle as k1, setLayout as k2, setLegend as k3, setNodeStyle as k4, setNodeText as k5, setRepeatCount as k6, setRepeatMode as k7, setSequence as k8, setSeriesChartType as k9, splitCursorCell as kA, splitMergedCell as kB, statusKind as kC, statusLabel$1 as kD, storeAudienceContent as kE, stringFromEvent$5 as kF, strokeColorOf as kG, strokeToInkElement as kH, strokeWidthOf as kI, styleShadowFilter as kJ, textAdvancedPatch as kK, textAdvancedStateFromStyle as kL, textAdvancedStateOf as kM, textColorOf as kN, textDirectionPatch as kO, textStyleOf as kP, textStylePatch as kQ, themeStyle as kR, themeToCssVars as kS, thumbnailHeight as kT, thumbnailZoom as kU, toggleCommentResolvedInList as kV, toggleNodeBold as kW, toggleNodeItalic as kX, toggleSheet as kY, topLevelNodeCount as kZ, transformSelectedTextCase as k_, setSeriesColor as ka, setSeriesErrorBars as kb, setSeriesMarker as kc, setSeriesName as kd, setSeriesTrendline as ke, setSeriesValue as kf, setStyle as kg, setTimingCurve as kh, setTitle as ki, setTrigger as kj, setTriggerShapeId as kk, shapeStylePatch as kl, sheetAfterNavigate as km, shouldBlockClickAdvance as kn, shouldUseSvgWarp as ko, showDirectionPicker as kp, showsTemplateAffordance as kq, signatureCountLabel as kr, signatureKey as ks, signatureTimestamp as kt, signerName as ku, statusLabel as kv, slideNumberOf as kw, smartArtNodes as kx, paletteColour as ky, snapToGridStep as kz, AiChatService as l, ungroupElements as l0, updateElementById as l1, updateGlowPatch as l2, updateGradientStopPatch as l3, updateInnerShadowPatch as l4, updateOuterShadowPatch as l5, updateReflectionPatch as l6, vAlignPatch as l7, validatePassword as l8, validatePrintSettings as l9, validateRoomId as la, valueToY as lb, vermilionDarkColors as lc, vermilionDarkTheme as ld, vermilionLightColors as le, vermilionLightTheme as lf, vermilionRadius as lg, waypointsToPathD as lh, worstStatus as li, zoomTargetSlideIndex as lj, AiComposerComponent as m, AiFocusBarComponent as n, AiFocusHighlightOverlayComponent as o, AiMessageListComponent as p, AiPanelStore as q, AiProposalCardComponent as r, AiSettingsSectionComponent as s, toChatSummary as t, AiToolCallCardComponent as u, AnimationAuthorPanelComponent as v, AnimationPanelComponent as w, AnimationPlaybackService as x, AutosaveService as y, CURSOR_PALETTE as z };
121104
- //# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-C20L99Zu.mjs.map
123694
+ export { CustomShowsComponent as $, ALIGN_OPTIONS as A, BroadcastDialogComponent as B, CHART_EDITOR_STYLES as C, CanvasFitService as D, ChartAxisOptionsComponent as E, ChartAxisStyleOptionsComponent as F, ChartComboTypeOptionsComponent as G, ChartDataEditorComponent as H, ChartDataLabelOptionsComponent as I, ChartDatapointMarkerOptionsComponent as J, ChartDatapointOptionsComponent as K, ChartDisplayOptionsComponent as L, ChartElementViewComponent as M, ChartErrorBarOptionsComponent as N, ChartMarkerOptionsComponent as O, ChartPartSelectionService as P, ChartPrimitivesComponent as Q, ChartRendererComponent as R, ChartTrendlineOptionsComponent as S, CollaborationCursorsComponent as T, CollaborationService as U, ColorChangedImageComponent as V, CommentsPanelComponent as W, CommentsService as X, ComparePanelComponent as Y, ConnectorRendererComponent as Z, ConnectorTextOverlayComponent as _, ANIMATION_PRESET_CATEGORIES as a, LocalPresencePublisher as a$, DATA_TABLE_HEADER_H as a0, DATA_TABLE_KEY_W as a1, DATA_TABLE_PADDING as a2, DATA_TABLE_ROW_H as a3, DEFAULT_BOUNDS as a4, DEFAULT_BROADCAST_SERVER_URL as a5, DEFAULT_CANVAS_HEIGHT as a6, DEFAULT_CANVAS_WIDTH as a7, DEFAULT_COLOR_SCHEME as a8, DEFAULT_FILL_COLOR as a9, EquationRendererComponent as aA, EquationTemplateGalleryComponent as aB, ExportProgressModalComponent as aC, ExportService as aD, FieldContextService as aE, FindBarComponent as aF, FindReplaceBarComponent as aG, FollowModeBarComponent as aH, FontEmbeddingListComponent as aI, FontEmbeddingPanelComponent as aJ, GALLERY_THEME_PRESETS as aK, GradientPickerComponent as aL, HANDOUT_OPTIONS as aM, HeaderFooterDialogComponent as aN, HyperlinkDialogComponent as aO, ImagePropertiesPanelComponent as aP, InkDrawingService as aQ, InkRendererComponent as aR, InsertSmartArtDialogComponent as aS, InspectorPaneHeaderComponent as aT, InspectorPanelComponent as aU, IsMobileService as aV, KeepAnnotationsDialogComponent as aW, LOCALE_CATALOG as aX, LONG_PRESS_DURATION_MS as aY, LONG_PRESS_MOVE_TOLERANCE_PX as aZ, LoadContentService as a_, DEFAULT_LAYOUT as aa, DEFAULT_PALETTE$1 as ab, DEFAULT_PRINT_SETTINGS as ac, DEFAULT_SLIDE_BACKGROUND as ad, DEFAULT_STROKE_COLOR as ae, DEFAULT_STYLE as af, DEFAULT_TABLE_ROW_HEIGHT as ag, DEFAULT_TEXT_COLOR$1 as ah, DEFAULT_VIEWER_PROFILE as ai, DIRECTIONAL_PRESETS as aj, DIRECTION_OPTIONS as ak, DocumentPropertiesCardComponent as al, EMBEDDED_FONTS_STYLE_ID as am, EMPHASIS_PRESETS as an, ENTRANCE_PRESETS as ao, TEMPLATES as ap, EXIT_PRESETS as aq, EditorContextMenuComponent as ar, EditorHistory as as, EditorStateService as at, EditorToolbarComponent as au, EffectsPanelComponent as av, ElementRendererComponent as aw, EmbeddedFontsService as ax, EncryptedFileDialogComponent as ay, EquationEditorDialogComponent as az, AUDIENCE_HASH as b, RibbonParagraphControlsComponent as b$, MAX_ZOOM_SCALE as b0, MIN_ZOOM_SCALE as b1, MOTION_PATH_COLUMNS as b2, MediaPreviewComponent as b3, MediaPropertiesPanelComponent as b4, MediaRendererComponent as b5, MediaTrimTimelineComponent as b6, MobileBottomBarComponent as b7, MobileMenuSheetComponent as b8, MobilePresenterViewComponent as b9, PresenterWindowService as bA, PrintDialogComponent as bB, PrintService as bC, PrintSettingsPanelComponent as bD, PropertiesDialogComponent as bE, REPEAT_MODE_OPTIONS as bF, RESIZE_HANDLES as bG, RULER_FONT_SIZE as bH, RULER_THICKNESS as bI, ReadingViewOverlayComponent as bJ, RemoteSelectionOverlayComponent as bK, RibbonAnimationGalleryComponent as bL, RibbonAnimationsSectionComponent as bM, RibbonArrangeSectionComponent as bN, RibbonColorPopoverComponent as bO, RibbonComponent as bP, RibbonDesignSectionComponent as bQ, RibbonDrawSectionComponent as bR, RibbonDrawingGroupComponent as bS, RibbonEditingSectionComponent as bT, RibbonFileSectionComponent as bU, RibbonFontControlsComponent as bV, RibbonHomeSectionComponent as bW, RibbonHyperlinkButtonComponent as bX, RibbonInsertFieldsComponent as bY, RibbonInsertSectionComponent as bZ, RibbonMotionPathGalleryComponent as b_, MobileSheetComponent as ba, MobileSlidesSheetComponent as bb, MobileToolbarComponent as bc, ModalDialogComponent as bd, Model3DRendererComponent as be, NotesHandoutCardComponent as bf, NotesPanelComponent as bg, NotesToolbarComponent as bh, OleRendererComponent as bi, OutlineViewOverlayComponent as bj, POWER_POINT_VIEWER_PROVIDERS as bk, PRESENTER_CHANNEL_NAME as bl, PRESENTER_MSG_ORIGIN as bm, PX_PER_CM as bn, PX_PER_INCH as bo, PasswordProtectionDialogComponent as bp, PasswordStrengthMeterComponent as bq, PowerPointViewerComponent as br, PresentationAnnotationOverlayComponent as bs, PresentationAnnotationsService as bt, PresentationOverlayComponent as bu, PresentationPropertiesPanelComponent as bv, PresentationSettingsCardComponent as bw, PresentationSubtitleBarComponent as bx, PresentationTransitionOverlayComponent as by, PresenterViewComponent as bz, AUDIENCE_NONCE_KEY as c, TableSelectionService as c$, RibbonPrimaryRowComponent as c0, RibbonReviewSectionComponent as c1, RibbonShapeExtrasComponent as c2, RibbonSlideshowSectionComponent as c3, RibbonTransitionsSectionComponent as c4, RibbonViewSectionComponent as c5, RulerGuidesService as c6, SEQUENCE_OPTIONS as c7, SEVERITY_GROUPS as c8, SEVERITY_LABELS as c9, SlideDiffRowComponent as cA, SlideDiffThumbnailsComponent as cB, SlideSizeCardComponent as cC, SlideSorterOverlayComponent as cD, SlideThemeOverridePanelComponent as cE, SlideTransitionCardComponent as cF, SlidesPanelComponent as cG, SmartArt3DRendererComponent as cH, SmartArt3DService as cI, SmartArtPreviewComponent as cJ, SmartArtPropertiesComponent as cK, SmartArtRendererComponent as cL, StatusBarComponent as cM, TABLE_STRUCTURE_TOGGLES as cN, TEXT_3D_BOTTOM_BEVEL_KEYS as cO, TEXT_3D_TOP_BEVEL_KEYS as cP, TEXT_DIRECTION_OPTIONS$1 as cQ, THEME_CATALOG as cR, TIMING_CURVE_OPTIONS as cS, TRIGGER_OPTIONS as cT, TYPE_LABELS as cU, TableCellAdvancedFillComponent as cV, TableCellFormattingComponent as cW, TableDataEditorComponent as cX, TablePropertiesComponent as cY, TableRendererComponent as cZ, TableResizeOverlayComponent as c_, SHORTCUT_REFERENCE_ITEMS as ca, SLIDE_TRANSITION_KEYFRAMES as cb, DEFAULT_PALETTE as cc, PALETTES$1 as cd, SMART_ART_COLOR_SCHEMES as ce, SMART_ART_STYLE_OPTIONS as cf, SUB_ITEM_LABEL as cg, SVG_WARP_PRESETS as ch, SWIPE_MAX_VERTICAL_PX as ci, SWIPE_THRESHOLD_PX as cj, SelectionPaneComponent as ck, SetUpSlideShowDialogComponent as cl, SettingsAppearanceTabComponent as cm, SettingsDialogComponent as cn, SettingsLanguageTabComponent as co, ShareDialogComponent as cp, ShortcutPanelComponent as cq, ShowOptionsFieldsetComponent as cr, ShowSlidesFieldsetComponent as cs, SignatureStrippedDialogComponent as ct, SignaturesPanelComponent as cu, SignaturesService as cv, SlideBackgroundCardComponent as cw, SlideCanvasComponent as cx, SlideDefaultInspectorComponent as cy, SlideDiffChangesComponent as cz, AVATAR_COLOR_SWATCHES as d, bringForward as d$, TagsCardComponent as d0, Text3DBevelSectionComponent as d1, Text3DPanelComponent as d2, TextAdvancedPanelComponent as d3, ThemeEditorFieldsComponent as d4, ThemeGalleryComponent as d5, ThemeSelectorCardComponent as d6, TitleBarComponent as d7, TransitionDirectionPickerComponent as d8, TransitionPreviewComponent as d9, ZoomRendererComponent as dA, ZoomTargetService as dB, addCategory as dC, addCommentToList as dD, addGradientStopPatch as dE, addItem as dF, addSeries as dG, addSubItem as dH, advanceStep as dI, aiToggleVisible as dJ, alignPatch as dK, animationFor as dL, animationPresetLabelKey as dM, annotationMapToInkInserts as dN, applyAcceptedDiff as dO, applyAnimationPreset as dP, applyFindReplacements as dQ, applyFormatToElement as dR, applyMove as dS, applyResize as dT, applyTableStylePreset as dU, asMediaElement as dV, assignUserColor as dW, attachTouchGestures as dX, beginNodeEdit as dY, bevelSizePatch as dZ, boolFromEvent as d_, VALIGN_OPTIONS as da, VIEWER_THEME as db, VersionHistoryPanelComponent as dc, ViewerCanvasEditingService as dd, ViewerCollabCursorService as de, ViewerCollaborationSessionService as df, ViewerCompareService as dg, ViewerCustomShowsService as dh, ViewerDialogsService as di, ViewerDocumentPropertiesService as dj, ViewerExportService as dk, ViewerExtraDialogsComponent as dl, ViewerFileIOService as dm, ViewerFindReplaceService as dn, ViewerFormatPainterService as dp, ViewerInspectorPanelService as dq, ViewerKeyboardService as dr, ViewerMobileSheetService as ds, ViewerPresentationModeService as dt, ViewerThemeGalleryService as du, ViewerTouchGesturesService as dv, ViewerZoomService as dw, WEBM_MIME_CANDIDATES as dx, WriteBackScheduler as dy, ZoomNavigationService as dz, AccessibilityPanelComponent as e, changeCountLabel as e$, bringToFront as e0, buildBarActions as e1, buildBroadcastConfig as e2, buildBroadcastViewerUrl as e3, buildCategoryLabels as e4, buildCellParagraphs as e5, buildChartViewModel as e6, buildChatLogExport as e7, buildChatLogMarkdown as e8, buildChromeStyle as e9, buildRegionMapViewModel as eA, buildSaveSlides as eB, buildShareUrl as eC, buildSmartArtInsertElement as eD, buildSmartArtNodes as eE, buildStockViewModel as eF, buildSurfaceViewModel as eG, buildTableViewModel as eH, buildTreemapViewModel as eI, buildTrimFragment as eJ, buildWaterfallViewModel as eK, buildZeroLine as eL, buildZoomContainerStyle as eM, buildZoomViewModel as eN, bulletIndentPx as eO, canAddTopLevelNode as eP, canGroupSelection as eQ, canRemoveTopLevelNode as eR, canSetStrokeWidth as eS, canStartBroadcast as eT, canStartShare as eU, canUngroupSelection as eV, canUseClipboard as eW, captionDisplayText as eX, cellRunStyle as eY, cellStyleToStyleMap as eZ, cellTdStyle as e_, buildClearHyperlinkPatch as ea, buildClickGroups as eb, buildColStyles as ec, buildCollaborationConfig as ed, buildComboViewModel as ee, buildCssGradientFromShapeStyle as ef, buildDuotoneFilter as eg, buildDuotoneFilterId as eh, buildEmbeddedFontStyles as ei, buildEquationElement as ej, buildEquationSegment as ek, buildFallbackViewModel as el, buildFontFaceRule as em, buildGradientFillCss as en, buildGridlinesAndLabels as eo, buildHyperlinkPatch as ep, buildInkContainerStyle as eq, buildInkStrokes as er, buildLegend as es, buildModel3DContainerStyle as et, buildModel3DViewModel as eu, buildOleActionModel as ev, buildOleInfoRows as ew, buildPatternFillCss as ex, buildPrintHtmlDocument as ey, buildPropertiesPatch as ez, AccessibilityService as f, currentColorScheme as f$, changeIcon as f0, characterSpacingPatch as f1, checkFontAvailable as f2, clampCursorPosition as f3, clampGifDimensions as f4, clampIndex as f5, clampNotesFontSize as f6, clampScale as f7, clampStep as f8, clearAllLocalViewerData as f9, computePieLayout as fA, computePieSlicePath as fB, computePieSlices as fC, computePlotLayout as fD, computeRSquared as fE, computeRadarPoints as fF, computeScatterDots as fG, computeSelectionBoxes as fH, computeSingleSelected as fI, computeSlideIndices as fJ, computeSnap as fK, computeStackedBarRects as fL, computeStackedValueRange as fM, computeTextLines as fN, computeTimerProgress as fO, computeTrendlinePrimitives as fP, computeValueRange as fQ, convertOmmlToMathMl as fR, copyFormatFromElement as fS, countAccessibilityIssues as fT, countAnnotationStrokes as fU, createAngularAiBridge as fV, createCustomShow as fW, createSwipeDismissDrag as fX, createWebrtcBundle as fY, createWebsocketBundle as fZ, cssObjectToStyleMap as f_, clearAudienceContent as fa, cn as fb, collectAccessibilityIssues as fc, collectElementText as fd, collectSlideText as fe, collectStoredChats as ff, collectUsedFontFamilies as fg, columnWidthStyle as fh, commitNodeText as fi, computeAlign as fj, computeAxisTitlePrimitives as fk, computeBarRects as fl, computeBubbleRadius as fm, computeCornerHandle as fn, computeDataTablePrimitives as fo, computeDistribute as fp, computeDrawingViewBox as fq, computeErrorBarPrimitives as fr, computeFocusTargets as fs, computeHandleBoxes as ft, computeHandoutLayout as fu, computeIsMobile as fv, computeIsTablet as fw, computeLinePoints as fx, computeLinearRegression as fy, computePageCount as fz, AccountPageComponent as g, getOleDownloadFileName as g$, currentLayout as g0, currentStyle as g1, defaultCssVars as g2, defaultRadius as g3, defaultThemeColors as g4, deleteElementsByIds as g5, deleteVersion as g6, demoteNode as g7, deriveModel3DBlobUrl as g8, derivePresenceList as g9, fitPolynomial as gA, fitZoom as gB, focusTargetChips as gC, fontMimeForFormat as gD, fontSizeOf as gE, formatAutoNumber as gF, formatAxisValue as gG, formatBytes as gH, formatCursorLabel as gI, formatElapsed as gJ, formatFileSize as gK, formatPropertyDate as gL, formatTime as gM, fpsToFrameIntervalMs as gN, generateBroadcastRoomId as gO, generateCommentId as gP, generateCustomShowId as gQ, generatePressureCircles as gR, generateTicks as gS, getClrChangeParams as gT, getContainerStyle as gU, getDuotoneFilterDef as gV, getImageSrc as gW, getLocalStorageUsageSummary as gX, getOleAriaLabel as gY, getOleBadgeLabel as gZ, getOleDisplayName as g_, describeSmartArtBounds as ga, disableGlowPatch as gb, disableInnerShadowPatch as gc, disableOuterShadowPatch as gd, disableReflectionPatch as ge, disableSoftEdgePatch as gf, duplicateElementById as gg, durationOf as gh, effectsStateOf as gi, enableGlowPatch as gj, enableInnerShadowPatch as gk, enableOuterShadowPatch as gl, enableReflectionPatch as gm, enableSoftEdgePatch as gn, encodeGif as go, estimatePageCount as gp, evenColumnWidths as gq, evenRowHeights as gr, exitPresentationFullscreen as gs, exportAiChatLogs as gt, extractPathPoints as gu, eyedropperAvailable as gv, fillColorOf as gw, findInSlides as gx, findOwningSlideIndex as gy, findSlideIndexByElementId as gz, ActionSettingsPanelComponent as h, mergeRight as h$, getOleTypeColor as h0, getOleTypeLabel as h1, getPasswordStrength as h2, getPatternSvg as h3, getPlaceholderStyle as h4, getVersions as h5, getResolvedShapeClipPath as h6, getResolvedShapeClipPathFor as h7, getShapeFillStrokeStyle as h8, getSlideBackgroundStyle as h9, interpolateWidth as hA, isAudienceTab as hB, isBold as hC, isBrowserOpenableMime as hD, isChildNode as hE, isElementInteractive as hF, isInjectableUrl as hG, isItalic as hH, isPpactionUrl as hI, isPresenterMessage as hJ, isSigned as hK, isTextElement as hL, isTwoTableFocus as hM, isUnderline as hN, isUrlSafe as hO, isValidRoomId as hP, isViewportBackgroundPressTarget as hQ, isZoomActivationKey as hR, issueTrackKey as hS, issueTypeLabel as hT, keyToLabel as hU, latexToMathml as hV, linePointsToSvgString as hW, lineSpacingPatch as hX, loadAudienceContent as hY, mergeCaptionResults as hZ, mergeDown as h_, getSlideTransitionAnimations as ha, getSmartArtNodeBounds as hb, getSpeechRecognitionCtor as hc, getTextBlockStyle as hd, getTextWarp as he, getTouchDistance as hf, getWarpCategory as hg, getWarpPath as hh, gradientStateFromStyle as hi, gradientStateOf as hj, gradientStatePatch as hk, gridColumns as hl, groupElements as hm, groupIssuesBySeverity as hn, hasAnimation as ho, hasCopyableFormat as hp, hasExistingLink as hq, hasExitedFullscreen as hr, hasGradientFill as hs, hasPressureVariation as ht, hasVisibleSlideAfter as hu, headerLabel as hv, imageDimensions as hw, inkViewBox as hx, insertTableElementColumn as hy, insertTableElementRow as hz, AdvancedChartEditorComponent as i, removeTableElementColumn as i$, mergeSelection as i0, moveElementBy as i1, moveNodeDown as i2, moveNodeUp as i3, msToFrameDelayCs as i4, narrowToCircle as i5, narrowToPolygon as i6, narrowToRect as i7, newChartElement as i8, newEquationElement as i9, partitionSlides as iA, patchChartData as iB, patchChartStyle as iC, patchTableData as iD, patchTextStyle as iE, pendingElementStyles as iF, pickColorByClickFallback as iG, pickFile as iH, pickSupportedMimeType as iI, planGifFrames as iJ, planVideoSegments as iK, pointsToSvgPathD as iL, presenceToCursors as iM, presetByLayout as iN, presetsForCategory as iO, pressuresToWidths as iP, prevVisibleIndex as iQ, projectDrawingShapes as iR, promoteNode as iS, provideViewerTheme as iT, radarAngle as iU, radarRingPoints as iV, readAsDataUrl as iW, recordWebm as iX, redistributeColumnWidth as iY, removeAnimation as iZ, removeCategory as i_, newPresetShapeElement as ia, newShapeElement as ib, newSmartArtElement as ic, newTableElement as id, newTextElement as ie, nextVisibleIndex as ig, nodeBold as ih, nodeEditBox as ii, nodeFillColor as ij, nodeFontColor as ik, nodeIdFromKey as il, nodeItalic as im, nodeStyle as io, normalizeFontFormat as ip, normalizeSlidesPerPage as iq, normalizeValue as ir, numFromEvent as is, ommlToMathml as it, ooxmlDashToCssBorderStyle as iu, openNativeEyeDropper as iv, overallStatus as iw, paletteColor as ix, parseAudienceNonce as iy, parseNodeTextarea as iz, AiChangeOverlayComponent as j, setDataPointFill as j$, removeCommentFromList as j0, removeElementAnimation as j1, removeGradientStopPatch as j2, removeNode as j3, removeTableElementRow as j4, removeSeries as j5, renderToCanvas as j6, reorderAnimationDown as j7, reorderAnimationUp as j8, replaceInSlides as j9, sanitizeSlideIndex as jA, sanitizeUserName as jB, saveViewerProfile as jC, scanAvailableFonts as jD, searchSlides as jE, seedBroadcastFields as jF, seedHyperlinkDraft as jG, seedPropertiesDraft as jH, seedShareFields as jI, segmentFrameCount as jJ, selectValue$2 as jK, sendBackward as jL, sendToBack as jM, sequentialColorScale as jN, serializeWriteBack as jO, seriesColor as jP, setAnimationEmphasis as jQ, setAnimationEntrance as jR, setAnimationExit as jS, setAxis as jT, setAxisLogScale as jU, setAxisTitleStyle as jV, setCategoryLabel as jW, setCellText as jX, setColorScheme as jY, setDataLabels as jZ, setDataPointExplosion as j_, replaceMatch as ja, requestPresentationFullscreen as jb, resizeElement as jc, resolveCaptionTracks as jd, resolveChartKind as je, resolveFontVariant as jf, resolveHyperlinkHref as jg, resolveInteractiveElementId as jh, resolveMediaSrc as ji, resolveOleType as jj, resolveParagraphBullet as jk, resolvePresenterNotes as jl, resolveProfileInitial as jm, resolveRegionCode as jn, resolveSlideAutoAdvanceMs as jo, resolvePalette as jp, resolveThemeCatalogEntry as jq, resolveTransitionDuration as jr, revealedElementStyles as js, routeOrthogonalConnector as jt, rowStyle as ju, rulerDragToGuidePosition as jv, rulerHighlight as jw, rulerStripTicks as jx, sampleColorFromSlide as jy, sanitizeColor as jz, AiChatPanelComponent as k, toggleNodeBold as k$, setDataPointLabel as k0, setDataPointMarker as k1, setDelay as k2, setDirection as k3, setDuration as k4, setElementPosition as k5, setGridlineStyle as k6, setLayout as k7, setLegend as k8, setNodeStyle as k9, statusLabel as kA, slideNumberOf as kB, smartArtNodes as kC, paletteColour as kD, snapToGridStep as kE, splitCursorCell as kF, splitMergedCell as kG, statusKind as kH, statusLabel$1 as kI, storeAudienceContent as kJ, stringFromEvent$5 as kK, strokeColorOf as kL, strokeToInkElement as kM, strokeWidthOf as kN, styleShadowFilter as kO, textAdvancedPatch as kP, textAdvancedStateFromStyle as kQ, textAdvancedStateOf as kR, textColorOf as kS, textDirectionPatch as kT, textStyleOf as kU, textStylePatch as kV, themeStyle as kW, themeToCssVars as kX, thumbnailHeight as kY, thumbnailZoom as kZ, toggleCommentResolvedInList as k_, setNodeText as ka, setRepeatCount as kb, setRepeatMode as kc, setSequence as kd, setSeriesChartType as ke, setSeriesColor as kf, setSeriesErrorBars as kg, setSeriesMarker as kh, setSeriesName as ki, setSeriesTrendline as kj, setSeriesValue as kk, setStyle as kl, setTimingCurve as km, setTitle as kn, setTrigger as ko, setTriggerShapeId as kp, shapeStylePatch as kq, sheetAfterNavigate as kr, shouldBlockClickAdvance as ks, shouldUseSvgWarp as kt, showDirectionPicker as ku, showsTemplateAffordance as kv, signatureCountLabel as kw, signatureKey as kx, signatureTimestamp as ky, signerName as kz, AiChatService as l, toggleNodeItalic as l0, toggleSheet as l1, topLevelNodeCount as l2, transformSelectedTextCase as l3, translationsEn as l4, ungroupElements as l5, updateElementById as l6, updateGlowPatch as l7, updateGradientStopPatch as l8, updateInnerShadowPatch as l9, updateOuterShadowPatch as la, updateReflectionPatch as lb, vAlignPatch as lc, validatePassword as ld, validatePrintSettings as le, validateRoomId as lf, valueToY as lg, vermilionDarkColors as lh, vermilionDarkTheme as li, vermilionLightColors as lj, vermilionLightTheme as lk, vermilionRadius as ll, waypointsToPathD as lm, worstStatus as ln, zoomTargetSlideIndex as lo, AiComposerComponent as m, AiFocusBarComponent as n, AiFocusHighlightOverlayComponent as o, AiMessageListComponent as p, AiPanelStore as q, AiProposalCardComponent as r, AiSettingsSectionComponent as s, toChatSummary as t, AiToolCallCardComponent as u, AnimationAuthorPanelComponent as v, AnimationPanelComponent as w, AnimationPlaybackService as x, AutosaveService as y, CURSOR_PALETTE as z };
123695
+ //# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-DS2J9zVf.mjs.map