pptx-angular-viewer 1.1.12 → 1.1.14

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,9 +1,10 @@
1
- import * as pptx_viewer_core from 'pptx-viewer-core';
2
- import { PptxSlide, PptxElement, PptxTheme, PptxSlideMaster, PptxTableCell, Model3DPptxElement, ZoomPptxElement, TextSegment, XmlObject, ShapeStyle } from 'pptx-viewer-core';
3
1
  import * as _angular_core from '@angular/core';
4
- import { OnInit, OnDestroy, InjectionToken, Provider } from '@angular/core';
2
+ import { Signal, OnInit, OnDestroy, InjectionToken, Provider } from '@angular/core';
3
+ import * as pptx_viewer_core from 'pptx-viewer-core';
4
+ import { PptxSlide, AccessibilityCheckOptions, AccessibilityIssue, PptxElement, PptxTheme, PptxSlideMaster, PptxEmbeddedFont, PptxCoreProperties, PptxComment, PptxTableCell, Model3DPptxElement, ZoomPptxElement, TextSegment, XmlObject, ParsedSignature, SignatureStatus, AccessibilityIssueSeverity, AccessibilityIssueType, PptxElementAnimation, PptxTransitionType, PptxSlideTransition, ShapeStyle } from 'pptx-viewer-core';
5
5
  import { Options } from 'html2canvas-pro';
6
6
  import { SafeHtml } from '@angular/platform-browser';
7
+ import * as pptx_angular_viewer from 'pptx-angular-viewer';
7
8
 
8
9
  /**
9
10
  * Theme configuration types for the PowerPoint viewer.
@@ -166,6 +167,194 @@ declare const DEFAULT_FILL_COLOR = "#3b82f6";
166
167
  /** Fallback shape stroke colour. */
167
168
  declare const DEFAULT_STROKE_COLOR = "#1f2937";
168
169
 
170
+ declare class AccessibilityService {
171
+ /** Parsed slides of the current presentation. */
172
+ readonly slides: _angular_core.WritableSignal<PptxSlide[]>;
173
+ /** Check configuration mirroring {@link AccessibilityCheckOptions}. */
174
+ readonly options: _angular_core.WritableSignal<AccessibilityCheckOptions>;
175
+ /** All detected issues, sorted by slide index then severity. */
176
+ readonly issues: Signal<AccessibilityIssue[]>;
177
+ /** Total number of detected issues. */
178
+ readonly issueCount: Signal<number>;
179
+ /** Number of `error`-severity issues. */
180
+ readonly errorCount: Signal<number>;
181
+ /** Number of `warning`-severity issues. */
182
+ readonly warningCount: Signal<number>;
183
+ /** Number of `tip`-severity issues. */
184
+ readonly tipCount: Signal<number>;
185
+ /** True when the presentation passes every check. */
186
+ readonly isClean: Signal<boolean>;
187
+ /** Replace the slides to check. */
188
+ setSlides(slides: PptxSlide[]): void;
189
+ /** Replace the check options. */
190
+ setOptions(options: AccessibilityCheckOptions): void;
191
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AccessibilityService, never>;
192
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<AccessibilityService>;
193
+ }
194
+
195
+ /**
196
+ * collaboration-helpers.ts — Pure, framework-agnostic logic for the real-time
197
+ * collaboration subsystem (Yjs-backed presence + remote cursors).
198
+ *
199
+ * Everything here is a plain function with no Angular / Yjs dependency so it can
200
+ * be unit-tested in isolation (vitest + happy-dom, no TestBed, no live provider).
201
+ * The Angular service (`collaboration.service.ts`) and the cursors component
202
+ * (`collaboration-cursors.component.ts`) consume these helpers.
203
+ *
204
+ * Responsibilities:
205
+ * - Validate/sanitise inbound awareness data (XSS, bounds, colour, room id).
206
+ * - Map awareness state → a `RemoteCursor` view-model for rendering.
207
+ * - Derive the presence list (remote users only, stale entries dropped).
208
+ * - Deterministic per-user colour assignment + cursor label formatting.
209
+ *
210
+ * Mirrors the Vue composable (`useCollaboration.ts`) and the React
211
+ * `sanitize.ts` / `usePresenceTracking.ts` helpers.
212
+ */
213
+
214
+ /** A single remote collaborator's cursor, in unscaled slide coordinates. */
215
+ interface RemoteCursor {
216
+ /** Stable id for the remote client (awareness clientId or peer id). */
217
+ clientId: number | string;
218
+ /** Display name shown in the label chip. */
219
+ userName: string;
220
+ /** Cursor + chip colour (any CSS colour string). */
221
+ color: string;
222
+ /** Unscaled slide-space X coordinate (px). */
223
+ x: number;
224
+ /** Unscaled slide-space Y coordinate (px). */
225
+ y: number;
226
+ /** Optional ids of elements this user has selected. */
227
+ selectionIds?: string[];
228
+ }
229
+ /**
230
+ * Presence data for a remote (or local) collaborator, sanitised from the
231
+ * awareness protocol. Cursor position is in unscaled slide coordinates.
232
+ */
233
+ interface RemotePresence {
234
+ /** Awareness client id. */
235
+ clientId: number;
236
+ /** Sanitised display name. */
237
+ userName: string;
238
+ /** Hex colour for the user's cursor/avatar. */
239
+ userColor: string;
240
+ /** Validated avatar URL, when present. */
241
+ userAvatar?: string;
242
+ /** Zero-based slide index the user is viewing. */
243
+ activeSlideIndex: number;
244
+ /** Cursor X (unscaled slide px, clamped to bounds). */
245
+ cursorX: number;
246
+ /** Cursor Y (unscaled slide px, clamped to bounds). */
247
+ cursorY: number;
248
+ /** ISO timestamp of the last update (for stale-presence cleanup). */
249
+ lastUpdated: string;
250
+ /** Currently selected element id, when present. */
251
+ selectedElementId?: string;
252
+ /** Session role. */
253
+ role?: CollaborationRole;
254
+ }
255
+ /**
256
+ * Palette used for deterministic per-user colour assignment. Distinct, legible
257
+ * hues with white text contrast. Mirrors the React default-colour set.
258
+ */
259
+ declare const CURSOR_PALETTE: readonly string[];
260
+ /** True when `roomId` is a safe 1–128 char alphanumeric/`-`/`_` token. */
261
+ declare function isValidRoomId(roomId: string): boolean;
262
+ /**
263
+ * Validate a room id, returning it when valid and throwing otherwise. Mirrors
264
+ * the React `validateRoomId`.
265
+ */
266
+ declare function validateRoomId(roomId: string): string;
267
+ /** Strip HTML tags, trim, and clamp to 64 chars; falls back to `'Anonymous'`. */
268
+ declare function sanitizeUserName(name: unknown): string;
269
+ /** Validate a 6-digit hex colour; returns `fallback` when invalid. */
270
+ declare function sanitizeColor(color: unknown, fallback?: string): string;
271
+ /** Coerce a value to a non-negative integer slide index. */
272
+ declare function sanitizeSlideIndex(value: unknown): number;
273
+ /** Clamp a cursor coordinate to `[min - margin, max + margin]`. */
274
+ declare function clampCursorPosition(value: unknown, min: number, max: number): number;
275
+ /**
276
+ * Deterministically pick a palette colour for a user. The same `seed` (client
277
+ * id or user name) always maps to the same colour so a peer keeps a stable hue.
278
+ */
279
+ declare function assignUserColor(seed: number | string, palette?: readonly string[]): string;
280
+ /** Clamp/format a cursor label so long names don't overflow the chip. */
281
+ declare function formatCursorLabel(userName: string, maxChars?: number): string;
282
+ /**
283
+ * Derive the remote-presence list from a raw awareness state map. Skips the
284
+ * local client, sanitises each entry, and drops stale entries (older than
285
+ * `staleMs`, evaluated against `now`).
286
+ */
287
+ declare function derivePresenceList(states: Map<number, Record<string, unknown>>, localClientId: number, canvasWidth: number, canvasHeight: number, now?: number, staleMs?: number): RemotePresence[];
288
+ /**
289
+ * Map a sanitised presence list into the cursor view-model for the overlay,
290
+ * optionally filtering to a single slide (so cursors only show on the slide the
291
+ * local user is viewing). Pass `activeSlideIndex` undefined to show all.
292
+ */
293
+ declare function presenceToCursors(presence: readonly RemotePresence[], activeSlideIndex?: number): RemoteCursor[];
294
+
295
+ /** Options the host viewer supplies when wiring collaboration. */
296
+ interface ConnectOptions {
297
+ /** Called when a remote peer broadcasts a newer slide set. */
298
+ onRemoteSlides?: (slides: PptxSlide[]) => void;
299
+ /**
300
+ * Canvas dimensions (unscaled slide px) used to clamp incoming cursor
301
+ * coordinates. Defaults to a generous bound when omitted.
302
+ */
303
+ canvasWidth?: number;
304
+ canvasHeight?: number;
305
+ }
306
+ declare class CollaborationService {
307
+ /** True once the websocket provider reports a `connected` status. */
308
+ readonly connected: _angular_core.WritableSignal<boolean>;
309
+ /** True while a session is active (provider constructed, not yet stopped). */
310
+ readonly active: _angular_core.WritableSignal<boolean>;
311
+ /** Sanitised remote presence list (excludes the local user, stale dropped). */
312
+ readonly presence: _angular_core.WritableSignal<RemotePresence[]>;
313
+ /** Remote collaborators' live cursors, derived from presence. */
314
+ readonly cursors: _angular_core.Signal<RemoteCursor[]>;
315
+ /** Total connected participants (remote + self when active). */
316
+ readonly connectedCount: _angular_core.Signal<number>;
317
+ private ydoc;
318
+ private provider;
319
+ private ymap;
320
+ private awareness;
321
+ private selfId;
322
+ private applyingRemote;
323
+ private onRemoteSlides;
324
+ private canvasWidth;
325
+ private canvasHeight;
326
+ private userName;
327
+ private userColor;
328
+ private userAvatar;
329
+ private role;
330
+ private readonly refreshPresence;
331
+ constructor();
332
+ /**
333
+ * Connect to a room and begin syncing. Any existing session is torn down
334
+ * first. Returns when the session has been established (or silently when
335
+ * yjs/y-websocket are unavailable / the connection fails).
336
+ */
337
+ connect(config: CollaborationConfig, options?: ConnectOptions): Promise<void>;
338
+ /** Disconnect and tear down the session, resetting all reactive state. */
339
+ disconnect(): void;
340
+ /**
341
+ * Broadcast the local slide set to peers (no-op while applying a remote
342
+ * update, to avoid an echo loop). Call this whenever the editor's slides
343
+ * change.
344
+ */
345
+ broadcastSlides(slides: readonly PptxSlide[]): void;
346
+ /**
347
+ * Publish this user's cursor position (unscaled slide px) plus the slide
348
+ * they're viewing. Updates both the foundational `cursor` field and the full
349
+ * `presence` record so either consumer path sees it.
350
+ */
351
+ setCursor(x: number, y: number, activeSlideIndex?: number): void;
352
+ /** Broadcast the local user's currently selected element ids. */
353
+ setSelection(selectedElementId: string | undefined, activeSlideIndex?: number): void;
354
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<CollaborationService, never>;
355
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<CollaborationService>;
356
+ }
357
+
169
358
  /**
170
359
  * Pure geometry helpers for aligning and distributing selected editor elements.
171
360
  *
@@ -297,6 +486,14 @@ declare class LoadContentService {
297
486
  readonly slideMasters: _angular_core.WritableSignal<PptxSlideMaster[]>;
298
487
  /** Archive-path → displayable URL map for media + poster frames. */
299
488
  readonly mediaDataUrls: _angular_core.WritableSignal<Map<string, string>>;
489
+ /** Embedded font data (name + binary) extracted from the presentation. */
490
+ readonly embeddedFonts: _angular_core.WritableSignal<PptxEmbeddedFont[]>;
491
+ /** Core document properties from `docProps/core.xml`. */
492
+ readonly coreProperties: _angular_core.WritableSignal<PptxCoreProperties | undefined>;
493
+ /** Whether the presentation contains digital signatures. */
494
+ readonly hasDigitalSignatures: _angular_core.WritableSignal<boolean>;
495
+ /** Number of digital signatures found. */
496
+ readonly digitalSignatureCount: _angular_core.WritableSignal<number>;
300
497
  /** True while a load is in flight. */
301
498
  readonly loading: _angular_core.WritableSignal<boolean>;
302
499
  /** Error message from the last failed load, or null. */
@@ -324,6 +521,162 @@ declare class LoadContentService {
324
521
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<LoadContentService>;
325
522
  }
326
523
 
524
+ /**
525
+ * Pure print helper utilities — no Angular, no DOM side effects, no
526
+ * `window.print()`. Everything here is deterministic and unit-testable in
527
+ * isolation (vitest + happy-dom, no TestBed).
528
+ *
529
+ * Mirrors the React print subsystem:
530
+ * - `print-dialog-types.ts` → print settings types / defaults / constants
531
+ * - `usePrintHandlers.ts` → slide-index, colour-filter, outline + handout markup
532
+ * - `handout-layout-utils.ts` → A4 page-layout / slides-per-page grid maths
533
+ *
534
+ * The {@link PrintService} consumes these helpers and applies the DOM /
535
+ * print-window side effects.
536
+ *
537
+ * ng-packagr constraints honoured here:
538
+ * - NO `String.prototype.replaceAll` (use `.split(x).join(y)`).
539
+ * - NO regex named-capture groups.
540
+ * - All regexes carry the `/u` flag.
541
+ */
542
+
543
+ /** What to print. */
544
+ type PrintWhat = 'slides' | 'handouts' | 'notes' | 'outline';
545
+ /** Page orientation for the printed output. */
546
+ type PrintOrientation = 'portrait' | 'landscape';
547
+ /** Colour mode for the printed output. */
548
+ type PrintColorMode = 'color' | 'grayscale' | 'blackAndWhite';
549
+ /** Handout slides-per-page options. */
550
+ type HandoutSlidesPerPage = 1 | 2 | 3 | 4 | 6 | 9;
551
+ /** Slide range mode. */
552
+ type PrintSlideRange = 'all' | 'current' | 'custom';
553
+ /** Resolved print settings emitted on confirm. */
554
+ interface PrintSettings {
555
+ printWhat: PrintWhat;
556
+ orientation: PrintOrientation;
557
+ colorMode: PrintColorMode;
558
+ frameSlides: boolean;
559
+ slidesPerPage: HandoutSlidesPerPage;
560
+ slideRange: PrintSlideRange;
561
+ customRangeFrom: number;
562
+ customRangeTo: number;
563
+ }
564
+ /** Computed cell position within a handout page. */
565
+ interface HandoutCellPosition {
566
+ /** Zero-based index of the slide in the source array (or -1 for empty). */
567
+ slideIndex: number;
568
+ /** Row in the grid (0-based). */
569
+ row: number;
570
+ /** Column in the grid (0-based). */
571
+ col: number;
572
+ /** X offset in mm from the printable area left edge. */
573
+ x: number;
574
+ /** Y offset in mm from the printable area top edge. */
575
+ y: number;
576
+ /** Width of the cell in mm. */
577
+ width: number;
578
+ /** Height of the cell in mm. */
579
+ height: number;
580
+ }
581
+ /** A single page of a handout layout. */
582
+ interface HandoutPage {
583
+ pageIndex: number;
584
+ cells: HandoutCellPosition[];
585
+ /** Whether this layout includes note lines (3-per-page). */
586
+ hasNoteLines: boolean;
587
+ }
588
+ /** Available slides-per-page options for handouts. */
589
+ declare const HANDOUT_OPTIONS: HandoutSlidesPerPage[];
590
+ /** Default print settings used when the dialog opens. */
591
+ declare const DEFAULT_PRINT_SETTINGS: PrintSettings;
592
+ /**
593
+ * Coerce an arbitrary number to a supported {@link HandoutSlidesPerPage}.
594
+ * Falls back to 6 (the PowerPoint default) when the value is unsupported.
595
+ */
596
+ declare function normalizeSlidesPerPage(value: number | undefined): HandoutSlidesPerPage;
597
+ /**
598
+ * Validate and clamp partial print settings into a fully-resolved
599
+ * {@link PrintSettings}. Custom-range bounds are clamped to `[1, slideCount]`
600
+ * (with `from <= to`), and `notes`/`outline`/`handouts` force portrait
601
+ * orientation (matching the React dialog's `effectiveOrientation`).
602
+ *
603
+ * @param partial - Caller-provided settings (any subset).
604
+ * @param slideCount - Total number of slides in the presentation.
605
+ */
606
+ declare function validatePrintSettings(partial: Partial<PrintSettings>, slideCount: number): PrintSettings;
607
+ /**
608
+ * Compute the zero-based slide indices to print from a slide-range setting.
609
+ * Custom ranges use 1-based, inclusive `from`/`to` and are clamped to bounds.
610
+ */
611
+ declare function computeSlideIndices(slideRange: PrintSlideRange, activeSlideIndex: number, slideCount: number, customRangeFrom: number, customRangeTo: number): number[];
612
+ /** Number of pages needed for `slideCount` slides at `slidesPerPage`. */
613
+ declare function computePageCount(slideCount: number, slidesPerPage: HandoutSlidesPerPage): number;
614
+ /**
615
+ * Estimate the printed page count for the full settings object — slides and
616
+ * notes are one page each; outline is a single page; handouts paginate.
617
+ */
618
+ declare function estimatePageCount(printWhat: PrintWhat, slideCount: number, slidesPerPage: HandoutSlidesPerPage): number;
619
+ /**
620
+ * Compute the complete handout layout: all pages with positioned cells,
621
+ * remapped to the actual source slide indices.
622
+ */
623
+ declare function computeHandoutLayout(slideIndices: number[], slidesPerPage: HandoutSlidesPerPage, orientation?: PrintOrientation): HandoutPage[];
624
+ /** Options for {@link buildPrintDocument}. */
625
+ interface PrintDocumentOptions {
626
+ title: string;
627
+ bodyHtml: string;
628
+ orientation: PrintOrientation;
629
+ colorFilter: string;
630
+ frameSlides: boolean;
631
+ }
632
+ /**
633
+ * Assemble the complete printable HTML document string (doctype + head with
634
+ * print CSS + body). Pure: the caller writes it into a print window.
635
+ */
636
+ declare function buildPrintDocument(options: PrintDocumentOptions): string;
637
+
638
+ /**
639
+ * Captures the slide at `index` (zero-based) to a PNG `data:` URL. The viewer
640
+ * supplies this — it flips the live stage to `index` and rasterises it.
641
+ * Returning `null`/empty for a slide skips it.
642
+ */
643
+ type CaptureSlideFn = (index: number) => Promise<string | null>;
644
+ declare class PrintService {
645
+ /** Whether the print dialog is currently open. */
646
+ readonly isDialogOpen: _angular_core.WritableSignal<boolean>;
647
+ /** The current (validated) print settings. */
648
+ readonly settings: _angular_core.WritableSignal<PrintSettings>;
649
+ /** Open the print dialog. */
650
+ openDialog(): void;
651
+ /** Close the print dialog. */
652
+ closeDialog(): void;
653
+ /**
654
+ * Validate + store settings against the slide count. Returns the resolved
655
+ * settings so callers can use them immediately.
656
+ */
657
+ updateSettings(partial: Partial<PrintSettings>, slideCount: number): PrintSettings;
658
+ /**
659
+ * Run the full print flow for the given settings:
660
+ * 1. Resolve the slide index list.
661
+ * 2. Build the printable HTML (capturing slides as needed).
662
+ * 3. Open a print window and trigger `window.print()`.
663
+ *
664
+ * The outline path needs no rasterisation. All other modes call
665
+ * `captureSlide` once per slide (sequentially) and skip slides that fail.
666
+ *
667
+ * @returns `true` if a print window was opened, `false` if blocked (popup
668
+ * blocker) or there was nothing to print.
669
+ */
670
+ print(rawSettings: PrintSettings, slides: PptxSlide[], activeSlideIndex: number, captureSlide: CaptureSlideFn): Promise<boolean>;
671
+ /**
672
+ * Open a print window, write the document, focus, and trigger printing.
673
+ * Returns `false` if the popup was blocked.
674
+ */
675
+ private _open;
676
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PrintService, never>;
677
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<PrintService>;
678
+ }
679
+
327
680
  /**
328
681
  * PowerPointViewerComponent — Angular port of the React `PowerPointViewer.tsx`
329
682
  * and Vue `PowerPointViewer.vue`.
@@ -350,26 +703,34 @@ declare class PowerPointViewerComponent {
350
703
  readonly class: _angular_core.InputSignal<string>;
351
704
  /** Theme configuration for customising the viewer's appearance. */
352
705
  readonly theme: _angular_core.InputSignal<ViewerTheme | undefined>;
353
- /** Optional real-time collaboration config (accepted for API parity; not yet implemented). */
706
+ /** Optional real-time collaboration config; when set, connects and shows remote cursors. */
354
707
  readonly collaboration: _angular_core.InputSignal<CollaborationConfig | undefined>;
355
708
  /** Fired when the active slide changes. */
356
709
  readonly activeSlideChange: _angular_core.OutputEmitterRef<number>;
357
- /** Fired when the unsaved-changes flag toggles. (Editing not yet ported.) */
710
+ /** Fired when the unsaved-changes flag toggles. */
358
711
  readonly dirtyChange: _angular_core.OutputEmitterRef<boolean>;
359
- /** Fired when the in-memory content changes after edits. (Editing not yet ported.) */
712
+ /** Fired with freshly-serialised `.pptx` bytes whenever {@link getContent} materialises the deck. */
360
713
  readonly contentChange: _angular_core.OutputEmitterRef<Uint8Array<ArrayBufferLike>>;
714
+ /** Fired when the user edits document properties in the Info dialog. */
715
+ readonly propertiesChange: _angular_core.OutputEmitterRef<Partial<PptxCoreProperties>>;
361
716
  protected readonly loader: LoadContentService;
362
717
  private readonly exportSvc;
363
718
  protected readonly editor: EditorStateService;
719
+ private readonly fonts;
720
+ protected readonly collab: CollaborationService;
721
+ protected readonly accessibility: AccessibilityService;
722
+ protected readonly print: PrintService;
364
723
  /** The `<main>` host; used to locate the live `.pptx-ng-canvas-stage`. */
365
724
  private readonly mainEl;
366
725
  /** True while a PNG/PDF export is in progress (disables the buttons). */
367
726
  protected readonly exporting: _angular_core.WritableSignal<boolean>;
368
727
  protected readonly activeSlideIndex: _angular_core.WritableSignal<number>;
369
728
  /** Slides to display: the editable deck when `canEdit`, else the loaded deck. */
370
- protected readonly displaySlides: _angular_core.Signal<readonly pptx_viewer_core.PptxSlide[]>;
729
+ protected readonly displaySlides: _angular_core.Signal<readonly PptxSlide[]>;
371
730
  protected readonly slideCount: _angular_core.Signal<number>;
372
- protected readonly activeSlide: _angular_core.Signal<pptx_viewer_core.PptxSlide>;
731
+ /** Mutable copy of the display deck for inputs that require a non-readonly array. */
732
+ protected readonly displaySlidesMut: _angular_core.Signal<PptxSlide[]>;
733
+ protected readonly activeSlide: _angular_core.Signal<PptxSlide>;
373
734
  protected readonly rootStyle: _angular_core.Signal<Record<string, string>>;
374
735
  protected readonly zoom: _angular_core.WritableSignal<number>;
375
736
  protected readonly zoomPercent: _angular_core.Signal<number>;
@@ -381,6 +742,18 @@ declare class PowerPointViewerComponent {
381
742
  protected readonly showNotes: _angular_core.WritableSignal<boolean>;
382
743
  /** Find-in-slides bar visibility. */
383
744
  protected readonly showFind: _angular_core.WritableSignal<boolean>;
745
+ /** Active right-docked tool panel (comments / accessibility), or null. */
746
+ protected readonly activePanel: _angular_core.WritableSignal<"comments" | "accessibility" | null>;
747
+ /** Document-properties (Info) dialog visibility. */
748
+ protected readonly showProperties: _angular_core.WritableSignal<boolean>;
749
+ /** Hyperlink-edit dialog visibility. */
750
+ protected readonly showHyperlink: _angular_core.WritableSignal<boolean>;
751
+ /** Local overrides applied to document properties via the Info dialog. */
752
+ private readonly coreOverride;
753
+ /** Comments on the active slide. */
754
+ protected readonly activeComments: _angular_core.Signal<PptxComment[]>;
755
+ /** Document core properties (loaded, with any in-session edits merged in). */
756
+ protected readonly coreProperties: _angular_core.Signal<PptxCoreProperties>;
384
757
  /** Open editor context-menu position (client coords), or null. */
385
758
  protected readonly contextMenuPos: _angular_core.WritableSignal<{
386
759
  x: number;
@@ -433,6 +806,26 @@ declare class PowerPointViewerComponent {
433
806
  present(): void;
434
807
  /** Toggle the speaker-notes strip. */
435
808
  toggleNotes(): void;
809
+ /** Toggle a right-docked tool panel (clicking the active one closes it). */
810
+ togglePanel(panel: 'comments' | 'accessibility'): void;
811
+ /** Append a comment to the active slide (one history entry). */
812
+ onCommentAdd(text: string): void;
813
+ /** Remove a comment from the active slide. */
814
+ onCommentRemove(id: string): void;
815
+ /** Toggle a comment's resolved flag on the active slide. */
816
+ onCommentResolve(id: string): void;
817
+ /**
818
+ * Persist a document-properties edit from the Info dialog. Gated on
819
+ * {@link canEdit} — viewers may inspect properties but not mutate them
820
+ * (mirrors the comments / hyperlink edit paths).
821
+ */
822
+ onPropertiesSave(patch: Partial<PptxCoreProperties>): void;
823
+ /** Apply a hyperlink edit to the selected element (one history entry). */
824
+ onHyperlinkSave(patch: Partial<PptxElement>): void;
825
+ /** Run a print job for the chosen settings, rasterising each slide off the live stage. */
826
+ onPrint(settings: PrintSettings): Promise<void>;
827
+ /** Flip the live stage to `index`, let it settle, and capture it to a PNG data URL. */
828
+ private captureSlideDataUrl;
436
829
  /**
437
830
  * Handle an element press from the canvas. Additive (Shift/Ctrl) toggles
438
831
  * membership; a plain press selects the element (keeping it selected if it
@@ -474,7 +867,7 @@ declare class PowerPointViewerComponent {
474
867
  */
475
868
  exportPdf(): Promise<void>;
476
869
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<PowerPointViewerComponent, never>;
477
- 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; }; }, { "activeSlideChange": "activeSlideChange"; "dirtyChange": "dirtyChange"; "contentChange": "contentChange"; }, never, never, true, never>;
870
+ 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; }; }, { "activeSlideChange": "activeSlideChange"; "dirtyChange": "dirtyChange"; "contentChange": "contentChange"; "propertiesChange": "propertiesChange"; }, never, never, true, never>;
478
871
  }
479
872
 
480
873
  /**
@@ -630,11 +1023,31 @@ declare class SlideCanvasComponent {
630
1023
  readonly snapGuides: _angular_core.WritableSignal<readonly SnapGuide[]>;
631
1024
  private readonly textEditor;
632
1025
  private readonly stageRef;
1026
+ private readonly viewportRef;
1027
+ /**
1028
+ * Auto-fit scale (≤ 1): how much the fixed-size slide must shrink to fit the
1029
+ * scroll viewport. The authored slide is e.g. 1280×720, which overflows a
1030
+ * phone; without this it renders off-screen at `zoom=1`. Mirrors the React
1031
+ * viewer's `fitScale * scale` model (useZoomViewport.ts) so "100%" means "fit
1032
+ * to viewport". Measured from the viewport via ResizeObserver below.
1033
+ */
1034
+ private readonly fitScale;
1035
+ /**
1036
+ * The on-screen scale used for ALL rendering and pointer→stage coordinate
1037
+ * math: the user's zoom folded with the auto-fit. The parent keeps showing the
1038
+ * raw user zoom as the percentage, so this stays internal to the canvas.
1039
+ */
1040
+ private readonly effectiveScale;
633
1041
  /** The editing id we've already initialised the textarea for, to avoid re-seeding its value mid-edit. */
634
1042
  private seededEditId;
635
1043
  /** Last-tap timestamp + element id, for synthetic double-tap detection on touch. */
636
1044
  private lastTap;
637
1045
  constructor();
1046
+ /**
1047
+ * Compute the largest scale (≤ 1) at which the whole slide fits the viewport,
1048
+ * reserving the 1rem gutter + drop shadow. Sets fitScale to 1 when unmeasured.
1049
+ */
1050
+ private recomputeFit;
638
1051
  readonly elements: _angular_core.Signal<PptxElement[]>;
639
1052
  /** Bounding boxes (stage coords) for the selected elements. */
640
1053
  readonly selectionBoxes: _angular_core.Signal<{
@@ -2208,6 +2621,1172 @@ declare function convertOmmlToMathMl(ommlNode: OmmlNode): string;
2208
2621
  */
2209
2622
  declare function ommlToMathml(omml: Record<string, unknown> | string | undefined): string;
2210
2623
 
2624
+ declare class CommentsPanelComponent {
2625
+ /** The active slide's comments (already filtered to the active slide). */
2626
+ readonly comments: _angular_core.InputSignal<PptxComment[]>;
2627
+ /** Display name shown in the compose label ("Commenting as …"). */
2628
+ readonly authorName: _angular_core.InputSignal<string>;
2629
+ /** Emits the trimmed comment text the user wants to add. */
2630
+ readonly add: _angular_core.OutputEmitterRef<string>;
2631
+ /** Emits the id of a comment the user wants to remove. */
2632
+ readonly remove: _angular_core.OutputEmitterRef<string>;
2633
+ /** Emits the id of a comment whose resolved flag should toggle. */
2634
+ readonly resolve: _angular_core.OutputEmitterRef<string>;
2635
+ /** Current text typed into the compose textarea. */
2636
+ readonly draft: _angular_core.WritableSignal<string>;
2637
+ /** Whether the draft has non-whitespace content (enables the submit button). */
2638
+ readonly canAdd: _angular_core.Signal<boolean>;
2639
+ onDraftInput(event: Event): void;
2640
+ submit(event: Event): void;
2641
+ formatTimestamp(value: string | undefined): string;
2642
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<CommentsPanelComponent, never>;
2643
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<CommentsPanelComponent, "pptx-comments-panel", never, { "comments": { "alias": "comments"; "required": false; "isSignal": true; }; "authorName": { "alias": "authorName"; "required": false; "isSignal": true; }; }, { "add": "add"; "remove": "remove"; "resolve": "resolve"; }, never, never, true, never>;
2644
+ }
2645
+
2646
+ /**
2647
+ * comments-helpers.ts — Pure, framework-agnostic comment-array transforms.
2648
+ *
2649
+ * Mirrors the mutation logic in the Vue `useComments` composable and the React
2650
+ * `useComments-helpers`, but operating on a single slide's comment list (the
2651
+ * "active slide" slice). Each mutator returns the NEW full comment array, or
2652
+ * `null` when nothing changed (blank text / id-not-found), so the host can write
2653
+ * the result back history-aware.
2654
+ *
2655
+ * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
2656
+ * no regex named-capture-groups (ng-packagr lib-target constraints).
2657
+ */
2658
+
2659
+ /** Generate a stable, collision-resistant comment id. */
2660
+ declare function generateCommentId(): string;
2661
+ /**
2662
+ * Append a new comment to a comment list.
2663
+ * @returns the NEW full comment array, or `null` when `text` is blank.
2664
+ */
2665
+ declare function addCommentToList(comments: PptxComment[], text: string, authorName: string, x?: number, y?: number): PptxComment[] | null;
2666
+ /**
2667
+ * Remove a comment (by id) from a comment list.
2668
+ * @returns the NEW full comment array, or `null` when nothing changed.
2669
+ */
2670
+ declare function removeCommentFromList(comments: PptxComment[], id: string): PptxComment[] | null;
2671
+ /**
2672
+ * Toggle the `resolved` flag of a comment (by id) in a comment list.
2673
+ * @returns the NEW full comment array, or `null` when nothing changed.
2674
+ */
2675
+ declare function toggleCommentResolvedInList(comments: PptxComment[], id: string): PptxComment[] | null;
2676
+
2677
+ declare class CommentsService {
2678
+ /**
2679
+ * The active slide's comments (the deck slice for `activeSlideIndex`).
2680
+ * `PptxComment` has no slide field, so the host slices the per-slide
2681
+ * `PptxSlide.comments` into this signal.
2682
+ */
2683
+ private readonly _comments;
2684
+ /** Index of the slide whose comments these are. */
2685
+ private readonly _activeSlideIndex;
2686
+ /** Display name written as the `author` of newly-created comments. */
2687
+ private readonly _authorName;
2688
+ /** The active slide's comments (read-only mirror of the input signal). */
2689
+ readonly slideComments: Signal<PptxComment[]>;
2690
+ /** Index of the slide whose comments these are. */
2691
+ readonly activeSlideIndex: Signal<number>;
2692
+ /** Display name written as the `author` of newly-created comments. */
2693
+ readonly authorName: Signal<string>;
2694
+ /** Set the active slide's comments (the deck slice for `activeSlideIndex`). */
2695
+ setComments(comments: PptxComment[] | null | undefined): void;
2696
+ /** Set the index of the slide whose comments these are. */
2697
+ setActiveSlideIndex(index: number): void;
2698
+ /** Set the display name written as the `author` of newly-created comments. */
2699
+ setAuthorName(name: string): void;
2700
+ /**
2701
+ * Append a new comment to the active slide.
2702
+ * @returns the NEW full comment array for the active slide, or `null` when
2703
+ * `text` is blank.
2704
+ */
2705
+ addComment(text: string, x?: number, y?: number): PptxComment[] | null;
2706
+ /**
2707
+ * Remove a comment (by id) from the active slide.
2708
+ * @returns the NEW full comment array, or `null` when nothing changed.
2709
+ */
2710
+ removeComment(id: string): PptxComment[] | null;
2711
+ /**
2712
+ * Toggle the `resolved` flag of a comment (by id) on the active slide.
2713
+ * @returns the NEW full comment array, or `null` when nothing changed.
2714
+ */
2715
+ resolveComment(id: string): PptxComment[] | null;
2716
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<CommentsService, never>;
2717
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<CommentsService>;
2718
+ }
2719
+
2720
+ declare class SignaturesPanelComponent {
2721
+ /** Parsed signatures to inspect (host parses and supplies them). */
2722
+ readonly signatures: _angular_core.InputSignal<ParsedSignature[]>;
2723
+ /** True when the package carries at least one signature part. */
2724
+ readonly signed: _angular_core.Signal<boolean>;
2725
+ /** Coarse-grained "Signed / Invalid / Not signed" classification. */
2726
+ readonly overall: _angular_core.Signal<pptx_angular_viewer.OverallSignatureStatus>;
2727
+ /** Header label for the panel. */
2728
+ readonly headerLabel: _angular_core.Signal<string>;
2729
+ /** "N signature(s)" count label. */
2730
+ readonly countLabel: _angular_core.Signal<string>;
2731
+ key(sig: ParsedSignature, index: number): string;
2732
+ kind(sig: ParsedSignature): string;
2733
+ label(sig: ParsedSignature): string;
2734
+ signer(sig: ParsedSignature): string;
2735
+ timestamp(sig: ParsedSignature): string | undefined;
2736
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SignaturesPanelComponent, never>;
2737
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SignaturesPanelComponent, "pptx-signatures-panel", never, { "signatures": { "alias": "signatures"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2738
+ }
2739
+
2740
+ /**
2741
+ * signatures-helpers.ts — Pure functions for digital-signature status
2742
+ * derivation and presentation.
2743
+ *
2744
+ * Angular port of the logic in the Vue `useSignatures` composable and
2745
+ * `SignaturesPanel.vue`. These functions are framework-agnostic and fully
2746
+ * unit-testable: they never sign, strip, or mutate anything — they only
2747
+ * inspect a supplied list of already-parsed signatures and derive display
2748
+ * state.
2749
+ *
2750
+ * Overall status precedence (worst-wins), mirroring how a signature panel
2751
+ * should warn the user:
2752
+ * invalid > expired > unknownCA > unverified > valid
2753
+ */
2754
+
2755
+ /** Coarse-grained "Signed / Invalid / Not signed" classification. */
2756
+ type OverallSignatureStatus = 'signed' | 'invalid' | 'unsigned';
2757
+ /** Coarse validity bucket for per-signature styling. */
2758
+ type SignatureStatusKind = 'valid' | 'invalid' | 'unknown';
2759
+ /** True when the package carries at least one signature part. */
2760
+ declare function isSigned(signatures: readonly ParsedSignature[]): boolean;
2761
+ /**
2762
+ * The single worst per-signature status across the package, or `undefined`
2763
+ * when the package is unsigned. Useful for badge colouring.
2764
+ */
2765
+ declare function worstStatus(signatures: readonly ParsedSignature[]): SignatureStatus | undefined;
2766
+ /** Coarse-grained "Signed / Invalid / Not signed" classification. */
2767
+ declare function overallStatus(signatures: readonly ParsedSignature[]): OverallSignatureStatus;
2768
+ /** Header label for the panel given the overall package status. */
2769
+ declare function headerLabel(overall: OverallSignatureStatus): string;
2770
+ /** Human-readable label for a per-signature status. */
2771
+ declare function statusLabel(status: SignatureStatus): string;
2772
+ /** Coarse validity bucket for styling: valid / invalid / unknown. */
2773
+ declare function statusKind(status: SignatureStatus): SignatureStatusKind;
2774
+ /** Best-effort signer name: certificate subject, else issuer, else path. */
2775
+ declare function signerName(sig: ParsedSignature): string;
2776
+ /**
2777
+ * Best-effort signing timestamp. The parsed signature does not carry a
2778
+ * dedicated signing-time field in the public shape, so we fall back to the
2779
+ * certificate's validity window when present. Returns `undefined` when no
2780
+ * timestamp can be derived.
2781
+ */
2782
+ declare function signatureTimestamp(sig: ParsedSignature): string | undefined;
2783
+ /** Stable list key for a signature row. */
2784
+ declare function signatureKey(sig: ParsedSignature, index: number): string;
2785
+ /** Human-readable "N signature(s)" count label. */
2786
+ declare function signatureCountLabel(count: number): string;
2787
+
2788
+ declare class SignaturesService {
2789
+ /** Backing source of parsed signatures. */
2790
+ private readonly _signatures;
2791
+ /** The signatures currently being inspected (read-only). */
2792
+ readonly signatures: _angular_core.Signal<ParsedSignature[]>;
2793
+ /** True when the package carries at least one signature part. */
2794
+ readonly isSigned: _angular_core.Signal<boolean>;
2795
+ /**
2796
+ * The single worst per-signature status across the package, or `undefined`
2797
+ * when the package is unsigned. Useful for badge colouring.
2798
+ */
2799
+ readonly status: _angular_core.Signal<SignatureStatus | undefined>;
2800
+ /** Coarse-grained "Signed / Invalid / Not signed" classification. */
2801
+ readonly overall: _angular_core.Signal<OverallSignatureStatus>;
2802
+ /** Number of signatures currently loaded. */
2803
+ readonly signatureCount: _angular_core.Signal<number>;
2804
+ /** Replace the inspected signature list (host parses and supplies it). */
2805
+ setSignatures(signatures: readonly ParsedSignature[] | null | undefined): void;
2806
+ /** Clear all inspected signatures (e.g. when a new file loads). */
2807
+ clear(): void;
2808
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SignaturesService, never>;
2809
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<SignaturesService>;
2810
+ }
2811
+
2812
+ /**
2813
+ * accessibility-helpers.ts — Pure WCAG / PowerPoint-style accessibility checks
2814
+ * for the PPTX viewer.
2815
+ *
2816
+ * No Angular imports; safe to use in web workers or server-side code.
2817
+ *
2818
+ * Mirrors `pptx-viewer-core`'s {@link checkPresentation} entry point, but
2819
+ * `checkPresentation` consumes a full `PptxData` object (it reads
2820
+ * `data.slides`). The viewer only has the slide array to hand, so
2821
+ * {@link collectAccessibilityIssues} re-implements the same aggregation by
2822
+ * calling the individual exported `check*` functions over the slides and
2823
+ * sorting the result identically (by slide index, then severity:
2824
+ * error → warning → tip).
2825
+ *
2826
+ * @module accessibility-helpers
2827
+ */
2828
+
2829
+ /** Severity groups in display order (errors first, then warnings, then tips). */
2830
+ declare const SEVERITY_GROUPS: readonly AccessibilityIssueSeverity[];
2831
+ /** Human-readable heading for each severity group. */
2832
+ declare const SEVERITY_LABELS: Record<AccessibilityIssueSeverity, string>;
2833
+ /** Human-readable label for each issue type. */
2834
+ declare const TYPE_LABELS: Record<AccessibilityIssueType, string>;
2835
+ /** A severity group with its (non-empty) issues, used for rendering. */
2836
+ interface AccessibilityIssueGroup {
2837
+ severity: AccessibilityIssueSeverity;
2838
+ label: string;
2839
+ issues: AccessibilityIssue[];
2840
+ }
2841
+ /**
2842
+ * Run all accessibility checks over the supplied slides and return the issues
2843
+ * sorted by slide index, then severity (error → warning → tip).
2844
+ *
2845
+ * Equivalent to core's {@link checkPresentation}, but operates on a slide array
2846
+ * rather than a full `PptxData` object.
2847
+ *
2848
+ * @param slides - Parsed slides for the current presentation.
2849
+ * @param options - Optional check configuration mirroring
2850
+ * {@link AccessibilityCheckOptions}.
2851
+ */
2852
+ declare function collectAccessibilityIssues(slides: readonly PptxSlide[], options?: AccessibilityCheckOptions): AccessibilityIssue[];
2853
+ /** Total number of issues in a collected list (trivial, but mirrors Vue). */
2854
+ declare function countAccessibilityIssues(issues: readonly AccessibilityIssue[]): number;
2855
+ /**
2856
+ * Partition issues into non-empty severity groups in display order
2857
+ * (errors → warnings → tips). Mirrors the `groups` computed in
2858
+ * `AccessibilityPanel.vue`.
2859
+ */
2860
+ declare function groupIssuesBySeverity(issues: readonly AccessibilityIssue[]): AccessibilityIssueGroup[];
2861
+ /** Human-readable label for an issue type. */
2862
+ declare function issueTypeLabel(type: AccessibilityIssueType): string;
2863
+ /**
2864
+ * Stable-ish track key for an issue (issues have no id of their own).
2865
+ * Mirrors the `issueKey` helper in `AccessibilityPanel.vue`.
2866
+ */
2867
+ declare function issueTrackKey(issue: AccessibilityIssue, index: number): string;
2868
+
2869
+ declare class AccessibilityPanelComponent {
2870
+ /** Already-computed accessibility issues (see {@link AccessibilityService}). */
2871
+ readonly issues: _angular_core.InputSignal<AccessibilityIssue[]>;
2872
+ /** Emits the issue's zero-based slide index when an issue is clicked. */
2873
+ readonly selectSlide: _angular_core.OutputEmitterRef<number>;
2874
+ /** Non-empty severity groups in display order. */
2875
+ readonly groups: _angular_core.Signal<AccessibilityIssueGroup[]>;
2876
+ /** True when there is at least one issue. */
2877
+ readonly hasIssues: _angular_core.Signal<boolean>;
2878
+ typeLabel(type: AccessibilityIssueType): string;
2879
+ /** Stable-ish track key; issues have no id of their own. */
2880
+ issueKey(issue: AccessibilityIssue, index: number): string;
2881
+ onSelect(issue: AccessibilityIssue): void;
2882
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AccessibilityPanelComponent, never>;
2883
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AccessibilityPanelComponent, "pptx-accessibility-panel", never, { "issues": { "alias": "issues"; "required": false; "isSignal": true; }; }, { "selectSlide": "selectSlide"; }, never, never, true, never>;
2884
+ }
2885
+
2886
+ declare class EmbeddedFontsService {
2887
+ /** The generated `@font-face` stylesheet text (also injected into `<head>`). */
2888
+ readonly fontFaceCss: _angular_core.WritableSignal<string>;
2889
+ /**
2890
+ * Distinct CSS `font-family` strings for the embedded families, each with
2891
+ * substitution fallbacks resolved (e.g. `'"Calibri", "Carlito", …'`).
2892
+ */
2893
+ readonly fontFamilies: _angular_core.WritableSignal<string[]>;
2894
+ private styleEl;
2895
+ /** Object URLs minted on the most recent `setFonts`; revoked when superseded. */
2896
+ private liveObjectUrls;
2897
+ /** Mints a Blob object URL for de-obfuscated font bytes (impure side effect). */
2898
+ private readonly mintObjectUrl;
2899
+ constructor();
2900
+ /**
2901
+ * Resolve the supplied embedded fonts into `@font-face` rules, inject them
2902
+ * into the managed `<style>` element, and update the exposed signals.
2903
+ *
2904
+ * Object URLs minted on the *previous* call are revoked here so they don't
2905
+ * leak across re-parses. Pass an empty list (or `null`) to clear everything.
2906
+ */
2907
+ setFonts(fonts: readonly PptxEmbeddedFont[] | null | undefined): void;
2908
+ /**
2909
+ * Remove the injected `<style>` element, revoke all live object URLs, and
2910
+ * reset the signals. Called automatically on destroy; safe to call manually.
2911
+ */
2912
+ dispose(): void;
2913
+ /** Create / update / remove the managed `<style>` element to match `css`. */
2914
+ private syncStyleElement;
2915
+ private removeStyleElement;
2916
+ private revokeObjectUrls;
2917
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<EmbeddedFontsService, never>;
2918
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<EmbeddedFontsService>;
2919
+ }
2920
+
2921
+ /**
2922
+ * embedded-fonts-helpers.ts — Pure (no Angular, no DOM-injection) helpers for
2923
+ * the embedded-font subsystem.
2924
+ *
2925
+ * Angular port of the string-building / format-mapping half of the Vue
2926
+ * `useEmbeddedFonts` composable (which itself ports the `@font-face` half of
2927
+ * the React `useFontInjection` hook). All DOM side effects (injecting the
2928
+ * managed `<style>` element, minting / revoking object URLs, signal wiring)
2929
+ * live in `embedded-fonts.service.ts`; everything here is pure and unit-tested.
2930
+ *
2931
+ * The core load pipeline (`PptxHandlerRuntimeEmbeddedFonts`) already
2932
+ * de-obfuscates the OOXML XOR layer and produces a ready-to-use `dataUrl` for
2933
+ * each style variant, so one {@link PptxEmbeddedFont} corresponds to a single
2934
+ * style variant (regular / bold / italic / boldItalic), distinguished by its
2935
+ * `bold` and `italic` flags. As a defence-in-depth fallback, an entry without a
2936
+ * usable `dataUrl` but carrying obfuscated `originalPartBytes` + `fontGuid`
2937
+ * (or clear-text `rawFontData`) is de-obfuscated here and minted into a `Blob`
2938
+ * object URL by the service.
2939
+ */
2940
+
2941
+ /** DOM id of the managed `<style>` element the service injects into `<head>`. */
2942
+ declare const EMBEDDED_FONTS_STYLE_ID = "pptx-angular-embedded-fonts";
2943
+ /**
2944
+ * True when `url` is safe to interpolate into `src: url("…")`.
2945
+ *
2946
+ * Accepts `blob:` URLs (minted by the service from de-obfuscated bytes) and
2947
+ * base64 `data:font/…` URLs produced by the core loader; everything else is
2948
+ * rejected to prevent CSS-injection / exfiltration via a hostile PPTX.
2949
+ */
2950
+ declare function isInjectableUrl(url: string): boolean;
2951
+ /** Map a resolved CSS font format to the MIME type for a minted `Blob`. */
2952
+ declare function fontMimeForFormat(format: string): string;
2953
+ /**
2954
+ * Normalise an entry's declared format to one of the allowed `format()` hints,
2955
+ * falling back to `truetype`.
2956
+ */
2957
+ declare function normalizeFontFormat(format: string | undefined): string;
2958
+ /** A single resolved variant ready to be emitted as a `@font-face` rule. */
2959
+ interface ResolvedFontVariant {
2960
+ name: string;
2961
+ url: string;
2962
+ format: string;
2963
+ weight: string;
2964
+ style: string;
2965
+ /** Object URL minted for this variant (must be revoked on cleanup). */
2966
+ objectUrl?: string;
2967
+ }
2968
+ /**
2969
+ * Factory the (impure) object-URL minting is delegated to, so the resolution
2970
+ * logic stays pure and testable. The service supplies a real implementation
2971
+ * backed by `Blob` + `URL.createObjectURL`; tests can stub it. Returning
2972
+ * `null` signals object URLs are unsupported in the current runtime.
2973
+ */
2974
+ type ObjectUrlFactory = (bytes: Uint8Array, mime: string) => string | null;
2975
+ /**
2976
+ * Resolve a single embedded-font entry to an injectable variant.
2977
+ *
2978
+ * Prefers the de-obfuscated `dataUrl` produced by the core loader. Falls back
2979
+ * to de-obfuscating `originalPartBytes` with `fontGuid` (or using clear-text
2980
+ * `rawFontData` directly) and minting an object URL via `mintObjectUrl` when no
2981
+ * usable data URL is present. Returns `null` when the entry cannot be safely
2982
+ * rendered.
2983
+ *
2984
+ * Pure aside from the injected `mintObjectUrl` callback (which the service owns).
2985
+ */
2986
+ declare function resolveFontVariant(font: PptxEmbeddedFont, mintObjectUrl: ObjectUrlFactory): ResolvedFontVariant | null;
2987
+ /** Build the `@font-face` rule text for one resolved variant. */
2988
+ declare function buildFontFaceRule(variant: ResolvedFontVariant): string;
2989
+ /** The product of resolving an embedded-font list: CSS, families, live URLs. */
2990
+ interface EmbeddedFontStyles {
2991
+ /** The generated `@font-face` stylesheet text. */
2992
+ fontFaceCss: string;
2993
+ /**
2994
+ * Distinct CSS `font-family` strings for the embedded families, each with
2995
+ * substitution fallbacks resolved (e.g. `'"Calibri", "Carlito", …'`).
2996
+ */
2997
+ fontFamilies: string[];
2998
+ /** Object URLs minted while resolving (the caller must revoke these). */
2999
+ objectUrls: string[];
3000
+ }
3001
+ /**
3002
+ * Resolve a list of embedded fonts into the injectable `@font-face` stylesheet,
3003
+ * the distinct substitution-resolved family strings, and any object URLs minted
3004
+ * along the way (so the caller can track them for revocation).
3005
+ *
3006
+ * Pure aside from the injected `mintObjectUrl` callback.
3007
+ */
3008
+ declare function buildEmbeddedFontStyles(fonts: readonly PptxEmbeddedFont[] | null | undefined, mintObjectUrl: ObjectUrlFactory): EmbeddedFontStyles;
3009
+
3010
+ /**
3011
+ * animation-playback-helpers.ts
3012
+ *
3013
+ * Pure functions backing {@link AnimationPlaybackService} and
3014
+ * {@link AnimationPanelComponent}. Exported separately so they can be
3015
+ * unit-tested without TestBed (vitest + happy-dom).
3016
+ *
3017
+ * A slide carries an ordered list of {@link PptxElementAnimation}s. PowerPoint
3018
+ * groups them into "click groups": an animation triggered `onClick` /
3019
+ * `onShapeClick` / `onHover` starts a new group, while `withPrevious` /
3020
+ * `afterPrevious` / `afterDelay` animations fold into the group that precedes
3021
+ * them (running together or sequentially within that group). Advancing the
3022
+ * presentation one step reveals one more click group.
3023
+ *
3024
+ * Everything here is framework-light: only the preset → CSS mapping is
3025
+ * delegated to {@link resolveAnimationCss} / {@link initialHiddenStyle} from
3026
+ * the shared render layer.
3027
+ */
3028
+
3029
+ /** Minimal CSS-properties shape: kebab-case property → value. */
3030
+ type CSSProperties = Record<string, string>;
3031
+ /** A single click-triggered group of animations that play as one step. */
3032
+ interface AnimationClickGroup {
3033
+ /** Animations belonging to this group, in document order. */
3034
+ animations: PptxElementAnimation[];
3035
+ }
3036
+ /**
3037
+ * Splits an ordered animation list into click groups. The first animation
3038
+ * always begins a group even if it isn't explicitly `onClick` (PowerPoint shows
3039
+ * the first build on the first advance). Subsequent `withPrevious` /
3040
+ * `afterPrevious` animations attach to the group in progress.
3041
+ */
3042
+ declare function buildClickGroups(animations: readonly PptxElementAnimation[]): AnimationClickGroup[];
3043
+ /** Clamp a step into `[0, count]`. */
3044
+ declare function clampStep(value: number, count: number): number;
3045
+ /**
3046
+ * Reveal the next click group. Returns the next step, clamped to `count`.
3047
+ * Equivalent to `clampStep(step + 1, count)`.
3048
+ */
3049
+ declare function advanceStep(step: number, count: number): number;
3050
+ /** Parse the numeric ms duration out of a resolved style's `animation-duration`. */
3051
+ declare function durationOf(style: CSSProperties): number;
3052
+ /**
3053
+ * Resolve the CSS for every animation in the revealed groups (the first `step`
3054
+ * groups). Within a group, `afterPrevious` animations are pushed back by the
3055
+ * accumulated duration of the preceding animations so sequential chains play in
3056
+ * order; `withPrevious` shares the running delay. The last write for an element
3057
+ * id wins (a later emphasis/exit overrides an earlier entrance), matching how a
3058
+ * single CSS `animation` shorthand can only hold one running effect.
3059
+ */
3060
+ declare function revealedElementStyles(groups: readonly AnimationClickGroup[], step: number): Map<string, CSSProperties>;
3061
+ /**
3062
+ * Elements with a pending entrance (in a not-yet-revealed group, i.e. groups at
3063
+ * or beyond `step`) that should be hidden until their group plays. An element
3064
+ * an already-revealed group made visible is never re-hidden.
3065
+ */
3066
+ declare function pendingElementStyles(groups: readonly AnimationClickGroup[], step: number): Map<string, CSSProperties>;
3067
+
3068
+ /** A single rendered playback step (one click group). */
3069
+ interface AnimationStepView {
3070
+ /** 1-based step number for display. */
3071
+ readonly index: number;
3072
+ /** Human label for the step (first animation's preset, "+N more"). */
3073
+ readonly label: string;
3074
+ /** Human label for the step's start trigger. */
3075
+ readonly trigger: string;
3076
+ /** True when this step has been revealed at the current playback position. */
3077
+ readonly revealed: boolean;
3078
+ }
3079
+ declare class AnimationPanelComponent {
3080
+ readonly groups: _angular_core.InputSignal<AnimationClickGroup[]>;
3081
+ readonly step: _angular_core.InputSignal<number>;
3082
+ readonly isPlaying: _angular_core.InputSignal<boolean>;
3083
+ readonly playRequested: _angular_core.OutputEmitterRef<void>;
3084
+ readonly pauseRequested: _angular_core.OutputEmitterRef<void>;
3085
+ readonly stepRequested: _angular_core.OutputEmitterRef<void>;
3086
+ readonly resetRequested: _angular_core.OutputEmitterRef<void>;
3087
+ /** Jump to a 1-based step index (the number of groups to reveal). */
3088
+ readonly seek: _angular_core.OutputEmitterRef<number>;
3089
+ protected readonly stepCount: _angular_core.Signal<number>;
3090
+ protected readonly isComplete: _angular_core.Signal<boolean>;
3091
+ protected readonly steps: _angular_core.Signal<AnimationStepView[]>;
3092
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AnimationPanelComponent, never>;
3093
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AnimationPanelComponent, "pptx-animation-panel", never, { "groups": { "alias": "groups"; "required": true; "isSignal": true; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "isPlaying": { "alias": "isPlaying"; "required": false; "isSignal": true; }; }, { "playRequested": "playRequested"; "pauseRequested": "pauseRequested"; "stepRequested": "stepRequested"; "resetRequested": "resetRequested"; "seek": "seek"; }, never, never, true, never>;
3094
+ }
3095
+
3096
+ declare class AnimationPlaybackService {
3097
+ private readonly destroyRef;
3098
+ /** The current slide's animations, in document/timeline order. */
3099
+ private readonly animations;
3100
+ /**
3101
+ * Externally-controlled playback step (e.g. derived from a parent click
3102
+ * counter). `undefined` means there is no external driver. The internal
3103
+ * manual step (set via advance/play/reset) takes precedence when present.
3104
+ */
3105
+ private readonly externalIndex;
3106
+ /**
3107
+ * Internal, unclamped step. `null` means "follow the external index"; any
3108
+ * number means the host has taken manual control via advance/play/reset.
3109
+ */
3110
+ private readonly manualStep;
3111
+ /** Click groups for the current slide's animations. */
3112
+ readonly groups: _angular_core.Signal<AnimationClickGroup[]>;
3113
+ /** Number of click groups on this slide (i.e. how many `advance()` steps). */
3114
+ readonly groupCount: _angular_core.Signal<number>;
3115
+ /**
3116
+ * The current playback step: how many click groups have been revealed.
3117
+ * Always clamped to the current group count. The manual override wins;
3118
+ * otherwise it follows the external index, defaulting to 0.
3119
+ */
3120
+ readonly step: _angular_core.Signal<number>;
3121
+ /** True when every click group has been revealed. */
3122
+ readonly isComplete: _angular_core.Signal<boolean>;
3123
+ /**
3124
+ * Reactive map of `elementId → CSS properties` to apply for the current step.
3125
+ * Only elements in revealed click groups appear.
3126
+ */
3127
+ readonly elementStyles: _angular_core.Signal<Map<string, CSSProperties>>;
3128
+ /**
3129
+ * Reactive map of `elementId → CSS properties` for elements whose entrance
3130
+ * has not yet been revealed (they should be hidden so they don't flash
3131
+ * visible before their group plays).
3132
+ */
3133
+ readonly pendingStyles: _angular_core.Signal<Map<string, CSSProperties>>;
3134
+ /** Handle of the scheduled rAF auto-advance, or null when idle. */
3135
+ private rafHandle;
3136
+ constructor();
3137
+ /** Feed the current slide's animation list. Resets manual control. */
3138
+ setAnimations(animations: readonly PptxElementAnimation[] | undefined): void;
3139
+ /** Update the external playback index (parent-driven build counter). */
3140
+ setExternalIndex(index: number | undefined): void;
3141
+ /**
3142
+ * Reveal the next click group. Returns `true` if a group was revealed,
3143
+ * `false` if playback was already complete (so the caller can fall through
3144
+ * to slide navigation).
3145
+ */
3146
+ advance(): boolean;
3147
+ /** Reveal every click group at once (jump to the slide's final state). */
3148
+ play(): void;
3149
+ /** Reset playback to before the first click group. */
3150
+ reset(): void;
3151
+ /**
3152
+ * Jump directly to a given step (clamped to the group count) and take manual
3153
+ * control. Useful for scrubbing.
3154
+ */
3155
+ setStep(step: number): void;
3156
+ /**
3157
+ * Auto-advance through every remaining click group on the animation frame,
3158
+ * one group per frame. Stops automatically once playback completes or the
3159
+ * service is destroyed. A no-op when already complete or when
3160
+ * `requestAnimationFrame` is unavailable (SSR).
3161
+ */
3162
+ autoPlay(): void;
3163
+ /** Cancel any in-flight rAF auto-advance. */
3164
+ cancelAutoPlay(): void;
3165
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AnimationPlaybackService, never>;
3166
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<AnimationPlaybackService>;
3167
+ }
3168
+
3169
+ /** A positioned cursor view-model used by the template. */
3170
+ interface PositionedCursor {
3171
+ clientId: number | string;
3172
+ color: string;
3173
+ label: string;
3174
+ transform: string;
3175
+ }
3176
+ declare class CollaborationCursorsComponent {
3177
+ /** Remote collaborators to render, in unscaled slide coordinates. */
3178
+ readonly cursors: _angular_core.InputSignal<RemoteCursor[]>;
3179
+ /** Current canvas zoom factor; cursor positions scale by this. */
3180
+ readonly zoom: _angular_core.InputSignal<number>;
3181
+ /** Precompute positions + labels so the template stays declarative. */
3182
+ protected readonly positioned: _angular_core.Signal<PositionedCursor[]>;
3183
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<CollaborationCursorsComponent, never>;
3184
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<CollaborationCursorsComponent, "pptx-collaboration-cursors", never, { "cursors": { "alias": "cursors"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3185
+ }
3186
+
3187
+ declare class ModalDialogComponent {
3188
+ /** Whether the dialog is visible. */
3189
+ readonly open: _angular_core.InputSignal<boolean>;
3190
+ /** Optional heading shown in the header bar. */
3191
+ readonly title: _angular_core.InputSignal<string>;
3192
+ /** Fired on backdrop click, the `×` button, and `Escape`. */
3193
+ readonly close: _angular_core.OutputEmitterRef<void>;
3194
+ /** Close on `Escape`, regardless of where focus currently sits. */
3195
+ onDocumentKeydown(event: KeyboardEvent): void;
3196
+ requestClose(): void;
3197
+ /**
3198
+ * Backdrop clicks close the dialog; clicks bubbling up from the panel are
3199
+ * stopped in the template, so this only ever fires for the backdrop itself.
3200
+ */
3201
+ onBackdropClick(): void;
3202
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ModalDialogComponent, never>;
3203
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ModalDialogComponent, "pptx-modal-dialog", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; }, { "close": "close"; }, never, ["*", "[footer]"], true, never>;
3204
+ }
3205
+
3206
+ /**
3207
+ * properties-dialog-helpers.ts — Pure (no Angular) helpers for the document
3208
+ * properties dialog.
3209
+ *
3210
+ * Mirrors the read / format / diff logic of the Vue `PropertiesDialog.vue`:
3211
+ * seed an editable draft from the core properties, format read-only
3212
+ * timestamps, and build a minimal `Partial<PptxCoreProperties>` patch that
3213
+ * carries only the fields the user actually changed.
3214
+ *
3215
+ * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
3216
+ * no regex named-capture-groups (ng-packagr lib-target constraints).
3217
+ */
3218
+
3219
+ /**
3220
+ * Subset of {@link PptxCoreProperties} surfaced by the Properties dialog: the
3221
+ * editable metadata (title / creator / subject / keywords) plus read-only
3222
+ * timestamps (created / modified).
3223
+ */
3224
+ type DocumentProperties = Pick<PptxCoreProperties, 'title' | 'creator' | 'subject' | 'keywords' | 'created' | 'modified'>;
3225
+ /** The editable draft fields, all coerced to non-null strings. */
3226
+ interface PropertiesDraft {
3227
+ title: string;
3228
+ creator: string;
3229
+ subject: string;
3230
+ keywords: string;
3231
+ }
3232
+ /**
3233
+ * Build an editable draft from the source properties, coercing each absent
3234
+ * field to an empty string.
3235
+ */
3236
+ declare function seedPropertiesDraft(properties: DocumentProperties): PropertiesDraft;
3237
+ /**
3238
+ * Format a (possibly absent or invalid) ISO timestamp for display. Returns an
3239
+ * em-dash for missing values and echoes the raw string for unparseable ones.
3240
+ */
3241
+ declare function formatPropertyDate(value: string | undefined): string;
3242
+ /**
3243
+ * Diff an edited draft against the source properties and produce a patch that
3244
+ * contains only the changed editable fields. An untouched dialog yields `{}`.
3245
+ */
3246
+ declare function buildPropertiesPatch(properties: DocumentProperties, draft: PropertiesDraft): Partial<PptxCoreProperties>;
3247
+
3248
+ declare class PropertiesDialogComponent {
3249
+ /** Whether the dialog is visible. */
3250
+ readonly open: _angular_core.InputSignal<boolean>;
3251
+ /** Current document metadata. */
3252
+ readonly properties: _angular_core.InputSignal<DocumentProperties>;
3253
+ /** Fired with the edited (changed-only) fields when the user saves. */
3254
+ readonly save: _angular_core.OutputEmitterRef<Partial<PptxCoreProperties>>;
3255
+ /** Fired when the dialog is dismissed. */
3256
+ readonly close: _angular_core.OutputEmitterRef<void>;
3257
+ readonly title: _angular_core.WritableSignal<string>;
3258
+ readonly creator: _angular_core.WritableSignal<string>;
3259
+ readonly subject: _angular_core.WritableSignal<string>;
3260
+ readonly keywords: _angular_core.WritableSignal<string>;
3261
+ constructor();
3262
+ createdDisplay(): string;
3263
+ modifiedDisplay(): string;
3264
+ asValue(event: Event): string;
3265
+ handleSave(): void;
3266
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PropertiesDialogComponent, never>;
3267
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PropertiesDialogComponent, "pptx-properties-dialog", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "properties": { "alias": "properties"; "required": true; "isSignal": true; }; }, { "save": "save"; "close": "close"; }, never, never, true, never>;
3268
+ }
3269
+
3270
+ /**
3271
+ * share-helpers.ts — Pure (no Angular) helpers for the Share dialog.
3272
+ *
3273
+ * Mirrors the form-validation / config-assembly logic of the Vue
3274
+ * `ShareDialog.vue` plus the share-link builder from the React `ShareDialog`.
3275
+ *
3276
+ * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
3277
+ * no regex named-capture-groups (ng-packagr lib-target constraints).
3278
+ */
3279
+
3280
+ /** Prefilled values for the Share form fields. */
3281
+ interface ShareDefaults {
3282
+ roomId?: string;
3283
+ userName?: string;
3284
+ serverUrl?: string;
3285
+ }
3286
+ /** The Share form's editable fields. */
3287
+ interface ShareFormFields {
3288
+ roomId: string;
3289
+ userName: string;
3290
+ serverUrl: string;
3291
+ }
3292
+ /** Seed the form fields from the (optional) defaults, coercing absent values. */
3293
+ declare function seedShareFields(defaults?: ShareDefaults): ShareFormFields;
3294
+ /** Whether all three required fields are non-blank (after trimming). */
3295
+ declare function canStartShare(fields: ShareFormFields): boolean;
3296
+ /**
3297
+ * Assemble a {@link CollaborationConfig} from the (trimmed) form fields, or
3298
+ * `null` when the form is incomplete.
3299
+ */
3300
+ declare function buildCollaborationConfig(fields: ShareFormFields): CollaborationConfig | null;
3301
+ /**
3302
+ * Build a shareable join URL for a session. Returns just the room id when no
3303
+ * `origin`/`pathname` are available (e.g. non-browser environments).
3304
+ */
3305
+ declare function buildShareUrl(roomId: string, serverUrl: string, location?: {
3306
+ origin: string;
3307
+ pathname: string;
3308
+ }): string;
3309
+
3310
+ declare class ShareDialogComponent {
3311
+ /** Whether the dialog is visible. */
3312
+ readonly open: _angular_core.InputSignal<boolean>;
3313
+ /** Prefilled values for the form fields. */
3314
+ readonly defaults: _angular_core.InputSignal<ShareDefaults | undefined>;
3315
+ /** Whether a collaboration session is currently active. */
3316
+ readonly active: _angular_core.InputSignal<boolean>;
3317
+ /** Fired with the assembled config when the user starts sharing. */
3318
+ readonly start: _angular_core.OutputEmitterRef<CollaborationConfig>;
3319
+ /** Fired when the user stops an active session. */
3320
+ readonly stop: _angular_core.OutputEmitterRef<void>;
3321
+ /** Fired when the dialog is dismissed. */
3322
+ readonly close: _angular_core.OutputEmitterRef<void>;
3323
+ readonly roomId: _angular_core.WritableSignal<string>;
3324
+ readonly userName: _angular_core.WritableSignal<string>;
3325
+ readonly serverUrl: _angular_core.WritableSignal<string>;
3326
+ readonly canStart: _angular_core.Signal<boolean>;
3327
+ constructor();
3328
+ asValue(event: Event): string;
3329
+ handleStart(): void;
3330
+ handleStop(): void;
3331
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShareDialogComponent, never>;
3332
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ShareDialogComponent, "pptx-share-dialog", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "defaults": { "alias": "defaults"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, { "start": "start"; "stop": "stop"; "close": "close"; }, never, never, true, never>;
3333
+ }
3334
+
3335
+ declare class HyperlinkDialogComponent {
3336
+ /** Whether the dialog is open. */
3337
+ readonly open: _angular_core.InputSignal<boolean>;
3338
+ /** The element whose hyperlink is being edited, or `null`. */
3339
+ readonly element: _angular_core.InputSignal<PptxElement | null>;
3340
+ /** Emitted when the user applies a change. Payload is a merge patch. */
3341
+ readonly save: _angular_core.OutputEmitterRef<Partial<PptxElement>>;
3342
+ /** Emitted when the dialog should close without saving. */
3343
+ readonly close: _angular_core.OutputEmitterRef<void>;
3344
+ readonly url: _angular_core.WritableSignal<string>;
3345
+ readonly tooltip: _angular_core.WritableSignal<string>;
3346
+ /** Whether the current element already has a hyperlink set. */
3347
+ readonly hasLink: _angular_core.Signal<boolean>;
3348
+ constructor();
3349
+ asValue(event: Event): string;
3350
+ onEnter(event: Event): void;
3351
+ onClose(): void;
3352
+ apply(): void;
3353
+ clear(): void;
3354
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HyperlinkDialogComponent, never>;
3355
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HyperlinkDialogComponent, "pptx-hyperlink-dialog", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "element": { "alias": "element"; "required": false; "isSignal": true; }; }, { "save": "save"; "close": "close"; }, never, never, true, never>;
3356
+ }
3357
+
3358
+ /**
3359
+ * broadcast-helpers.ts — Pure (no Angular) helpers for the Broadcast dialog.
3360
+ *
3361
+ * A broadcast is a one-way collaboration session: the presenter drives slide
3362
+ * navigation and viewers follow along via a shareable link. These helpers
3363
+ * cover the testable, framework-agnostic parts of the Vue `BroadcastDialog.vue`
3364
+ * and React `BroadcastDialog.tsx`: room-id generation, form validation, and
3365
+ * the viewer-link builder.
3366
+ *
3367
+ * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
3368
+ * no regex named-capture-groups (ng-packagr lib-target constraints).
3369
+ */
3370
+ /** Default y-websocket server URL used when no default is supplied. */
3371
+ declare const DEFAULT_BROADCAST_SERVER_URL = "ws://localhost:1234";
3372
+ /** Optional seed values for the broadcast start form. */
3373
+ interface BroadcastDefaults {
3374
+ roomId?: string;
3375
+ serverUrl?: string;
3376
+ }
3377
+ /** The configuration emitted when a broadcast starts. */
3378
+ interface BroadcastConfig {
3379
+ roomId: string;
3380
+ serverUrl: string;
3381
+ }
3382
+ /** Generate a fresh, broadcast-scoped room id (`broadcast-<suffix>`). */
3383
+ declare function generateBroadcastRoomId(): string;
3384
+ /**
3385
+ * Seed the start form from the (optional) defaults, generating a fresh room id
3386
+ * when none is supplied and falling back to the default server URL.
3387
+ */
3388
+ declare function seedBroadcastFields(defaults?: BroadcastDefaults): BroadcastConfig;
3389
+ /** Whether both required fields are non-blank (after trimming). */
3390
+ declare function canStartBroadcast(fields: BroadcastConfig): boolean;
3391
+ /**
3392
+ * Assemble a {@link BroadcastConfig} from the (trimmed) form fields, or `null`
3393
+ * when incomplete.
3394
+ */
3395
+ declare function buildBroadcastConfig(fields: BroadcastConfig): BroadcastConfig | null;
3396
+ /**
3397
+ * Build the shareable viewer follow-link for a broadcast. Returns just the
3398
+ * room id when no `origin`/`pathname` are available (non-browser environments).
3399
+ */
3400
+ declare function buildBroadcastViewerUrl(roomId: string, serverUrl: string, location?: {
3401
+ origin: string;
3402
+ pathname: string;
3403
+ }): string;
3404
+ /** Whether the runtime exposes a usable async clipboard write API. */
3405
+ declare function canUseClipboard(nav: Navigator | undefined): boolean;
3406
+
3407
+ declare class BroadcastDialogComponent {
3408
+ /** Whether the dialog is visible. */
3409
+ readonly open: _angular_core.InputSignal<boolean>;
3410
+ /** Optional `{ roomId, serverUrl }` seed for the start form. */
3411
+ readonly defaults: _angular_core.InputSignal<BroadcastDefaults | undefined>;
3412
+ /** Whether a broadcast is currently running. */
3413
+ readonly active: _angular_core.InputSignal<boolean>;
3414
+ /** The shareable follow link (shown while `active`). */
3415
+ readonly viewerUrl: _angular_core.InputSignal<string | undefined>;
3416
+ /** Fired when the presenter starts a broadcast. */
3417
+ readonly start: _angular_core.OutputEmitterRef<BroadcastConfig>;
3418
+ /** Fired when the presenter stops the active broadcast. */
3419
+ readonly stop: _angular_core.OutputEmitterRef<void>;
3420
+ /** Fired when the dialog is dismissed. */
3421
+ readonly close: _angular_core.OutputEmitterRef<void>;
3422
+ readonly roomId: _angular_core.WritableSignal<string>;
3423
+ readonly serverUrl: _angular_core.WritableSignal<string>;
3424
+ readonly copied: _angular_core.WritableSignal<boolean>;
3425
+ readonly canStart: _angular_core.Signal<boolean>;
3426
+ readonly dialogTitle: _angular_core.Signal<"Broadcasting" | "Broadcast to a live audience">;
3427
+ readonly canCopy: _angular_core.Signal<boolean>;
3428
+ constructor();
3429
+ asValue(event: Event): string;
3430
+ selectAll(event: Event): void;
3431
+ onClose(): void;
3432
+ onStart(): void;
3433
+ onStop(): void;
3434
+ onCopyLink(): void;
3435
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BroadcastDialogComponent, never>;
3436
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BroadcastDialogComponent, "pptx-broadcast-dialog", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "defaults": { "alias": "defaults"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "viewerUrl": { "alias": "viewerUrl"; "required": false; "isSignal": true; }; }, { "start": "start"; "stop": "stop"; "close": "close"; }, never, never, true, never>;
3437
+ }
3438
+
3439
+ /**
3440
+ * hyperlink-dialog-helpers.ts — Pure (no Angular) helpers for the hyperlink
3441
+ * dialog.
3442
+ *
3443
+ * Mirrors the URL + tooltip editing of the Vue `HyperlinkDialog.vue`, which
3444
+ * stores the link on the element-level `actionClick` field (a `PptxAction`).
3445
+ * This module builds the `{ actionClick }` merge patch:
3446
+ * - **Set:** `{ actionClick: { ...existing, url, tooltip } }` (any preexisting
3447
+ * OOXML `action` verb on the element is preserved so slide-jump links
3448
+ * survive).
3449
+ * - **Clear:** `{ actionClick: undefined }` (empty URL, an unsafe URL, or an
3450
+ * explicit "Remove link").
3451
+ *
3452
+ * Reuses the viewer's URL-safety guard (`./hyperlink`) so the dialog never
3453
+ * applies an unsafe href (e.g. `javascript:` / `data:`).
3454
+ *
3455
+ * No `any`; all regexes use the `/u` flag; no `String.prototype.replaceAll`,
3456
+ * no regex named-capture-groups (ng-packagr lib-target constraints).
3457
+ */
3458
+
3459
+ /** Whether the element already has a hyperlink URL set. */
3460
+ declare function hasExistingLink(element: PptxElement | null): boolean;
3461
+ /** The current URL + tooltip seeded into the form from an element. */
3462
+ interface HyperlinkDraft {
3463
+ url: string;
3464
+ tooltip: string;
3465
+ }
3466
+ /** Read the element's current `actionClick.url` / `.tooltip` into a draft. */
3467
+ declare function seedHyperlinkDraft(element: PptxElement | null): HyperlinkDraft;
3468
+ /**
3469
+ * Build the merge patch for applying the dialog's draft to an element.
3470
+ *
3471
+ * Returns `{ actionClick: undefined }` (a clearing patch) when:
3472
+ * - the trimmed URL is empty, or
3473
+ * - the URL is unsafe (blocked scheme) and is NOT a preserved `ppaction://`
3474
+ * verb.
3475
+ *
3476
+ * Otherwise returns `{ actionClick: { ...existing, url, tooltip } }`, dropping
3477
+ * the tooltip when blank and preserving any existing OOXML `action` verb.
3478
+ */
3479
+ declare function buildHyperlinkPatch(element: PptxElement, draft: HyperlinkDraft): Partial<PptxElement>;
3480
+ /** A patch that clears the element's hyperlink entirely. */
3481
+ declare function buildClearHyperlinkPatch(): Partial<PptxElement>;
3482
+
3483
+ declare class PrintDialogComponent {
3484
+ /** All slides in the current presentation. */
3485
+ readonly slides: _angular_core.InputSignal<PptxSlide[]>;
3486
+ /** Zero-based index of the active slide. */
3487
+ readonly activeSlideIndex: _angular_core.InputSignal<number>;
3488
+ /** Default slides-per-page from presentation properties. */
3489
+ readonly defaultSlidesPerPage: _angular_core.InputSignal<number | undefined>;
3490
+ /** Default frame-slides from presentation properties. */
3491
+ readonly defaultFrameSlides: _angular_core.InputSignal<boolean | undefined>;
3492
+ /** Emits the resolved, validated settings when the user clicks Print. */
3493
+ readonly print: _angular_core.OutputEmitterRef<PrintSettings>;
3494
+ /** Emits when the dialog is dismissed (Cancel / Escape / backdrop). */
3495
+ readonly cancel: _angular_core.OutputEmitterRef<void>;
3496
+ /** Authoritative print settings, seeded from defaults. */
3497
+ readonly settings: _angular_core.WritableSignal<PrintSettings>;
3498
+ private _seeded;
3499
+ constructor();
3500
+ /** Number of slides selected by the current range. */
3501
+ readonly slideCount: _angular_core.Signal<number>;
3502
+ /** Estimated printed page count for the current settings. */
3503
+ readonly pageCount: _angular_core.Signal<number>;
3504
+ /** Apply a partial patch from the settings panel and re-validate. */
3505
+ patch(partial: Partial<PrintSettings>): void;
3506
+ onConfirm(): void;
3507
+ onCancel(): void;
3508
+ onBackdropClick(event: MouseEvent): void;
3509
+ onKeydown(event: KeyboardEvent): void;
3510
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PrintDialogComponent, never>;
3511
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PrintDialogComponent, "pptx-print-dialog", never, { "slides": { "alias": "slides"; "required": true; "isSignal": true; }; "activeSlideIndex": { "alias": "activeSlideIndex"; "required": true; "isSignal": true; }; "defaultSlidesPerPage": { "alias": "defaultSlidesPerPage"; "required": false; "isSignal": true; }; "defaultFrameSlides": { "alias": "defaultFrameSlides"; "required": false; "isSignal": true; }; }, { "print": "print"; "cancel": "cancel"; }, never, never, true, never>;
3512
+ }
3513
+
3514
+ declare class PrintSettingsPanelComponent {
3515
+ /** Current print settings (parent-owned). */
3516
+ readonly settings: _angular_core.InputSignal<PrintSettings>;
3517
+ /** Total number of slides in the presentation. */
3518
+ readonly totalSlides: _angular_core.InputSignal<number>;
3519
+ /** Zero-based index of the active slide (for the "current" range label). */
3520
+ readonly activeSlideIndex: _angular_core.InputSignal<number>;
3521
+ /** Emits a partial patch whenever the user changes a field. */
3522
+ readonly settingsChange: _angular_core.OutputEmitterRef<Partial<PrintSettings>>;
3523
+ protected readonly handoutOptions: HandoutSlidesPerPage[];
3524
+ protected readonly printWhatOptions: {
3525
+ value: PrintWhat;
3526
+ label: string;
3527
+ }[];
3528
+ protected readonly orientationOptions: {
3529
+ value: PrintOrientation;
3530
+ label: string;
3531
+ }[];
3532
+ protected readonly colorModeOptions: {
3533
+ value: PrintColorMode;
3534
+ label: string;
3535
+ }[];
3536
+ /** Emit a settings patch. */
3537
+ emit(patch: Partial<PrintSettings>): void;
3538
+ onRangeChange(event: Event, edge: 'from' | 'to'): void;
3539
+ onFrameChange(event: Event): void;
3540
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PrintSettingsPanelComponent, never>;
3541
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PrintSettingsPanelComponent, "pptx-print-settings-panel", never, { "settings": { "alias": "settings"; "required": true; "isSignal": true; }; "totalSlides": { "alias": "totalSlides"; "required": true; "isSignal": true; }; "activeSlideIndex": { "alias": "activeSlideIndex"; "required": true; "isSignal": true; }; }, { "settingsChange": "settingsChange"; }, never, never, true, never>;
3542
+ }
3543
+
3544
+ /**
3545
+ * transition-helpers.ts
3546
+ *
3547
+ * Pure logic for mapping a PowerPoint slide transition (`PptxSlideTransition`
3548
+ * / `PptxTransitionType` from `pptx-viewer-core`) to concrete CSS animation
3549
+ * descriptors for the outgoing (old) and incoming (new) slide layers.
3550
+ *
3551
+ * Angular port of the React `utils/transition-keyframes.ts`,
3552
+ * `utils/transition-helpers.ts`, and `utils/transition-resolver.ts`. Kept
3553
+ * framework-free so it can be unit-tested without TestBed (vitest + happy-dom).
3554
+ *
3555
+ * The CSS `@keyframes` block (`SLIDE_TRANSITION_KEYFRAMES`) is injected once by
3556
+ * `PresentationTransitionOverlayComponent` so the named animations resolve.
3557
+ *
3558
+ * ng-packagr lib-target constraints respected here:
3559
+ * - no `String.prototype.replaceAll`
3560
+ * - no regex named-capture-groups (no regexes used at all)
3561
+ */
3562
+
3563
+ interface SlideTransitionAnimations {
3564
+ /** CSS animation string for the outgoing (old) slide layer. */
3565
+ outgoing: string;
3566
+ /** CSS animation string for the incoming (new) slide layer. */
3567
+ incoming: string;
3568
+ /** Whether the outgoing layer should render on top of the incoming layer. */
3569
+ outgoingOnTop: boolean;
3570
+ }
3571
+ /**
3572
+ * Resolve the effective transition duration (ms) from an optional authored
3573
+ * value, applying the minimum floor and a sensible default. Mirrors the React
3574
+ * `executeSlideTransition` clamping (`Math.max(120, durationMs || 320)`).
3575
+ */
3576
+ declare function resolveTransitionDuration(durationMs: number | undefined): number;
3577
+ declare const SLIDE_TRANSITION_KEYFRAMES = "\n/* Fade */\n@keyframes pptx-tr-fade-in {\n\tfrom { opacity: 0; }\n\tto { opacity: 1; }\n}\n@keyframes pptx-tr-fade-out {\n\tfrom { opacity: 1; }\n\tto { opacity: 0; }\n}\n\n/* Push */\n@keyframes pptx-tr-push-in-from-right {\n\tfrom { transform: translateX(100%); }\n\tto { transform: translateX(0); }\n}\n@keyframes pptx-tr-push-out-to-left {\n\tfrom { transform: translateX(0); }\n\tto { transform: translateX(-100%); }\n}\n@keyframes pptx-tr-push-in-from-left {\n\tfrom { transform: translateX(-100%); }\n\tto { transform: translateX(0); }\n}\n@keyframes pptx-tr-push-out-to-right {\n\tfrom { transform: translateX(0); }\n\tto { transform: translateX(100%); }\n}\n@keyframes pptx-tr-push-in-from-bottom {\n\tfrom { transform: translateY(100%); }\n\tto { transform: translateY(0); }\n}\n@keyframes pptx-tr-push-out-to-top {\n\tfrom { transform: translateY(0); }\n\tto { transform: translateY(-100%); }\n}\n@keyframes pptx-tr-push-in-from-top {\n\tfrom { transform: translateY(-100%); }\n\tto { transform: translateY(0); }\n}\n@keyframes pptx-tr-push-out-to-bottom {\n\tfrom { transform: translateY(0); }\n\tto { transform: translateY(100%); }\n}\n\n/* Cover (incoming slides over stationary outgoing) */\n@keyframes pptx-tr-cover-from-right {\n\tfrom { transform: translateX(100%); }\n\tto { transform: translateX(0); }\n}\n@keyframes pptx-tr-cover-from-left {\n\tfrom { transform: translateX(-100%); }\n\tto { transform: translateX(0); }\n}\n@keyframes pptx-tr-cover-from-bottom {\n\tfrom { transform: translateY(100%); }\n\tto { transform: translateY(0); }\n}\n@keyframes pptx-tr-cover-from-top {\n\tfrom { transform: translateY(-100%); }\n\tto { transform: translateY(0); }\n}\n@keyframes pptx-tr-cover-from-lu {\n\tfrom { transform: translate(-100%, -100%); }\n\tto { transform: translate(0, 0); }\n}\n@keyframes pptx-tr-cover-from-ld {\n\tfrom { transform: translate(-100%, 100%); }\n\tto { transform: translate(0, 0); }\n}\n@keyframes pptx-tr-cover-from-ru {\n\tfrom { transform: translate(100%, -100%); }\n\tto { transform: translate(0, 0); }\n}\n@keyframes pptx-tr-cover-from-rd {\n\tfrom { transform: translate(100%, 100%); }\n\tto { transform: translate(0, 0); }\n}\n\n/* Uncover (outgoing slides away revealing stationary incoming) */\n@keyframes pptx-tr-uncover-to-left {\n\tfrom { transform: translateX(0); }\n\tto { transform: translateX(-100%); }\n}\n@keyframes pptx-tr-uncover-to-right {\n\tfrom { transform: translateX(0); }\n\tto { transform: translateX(100%); }\n}\n@keyframes pptx-tr-uncover-to-top {\n\tfrom { transform: translateY(0); }\n\tto { transform: translateY(-100%); }\n}\n@keyframes pptx-tr-uncover-to-bottom {\n\tfrom { transform: translateY(0); }\n\tto { transform: translateY(100%); }\n}\n@keyframes pptx-tr-uncover-to-lu {\n\tfrom { transform: translate(0, 0); }\n\tto { transform: translate(-100%, -100%); }\n}\n@keyframes pptx-tr-uncover-to-ld {\n\tfrom { transform: translate(0, 0); }\n\tto { transform: translate(-100%, 100%); }\n}\n@keyframes pptx-tr-uncover-to-ru {\n\tfrom { transform: translate(0, 0); }\n\tto { transform: translate(100%, -100%); }\n}\n@keyframes pptx-tr-uncover-to-rd {\n\tfrom { transform: translate(0, 0); }\n\tto { transform: translate(100%, 100%); }\n}\n\n/* Wipe (clip-path reveal) */\n@keyframes pptx-tr-wipe-from-left {\n\tfrom { clip-path: inset(0 100% 0 0); }\n\tto { clip-path: inset(0 0 0 0); }\n}\n@keyframes pptx-tr-wipe-from-right {\n\tfrom { clip-path: inset(0 0 0 100%); }\n\tto { clip-path: inset(0 0 0 0); }\n}\n@keyframes pptx-tr-wipe-from-top {\n\tfrom { clip-path: inset(0 0 100% 0); }\n\tto { clip-path: inset(0 0 0 0); }\n}\n@keyframes pptx-tr-wipe-from-bottom {\n\tfrom { clip-path: inset(100% 0 0 0); }\n\tto { clip-path: inset(0 0 0 0); }\n}\n\n/* Split */\n@keyframes pptx-tr-split-h-out {\n\tfrom { clip-path: inset(0 50%); }\n\tto { clip-path: inset(0 0); }\n}\n@keyframes pptx-tr-split-v-out {\n\tfrom { clip-path: inset(50% 0); }\n\tto { clip-path: inset(0 0); }\n}\n@keyframes pptx-tr-split-h-in {\n\tfrom { clip-path: inset(0 0); }\n\tto { clip-path: inset(0 50%); }\n}\n@keyframes pptx-tr-split-v-in {\n\tfrom { clip-path: inset(0 0); }\n\tto { clip-path: inset(50% 0); }\n}\n\n/* Dissolve */\n@keyframes pptx-tr-dissolve-in {\n\tfrom { opacity: 0; filter: blur(4px); }\n\tto { opacity: 1; filter: blur(0px); }\n}\n\n/* Circle / Diamond / Plus (clip-path shapes) */\n@keyframes pptx-tr-circle-in {\n\tfrom { clip-path: circle(0% at 50% 50%); }\n\tto { clip-path: circle(75% at 50% 50%); }\n}\n@keyframes pptx-tr-diamond-in {\n\tfrom { clip-path: polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%); }\n\tto { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); }\n}\n@keyframes pptx-tr-plus-in {\n\tfrom {\n\t\tclip-path: polygon(\n\t\t\t50% 50%, 50% 50%, 50% 50%, 50% 50%,\n\t\t\t50% 50%, 50% 50%, 50% 50%, 50% 50%,\n\t\t\t50% 50%, 50% 50%, 50% 50%, 50% 50%\n\t\t);\n\t}\n\tto {\n\t\tclip-path: polygon(\n\t\t\t33% 0%, 66% 0%, 66% 33%, 100% 33%,\n\t\t\t100% 66%, 66% 66%, 66% 100%, 33% 100%,\n\t\t\t33% 66%, 0% 66%, 0% 33%, 33% 33%\n\t\t);\n\t}\n}\n\n/* Wedge */\n@keyframes pptx-tr-wedge-in {\n\tfrom { clip-path: polygon(50% 0%, 50% 0%, 50% 0%); }\n\tto { clip-path: polygon(50% 0%, 100% 100%, 0% 100%); }\n}\n\n/* Zoom */\n@keyframes pptx-tr-zoom-in {\n\tfrom { transform: scale(0); opacity: 0; }\n\tto { transform: scale(1); opacity: 1; }\n}\n@keyframes pptx-tr-zoom-out {\n\tfrom { transform: scale(1); opacity: 1; }\n\tto { transform: scale(2); opacity: 0; }\n}\n\n/* Blinds */\n@keyframes pptx-tr-blinds-h {\n\tfrom { clip-path: inset(0 0 100% 0); }\n\tto { clip-path: inset(0); }\n}\n@keyframes pptx-tr-blinds-v {\n\tfrom { clip-path: inset(0 100% 0 0); }\n\tto { clip-path: inset(0); }\n}\n\n/* Checker (approximate with dissolve + contrast) */\n@keyframes pptx-tr-checker-in {\n\tfrom { opacity: 0; filter: contrast(2) blur(2px); }\n\tto { opacity: 1; filter: contrast(1) blur(0); }\n}\n\n/* Comb */\n@keyframes pptx-tr-comb-h {\n\tfrom { clip-path: inset(0 100% 0 0); }\n\tto { clip-path: inset(0); }\n}\n@keyframes pptx-tr-comb-v {\n\tfrom { clip-path: inset(100% 0 0 0); }\n\tto { clip-path: inset(0); }\n}\n\n/* Strips (diagonal) */\n@keyframes pptx-tr-strips-lu {\n\tfrom { clip-path: polygon(0% 0%, 0% 0%, 0% 0%); }\n\tto { clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); }\n}\n@keyframes pptx-tr-strips-ld {\n\tfrom { clip-path: polygon(0% 100%, 0% 100%, 0% 100%); }\n\tto { clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); }\n}\n@keyframes pptx-tr-strips-ru {\n\tfrom { clip-path: polygon(100% 0%, 100% 0%, 100% 0%); }\n\tto { clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); }\n}\n@keyframes pptx-tr-strips-rd {\n\tfrom { clip-path: polygon(100% 100%, 100% 100%, 100% 100%); }\n\tto { clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); }\n}\n\n/* RandomBar */\n@keyframes pptx-tr-randombar-h {\n\tfrom { opacity: 0; clip-path: inset(0 0 100% 0); }\n\tto { opacity: 1; clip-path: inset(0); }\n}\n@keyframes pptx-tr-randombar-v {\n\tfrom { opacity: 0; clip-path: inset(0 100% 0 0); }\n\tto { opacity: 1; clip-path: inset(0); }\n}\n\n/* Newsflash */\n@keyframes pptx-tr-newsflash-in {\n\tfrom { transform: rotate(720deg) scale(0); opacity: 0; }\n\tto { transform: rotate(0deg) scale(1); opacity: 1; }\n}\n\n/* Wheel */\n@keyframes pptx-tr-wheel-in {\n\tfrom { clip-path: circle(0% at 50% 50%); transform: rotate(-180deg); }\n\tto { clip-path: circle(75% at 50% 50%); transform: rotate(0deg); }\n}\n";
3578
+ /**
3579
+ * Return CSS animation descriptors for the outgoing (old) and incoming (new)
3580
+ * slide layers based on the transition type, duration, and direction.
3581
+ *
3582
+ * Mirrors the React `getSlideTransitionAnimations` resolver one-for-one.
3583
+ */
3584
+ declare function getSlideTransitionAnimations(type: PptxTransitionType, durationMs: number, direction: string | undefined, orient?: string | undefined, spokes?: number | undefined): SlideTransitionAnimations;
3585
+
3586
+ /**
3587
+ * PresentationTransitionOverlayComponent — plays a PowerPoint slide transition
3588
+ * over the presentation stage.
3589
+ *
3590
+ * Renders the *outgoing* (previous) slide as an absolutely-positioned layer
3591
+ * with the resolved CSS exit animation; the *incoming* slide is rendered by the
3592
+ * underlying stage (the existing `pptx-presentation-overlay`). The `outgoingOnTop`
3593
+ * descriptor controls whether the outgoing layer sits above or below the stage,
3594
+ * letting cover/uncover/push read correctly. Fires `complete` once the animation
3595
+ * finishes so the orchestrator can tear the overlay down.
3596
+ *
3597
+ * Selector: `pptx-presentation-transition-overlay`
3598
+ *
3599
+ * Inputs:
3600
+ * - `outgoingSlide` (required) — the leaving slide to animate
3601
+ * - `canvasSize` (required) — logical slide dimensions (px)
3602
+ * - `transition` (required) — the transition definition (from the slide)
3603
+ * - `templateElements` — master/layout elements behind the outgoing slide
3604
+ * - `mediaDataUrls` — data-URL map for media assets
3605
+ * - `durationMs` — explicit override; otherwise derived from `transition`
3606
+ *
3607
+ * Outputs:
3608
+ * - `complete` — emits void when the transition animation completes
3609
+ *
3610
+ * Designed to compose with `PresentationOverlayComponent` WITHOUT editing it:
3611
+ * the orchestrator stacks this overlay on top of the stage while a transition
3612
+ * is in flight and removes it on `complete`.
3613
+ */
3614
+ declare class PresentationTransitionOverlayComponent {
3615
+ readonly outgoingSlide: _angular_core.InputSignal<PptxSlide>;
3616
+ readonly canvasSize: _angular_core.InputSignal<CanvasSize>;
3617
+ readonly transition: _angular_core.InputSignal<PptxSlideTransition>;
3618
+ readonly templateElements: _angular_core.InputSignal<readonly PptxElement[]>;
3619
+ readonly mediaDataUrls: _angular_core.InputSignal<Map<string, string>>;
3620
+ /** Explicit duration override (ms). When omitted, derived from `transition`. */
3621
+ readonly durationMs: _angular_core.InputSignal<number | undefined>;
3622
+ readonly complete: _angular_core.OutputEmitterRef<void>;
3623
+ private readonly destroyRef;
3624
+ /** Active completion timer handle, so re-running re-arms cleanly. */
3625
+ private completeTimer;
3626
+ /** Active transition-sound element, paused on teardown. */
3627
+ private audio;
3628
+ /** Whether `complete` has already fired for the current run. */
3629
+ private fired;
3630
+ constructor();
3631
+ /** Effective transition duration (ms), floored/defaulted. */
3632
+ protected readonly resolvedDurationMs: _angular_core.Signal<number>;
3633
+ /** Resolved CSS animation descriptors for the outgoing/incoming layers. */
3634
+ protected readonly animations: _angular_core.Signal<SlideTransitionAnimations>;
3635
+ /** The slide rendered in the animated layer (outgoing + its template). */
3636
+ protected readonly layerSlide: _angular_core.Signal<PptxSlide>;
3637
+ /** Layer container style — animation + stacking relative to the stage. */
3638
+ protected readonly layerStyle: _angular_core.Signal<StyleMap>;
3639
+ /** Fixed-size slide box (the SlideCanvas auto-fits within it). */
3640
+ protected readonly slideBoxStyle: _angular_core.Signal<StyleMap>;
3641
+ private armCompletion;
3642
+ private clearTimer;
3643
+ private playSound;
3644
+ private stopSound;
3645
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PresentationTransitionOverlayComponent, never>;
3646
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PresentationTransitionOverlayComponent, "pptx-presentation-transition-overlay", never, { "outgoingSlide": { "alias": "outgoingSlide"; "required": true; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "transition": { "alias": "transition"; "required": true; "isSignal": true; }; "templateElements": { "alias": "templateElements"; "required": false; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "durationMs": { "alias": "durationMs"; "required": false; "isSignal": true; }; }, { "complete": "complete"; }, never, never, true, never>;
3647
+ }
3648
+
3649
+ /**
3650
+ * PresenterViewComponent — split-screen presenter layout with the current
3651
+ * slide, a next-slide preview, speaker notes (rich or plain), a live clock +
3652
+ * elapsed timer, font-size controls, and navigation controls.
3653
+ *
3654
+ * Angular port of the React `PresenterView.tsx`. Keyboard navigation is owned
3655
+ * by the orchestrator (mirroring React, where `usePresentationKeyboard` handles
3656
+ * keys) — this component registers no document keydown listener. Slide previews
3657
+ * reuse `pptx-slide-canvas` (the master/layout elements are prepended to each
3658
+ * slide's own elements, like the React `templateElements` merge).
3659
+ *
3660
+ * Selector: `pptx-presenter-view`
3661
+ *
3662
+ * Inputs:
3663
+ * - `slides` (required) — all slides in the deck
3664
+ * - `currentSlideIndex` (required) — zero-based active slide index
3665
+ * - `canvasSize` (required) — logical slide dimensions (px)
3666
+ * - `templateElements` — master/layout elements behind every slide
3667
+ * - `mediaDataUrls` — data-URL map for media assets
3668
+ * - `presentationStartTime` — epoch ms when the presentation began (or null)
3669
+ * - `isAudienceWindowOpen` — whether a separate audience window is open
3670
+ *
3671
+ * Outputs:
3672
+ * - `movePresentationSlide` — emits +1 / -1 to step the active slide
3673
+ * - `exit` — emits void to end the presentation
3674
+ * - `openAudienceWindow` — request opening the audience display window
3675
+ * - `closeAudienceWindow` — request closing the audience display window
3676
+ */
3677
+ declare class PresenterViewComponent {
3678
+ protected readonly NOTES_FONT_SIZE_MIN = 10;
3679
+ protected readonly NOTES_FONT_SIZE_MAX = 32;
3680
+ readonly slides: _angular_core.InputSignal<PptxSlide[]>;
3681
+ readonly currentSlideIndex: _angular_core.InputSignal<number>;
3682
+ readonly canvasSize: _angular_core.InputSignal<CanvasSize>;
3683
+ readonly templateElements: _angular_core.InputSignal<readonly PptxElement[]>;
3684
+ readonly mediaDataUrls: _angular_core.InputSignal<Map<string, string>>;
3685
+ readonly presentationStartTime: _angular_core.InputSignal<number | null>;
3686
+ readonly isAudienceWindowOpen: _angular_core.InputSignal<boolean>;
3687
+ readonly movePresentationSlide: _angular_core.OutputEmitterRef<1 | -1>;
3688
+ readonly exit: _angular_core.OutputEmitterRef<void>;
3689
+ readonly openAudienceWindow: _angular_core.OutputEmitterRef<void>;
3690
+ readonly closeAudienceWindow: _angular_core.OutputEmitterRef<void>;
3691
+ /** Live wall-clock (epoch ms), advanced once per second. */
3692
+ private readonly now;
3693
+ /** Speaker-notes font size (px), clamped to the allowed range. */
3694
+ protected readonly notesFontSize: _angular_core.WritableSignal<number>;
3695
+ constructor();
3696
+ protected readonly currentSlide: _angular_core.Signal<PptxSlide | undefined>;
3697
+ protected readonly nextSlide: _angular_core.Signal<PptxSlide | undefined>;
3698
+ /** Current slide with master/layout elements prepended for preview. */
3699
+ protected readonly currentPreviewSlide: _angular_core.Signal<PptxSlide | undefined>;
3700
+ /** Next slide with master/layout elements prepended for preview. */
3701
+ protected readonly nextPreviewSlide: _angular_core.Signal<PptxSlide | undefined>;
3702
+ protected readonly notes: _angular_core.Signal<pptx_angular_viewer.PresenterNotes>;
3703
+ protected readonly clockLabel: _angular_core.Signal<string>;
3704
+ protected readonly elapsedMs: _angular_core.Signal<number>;
3705
+ protected readonly elapsedLabel: _angular_core.Signal<string>;
3706
+ private readonly timerProgress;
3707
+ protected readonly timerPercent: _angular_core.Signal<number>;
3708
+ protected readonly progressValue: _angular_core.Signal<number>;
3709
+ protected readonly slideBadge: _angular_core.Signal<string>;
3710
+ protected readonly counterLabel: _angular_core.Signal<string>;
3711
+ protected readonly notesBodyStyle: _angular_core.Signal<StyleMap>;
3712
+ protected increaseNotesFontSize(): void;
3713
+ protected decreaseNotesFontSize(): void;
3714
+ protected onToggleAudienceWindow(): void;
3715
+ private withTemplate;
3716
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PresenterViewComponent, never>;
3717
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PresenterViewComponent, "pptx-presenter-view", never, { "slides": { "alias": "slides"; "required": true; "isSignal": true; }; "currentSlideIndex": { "alias": "currentSlideIndex"; "required": true; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "templateElements": { "alias": "templateElements"; "required": false; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "presentationStartTime": { "alias": "presentationStartTime"; "required": false; "isSignal": true; }; "isAudienceWindowOpen": { "alias": "isAudienceWindowOpen"; "required": false; "isSignal": true; }; }, { "movePresentationSlide": "movePresentationSlide"; "exit": "exit"; "openAudienceWindow": "openAudienceWindow"; "closeAudienceWindow": "closeAudienceWindow"; }, never, never, true, never>;
3718
+ }
3719
+
3720
+ /**
3721
+ * presenter-view-helpers.ts
3722
+ *
3723
+ * Pure (framework-free) helpers for `PresenterViewComponent` — time/elapsed
3724
+ * formatting, notes font-size clamping, rich-notes segment → view-model
3725
+ * derivation, and current/next-slide selection.
3726
+ *
3727
+ * Angular port of the React `components/presenter-view-utils.ts`. The React
3728
+ * `renderNotesSegments` produced React nodes; here we instead derive a plain
3729
+ * `NotesSegmentViewModel[]` (text + style + break flag) so the logic stays
3730
+ * testable without a renderer and the component template owns the markup.
3731
+ *
3732
+ * Kept TestBed-free (vitest + happy-dom). ng-packagr lib-target constraints:
3733
+ * no `String.prototype.replaceAll`, no regex named-capture-groups.
3734
+ */
3735
+
3736
+ /**
3737
+ * Clamp a notes font size value to the allowed range.
3738
+ */
3739
+ declare function clampNotesFontSize(size: number): number;
3740
+ /**
3741
+ * Format a Date as a locale time string (HH:MM:SS).
3742
+ */
3743
+ declare function formatTime(date: Date): string;
3744
+ /**
3745
+ * Format a millisecond duration as MM:SS, or HH:MM:SS when the elapsed
3746
+ * time is one hour or longer. Sub-second values are floored; negative inputs
3747
+ * are treated as zero.
3748
+ */
3749
+ declare function formatElapsed(elapsedMs: number): string;
3750
+ interface TimerProgress {
3751
+ /** Fill percentage of the current segment, clamped to [0, 100]. */
3752
+ percent: number;
3753
+ /** Zero-based index of the current 5-minute segment. */
3754
+ segment: number;
3755
+ }
3756
+ /**
3757
+ * Derive the timer progress-bar fill (percent within the current 5-minute
3758
+ * segment) and the segment index from an elapsed duration. Mirrors the React
3759
+ * PresenterView `timerProgress` / `timerSegment` computation.
3760
+ */
3761
+ declare function computeTimerProgress(elapsedMs: number): TimerProgress;
3762
+ /** A single rendered notes token for the presenter notes pane. */
3763
+ interface NotesSegmentViewModel {
3764
+ /** Stable key for `@for` tracking. */
3765
+ key: string;
3766
+ /** True when this token is a paragraph break (rendered as a line break). */
3767
+ isBreak: boolean;
3768
+ /** Plain text to render (empty for breaks). */
3769
+ text: string;
3770
+ /** Inline style derived from the segment's text style. */
3771
+ style: StyleMap;
3772
+ }
3773
+ interface PresenterNotes {
3774
+ /** True when rich-text segments are available and non-empty. */
3775
+ hasRichNotes: boolean;
3776
+ /** Derived rich-text view-model tokens (empty when no rich notes). */
3777
+ segments: NotesSegmentViewModel[];
3778
+ /** Plain notes text (used when no rich segments are present). */
3779
+ plainText: string;
3780
+ /** True when there is any displayable notes content (rich or plain). */
3781
+ hasAnyNotes: boolean;
3782
+ }
3783
+ /**
3784
+ * Resolve the presenter notes for a slide: prefer rich `notesSegments`, else
3785
+ * fall back to the plain `notes` string. Mirrors the React PresenterView's
3786
+ * `hasRichNotes` / `notesText` branching.
3787
+ */
3788
+ declare function resolvePresenterNotes(slide: PptxSlide | undefined): PresenterNotes;
3789
+
2211
3790
  /**
2212
3791
  * Shape-geometry clip-path helper.
2213
3792
  *
@@ -2427,5 +4006,5 @@ declare function themeStyle(theme: ViewerTheme | undefined): Record<string, stri
2427
4006
  type ClassValue = string | number | false | null | undefined;
2428
4007
  declare function cn(...values: ClassValue[]): string;
2429
4008
 
2430
- export { ChartRendererComponent, ConnectorRendererComponent, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_FILL_COLOR, DEFAULT_SLIDE_BACKGROUND, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_COLOR, EditorContextMenuComponent, EditorHistory, EditorStateService, EditorToolbarComponent, ElementRendererComponent, EquationRendererComponent, ExportService, FindBarComponent, InkRendererComponent, InspectorPanelComponent, LoadContentService, Model3DRendererComponent, OleRendererComponent, PowerPointViewerComponent, PresentationOverlayComponent, RESIZE_HANDLES, SlideCanvasComponent, SlideSorterOverlayComponent, SlidesPanelComponent, SmartArtRendererComponent, TableRendererComponent, VIEWER_THEME, ZoomRendererComponent, applyMove, applyResize, bringForward, bringToFront, buildCssGradientFromShapeStyle, buildPatternFillCss, bulletIndentPx, cn, collectElementText, collectSlideText, convertOmmlToMathMl, defaultCssVars, defaultRadius, defaultThemeColors, deleteElementsByIds, duplicateElementById, formatAutoNumber, getContainerStyle, getImageSrc, getPatternSvg, getResolvedShapeClipPath, getResolvedShapeClipPathFor, getShapeFillStrokeStyle, getSlideBackgroundStyle, getTextBlockStyle, isPpactionUrl, isUrlSafe, moveElementBy, ommlToMathml, provideViewerTheme, renderToCanvas, resizeElement, resolveHyperlinkHref, resolveParagraphBullet, searchSlides, sendBackward, sendToBack, setElementPosition, themeStyle, themeToCssVars, updateElementById };
2431
- export type { Box, CanvasSize, ClassValue, CollaborationConfig, CollaborationRole, ResizeHandle, StyleMap, ViewerTheme, ViewerThemeColors };
4009
+ export { AccessibilityPanelComponent, AccessibilityService, AnimationPanelComponent, AnimationPlaybackService, BroadcastDialogComponent, CURSOR_PALETTE, ChartRendererComponent, CollaborationCursorsComponent, CollaborationService, CommentsPanelComponent, CommentsService, ConnectorRendererComponent, DEFAULT_BROADCAST_SERVER_URL, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_FILL_COLOR, DEFAULT_PRINT_SETTINGS, DEFAULT_SLIDE_BACKGROUND, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_COLOR, EMBEDDED_FONTS_STYLE_ID, EditorContextMenuComponent, EditorHistory, EditorStateService, EditorToolbarComponent, ElementRendererComponent, EmbeddedFontsService, EquationRendererComponent, ExportService, FindBarComponent, HANDOUT_OPTIONS, HyperlinkDialogComponent, InkRendererComponent, InspectorPanelComponent, LoadContentService, ModalDialogComponent, Model3DRendererComponent, OleRendererComponent, PowerPointViewerComponent, PresentationOverlayComponent, PresentationTransitionOverlayComponent, PresenterViewComponent, PrintDialogComponent, PrintService, PrintSettingsPanelComponent, PropertiesDialogComponent, RESIZE_HANDLES, SEVERITY_GROUPS, SEVERITY_LABELS, SLIDE_TRANSITION_KEYFRAMES, ShareDialogComponent, SignaturesPanelComponent, SignaturesService, SlideCanvasComponent, SlideSorterOverlayComponent, SlidesPanelComponent, SmartArtRendererComponent, TYPE_LABELS, TableRendererComponent, VIEWER_THEME, ZoomRendererComponent, addCommentToList, advanceStep, applyMove, applyResize, assignUserColor, bringForward, bringToFront, buildBroadcastConfig, buildBroadcastViewerUrl, buildClearHyperlinkPatch, buildClickGroups, buildCollaborationConfig, buildCssGradientFromShapeStyle, buildEmbeddedFontStyles, buildFontFaceRule, buildHyperlinkPatch, buildPatternFillCss, buildPrintDocument, buildPropertiesPatch, buildShareUrl, bulletIndentPx, canStartBroadcast, canStartShare, canUseClipboard, clampCursorPosition, clampNotesFontSize, clampStep, cn, collectAccessibilityIssues, collectElementText, collectSlideText, computeHandoutLayout, computePageCount, computeSlideIndices, computeTimerProgress, convertOmmlToMathMl, countAccessibilityIssues, defaultCssVars, defaultRadius, defaultThemeColors, deleteElementsByIds, derivePresenceList, duplicateElementById, durationOf, estimatePageCount, fontMimeForFormat, formatAutoNumber, formatCursorLabel, formatElapsed, formatPropertyDate, formatTime, generateBroadcastRoomId, generateCommentId, getContainerStyle, getImageSrc, getPatternSvg, getResolvedShapeClipPath, getResolvedShapeClipPathFor, getShapeFillStrokeStyle, getSlideBackgroundStyle, getSlideTransitionAnimations, getTextBlockStyle, groupIssuesBySeverity, hasExistingLink, headerLabel, isInjectableUrl, isPpactionUrl, isSigned, isUrlSafe, isValidRoomId, issueTrackKey, issueTypeLabel, moveElementBy, normalizeFontFormat, normalizeSlidesPerPage, ommlToMathml, overallStatus, pendingElementStyles, presenceToCursors, provideViewerTheme, removeCommentFromList, renderToCanvas, resizeElement, resolveFontVariant, resolveHyperlinkHref, resolveParagraphBullet, resolvePresenterNotes, resolveTransitionDuration, revealedElementStyles, sanitizeColor, sanitizeSlideIndex, sanitizeUserName, searchSlides, seedBroadcastFields, seedHyperlinkDraft, seedPropertiesDraft, seedShareFields, sendBackward, sendToBack, setElementPosition, signatureCountLabel, signatureKey, signatureTimestamp, signerName, statusKind, statusLabel, themeStyle, themeToCssVars, toggleCommentResolvedInList, updateElementById, validatePrintSettings, validateRoomId, worstStatus };
4010
+ export type { AccessibilityIssueGroup, AnimationClickGroup, Box, BroadcastConfig, BroadcastDefaults, CSSProperties, CanvasSize, ClassValue, CollaborationConfig, CollaborationRole, DocumentProperties, EmbeddedFontStyles, HandoutSlidesPerPage, HyperlinkDraft, NotesSegmentViewModel, ObjectUrlFactory, OverallSignatureStatus, PresenterNotes, PrintColorMode, PrintDocumentOptions, PrintOrientation, PrintSettings, PrintSlideRange, PrintWhat, PropertiesDraft, RemoteCursor, RemotePresence, ResizeHandle, ResolvedFontVariant, ShareDefaults, ShareFormFields, SignatureStatusKind, SlideTransitionAnimations, StyleMap, TimerProgress, ViewerTheme, ViewerThemeColors };