pptx-angular-viewer 1.1.43 → 1.1.45

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.
@@ -1,7 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { Signal, OnInit, OnChanges, SimpleChanges, InjectionToken, Provider } from '@angular/core';
2
+ import { Signal, OnDestroy, OnInit, OnChanges, SimpleChanges, InjectionToken, Provider } from '@angular/core';
3
3
  import * as pptx_viewer_core from 'pptx-viewer-core';
4
- import { ShapeStyle, PptxTextWarpPreset, XmlObject, PptxElementAnimation, PptxSlide, PptxElement, TextStyle, PptxTransitionType, PptxEmbeddedFont, AccessibilityIssueSeverity, AccessibilityIssue, AccessibilityIssueType, AccessibilityCheckOptions, PptxComment, PptxTheme, PptxSlideMaster, PptxCoreProperties, ParsedSignature, PptxTableCell, PptxThemePreset, InkPptxElement, TextSegment, Model3DPptxElement, ZoomPptxElement, PptxSlideTransition, TablePptxElement, ChartPptxElement, SmartArtPptxElement, PptxSmartArtData, PptxChartDataLabelOptions, PptxChartAxisType, PptxChartAxisFormatting, PptxChartSeries, PptxChartType, PptxChartDataPoint, PptxChartTrendline, PptxChartErrBars, PptxAnimationPreset, PptxAnimationTrigger, PptxAnimationTimingCurve, PptxAnimationRepeatMode, PptxAnimationDirection, PptxAnimationSequence, SignatureStatus } from 'pptx-viewer-core';
4
+ import { PptxElement, ShapeStyle, PptxTextWarpPreset, XmlObject, PptxElementAnimation, PptxSlide, TextSegment, TextStyle, PptxTransitionType, PptxEmbeddedFont, AccessibilityIssueSeverity, AccessibilityIssue, AccessibilityIssueType, AccessibilityCheckOptions, PptxComment, SmartArtLayout, PptxTheme, PptxSlideMaster, PptxCoreProperties, PptxCustomProperty, PptxHeaderFooter, ParsedSignature, PptxTableCell, PptxThemePreset, InkPptxElement, Model3DPptxElement, ZoomPptxElement, PptxSlideTransition, TablePptxElement, ChartPptxElement, SmartArtPptxElement, PptxSmartArtData, PptxChartDataLabelOptions, PptxChartAxisType, PptxChartAxisFormatting, PptxChartSeries, PptxChartType, PptxChartDataPoint, PptxChartTrendline, PptxChartErrBars, PptxAnimationPreset, PptxAnimationTrigger, PptxAnimationTimingCurve, PptxAnimationRepeatMode, PptxAnimationDirection, PptxAnimationSequence, SignatureStatus } from 'pptx-viewer-core';
5
5
  import * as pptx_angular_viewer from 'pptx-angular-viewer';
6
6
  import { Options } from 'html2canvas-pro';
7
7
  import { SafeHtml } from '@angular/platform-browser';
@@ -183,6 +183,52 @@ declare const DEFAULT_FILL_COLOR = "#3b82f6";
183
183
  /** Fallback shape stroke colour. */
184
184
  declare const DEFAULT_STROKE_COLOR = "#1f2937";
185
185
 
186
+ /**
187
+ * Shape geometry helpers — Vue port of the React package's
188
+ * `viewer/utils/resolved-shape-clip-path.ts` cascade.
189
+ *
190
+ * All the heavy lifting (the ECMA-376 preset evaluator, the adjustment-aware
191
+ * table, the cubic-Bezier cloud paths, and the static preset clip-path table)
192
+ * already lives in `pptx-viewer-core` and is framework-agnostic, so — unlike
193
+ * the React package, which keeps a local polygon fallback — the Vue binding
194
+ * imports those entry points directly. No `pptx-viewer-shared` extraction is
195
+ * required here.
196
+ *
197
+ * The resolution priority mirrors React exactly:
198
+ *
199
+ * 1. **Adjustment-aware** — when `shapeAdjustments` exist, consult
200
+ * {@link getAdjustmentAwareShapeClipPath} so `pie`, `arc`, `donut`,
201
+ * `blockArc`, and wedge callouts respond to their adjustment values.
202
+ * 2. **Spec-correct preset evaluator** — {@link getShapeClipPathFromPreset}
203
+ * produces a `path('…')` clip-path for any shape in the preset table.
204
+ * 3. **Cloud Bezier path** — {@link getCloudPathForRendering} for
205
+ * `cloud` / `cloudCallout`.
206
+ * 4. **Static preset table** — {@link getShapeClipPath} as the final
207
+ * fallback (core's comprehensive `PRESET_SHAPE_CLIP_PATHS`).
208
+ */
209
+
210
+ /**
211
+ * Resolve the best available CSS `clip-path` value for a shape type at a given
212
+ * pixel size. Implements the priority cascade described in the module
213
+ * docstring. Returns `undefined` when the shape needs no clipping.
214
+ *
215
+ * @param shapeType The OOXML preset geometry name (case-insensitive).
216
+ * @param width Element width in pixels (must be > 0 for path output).
217
+ * @param height Element height in pixels (must be > 0 for path output).
218
+ * @param adjustments Optional `shapeAdjustments` record from the element.
219
+ */
220
+ declare function getResolvedShapeClipPathFor(shapeType: string | undefined, width: number, height: number, adjustments?: Record<string, number>): string | undefined;
221
+ /**
222
+ * Element-level convenience wrapper. Pulls `shapeType`, `width`, `height`, and
223
+ * `shapeAdjustments` off a {@link PptxElement} and delegates to
224
+ * {@link getResolvedShapeClipPathFor}.
225
+ *
226
+ * @param element The PPTX element to resolve a clip-path for.
227
+ * @param width Optional width override (pixels). Defaults to `element.width`.
228
+ * @param height Optional height override (pixels). Defaults to `element.height`.
229
+ */
230
+ declare function getResolvedShapeClipPath(element: PptxElement, width?: number, height?: number): string | undefined;
231
+
186
232
  /**
187
233
  * Framework-agnostic fill resolver for the Vue viewer.
188
234
  *
@@ -272,6 +318,14 @@ type CssStyleMap = Record<string, string | number>;
272
318
  * SVG `<textPath>` renderer applies.
273
319
  */
274
320
 
321
+ /**
322
+ * Rendering-strategy category for a warp preset.
323
+ * - `path`: renders along an SVG `<textPath>` (arcs, waves, circles…)
324
+ * - `envelope`: non-uniform vertical stretch (inflate/deflate/can)
325
+ * - `simple`: basic 2D transforms (slant, fade, cascade)
326
+ * - `none`: no warp (`textNoShape`, `textPlain`, unknown)
327
+ */
328
+ type WarpCategory$1 = 'path' | 'envelope' | 'simple' | 'none';
275
329
  /**
276
330
  * Build the SVG path `d` attribute for a warp preset at a given line position.
277
331
  *
@@ -620,6 +674,9 @@ type AlignMode = AlignEdge;
620
674
  /** Axis along which to distribute spacing evenly (alias of {@link DistributeAxis}). */
621
675
  type DistributeMode = DistributeAxis;
622
676
 
677
+ /** The eight resize handles, named by compass direction. */
678
+ type ResizeHandleId = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
679
+
623
680
  /**
624
681
  * Slide-background style resolution.
625
682
  *
@@ -850,6 +907,28 @@ declare class EditorHistory<T> {
850
907
  clear(): void;
851
908
  }
852
909
 
910
+ /**
911
+ * ole-renderer-helpers.ts - framework-agnostic OLE type-resolution helpers.
912
+ *
913
+ * Resolves an embedded `OlePptxElement` to a narrowed application type and
914
+ * derives the per-type brand colour, human-readable label, accessible label,
915
+ * short badge text, display name, and placeholder box style. Shared by every
916
+ * binding's OLE renderer (React/Vue/Angular) so the branding stays identical.
917
+ *
918
+ * Pure: no framework or DOM dependencies. The JSX/template that paints icons and
919
+ * badges stays in each binding; only the type -> colour/label mapping lives here.
920
+ *
921
+ * @module shared/render/ole-renderer-helpers
922
+ */
923
+
924
+ /**
925
+ * Resolved OLE application type, narrowed from the raw `OleObjectType` union.
926
+ *
927
+ * `package` and `unknown` from the core type both collapse to `'unknown'` here
928
+ * so that every branch is guaranteed to have a colour and label.
929
+ */
930
+ type ResolvedOleType = 'excel' | 'word' | 'pdf' | 'visio' | 'mathtype' | 'unknown';
931
+
853
932
  /**
854
933
  * A single span guide line to draw.
855
934
  *
@@ -866,6 +945,85 @@ interface SnapGuide {
866
945
  end: number;
867
946
  }
868
947
 
948
+ /**
949
+ * Render the n-th (1-based) marker for an OOXML auto-numbering scheme.
950
+ *
951
+ * Suffix conventions: `…Period` → `label.`, `…ParenR` → `label)`,
952
+ * `…ParenBoth` → `(label)`, `…Plain` → bare numeral. Unrecognised schemes
953
+ * fall back to `"<n>."`.
954
+ */
955
+ declare function formatAutoNumber(autoNumType: string | undefined, n: number): string;
956
+
957
+ /**
958
+ * Pure paragraph bullet / list-marker + indent helpers (framework-agnostic).
959
+ *
960
+ * Mirrors React's paragraph + bullet renderer (`text-paragraph-render.tsx` +
961
+ * `text-segment-render.tsx`): bullet-glyph selection, auto-numbering (see
962
+ * {@link ./bullet-autonum}), bullet font/size/colour, and the marginLeft /
963
+ * hanging-indent layout. Shared by the React/Vue/Angular bindings.
964
+ */
965
+
966
+ /**
967
+ * Resolved bullet marker for a single paragraph. `marker` is the text to
968
+ * prepend (e.g. "•", "1.", "a)"); `isNumbered` is true for auto-numbered lists.
969
+ * `color`/`fontFamily`/`sizePts`/`sizePercent` carry optional explicit overrides
970
+ * (the latter as a percentage of the run font size).
971
+ */
972
+ interface ParagraphBulletResult {
973
+ marker: string;
974
+ isNumbered: boolean;
975
+ color?: string;
976
+ fontFamily?: string;
977
+ sizePts?: number;
978
+ sizePercent?: number;
979
+ }
980
+ /**
981
+ * Resolve the bullet marker for the first segment of a paragraph.
982
+ *
983
+ * Returns `undefined` when the segment is absent or carries no `bulletInfo`,
984
+ * when `bulletInfo.none`/`listType:'none'` suppresses it, or when neither a
985
+ * character bullet nor an auto-number type is present. For auto-numbered
986
+ * bullets the 1-based sequence index is `autoNumStartAt` (default 1) plus the
987
+ * 0-based `paragraphIndex`.
988
+ */
989
+ declare function resolveParagraphBullet(firstSegment: TextSegment | undefined): ParagraphBulletResult | undefined;
990
+ /**
991
+ * Return the left-indent in pixels for the given 0-based list nesting level
992
+ * (OOXML `a:p/@lvl`). `undefined`/negative is treated as level 0. Used as a
993
+ * fallback when the element carries no explicit per-paragraph `marginLeft`.
994
+ */
995
+ declare function bulletIndentPx(level: number | undefined): number;
996
+
997
+ /**
998
+ * Text-field placeholder substitution, shared by every binding's text
999
+ * renderer.
1000
+ *
1001
+ * Pure string logic: resolves OOXML field runs (slide number, date/time,
1002
+ * header/footer, document properties, slide title) into their display text.
1003
+ * Extracted from the React `viewer/utils/text-field-substitution` module so
1004
+ * every binding substitutes identically.
1005
+ */
1006
+ /** Context for substituting field placeholders (slide number, date/time, header/footer, etc.). */
1007
+ interface FieldSubstitutionContext {
1008
+ slideNumber?: number;
1009
+ dateTimeText?: string;
1010
+ /** OOXML date-format pattern from header/footer settings (e.g. "M/d/yyyy"). */
1011
+ dateFormat?: string;
1012
+ /** Footer text from PptxHeaderFooter settings. */
1013
+ footerText?: string;
1014
+ /** Header text from PptxHeaderFooter settings. */
1015
+ headerText?: string;
1016
+ /** Custom document properties for `docproperty` field substitution (keyed by property name). */
1017
+ customProperties?: ReadonlyArray<{
1018
+ name: string;
1019
+ value: string;
1020
+ }>;
1021
+ /** Locale string for date/time formatting (e.g. "en-US"). Falls back to browser default. */
1022
+ locale?: string;
1023
+ /** Title text extracted from the first title placeholder on the slide. */
1024
+ slideTitle?: string;
1025
+ }
1026
+
869
1027
  /**
870
1028
  * Pure (framework-agnostic) helpers for the advanced text panel.
871
1029
  *
@@ -1326,6 +1484,37 @@ interface SmartArtLayoutResult {
1326
1484
  family: LayoutFamily;
1327
1485
  }
1328
1486
 
1487
+ /**
1488
+ * smartart-accessibility.ts: framework-agnostic accessibility metadata for the
1489
+ * SmartArt renderer, shared across the React, Vue, and Angular bindings.
1490
+ *
1491
+ * The generated SmartArt SVG is otherwise opaque to assistive technology. These
1492
+ * pure helpers derive a screen-reader description of the whole diagram, a label
1493
+ * for each node, and a small `SmartArtA11y` view-model that a binding maps onto
1494
+ * `role="img"` + `aria-label` on the container and a `<title>` / `aria-label`
1495
+ * per node. No DOM, no framework imports.
1496
+ */
1497
+
1498
+ /** Accessibility view-model for one SmartArt node. */
1499
+ interface SmartArtNodeA11y {
1500
+ /** Stable node id (for keying). */
1501
+ id: string;
1502
+ /** `aria-label` / `<title>` text for the node. */
1503
+ label: string;
1504
+ }
1505
+ /**
1506
+ * Accessibility view-model for a whole SmartArt diagram. A binding maps this
1507
+ * onto `role="img"` + `aria-label` on the container and a per-node label.
1508
+ */
1509
+ interface SmartArtA11y {
1510
+ /** ARIA role for the container element. Always `"img"`. */
1511
+ role: 'img';
1512
+ /** Container `aria-label` (the full diagram description). */
1513
+ label: string;
1514
+ /** Per-node labels in flattened, depth-first order. */
1515
+ nodes: SmartArtNodeA11y[];
1516
+ }
1517
+
1329
1518
  /**
1330
1519
  * smartart-drawing.ts: Drawing-shape view-model helpers for the SmartArt
1331
1520
  * renderer, shared across the React, Vue, and Angular bindings.
@@ -1560,6 +1749,70 @@ interface CustomShow {
1560
1749
  slideIds: readonly string[];
1561
1750
  }
1562
1751
 
1752
+ /**
1753
+ * broadcast-helpers.ts: framework-agnostic helpers for the Broadcast dialog,
1754
+ * shared by the React, Vue and Angular bindings.
1755
+ *
1756
+ * A broadcast is a one-way collaboration session: the presenter drives slide
1757
+ * navigation and viewers follow along via a shareable link. These helpers
1758
+ * cover the testable, framework-agnostic parts of each binding's broadcast
1759
+ * dialog: room-id generation, form validation, and the viewer-link builder.
1760
+ *
1761
+ * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
1762
+ * no regex named-capture-groups (Angular vendors this source under its
1763
+ * ng-packagr lib target). `Math.random` is read inside the generator at call
1764
+ * time, never at module eval.
1765
+ */
1766
+ /** Default y-websocket server URL used when no default is supplied. */
1767
+ declare const DEFAULT_BROADCAST_SERVER_URL = "ws://localhost:1234";
1768
+ /** Optional seed values for the broadcast start form. */
1769
+ interface BroadcastDefaults {
1770
+ roomId?: string;
1771
+ serverUrl?: string;
1772
+ }
1773
+ /** The configuration emitted when a broadcast starts. */
1774
+ interface BroadcastConfig {
1775
+ roomId: string;
1776
+ serverUrl: string;
1777
+ }
1778
+ /** Generate a fresh, broadcast-scoped room id (`broadcast-<suffix>`). */
1779
+ declare function generateBroadcastRoomId(): string;
1780
+ /**
1781
+ * Seed the start form from the (optional) defaults, generating a fresh room id
1782
+ * when none is supplied and falling back to the default server URL.
1783
+ */
1784
+ declare function seedBroadcastFields(defaults?: BroadcastDefaults): BroadcastConfig;
1785
+ /** Whether both required fields are non-blank (after trimming). */
1786
+ declare function canStartBroadcast(fields: BroadcastConfig): boolean;
1787
+ /**
1788
+ * Assemble a {@link BroadcastConfig} from the (trimmed) form fields, or `null`
1789
+ * when incomplete.
1790
+ */
1791
+ declare function buildBroadcastConfig(fields: BroadcastConfig): BroadcastConfig | null;
1792
+ /**
1793
+ * Build the shareable viewer follow-link for a broadcast. Returns just the
1794
+ * room id when no `origin`/`pathname` are available (non-browser environments).
1795
+ */
1796
+ declare function buildBroadcastViewerUrl(roomId: string, serverUrl: string, location?: {
1797
+ origin: string;
1798
+ pathname: string;
1799
+ }): string;
1800
+ /** Whether the runtime exposes a usable async clipboard write API. */
1801
+ declare function canUseClipboard(nav: Navigator | undefined): boolean;
1802
+
1803
+ /**
1804
+ * presenter-view.ts: pure presenter-view helpers shared across bindings.
1805
+ *
1806
+ * Time formatting, notes font-size clamping, and rich-text notes -> render-spec
1807
+ * conversion. DOM-free; each binding renders the returned `NotesSpan[]` spec
1808
+ * into its own `<span>`/`<br>` nodes.
1809
+ */
1810
+
1811
+ /** Clamp a notes font size to the allowed range. */
1812
+ declare function clampNotesFontSize(size: number): number;
1813
+ /** Format a Date as a locale time string (HH:MM:SS). */
1814
+ declare function formatTime(date: Date): string;
1815
+
1563
1816
  /**
1564
1817
  * Pure helpers for the hyperlink-edit dialog, shared by every binding.
1565
1818
  *
@@ -2083,55 +2336,6 @@ declare class AccessibilityService {
2083
2336
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<AccessibilityService>;
2084
2337
  }
2085
2338
 
2086
- /**
2087
- * broadcast-helpers.ts: Pure (no Angular) helpers for the Broadcast dialog.
2088
- *
2089
- * A broadcast is a one-way collaboration session: the presenter drives slide
2090
- * navigation and viewers follow along via a shareable link. These helpers
2091
- * cover the testable, framework-agnostic parts of the Vue `BroadcastDialog.vue`
2092
- * and React `BroadcastDialog.tsx`: room-id generation, form validation, and
2093
- * the viewer-link builder.
2094
- *
2095
- * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
2096
- * no regex named-capture-groups (ng-packagr lib-target constraints).
2097
- */
2098
- /** Default y-websocket server URL used when no default is supplied. */
2099
- declare const DEFAULT_BROADCAST_SERVER_URL = "ws://localhost:1234";
2100
- /** Optional seed values for the broadcast start form. */
2101
- interface BroadcastDefaults {
2102
- roomId?: string;
2103
- serverUrl?: string;
2104
- }
2105
- /** The configuration emitted when a broadcast starts. */
2106
- interface BroadcastConfig {
2107
- roomId: string;
2108
- serverUrl: string;
2109
- }
2110
- /** Generate a fresh, broadcast-scoped room id (`broadcast-<suffix>`). */
2111
- declare function generateBroadcastRoomId(): string;
2112
- /**
2113
- * Seed the start form from the (optional) defaults, generating a fresh room id
2114
- * when none is supplied and falling back to the default server URL.
2115
- */
2116
- declare function seedBroadcastFields(defaults?: BroadcastDefaults): BroadcastConfig;
2117
- /** Whether both required fields are non-blank (after trimming). */
2118
- declare function canStartBroadcast(fields: BroadcastConfig): boolean;
2119
- /**
2120
- * Assemble a {@link BroadcastConfig} from the (trimmed) form fields, or `null`
2121
- * when incomplete.
2122
- */
2123
- declare function buildBroadcastConfig(fields: BroadcastConfig): BroadcastConfig | null;
2124
- /**
2125
- * Build the shareable viewer follow-link for a broadcast. Returns just the
2126
- * room id when no `origin`/`pathname` are available (non-browser environments).
2127
- */
2128
- declare function buildBroadcastViewerUrl(roomId: string, serverUrl: string, location?: {
2129
- origin: string;
2130
- pathname: string;
2131
- }): string;
2132
- /** Whether the runtime exposes a usable async clipboard write API. */
2133
- declare function canUseClipboard(nav: Navigator | undefined): boolean;
2134
-
2135
2339
  /**
2136
2340
  * Thin re-export shim → vendored `pptx-viewer-shared`
2137
2341
  * (`render/collaboration-presence`).
@@ -2148,8 +2352,10 @@ declare function canUseClipboard(nav: Navigator | undefined): boolean;
2148
2352
  * truncation, which differs from shared's three-dot (`...`) variant that the
2149
2353
  * React binding expects. Kept local so the rendered label and the colocated
2150
2354
  * test are unchanged.
2151
- * - `mapAwarenessCursors` is the foundational bare-`{ cursor, user }` mapping
2152
- * that has no shared equivalent yet.
2355
+ *
2356
+ * `mapAwarenessCursors` (the foundational bare-`{ cursor, user }` mapping) now
2357
+ * lives in shared and is re-exported here so existing Angular imports are
2358
+ * unchanged.
2153
2359
  */
2154
2360
 
2155
2361
  /**
@@ -2158,11 +2364,41 @@ declare function canUseClipboard(nav: Navigator | undefined): boolean;
2158
2364
  */
2159
2365
  declare function formatCursorLabel(userName: string, maxChars?: number): string;
2160
2366
 
2367
+ /**
2368
+ * template-mode.ts: separate-state model + per-element interactivity gate for the
2369
+ * editTemplateMode feature.
2370
+ *
2371
+ * Inherited master/layout (template) elements are merged BEHIND the slide's own
2372
+ * elements by the core loader and carry ids prefixed `layout-` / `master-`. At
2373
+ * load time we PARTITION them out of `slide.elements` into a dedicated
2374
+ * `templateElementsBySlideId` store ({@link partitionSlides}); the editor then
2375
+ * holds a template-free deck and renders the template store as a separate layer
2376
+ * behind the slide. They should only be selectable/draggable/deletable when the
2377
+ * user has explicitly turned on "edit template" mode; otherwise they are inert so
2378
+ * normal slide editing never disturbs the shared template. Every save merges the
2379
+ * two stores back together ({@link buildSaveSlides}).
2380
+ *
2381
+ * Pure (no Angular), so it stays unit-testable in isolation and the components
2382
+ * remain thin.
2383
+ *
2384
+ * @module viewer/template-mode
2385
+ */
2386
+
2387
+ /** Map of slide id -> the inherited template (master/layout) elements for it. */
2388
+ type TemplateElementsBySlideId = Record<string, PptxElement[]>;
2389
+
2161
2390
  interface ConnectOptions {
2162
2391
  onRemoteSlides?: (slides: PptxSlide[]) => void;
2163
2392
  canvasWidth?: number;
2164
2393
  canvasHeight?: number;
2165
2394
  getSourceBytes?: () => Uint8Array | null;
2395
+ /**
2396
+ * Returns the editor's separated template (master/layout) elements keyed by
2397
+ * slide id, so the elected-writer write-back can merge them back into the
2398
+ * broadcast (template-free) slides before serializing. Without this, template
2399
+ * edits would be dropped from the persisted deck.
2400
+ */
2401
+ getTemplateElements?: () => TemplateElementsBySlideId;
2166
2402
  }
2167
2403
  declare class CollaborationService {
2168
2404
  readonly connected: _angular_core.WritableSignal<boolean>;
@@ -2183,6 +2419,7 @@ declare class CollaborationService {
2183
2419
  private canvasWidth;
2184
2420
  private canvasHeight;
2185
2421
  private getSourceBytes;
2422
+ private getTemplateElements;
2186
2423
  private userName;
2187
2424
  private userColor;
2188
2425
  private userAvatar;
@@ -2210,14 +2447,37 @@ declare class EditorStateService {
2210
2447
  readonly selectedIds: _angular_core.WritableSignal<readonly string[]>;
2211
2448
  /** Whether the deck has unsaved edits. */
2212
2449
  readonly dirty: _angular_core.WritableSignal<boolean>;
2450
+ /**
2451
+ * When true, inherited master/layout (template) elements become interactive:
2452
+ * selectable, draggable, deletable, and editable. When false (default) they
2453
+ * render but are inert, so normal slide editing never disturbs the template.
2454
+ *
2455
+ * Note: a template element is shared by every slide inheriting the same
2456
+ * layout/master, so editing one updates the shared part for all of them.
2457
+ */
2458
+ readonly editTemplateMode: _angular_core.WritableSignal<boolean>;
2459
+ /**
2460
+ * Inherited master/layout (template) elements, separated out of every slide's
2461
+ * own elements at load time and keyed by slide id. They render as a dedicated
2462
+ * layer BEHIND the slide and are only mutated while {@link editTemplateMode} is
2463
+ * on; {@link buildSaveSlides} re-merges them for serialization.
2464
+ */
2465
+ readonly templateElementsBySlideId: _angular_core.WritableSignal<TemplateElementsBySlideId>;
2213
2466
  readonly canUndo: _angular_core.WritableSignal<boolean>;
2214
2467
  readonly canRedo: _angular_core.WritableSignal<boolean>;
2215
2468
  readonly undoLabel: _angular_core.WritableSignal<string | undefined>;
2216
2469
  readonly redoLabel: _angular_core.WritableSignal<string | undefined>;
2217
2470
  readonly hasSelection: _angular_core.Signal<boolean>;
2218
- /** Replace the editable deck (clones the source); resets selection + history. */
2471
+ /**
2472
+ * Replace the editable deck (clones the source); resets selection + history.
2473
+ *
2474
+ * Inherited template (master/layout) elements are PARTITIONED out of each
2475
+ * slide here: the editable deck keeps only its own elements while the template
2476
+ * elements move into {@link templateElementsBySlideId}, rendered as a separate
2477
+ * layer and re-merged on save.
2478
+ */
2219
2479
  setSlides(slides: readonly PptxSlide[]): void;
2220
- /** Current editable slides as a fresh (cloned) array. */
2480
+ /** Current editable (template-free) slides as a fresh (cloned) array. */
2221
2481
  snapshot(): readonly PptxSlide[];
2222
2482
  /**
2223
2483
  * Replace the whole deck with pre-computed slides (e.g. a find/replace
@@ -2225,6 +2485,16 @@ declare class EditorStateService {
2225
2485
  * preserves history and selection.
2226
2486
  */
2227
2487
  applyReplacement(newSlides: readonly PptxSlide[], label?: string): void;
2488
+ /** Capture the current deck + template store as one undo/redo snapshot. */
2489
+ private captureSnapshot;
2490
+ /** Restore both the deck and the template store from a snapshot. */
2491
+ private restoreSnapshot;
2492
+ /** The template (master/layout) elements separated out of a slide, by id. */
2493
+ private templatesForSlide;
2494
+ /** Replace a slide's template-element list (drops the entry when empty). */
2495
+ private writeTemplatesForSlide;
2496
+ /** Toggle whether inherited template (master/layout) elements are editable. */
2497
+ setEditTemplateMode(mode: boolean): void;
2228
2498
  select(ids: readonly string[]): void;
2229
2499
  toggleSelect(id: string, additive: boolean): void;
2230
2500
  clearSelection(): void;
@@ -2247,7 +2517,8 @@ declare class EditorStateService {
2247
2517
  /**
2248
2518
  * Apply a live transform during a gesture WITHOUT recording history (the
2249
2519
  * gesture's snapshot was taken in {@link beginTransform}). Accepts any subset
2250
- * of x/y/width/height.
2520
+ * of x/y/width/height. Routes by id: template (master/layout) elements mutate
2521
+ * the template store, normal elements mutate the slide.
2251
2522
  */
2252
2523
  applyTransform(slideIndex: number, id: string, box: {
2253
2524
  x?: number;
@@ -2366,23 +2637,23 @@ declare class FindReplaceBarComponent {
2366
2637
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<FindReplaceBarComponent, "pptx-find-replace-bar", never, { "matchCount": { "alias": "matchCount"; "required": false; "isSignal": true; }; "matchIndex": { "alias": "matchIndex"; "required": false; "isSignal": true; }; }, { "find": "find"; "navigate": "navigate"; "replaceOne": "replaceOne"; "replaceAll": "replaceAll"; "close": "close"; }, never, never, true, never>;
2367
2638
  }
2368
2639
 
2640
+ /** Payload emitted when the user confirms an insert. */
2641
+ interface SmartArtInsertEvent {
2642
+ layout: SmartArtLayout;
2643
+ items: string[];
2644
+ }
2645
+
2369
2646
  /**
2370
- * Decide whether the current environment should use the mobile layout:
2371
- * - a narrow viewport (`width < MOBILE_BREAKPOINT`), OR
2372
- * - a short *touch* viewport below the tablet width: a landscape phone, which
2373
- * is wide enough to look like a tablet but far too short for the desktop
2374
- * ribbon + side panels.
2375
- *
2376
- * This mirrors React's `isMobileViewport(width, height, isTouch)` so the three
2377
- * frameworks switch chrome at the same breakpoints (and the shared mobile e2e
2378
- * specs pass identically). A tall touch tablet (e.g. 820×1180) is NOT mobile.
2647
+ * Decide whether the current environment should use the mobile layout.
2648
+ * Delegates to the shared `isMobileViewport`; kept under this name for the
2649
+ * existing Angular barrel + test surface.
2379
2650
  *
2380
2651
  * @pure: no side effects, fully testable without a DOM.
2381
2652
  */
2382
2653
  declare function computeIsMobile(width: number, height: number, isTouch: boolean): boolean;
2383
2654
  /**
2384
2655
  * Decide whether the current environment is "tablet" (desktop chrome, but in
2385
- * the 7681023px width band). A short landscape-phone touch viewport is mobile,
2656
+ * the 768-1023px width band). A short landscape-phone touch viewport is mobile,
2386
2657
  * not tablet (handled by {@link computeIsMobile}).
2387
2658
  *
2388
2659
  * @pure
@@ -2479,6 +2750,10 @@ declare class LoadContentService {
2479
2750
  readonly embeddedFonts: _angular_core.WritableSignal<PptxEmbeddedFont[]>;
2480
2751
  /** Core document properties from `docProps/core.xml`. */
2481
2752
  readonly coreProperties: _angular_core.WritableSignal<PptxCoreProperties | undefined>;
2753
+ /** Custom document properties (used for `docproperty` field substitution). */
2754
+ readonly customProperties: _angular_core.WritableSignal<PptxCustomProperty[]>;
2755
+ /** Header/footer settings (footer/header/date-time text + format) for field substitution. */
2756
+ readonly headerFooter: _angular_core.WritableSignal<PptxHeaderFooter | undefined>;
2482
2757
  /** Whether the presentation contains digital signatures. */
2483
2758
  /** Parsed digital signatures (empty when unsigned or parsing fails). */
2484
2759
  readonly signatures: _angular_core.WritableSignal<ParsedSignature[]>;
@@ -3019,9 +3294,18 @@ declare class PowerPointViewerComponent {
3019
3294
  /** Slides to display: the editable deck when `canEdit`, else the loaded deck. */
3020
3295
  protected readonly displaySlides: _angular_core.Signal<readonly PptxSlide[]>;
3021
3296
  protected readonly slideCount: _angular_core.Signal<number>;
3022
- /** Mutable copy of the display deck for inputs that require a non-readonly array. */
3297
+ /**
3298
+ * The deck with the separated template (master/layout) elements merged back
3299
+ * into each slide. The editable {@link displaySlides} is template-free; any
3300
+ * consumer that needs the COMPLETE slide (export, print, slide thumbnails,
3301
+ * accessibility) renders this instead so template elements are not lost.
3302
+ */
3303
+ protected readonly mergedSlides: _angular_core.Signal<readonly PptxSlide[]>;
3304
+ /** Mutable copy of the merged display deck for inputs that require a non-readonly array. */
3023
3305
  protected readonly displaySlidesMut: _angular_core.Signal<PptxSlide[]>;
3024
3306
  protected readonly activeSlide: _angular_core.Signal<PptxSlide>;
3307
+ /** Inherited template (master/layout) elements for the active slide, when editing. */
3308
+ protected readonly activeTemplateElements: _angular_core.Signal<readonly PptxElement[]>;
3025
3309
  protected readonly rootStyle: _angular_core.Signal<Record<string, string>>;
3026
3310
  protected readonly zoom: _angular_core.WritableSignal<number>;
3027
3311
  protected readonly zoomPercent: _angular_core.Signal<number>;
@@ -3119,6 +3403,8 @@ declare class PowerPointViewerComponent {
3119
3403
  protected readonly showThemeGallery: _angular_core.WritableSignal<boolean>;
3120
3404
  /** Whether the custom-shows dialog is open. */
3121
3405
  protected readonly showCustomShows: _angular_core.WritableSignal<boolean>;
3406
+ /** Whether the Insert SmartArt gallery dialog is open. */
3407
+ protected readonly showSmartArtInsert: _angular_core.WritableSignal<boolean>;
3122
3408
  /** The list of user-defined custom shows for this session. */
3123
3409
  protected readonly customShows: _angular_core.WritableSignal<readonly CustomShow[]>;
3124
3410
  /** The id of the currently active custom show, or null. */
@@ -3335,6 +3621,12 @@ declare class PowerPointViewerComponent {
3335
3621
  onSelectionPaneBringForward(id: string): void;
3336
3622
  onSelectionPaneSendBackward(id: string): void;
3337
3623
  onToggleElementHidden(id: string): void;
3624
+ /**
3625
+ * Insert a new SmartArt element built from the dialog's chosen preset + item
3626
+ * texts. The element id is left empty so `EditorStateService.addElement`
3627
+ * assigns one; the insert is a single undo/redo history entry.
3628
+ */
3629
+ protected onInsertSmartArt(event: SmartArtInsertEvent): void;
3338
3630
  onCustomShowCreate(show: {
3339
3631
  name: string;
3340
3632
  slideIds: string[];
@@ -3397,14 +3689,8 @@ declare class PowerPointViewerComponent {
3397
3689
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<PowerPointViewerComponent, "pptx-viewer", never, { "content": { "alias": "content"; "required": false; "isSignal": true; }; "canEdit": { "alias": "canEdit"; "required": false; "isSignal": true; }; "class": { "alias": "class"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "collaboration": { "alias": "collaboration"; "required": false; "isSignal": true; }; "onOpenFile": { "alias": "onOpenFile"; "required": false; "isSignal": true; }; "smartArt3D": { "alias": "smartArt3D"; "required": false; "isSignal": true; }; }, { "activeSlideChange": "activeSlideChange"; "dirtyChange": "dirtyChange"; "contentChange": "contentChange"; "propertiesChange": "propertiesChange"; }, never, never, true, never>;
3398
3690
  }
3399
3691
 
3400
- /**
3401
- * Pure geometry for interactive element drag/resize.
3402
- *
3403
- * Stage-space deltas in, a new bounding box out: no DOM, no framework, so the
3404
- * SlideCanvas pointer wiring stays thin and the maths is unit-testable.
3405
- */
3406
3692
  /** The eight resize-handle positions around a selection box. */
3407
- type ResizeHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
3693
+ type ResizeHandle = ResizeHandleId;
3408
3694
  /** An axis-aligned box in stage (slide) coordinates. */
3409
3695
  interface Box {
3410
3696
  x: number;
@@ -3501,6 +3787,19 @@ declare class SlideCanvasComponent {
3501
3787
  readonly selectedIds: _angular_core.InputSignal<readonly string[]>;
3502
3788
  /** Id of the element currently being text-edited inline (or null). */
3503
3789
  readonly editingId: _angular_core.InputSignal<string | null>;
3790
+ /**
3791
+ * When true, inherited master/layout (template) elements become interactive
3792
+ * (selectable/draggable/deletable/editable) and show the editable affordance.
3793
+ * When false (default) template elements still render but are inert, so normal
3794
+ * slide editing never disturbs the shared template.
3795
+ */
3796
+ readonly editTemplateMode: _angular_core.InputSignal<boolean>;
3797
+ /**
3798
+ * Inherited master/layout (template) elements for this slide, separated out of
3799
+ * `slide.elements` by the editor. Rendered as a dedicated layer BEHIND the
3800
+ * slide's own elements; interactive only while {@link editTemplateMode} is on.
3801
+ */
3802
+ readonly templateElements: _angular_core.InputSignal<readonly PptxElement[]>;
3504
3803
  /** Active draw tool. When not 'select', pointer gestures draw ink strokes. */
3505
3804
  readonly drawTool: _angular_core.InputSignal<"pen" | "highlighter" | "eraser" | "select" | "freeform">;
3506
3805
  /** Active ink stroke colour (CSS colour string). */
@@ -3610,6 +3909,20 @@ declare class SlideCanvasComponent {
3610
3909
  */
3611
3910
  private recomputeFit;
3612
3911
  readonly elements: _angular_core.Signal<PptxElement[]>;
3912
+ /**
3913
+ * Template elements + the slide's own elements, template first (behind). Used
3914
+ * for every id-based lookup (hit-testing, selection boxes, inline-edit box) so
3915
+ * a selected/dragged template element resolves the same as a normal one.
3916
+ */
3917
+ readonly allElements: _angular_core.Signal<readonly PptxElement[]>;
3918
+ /**
3919
+ * OOXML field-substitution context for the slide being rendered. Built from
3920
+ * the viewer-scoped {@link FieldContextService} (header/footer + custom doc
3921
+ * properties) folded with this slide's number + title. `optional` injection
3922
+ * means canvases used outside the viewer subtree get no substitution.
3923
+ */
3924
+ private readonly fieldContextSvc;
3925
+ readonly fieldContext: _angular_core.Signal<FieldSubstitutionContext | undefined>;
3613
3926
  /**
3614
3927
  * Obstacle rects (absolute slide coords) for connector A* routing: every
3615
3928
  * non-connector element with a positive footprint. Bent connectors detour
@@ -3635,7 +3948,7 @@ declare class SlideCanvasComponent {
3635
3948
  }) | null>;
3636
3949
  /** Resize-handle render boxes (stage coords) for the single selection. */
3637
3950
  readonly handleBoxes: _angular_core.Signal<{
3638
- handle: ResizeHandle;
3951
+ handle: ResizeHandleId;
3639
3952
  left: number;
3640
3953
  top: number;
3641
3954
  size: number;
@@ -3658,7 +3971,24 @@ declare class SlideCanvasComponent {
3658
3971
  top: number;
3659
3972
  size: number;
3660
3973
  } | null>;
3974
+ /**
3975
+ * Resolve the id of the interactive element under a pointer target, or null.
3976
+ * An element host carries `data-element-id`, but template (master/layout)
3977
+ * elements are only interactive while editTemplateMode is on; when off they
3978
+ * are reported as null so the canvas treats them as background (no
3979
+ * select/drag/context-menu/inline-edit).
3980
+ */
3981
+ private interactiveElementIdAt;
3661
3982
  onStagePointerDown(event: PointerEvent): void;
3983
+ /**
3984
+ * Press on the scrollable viewport background (the empty workspace around a
3985
+ * centered slide). The slide stage owns its own empty-press deselect, but
3986
+ * clicks outside the slide borders land on the viewport container instead, so
3987
+ * without this they would leave the current selection intact. Only direct hits
3988
+ * on the viewport itself deselect; bubbled child events (wrapper, stage,
3989
+ * rulers, handles, content) keep their existing behavior.
3990
+ */
3991
+ onViewportPointerDown(event: PointerEvent): void;
3662
3992
  /** Box + current plain text for the element under inline edit, or null. */
3663
3993
  readonly editingBox: _angular_core.Signal<{
3664
3994
  id: string;
@@ -3705,7 +4035,22 @@ declare class SlideCanvasComponent {
3705
4035
  readonly vRulerTicks: _angular_core.Signal<readonly RulerTick[]>;
3706
4036
  readonly stageStyle: _angular_core.Signal<StyleMap>;
3707
4037
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SlideCanvasComponent, never>;
3708
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SlideCanvasComponent, "pptx-slide-canvas", never, { "slide": { "alias": "slide"; "required": false; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "showGrid": { "alias": "showGrid"; "required": false; "isSignal": true; }; "showRulers": { "alias": "showRulers"; "required": false; "isSignal": true; }; "showGuides": { "alias": "showGuides"; "required": false; "isSignal": true; }; "snapToGrid": { "alias": "snapToGrid"; "required": false; "isSignal": true; }; "snapToGuides": { "alias": "snapToGuides"; "required": false; "isSignal": true; }; "autoFit": { "alias": "autoFit"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "selectedIds": { "alias": "selectedIds"; "required": false; "isSignal": true; }; "editingId": { "alias": "editingId"; "required": false; "isSignal": true; }; "drawTool": { "alias": "drawTool"; "required": false; "isSignal": true; }; "drawColor": { "alias": "drawColor"; "required": false; "isSignal": true; }; "drawWidth": { "alias": "drawWidth"; "required": false; "isSignal": true; }; }, { "elementSelect": "elementSelect"; "backgroundClick": "backgroundClick"; "transformStart": "transformStart"; "transformUpdate": "transformUpdate"; "contextMenu": "contextMenu"; "textEditStart": "textEditStart"; "textCommit": "textCommit"; "textCancel": "textCancel"; "rotateUpdate": "rotateUpdate"; "marqueeSelect": "marqueeSelect"; "inkStrokeComplete": "inkStrokeComplete"; "eraserHit": "eraserHit"; "cellCommit": "cellCommit"; }, never, never, true, never>;
4038
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SlideCanvasComponent, "pptx-slide-canvas", never, { "slide": { "alias": "slide"; "required": false; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "showGrid": { "alias": "showGrid"; "required": false; "isSignal": true; }; "showRulers": { "alias": "showRulers"; "required": false; "isSignal": true; }; "showGuides": { "alias": "showGuides"; "required": false; "isSignal": true; }; "snapToGrid": { "alias": "snapToGrid"; "required": false; "isSignal": true; }; "snapToGuides": { "alias": "snapToGuides"; "required": false; "isSignal": true; }; "autoFit": { "alias": "autoFit"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "selectedIds": { "alias": "selectedIds"; "required": false; "isSignal": true; }; "editingId": { "alias": "editingId"; "required": false; "isSignal": true; }; "editTemplateMode": { "alias": "editTemplateMode"; "required": false; "isSignal": true; }; "templateElements": { "alias": "templateElements"; "required": false; "isSignal": true; }; "drawTool": { "alias": "drawTool"; "required": false; "isSignal": true; }; "drawColor": { "alias": "drawColor"; "required": false; "isSignal": true; }; "drawWidth": { "alias": "drawWidth"; "required": false; "isSignal": true; }; }, { "elementSelect": "elementSelect"; "backgroundClick": "backgroundClick"; "transformStart": "transformStart"; "transformUpdate": "transformUpdate"; "contextMenu": "contextMenu"; "textEditStart": "textEditStart"; "textCommit": "textCommit"; "textCancel": "textCancel"; "rotateUpdate": "rotateUpdate"; "marqueeSelect": "marqueeSelect"; "inkStrokeComplete": "inkStrokeComplete"; "eraserHit": "eraserHit"; "cellCommit": "cellCommit"; }, never, never, true, never>;
4039
+ }
4040
+
4041
+ /**
4042
+ * Pure (Angular-free) helpers for the `<a:clrChange>` colour-change image
4043
+ * effect. Kept out of the component so they can be unit-tested without TestBed
4044
+ * or a DOM, mirroring `model3d-renderer-helpers.ts`.
4045
+ */
4046
+ /** Parsed `<a:clrChange>` parameters needed to drive the chroma-key. */
4047
+ interface ClrChangeParams {
4048
+ clrFrom: string;
4049
+ clrTo: string;
4050
+ /** Whether the target colour becomes fully transparent (alpha = 0). */
4051
+ clrToTransparent: boolean;
4052
+ /** Match tolerance percentage (0-100). */
4053
+ tolerance: number;
3709
4054
  }
3710
4055
 
3711
4056
  /**
@@ -3735,14 +4080,16 @@ declare class SlideCanvasComponent {
3735
4080
  */
3736
4081
 
3737
4082
  /** The four rendering strategy families. */
3738
- type WarpCategory = 'path' | 'envelope' | 'simple' | 'none';
4083
+ type WarpCategory = WarpCategory$1;
3739
4084
  /**
3740
4085
  * Classify a warp preset into a rendering strategy category.
3741
4086
  *
3742
4087
  * Returns `'none'` for unknown or empty presets so callers can safely
3743
- * skip rendering without an explicit allowlist check.
4088
+ * skip rendering without an explicit allowlist check. Thin alias for the
4089
+ * shared `classifyTextWarp` helper.
3744
4090
  */
3745
- declare function getWarpCategory(preset: string | undefined): WarpCategory;
4091
+ declare const getWarpCategory: (preset: string | undefined) => WarpCategory;
4092
+
3746
4093
  /**
3747
4094
  * A single pre-computed SVG path line for one text paragraph.
3748
4095
  *
@@ -3810,10 +4157,14 @@ type TextWarpDef = TextWarpPathDef | TextWarpCssDef;
3810
4157
  *
3811
4158
  * @param element Any `PptxElement`. Elements without text properties always
3812
4159
  * return `undefined`.
4160
+ * @param fieldContext Optional OOXML field-substitution context. When given,
4161
+ * field runs (slide number, date/time, footer, ...) in the warp
4162
+ * paragraphs are resolved to their display text, mirroring
4163
+ * React's warp-text-renderer.
3813
4164
  * @returns A `TextWarpDef` with `strategy: 'path'` for SVG textPath warps, or
3814
4165
  * `strategy: 'css'` for CSS-transform approximations.
3815
4166
  */
3816
- declare function getTextWarp(element: PptxElement): TextWarpDef | undefined;
4167
+ declare function getTextWarp(element: PptxElement, fieldContext?: FieldSubstitutionContext): TextWarpDef | undefined;
3817
4168
 
3818
4169
  interface TextRun {
3819
4170
  text: string;
@@ -3884,6 +4235,20 @@ declare class ElementRendererComponent {
3884
4235
  readonly interactive: _angular_core.InputSignal<boolean>;
3885
4236
  /** Whether inline editing (e.g. table-cell text input) is enabled. */
3886
4237
  readonly editable: _angular_core.InputSignal<boolean>;
4238
+ /**
4239
+ * OOXML field-substitution context (slide number, date/time, header/footer,
4240
+ * slide title, custom doc properties). Built once per slide by the slide
4241
+ * canvas and threaded down (including to recursive group children) so field
4242
+ * runs resolve to display text, mirroring React's `fieldContext`.
4243
+ */
4244
+ readonly fieldContext: _angular_core.InputSignal<FieldSubstitutionContext | undefined>;
4245
+ /**
4246
+ * When true, inherited master/layout (template) elements get a visual
4247
+ * affordance (amber outline ring + slightly reduced opacity) signalling that
4248
+ * they are now directly editable. Has no effect on normal slide elements, and
4249
+ * no effect at all when false, so default rendering is untouched.
4250
+ */
4251
+ readonly editTemplateMode: _angular_core.InputSignal<boolean>;
3887
4252
  /** Emitted when a table cell's text edit is committed. */
3888
4253
  readonly cellCommit: _angular_core.OutputEmitterRef<{
3889
4254
  id: string;
@@ -3891,15 +4256,34 @@ declare class ElementRendererComponent {
3891
4256
  }>;
3892
4257
  /** Duotone SVG `<filter>` descriptor for this element, if any. */
3893
4258
  readonly duotoneFilter: _angular_core.Signal<pptx_angular_viewer.DuotoneFilterDef | undefined>;
4259
+ /**
4260
+ * Outline ring + slight transparency applied to inherited template
4261
+ * (master/layout) elements while editTemplateMode is on. Empty otherwise, so
4262
+ * normal rendering is never altered.
4263
+ */
4264
+ readonly templateAffordanceStyle: _angular_core.Signal<StyleMap>;
3894
4265
  readonly containerStyle: _angular_core.Signal<StyleMap>;
3895
4266
  readonly shapeContainerStyle: _angular_core.Signal<StyleMap>;
3896
4267
  readonly textStyle: _angular_core.Signal<StyleMap>;
3897
4268
  readonly imageSrc: _angular_core.Signal<string | undefined>;
4269
+ /**
4270
+ * Parsed `<a:clrChange>` colour-change effect for this element, or
4271
+ * `undefined` when it carries none. When present the image / media branch
4272
+ * renders via {@link ColorChangedImageComponent} (offscreen-canvas chroma
4273
+ * key) instead of a plain `<img>`.
4274
+ */
4275
+ readonly clrChangeParams: _angular_core.Signal<ClrChangeParams | undefined>;
3898
4276
  /** Text-warp (WordArt) descriptor for the element, if any. */
3899
4277
  readonly textWarp: _angular_core.Signal<pptx_angular_viewer.TextWarpDef | undefined>;
3900
4278
  /** Only the SVG-textPath warp variant (for the `<svg>` overlay branch). */
3901
4279
  readonly pathWarp: _angular_core.Signal<TextWarpPathDef | undefined>;
3902
- /** Text block style, folding in a CSS-transform warp when present. */
4280
+ /** Text block 3D scene style (a:bodyPr/a:scene3d), mirroring React's ElementBody. */
4281
+ readonly scene3dStyle: _angular_core.Signal<StyleMap | undefined>;
4282
+ /**
4283
+ * Text block style, folding in a CSS-transform warp and the 3D scene
4284
+ * (perspective + rotation) when present. The warp transform and the scene
4285
+ * transform are composed rather than clobbering each other.
4286
+ */
3903
4287
  readonly warpedTextStyle: _angular_core.Signal<StyleMap>;
3904
4288
  readonly children: _angular_core.Signal<PptxElement[]>;
3905
4289
  readonly isShapeLike: _angular_core.Signal<boolean>;
@@ -3907,9 +4291,8 @@ declare class ElementRendererComponent {
3907
4291
  readonly paragraphs: _angular_core.Signal<Paragraph[]>;
3908
4292
  readonly hasText: _angular_core.Signal<boolean>;
3909
4293
  readonly placeholderLabel: _angular_core.Signal<string>;
3910
- private segmentStyle;
3911
4294
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ElementRendererComponent, never>;
3912
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ElementRendererComponent, "pptx-element-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "obstacles": { "alias": "obstacles"; "required": false; "isSignal": true; }; "canvasWidth": { "alias": "canvasWidth"; "required": false; "isSignal": true; }; "canvasHeight": { "alias": "canvasHeight"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; }, { "cellCommit": "cellCommit"; }, never, never, true, never>;
4295
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ElementRendererComponent, "pptx-element-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "obstacles": { "alias": "obstacles"; "required": false; "isSignal": true; }; "canvasWidth": { "alias": "canvasWidth"; "required": false; "isSignal": true; }; "canvasHeight": { "alias": "canvasHeight"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "fieldContext": { "alias": "fieldContext"; "required": false; "isSignal": true; }; "editTemplateMode": { "alias": "editTemplateMode"; "required": false; "isSignal": true; }; }, { "cellCommit": "cellCommit"; }, never, never, true, never>;
3913
4296
  }
3914
4297
 
3915
4298
  /**
@@ -4020,6 +4403,49 @@ declare class ChartRendererComponent {
4020
4403
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChartRendererComponent, "pptx-chart-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
4021
4404
  }
4022
4405
 
4406
+ /**
4407
+ * smart-art-inline-edit.ts: pure logic for on-canvas SmartArt node text editing.
4408
+ *
4409
+ * The Angular SmartArt renderer lets a user double-click a rendered node to edit
4410
+ * its text directly on the diagram. Everything that can be expressed as a pure
4411
+ * function lives here so it can be unit-tested in plain vitest (the Angular
4412
+ * package's vitest setup has no Angular compiler, so component/TestBed tests are
4413
+ * not available - see PORTING.md). The component
4414
+ * (`smart-art-renderer.component.ts`) stays thin: it owns only the edit-state
4415
+ * signal, the positioned `<textarea>`, and the call into the existing commit
4416
+ * path (`EditorStateService.updateElement`, the same channel the inspector's
4417
+ * `SmartArtPropertiesComponent` commits through, so undo/redo + save round-trip
4418
+ * are shared).
4419
+ *
4420
+ * The commit itself reuses the framework-agnostic core op
4421
+ * `updateSmartArtNodeText` (re-exported via `editor-insert.ts`).
4422
+ *
4423
+ * @module angular-viewer/smart-art-inline-edit
4424
+ */
4425
+
4426
+ /**
4427
+ * An axis-aligned box in element-local (viewBox) pixel coordinates, used to
4428
+ * position the inline `<textarea>` over a node. Because the SmartArt `<svg>`
4429
+ * uses a `viewBox` of `0 0 width height` matching the element's pixel size and
4430
+ * fills the element 100%, these coordinates are also the element-local CSS
4431
+ * pixels for the overlaid editor.
4432
+ */
4433
+ interface NodeEditBox {
4434
+ x: number;
4435
+ y: number;
4436
+ width: number;
4437
+ height: number;
4438
+ }
4439
+ /** The node currently being edited on the canvas, or `null`. */
4440
+ interface InlineEditState {
4441
+ /** SmartArt data-model node id (matches `PptxSmartArtNode.id`). */
4442
+ nodeId: string;
4443
+ /** Positioned editor box in element-local pixels. */
4444
+ box: NodeEditBox;
4445
+ /** Seed text shown when the editor opens (the node's current text). */
4446
+ text: string;
4447
+ }
4448
+
4023
4449
  /**
4024
4450
  * SmartArtRendererComponent: Angular SmartArt renderer.
4025
4451
  *
@@ -4039,6 +4465,27 @@ declare class SmartArtRendererComponent {
4039
4465
  /** The smartArt element to render. Must be `type === 'smartArt'`. */
4040
4466
  readonly element: _angular_core.InputSignal<PptxElement>;
4041
4467
  readonly zIndex: _angular_core.InputSignal<number>;
4468
+ /**
4469
+ * Whether inline on-canvas node-text editing is enabled. False in
4470
+ * presentation / read-only / thumbnail contexts (mirrors the table renderer's
4471
+ * `editable` input). Double-clicking a node only enters edit mode when true.
4472
+ */
4473
+ readonly editable: _angular_core.InputSignal<boolean>;
4474
+ /**
4475
+ * The editor state layer. Optional: the renderer is also used outside the
4476
+ * editing viewer (thumbnails, export), where this service is not provided.
4477
+ * Inline editing commits through `updateElement` here, the exact channel the
4478
+ * inspector's SmartArt panel uses, so undo/redo + save round-trip are shared.
4479
+ */
4480
+ private readonly editor;
4481
+ private readonly injector;
4482
+ /** The node currently being edited on the canvas, or null. */
4483
+ protected readonly editState: _angular_core.WritableSignal<InlineEditState | null>;
4484
+ /** The mounted `<textarea>` for the active node edit, if any. */
4485
+ private readonly nodeEditor;
4486
+ /** Whether node double-click / Enter enters inline edit (editable + has editor). */
4487
+ readonly canEditNodes: _angular_core.Signal<boolean>;
4488
+ constructor();
4042
4489
  private readonly smartArtData;
4043
4490
  readonly containerStyle: _angular_core.Signal<StyleMap>;
4044
4491
  readonly chromeStyle: _angular_core.Signal<StyleMap>;
@@ -4054,15 +4501,44 @@ declare class SmartArtRendererComponent {
4054
4501
  readonly renderedShapes: _angular_core.Signal<RenderedShape[]>;
4055
4502
  readonly layout: _angular_core.Signal<SmartArtLayoutResult>;
4056
4503
  readonly hasLayout: _angular_core.Signal<boolean>;
4504
+ /**
4505
+ * Screen-reader metadata for the whole diagram, derived by the shared
4506
+ * `buildSmartArtA11y`. The container SVG gets `role="img"` + this `label`;
4507
+ * each node gets a per-node `aria-label` + `<title>` resolved by node id.
4508
+ */
4509
+ readonly a11y: _angular_core.Signal<SmartArtA11y | undefined>;
4510
+ /** Map of node id -> accessibility label (for per-node `<title>` lookup). */
4511
+ private readonly a11yLabelById;
4512
+ /** Resolve the accessibility label for a rendered node (by parsed node id). */
4513
+ nodeAriaLabel(node: RenderedNode): string | null;
4514
+ /**
4515
+ * Polite live-region message announcing the most recent node-text commit.
4516
+ * Empty between commits so assistive tech only speaks on change.
4517
+ */
4518
+ readonly liveMessage: _angular_core.WritableSignal<string>;
4057
4519
  /** Narrow a `RenderedNode` to a circle, or `undefined`. */
4058
4520
  asCircle(node: RenderedNode): RenderedCircleNode | undefined;
4059
4521
  /** Narrow a `RenderedNode` to a polygon, or `undefined`. */
4060
4522
  asPolygon(node: RenderedNode): RenderedPolygonNode | undefined;
4061
4523
  /** Narrow a `RenderedNode` to a rect, or `undefined`. */
4062
4524
  asRect(node: RenderedNode): RenderedRectNode | undefined;
4525
+ /** Double-click a node enters inline edit mode (when editable). */
4526
+ onNodeDblClick(event: Event, node: RenderedNode): void;
4527
+ /** Enter / F2 on a focused node enters inline edit mode (when editable). */
4528
+ onNodeKeydown(event: KeyboardEvent, node: RenderedNode): void;
4529
+ /** Commit the current edit (called on blur). */
4530
+ commitEdit(event: Event): void;
4531
+ /** Enter commits (Shift+Enter inserts a newline); Escape cancels. */
4532
+ onEditorKeydown(event: KeyboardEvent): void;
4533
+ /** Resolve the node id + geometry and open the editor seeded with full text. */
4534
+ private enterEdit;
4535
+ /** The node's full (untruncated) data-model text, falling back to rendered text. */
4536
+ private rawNodeText;
4537
+ /** Commit edited text through the shared editor state (one history entry). */
4538
+ private applyCommit;
4063
4539
  readonly isEmpty: _angular_core.Signal<boolean>;
4064
4540
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SmartArtRendererComponent, never>;
4065
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SmartArtRendererComponent, "pptx-smart-art-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4541
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SmartArtRendererComponent, "pptx-smart-art-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4066
4542
  }
4067
4543
 
4068
4544
  /**
@@ -4109,25 +4585,46 @@ declare class InkRendererComponent {
4109
4585
  }
4110
4586
 
4111
4587
  /**
4112
- * Pure, framework-agnostic helpers for OLE renderer.
4588
+ * OLE renderer helpers for the Angular binding.
4113
4589
  *
4114
- * Ported from:
4115
- * - packages/react/src/viewer/components/elements/InkGroupRenderers.tsx
4116
- * (resolveOleType, getOleTypeColor, getOleTypeLabel, getOleAriaLabel)
4117
- * - packages/vue/src/viewer/components/OleRenderer.vue
4118
- * (placeholder style, badge label, display name)
4590
+ * The pure type-resolution helpers (resolveOleType / getOleType* / aria / badge /
4591
+ * display name / placeholder style) and the size/MIME helpers (formatBytes /
4592
+ * isBrowserOpenableMime) now live in `pptx-viewer-shared` and are re-exported
4593
+ * here so the component (and tests) keep importing them from one local module.
4119
4594
  *
4120
- * All functions are pure (no Angular dependencies) so they can be unit-tested
4121
- * with plain vitest without TestBed or the Angular compiler.
4595
+ * The presentational action-model builders below stay local for now; they are
4596
+ * pure but small and binding-shaped. They build on the shared helpers.
4122
4597
  */
4123
4598
 
4599
+ /** A single descriptive info row for the OLE caption / overlay. */
4600
+ interface OleInfoRow {
4601
+ /** Stable key for `@for` tracking (e.g. `'type'`, `'file'`). */
4602
+ key: string;
4603
+ /** Display label (e.g. `'Application'`). */
4604
+ label: string;
4605
+ /** Display value (e.g. `'Excel.Sheet.12'`). */
4606
+ value: string;
4607
+ }
4124
4608
  /**
4125
- * Resolved OLE application type, narrowed from the raw `OleObjectType` union.
4126
- *
4127
- * `package` and `unknown` from the core type both collapse to `'unknown'` here
4128
- * so that every branch is guaranteed to have a colour and label.
4609
+ * Presentational action model for an OLE element's embedded payload, derived
4610
+ * purely from the core-recovered fields. The binding wires the actual
4611
+ * `<a download>` / new-tab open; this only decides what is offered and with
4612
+ * what attributes.
4129
4613
  */
4130
- type ResolvedOleType = 'excel' | 'word' | 'pdf' | 'visio' | 'mathtype' | 'unknown';
4614
+ interface OleActionModel {
4615
+ /** True when an embedded payload data-URL is available to download. */
4616
+ canDownload: boolean;
4617
+ /** True when the payload can additionally be opened in a browser tab. */
4618
+ canOpen: boolean;
4619
+ /** Data-URL for the download anchor `href`, when available. */
4620
+ downloadHref: string | undefined;
4621
+ /** Suggested file name for the download anchor `download` attribute. */
4622
+ downloadFileName: string;
4623
+ /** Human-readable size, when known (e.g. `"2.3 MB"`). */
4624
+ sizeLabel: string | undefined;
4625
+ /** Descriptive info rows (type / file / size / application). */
4626
+ info: OleInfoRow[];
4627
+ }
4131
4628
 
4132
4629
  /**
4133
4630
  * OleRendererComponent: Angular port of the React `renderOleElement`
@@ -4170,17 +4667,22 @@ declare class OleRendererComponent {
4170
4667
  readonly ariaLabel: _angular_core.Signal<string>;
4171
4668
  /** Border + background style for the placeholder box. */
4172
4669
  readonly placeholderStyle: _angular_core.Signal<StyleMap>;
4670
+ /**
4671
+ * Download / Open action model derived from the recovered embedded payload.
4672
+ * When the input is not an OLE element, every action is disabled.
4673
+ */
4674
+ readonly actions: _angular_core.Signal<OleActionModel>;
4675
+ /**
4676
+ * Descriptive tooltip for the wrapper: the info rows joined as
4677
+ * "Label: value" pairs (e.g. "Type: Excel Spreadsheet, File: budget.xlsx,
4678
+ * Size: 2.3 KB, Application: Excel.Sheet.12"). Falls back to the aria label
4679
+ * when no rows are available.
4680
+ */
4681
+ readonly infoTitle: _angular_core.Signal<string>;
4173
4682
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<OleRendererComponent, never>;
4174
4683
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<OleRendererComponent, "pptx-ole-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4175
4684
  }
4176
4685
 
4177
- /**
4178
- * Pure helpers for `Model3DRendererComponent`.
4179
- *
4180
- * All functions are framework-agnostic (no Angular dependency) so they can be
4181
- * unit-tested without TestBed, following the same pattern as
4182
- * `connector-path.ts`.
4183
- */
4184
4686
  /** Narrowed view-model derived from a `Model3DPptxElement`. */
4185
4687
  interface Model3DViewModel {
4186
4688
  /** Resolved model element, or undefined when the element is not a model3d. */
@@ -4193,26 +4695,58 @@ interface Model3DViewModel {
4193
4695
  }
4194
4696
 
4195
4697
  /**
4196
- * Model3DRendererComponent: Angular port of the Vue `Model3DRenderer.vue`
4197
- * (and the React `Model3DRenderer` / `PosterFallback`), poster-only subset.
4698
+ * Model3DRendererComponent: Angular Model3D renderer with interactive 3D.
4198
4699
  *
4199
- * Interactive 3D rendering (three.js) is intentionally OUT OF SCOPE for the
4200
- * Angular port (see PORTING.md). This component always renders the
4201
- * poster/preview image (`posterImage`, falling back to `imageData`); when
4202
- * neither exists it draws a labelled "3D Model" placeholder, exactly like the
4203
- * React poster fallback.
4700
+ * When the element carries `modelData` and the optional `three` peer dependency
4701
+ * is installed, this mounts the shared, framework-agnostic vanilla-three
4702
+ * controller ({@link MountModel3D} from `pptx-viewer-shared`) into a container
4703
+ * `<div>` for interactive GLB/GLTF rendering (orbit + zoom). The controller's
4704
+ * scene runtime (and `three`) is imported lazily via dynamic `import()` so it
4705
+ * never lands in the main bundle.
4204
4706
  *
4205
- * All non-trivial pure computation lives in `model3d-renderer-helpers.ts` (no
4206
- * Angular dependency) so it can be unit-tested without TestBed.
4707
+ * It falls back to the poster/preview image (`posterImage`, then `imageData`)
4708
+ * when:
4709
+ * - the element has no `modelData`,
4710
+ * - `three` is not installed (`mountModel3D` resolves to `THREE_UNAVAILABLE`),
4711
+ * - or the model fails to load.
4712
+ * When no poster exists either, it draws a labelled "3D Model" placeholder,
4713
+ * exactly like the React poster fallback.
4714
+ *
4715
+ * All non-trivial pure computation (poster selection, blob-url derivation)
4716
+ * lives in `model3d-renderer-helpers.ts` (no Angular dependency) so it can be
4717
+ * unit-tested without TestBed.
4207
4718
  */
4208
- declare class Model3DRendererComponent {
4719
+ declare class Model3DRendererComponent implements OnDestroy {
4209
4720
  readonly element: _angular_core.InputSignal<PptxElement>;
4210
4721
  readonly zIndex: _angular_core.InputSignal<number>;
4211
4722
  readonly mediaDataUrls: _angular_core.InputSignal<Map<string, string>>;
4723
+ /**
4724
+ * Enable interactive orbit controls. `true` (the default) for the viewer /
4725
+ * editor; the root viewer can pass `false` in passive presentation mode.
4726
+ */
4727
+ readonly interactive: _angular_core.InputSignal<boolean>;
4728
+ private readonly sceneRef;
4212
4729
  readonly containerStyle: _angular_core.Signal<StyleMap>;
4213
4730
  readonly vm: _angular_core.Signal<Model3DViewModel>;
4731
+ /** Blob URL for the current model data; recomputed when `modelData` changes. */
4732
+ private readonly blobUrl;
4733
+ /** Lazily-loaded shared mount fn; `null` until the scene runtime resolves. */
4734
+ private readonly mountFn;
4735
+ /** `true` once a model is mountable: render the scene container, not poster. */
4736
+ readonly showScene: _angular_core.Signal<boolean>;
4737
+ /** Set when `three` is missing or the model failed to load: forces poster. */
4738
+ private readonly failed;
4739
+ private handle;
4740
+ /** The blob URL the live handle was mounted with, owned for revocation. */
4741
+ private mountedUrl;
4742
+ constructor();
4743
+ private loadScene;
4744
+ private mount;
4745
+ /** Dispose the live handle and revoke its blob URL. */
4746
+ private teardownHandle;
4747
+ ngOnDestroy(): void;
4214
4748
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Model3DRendererComponent, never>;
4215
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Model3DRendererComponent, "pptx-model3d-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4749
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Model3DRendererComponent, "pptx-model3d-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4216
4750
  }
4217
4751
 
4218
4752
  /**
@@ -4251,10 +4785,12 @@ interface ZoomViewModel {
4251
4785
  * showing the target slide number. A small "Slide Zoom" / "Section Zoom" badge
4252
4786
  * is drawn in the corner.
4253
4787
  *
4254
- * Navigation (click-to-jump in presentation mode) and live target-slide preview
4255
- * rendering are NOT ported; this is a static link tile only (see PORTING.md).
4256
- * The `slides` array is not threaded through, so the fallback uses the target
4257
- * slide index rather than the real target background.
4788
+ * In presentation mode the overlay provides a {@link ZoomNavigationService}, so
4789
+ * clicking (or Enter/Space) jumps to the target slide. Outside presentation mode
4790
+ * the service is not provided (optional injection yields `null`) and the tile
4791
+ * stays a static link, exactly as before. Live target-slide preview rendering is
4792
+ * still NOT ported; the `slides` array is not threaded through, so the fallback
4793
+ * uses the target slide index rather than the real target background.
4258
4794
  *
4259
4795
  * All non-trivial pure computation lives in `zoom-renderer-helpers.ts` (no
4260
4796
  * Angular dependency) so it can be unit-tested without TestBed.
@@ -4265,6 +4801,18 @@ declare class ZoomRendererComponent {
4265
4801
  readonly mediaDataUrls: _angular_core.InputSignal<Map<string, string>>;
4266
4802
  readonly containerStyle: _angular_core.Signal<StyleMap>;
4267
4803
  readonly vm: _angular_core.Signal<ZoomViewModel>;
4804
+ /**
4805
+ * Zoom-navigation context, present only inside a running presentation (the
4806
+ * overlay provides it). `null` in the editor tree, where the tile stays
4807
+ * static.
4808
+ */
4809
+ private readonly zoomNavigation;
4810
+ /** Interactive (click-to-jump) only when navigation is available for a zoom. */
4811
+ protected readonly interactive: _angular_core.Signal<boolean>;
4812
+ /** Navigate to the zoom target; no-op when the tile is not interactive. */
4813
+ private activate;
4814
+ protected onClick(event: MouseEvent): void;
4815
+ protected onKeydown(event: KeyboardEvent): void;
4268
4816
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ZoomRendererComponent, never>;
4269
4817
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ZoomRendererComponent, "pptx-zoom-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4270
4818
  }
@@ -4561,6 +5109,12 @@ declare class PresentationOverlayComponent implements OnInit {
4561
5109
  protected readonly playback: AnimationPlaybackService;
4562
5110
  /** Ink-annotation state (pen/highlighter/eraser/laser) for the show. */
4563
5111
  protected readonly annotations: PresentationAnnotationsService;
5112
+ /**
5113
+ * Zoom-navigation context (provided at this component level). The handler is
5114
+ * registered in the constructor so a descendant zoom tile can jump to its
5115
+ * target slide on click. Descendants resolve this same instance.
5116
+ */
5117
+ private readonly zoomNavigation;
4564
5118
  /** Whether the live-caption bar is shown. */
4565
5119
  protected readonly subtitlesVisible: _angular_core.WritableSignal<boolean>;
4566
5120
  /** The slide stage root; animation styles are applied to its elements. */
@@ -4629,6 +5183,13 @@ declare class PresentationOverlayComponent implements OnInit {
4629
5183
  protected onNext(event: MouseEvent): void;
4630
5184
  protected onNextTouch(event: TouchEvent): void;
4631
5185
  private navigate;
5186
+ /**
5187
+ * Jump directly to `index` (clamped to the slide range), committing the same
5188
+ * way `navigate()` does its final step. Used by the zoom-navigation context
5189
+ * for a click-to-jump from a zoom tile: this is a transition-less jump, so it
5190
+ * does NOT replay the target slide's transition.
5191
+ */
5192
+ private goToSlide;
4632
5193
  private emitClosed;
4633
5194
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<PresentationOverlayComponent, never>;
4634
5195
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<PresentationOverlayComponent, "pptx-presentation-overlay", never, { "slides": { "alias": "slides"; "required": true; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "startIndex": { "alias": "startIndex"; "required": false; "isSignal": true; }; }, { "indexChange": "indexChange"; "closed": "closed"; }, never, never, true, never>;
@@ -5624,76 +6185,6 @@ interface RecordWebmOptions {
5624
6185
  */
5625
6186
  declare function recordWebm(canvases: HTMLCanvasElement[], opts?: RecordWebmOptions): Promise<Blob>;
5626
6187
 
5627
- /**
5628
- * Pure paragraph bullet / list-marker helpers for the Angular text renderer.
5629
- *
5630
- * No Angular imports: all exports are plain TypeScript functions suitable for
5631
- * use inside computed signals or template helper calls.
5632
- *
5633
- * Numbering schemes follow the OOXML `ST_TextAutonumberScheme` enumeration
5634
- * (ECMA-376 §20.1.10.81). The `autoNumType` strings come from
5635
- * `BulletInfo.autoNumType` as parsed by `pptx-viewer-core`.
5636
- */
5637
-
5638
- /**
5639
- * Render the n-th (1-based) marker for an OOXML auto-numbering scheme.
5640
- *
5641
- * Suffix conventions:
5642
- * - `…Period` → `label.` (e.g. "1.", "a.", "i.")
5643
- * - `…ParenR` → `label)` (e.g. "1)", "a)", "i)")
5644
- * - `…ParenBoth` → `(label)` (e.g. "(1)", "(a)", "(i)")
5645
- * - `…Plain` → `label` (bare numeral)
5646
- *
5647
- * Unrecognised schemes fall back to `"<n>."`.
5648
- *
5649
- * @param autoNumType - The OOXML auto-number type string (e.g. "arabicPeriod").
5650
- * @param n - 1-based sequence number to format.
5651
- */
5652
- declare function formatAutoNumber(autoNumType: string | undefined, n: number): string;
5653
- /**
5654
- * Resolved bullet marker for a single paragraph.
5655
- *
5656
- * `marker` : the text to prepend (e.g. "•", "1.", "a)").
5657
- * `isNumbered` : true for auto-numbered lists; false for character bullets.
5658
- * `color` : optional explicit bullet colour (hex string from `BulletInfo.color`).
5659
- * `fontFamily` : optional explicit bullet font (from `BulletInfo.fontFamily`).
5660
- */
5661
- interface ParagraphBulletResult {
5662
- marker: string;
5663
- isNumbered: boolean;
5664
- color?: string;
5665
- fontFamily?: string;
5666
- }
5667
- /**
5668
- * Resolve the bullet marker for the first segment of a paragraph.
5669
- *
5670
- * Returns `undefined` when:
5671
- * - `firstSegment` is undefined or carries no `bulletInfo`.
5672
- * - `bulletInfo.none` is `true` (`a:buNone` explicitly suppresses the bullet).
5673
- * - The paragraph's `listType` is `'none'`.
5674
- * - Neither `char` nor `autoNumType` is present.
5675
- *
5676
- * For auto-numbered bullets the 1-based sequence index is derived from
5677
- * `bulletInfo.autoNumStartAt` (default 1) plus `bulletInfo.paragraphIndex`
5678
- * (0-based paragraph position within the text body).
5679
- *
5680
- * @param firstSegment - The first `TextSegment` of the paragraph (carries `bulletInfo`).
5681
- */
5682
- declare function resolveParagraphBullet(firstSegment: TextSegment | undefined): ParagraphBulletResult | undefined;
5683
- /**
5684
- * Return the left-indent in pixels for the given list nesting level.
5685
- *
5686
- * `level` is the 0-based `paragraphLevel` from `TextSegment` (matches
5687
- * OOXML `a:p/@lvl`). `undefined` or negative values are treated as level 0.
5688
- *
5689
- * @example
5690
- * bulletIndentPx(0) // 0
5691
- * bulletIndentPx(1) // 18
5692
- * bulletIndentPx(3) // 54
5693
- * bulletIndentPx(undefined) // 0
5694
- */
5695
- declare function bulletIndentPx(level: number | undefined): number;
5696
-
5697
6188
  /**
5698
6189
  * EquationRendererComponent: Angular port of the Vue `EquationRenderer.vue`
5699
6190
  * (single-equation variant).
@@ -6063,8 +6554,10 @@ interface PropertiesDraft {
6063
6554
  */
6064
6555
  declare function seedPropertiesDraft(properties: DocumentProperties): PropertiesDraft;
6065
6556
  /**
6066
- * Format a (possibly absent or invalid) ISO timestamp for display. Returns an
6067
- * em-dash for missing values and echoes the raw string for unparseable ones.
6557
+ * Format a (possibly absent or invalid) ISO timestamp for display. Returns the
6558
+ * no-value marker for missing values and echoes the raw string for unparseable
6559
+ * ones. Delegates to the shared `formatIsoDate`; kept under this name for the
6560
+ * existing Angular call sites.
6068
6561
  */
6069
6562
  declare function formatPropertyDate(value: string | undefined): string;
6070
6563
  /**
@@ -6640,27 +7133,25 @@ declare class MobilePresenterViewComponent {
6640
7133
  /**
6641
7134
  * presenter-view-helpers.ts
6642
7135
  *
6643
- * Pure (framework-free) helpers for `PresenterViewComponent`: time/elapsed
6644
- * formatting, notes font-size clamping, rich-notes segment → view-model
6645
- * derivation, and current/next-slide selection.
7136
+ * Helpers for `PresenterViewComponent`: time/elapsed formatting, notes
7137
+ * font-size clamping, rich-notes segment → view-model derivation, timer
7138
+ * progress, and current/next-slide selection.
6646
7139
  *
6647
- * Angular port of the React `components/presenter-view-utils.ts`. The React
6648
- * `renderNotesSegments` produced React nodes; here we instead derive a plain
6649
- * `NotesSegmentViewModel[]` (text + style + break flag) so the logic stays
6650
- * testable without a renderer and the component template owns the markup.
7140
+ * The identical pure helpers (notes font-size constants, `clampNotesFontSize`,
7141
+ * `formatTime`) now live in `pptx-viewer-shared` and are re-exported here from
7142
+ * `../internal/shared` so existing Angular imports of
7143
+ * `./presenter-view-helpers` keep resolving.
7144
+ *
7145
+ * Kept LOCAL (intentionally diverging from shared):
7146
+ * - `formatElapsed`: clamps negative input to zero (shared's does not).
7147
+ * - `NotesSegmentViewModel` / `buildNotesSegments`: produce a kebab-case
7148
+ * `StyleMap` with `px` font sizes for the Angular template, unlike shared's
7149
+ * camelCase `NotesSpan` (`pt`).
6651
7150
  *
6652
7151
  * Kept TestBed-free (vitest + happy-dom). ng-packagr lib-target constraints:
6653
7152
  * no `String.prototype.replaceAll`, no regex named-capture-groups.
6654
7153
  */
6655
7154
 
6656
- /**
6657
- * Clamp a notes font size value to the allowed range.
6658
- */
6659
- declare function clampNotesFontSize(size: number): number;
6660
- /**
6661
- * Format a Date as a locale time string (HH:MM:SS).
6662
- */
6663
- declare function formatTime(date: Date): string;
6664
7155
  /**
6665
7156
  * Format a millisecond duration as MM:SS, or HH:MM:SS when the elapsed
6666
7157
  * time is one hour or longer. Sub-second values are floored; negative inputs
@@ -6707,57 +7198,6 @@ interface PresenterNotes {
6707
7198
  */
6708
7199
  declare function resolvePresenterNotes(slide: PptxSlide | undefined): PresenterNotes;
6709
7200
 
6710
- /**
6711
- * Shape-geometry clip-path helper.
6712
- *
6713
- * Wires the core geometry APIs into the Angular viewer's shape clipping
6714
- * pipeline. Implements a priority cascade so renderers always pick the
6715
- * highest-fidelity geometry available for a given element:
6716
- *
6717
- * 1. **Adjustment-aware**: if `shapeAdjustments` exist, consult
6718
- * {@link getAdjustmentAwareShapeClipPath} so shapes like `pie`, `arc`,
6719
- * `donut`, `blockArc`, and the wedge callouts respond to their
6720
- * adjustment values.
6721
- * 2. **Spec-correct preset evaluator**: {@link getShapeClipPathFromPreset}
6722
- * uses the ECMA-376 `pathLst` evaluator to produce a `path('…')`
6723
- * clip-path for any shape covered by the preset table.
6724
- * 3. **Cloud Bezier path**: {@link getCloudPathForRendering} produces a
6725
- * DPI-stable cubic-Bezier `path('…')` for `cloud` / `cloudCallout`.
6726
- * 4. **Static polygon table**: {@link getShapeClipPath} (the core preset
6727
- * table) is the final fallback for shapes none of the higher-fidelity
6728
- * APIs cover.
6729
- *
6730
- * This module is a plain-TypeScript port of the React package's
6731
- * `viewer/utils/resolved-shape-clip-path.ts`, with no framework imports.
6732
- * It mirrors the Vue port's `viewer/composables/shape-geometry.ts` exactly
6733
- * so the exported function names and signatures are drop-in compatible.
6734
- */
6735
-
6736
- /**
6737
- * Resolve the best available CSS `clip-path` value for a shape type at a
6738
- * given pixel size. Implements the priority cascade described in the module
6739
- * docstring.
6740
- *
6741
- * @param shapeType The OOXML preset geometry name (case-insensitive).
6742
- * @param width Element width in pixels (must be > 0 for path output).
6743
- * @param height Element height in pixels (must be > 0 for path output).
6744
- * @param adjustments Optional `shapeAdjustments` record from the element.
6745
- * @returns A CSS `clip-path` value (`polygon(...)`, `path('…')`, or `inset(...)`),
6746
- * or `undefined` if no clipping is needed.
6747
- */
6748
- declare function getResolvedShapeClipPathFor(shapeType: string | undefined, width: number, height: number, adjustments?: Record<string, number>): string | undefined;
6749
- /**
6750
- * Element-level convenience wrapper. Pulls `shapeType`, `width`, `height`,
6751
- * and `shapeAdjustments` off a {@link PptxElement} and delegates to
6752
- * {@link getResolvedShapeClipPathFor}.
6753
- *
6754
- * @param element The PPTX element to resolve a clip-path for.
6755
- * @param width Optional width override (pixels). Defaults to `element.width`.
6756
- * @param height Optional height override (pixels). Defaults to `element.height`.
6757
- * @returns A CSS `clip-path` value, or `undefined`.
6758
- */
6759
- declare function getResolvedShapeClipPath(element: PptxElement, width?: number, height?: number): string | undefined;
6760
-
6761
7201
  /**
6762
7202
  * SVG path generators for WordArt text warp presets.
6763
7203
  *