pptx-angular-viewer 1.1.63 → 1.1.65

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,8 +1,8 @@
1
- import * as pptx_angular_viewer from 'pptx-angular-viewer';
2
1
  import * as _angular_core from '@angular/core';
3
2
  import { Signal, WritableSignal, OnDestroy, OnInit, OnChanges, SimpleChanges, InjectionToken, Provider } from '@angular/core';
4
3
  import * as pptx_viewer_core from 'pptx-viewer-core';
5
- import { PptxElement, ShapeStyle, PptxTextWarpPreset, XmlObject, PptxElementAnimation, PptxSlide, TextSegment, TextStyle, PptxTransitionType, PptxEmbeddedFont, AccessibilityIssueSeverity, AccessibilityIssue, AccessibilityIssueType, AccessibilityCheckOptions, PptxComment, SmartArtLayout, PptxTheme, PptxSlideMaster, PptxCoreProperties, PptxCustomProperty, PptxHeaderFooter, ParsedSignature, PptxTableCell, PptxTableData, PptxCustomShow, PptxPresentationProperties, PptxThemePreset, InkPptxElement, Model3DPptxElement, ZoomPptxElement, PptxSlideTransition, TablePptxElement, ChartPptxElement, SmartArtPptxElement, PptxSmartArtData, PptxTableCellStyle, PptxChartDataLabelOptions, PptxChartAxisType, PptxChartAxisFormatting, PptxChartSeries, PptxChartType, PptxChartDataPoint, PptxChartTrendline, PptxChartErrBars, PptxAnimationPreset, PptxAnimationTrigger, PptxAnimationTimingCurve, PptxAnimationRepeatMode, PptxAnimationDirection, PptxAnimationSequence, SignatureStatus } from 'pptx-viewer-core';
4
+ import { PptxElement, ShapeStyle, PptxTextWarpPreset, XmlObject, PptxElementAnimation, PptxSlide, TextSegment, TextStyle, PptxTransitionType, PptxEmbeddedFont, AccessibilityIssueSeverity, AccessibilityIssue, AccessibilityIssueType, AccessibilityCheckOptions, PptxComment, SmartArtLayout, PptxTheme, PptxSlideMaster, PptxCoreProperties, PptxCustomProperty, PptxHeaderFooter, ParsedSignature, PptxTableCell, PptxTableData, InkPptxElement, PptxCustomShow, PptxPresentationProperties, PptxThemePreset, Model3DPptxElement, ZoomPptxElement, PptxSlideTransition, TablePptxElement, ChartPptxElement, SmartArtPptxElement, PptxSmartArtData, PptxTableCellStyle, PptxChartDataLabelOptions, PptxChartAxisType, PptxChartAxisFormatting, PptxChartSeries, PptxChartType, PptxChartDataPoint, PptxChartTrendline, PptxChartErrBars, PptxAnimationPreset, PptxAnimationTrigger, PptxAnimationTimingCurve, PptxAnimationRepeatMode, PptxAnimationDirection, PptxAnimationSequence, SignatureStatus } from 'pptx-viewer-core';
5
+ import * as pptx_angular_viewer from 'pptx-angular-viewer';
6
6
  import { Options } from 'html2canvas-pro';
7
7
  import { SafeHtml } from '@angular/platform-browser';
8
8
 
@@ -3044,41 +3044,6 @@ declare class MobileBottomBarComponent {
3044
3044
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileBottomBarComponent, "pptx-mobile-bottom-bar", never, { "slideCount": { "alias": "slideCount"; "required": false; "isSignal": true; }; "commentCount": { "alias": "commentCount"; "required": false; "isSignal": true; }; "activeSheet": { "alias": "activeSheet"; "required": false; "isSignal": true; }; }, { "openSlides": "openSlides"; "insert": "insert"; "openFormat": "openFormat"; "openComments": "openComments"; "notes": "notes"; }, never, never, true, never>;
3045
3045
  }
3046
3046
 
3047
- /**
3048
- * presentation-annotations-helpers.ts: Pure geometry helpers for presentation
3049
- * ink annotations (pen, highlighter, eraser, laser).
3050
- *
3051
- * Ported from React:
3052
- * packages/react/src/viewer/components/PresentationAnnotationOverlay.tsx
3053
- * packages/react/src/viewer/hooks/usePresentationAnnotations.ts
3054
- *
3055
- * No Angular dependencies; all functions are pure so they can be unit-tested
3056
- * without TestBed.
3057
- */
3058
- /** A single {x, y} coordinate in slide-space pixels. */
3059
- interface AnnotationPoint {
3060
- x: number;
3061
- y: number;
3062
- }
3063
- /** An ink stroke: a sequence of points with visual properties. */
3064
- interface AnnotationStroke {
3065
- id: string;
3066
- points: AnnotationPoint[];
3067
- color: string;
3068
- width: number;
3069
- /** 1 = opaque (pen); 0.4 = semi-transparent (highlighter). */
3070
- opacity: number;
3071
- }
3072
- /** The tool currently armed in presentation mode. */
3073
- type PresentationTool = 'none' | 'pen' | 'highlighter' | 'eraser' | 'laser';
3074
- /** Per-slide annotation storage: slide index → strokes. */
3075
- type SlideAnnotationMap = Map<number, AnnotationStroke[]>;
3076
- /** Transient laser-pointer position in slide-space pixels. */
3077
- interface LaserPosition {
3078
- x: number;
3079
- y: number;
3080
- }
3081
-
3082
3047
  /** BroadcastChannel name shared between presenter and audience tabs. */
3083
3048
  declare const PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
3084
3049
  /** Unique origin identifier so we only react to our own messages. */
@@ -3517,6 +3482,124 @@ declare class TableRendererComponent {
3517
3482
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<TableRendererComponent, "pptx-table-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; }, { "cellCommit": "cellCommit"; "tableChange": "tableChange"; }, never, never, true, never>;
3518
3483
  }
3519
3484
 
3485
+ /** Live host accessors the canvas-editing controller needs. */
3486
+ interface CanvasEditingHost {
3487
+ readonly canEdit: () => boolean;
3488
+ readonly activeSlide: () => PptxSlide | undefined;
3489
+ readonly activeSlideIndex: () => number;
3490
+ }
3491
+ declare class ViewerCanvasEditingService {
3492
+ private readonly editor;
3493
+ private readonly dialogs;
3494
+ private readonly formatPainter;
3495
+ /** Id of the element being inline text-edited, or null. */
3496
+ readonly editingId: _angular_core.WritableSignal<string | null>;
3497
+ /** Open editor context-menu position (client coords), or null. */
3498
+ readonly contextMenuPos: _angular_core.WritableSignal<{
3499
+ x: number;
3500
+ y: number;
3501
+ } | null>;
3502
+ private host;
3503
+ /** Wire the host accessors (called once from the component constructor). */
3504
+ bind(host: CanvasEditingHost): void;
3505
+ private requireHost;
3506
+ /**
3507
+ * Double-click text edit entry: equations open the equation editor instead
3508
+ * of the inline text editor (mirrors React's dbl-click-to-edit-equation).
3509
+ */
3510
+ onTextEditStart(id: string): void;
3511
+ /** Apply a Ctrl/Cmd+B/I/U toggle from the inline editor (undoable). */
3512
+ onTextFormat(event: {
3513
+ id: string;
3514
+ updates: Partial<TextStyle>;
3515
+ }): void;
3516
+ /** Commit an inline text edit: replace the element's text (one history entry). */
3517
+ onTextCommit(event: {
3518
+ id: string;
3519
+ text: string;
3520
+ }): void;
3521
+ /** Receive a completed ink stroke and append it to the active slide. */
3522
+ onInkStrokeComplete(ink: InkPptxElement): void;
3523
+ /** Receive an eraser hit and delete the targeted ink element. */
3524
+ onEraserHit(id: string): void;
3525
+ /**
3526
+ * Handle an element press from the canvas. Additive (Shift/Ctrl) toggles
3527
+ * membership; a plain press selects the element (keeping it selected if it
3528
+ * already was, so a subsequent drag works).
3529
+ */
3530
+ onElementSelect(event: {
3531
+ id: string;
3532
+ additive: boolean;
3533
+ }): void;
3534
+ /** Empty-stage press: disarm the painter if armed, else clear the selection. */
3535
+ onBackgroundClick(): void;
3536
+ /** Right-click: select the element under the cursor and open the menu. */
3537
+ onContextMenu(event: {
3538
+ id: string | null;
3539
+ x: number;
3540
+ y: number;
3541
+ }): void;
3542
+ /** Update the active slide's background colour. */
3543
+ onSlideBackground(event: Event): void;
3544
+ /** Update the active slide's speaker notes. */
3545
+ onSlideNotes(event: Event): void;
3546
+ /** Update the active slide's speaker notes from the editable NotesPanel. */
3547
+ onNotesUpdate(notes: string): void;
3548
+ onSelectionPaneBringForward(id: string): void;
3549
+ onSelectionPaneSendBackward(id: string): void;
3550
+ onToggleElementHidden(id: string): void;
3551
+ /**
3552
+ * Commit a table cell's inline text edit. Finds the table element on the
3553
+ * active slide, rebuilds its `tableData` with the new cell text, and patches
3554
+ * it through the editor (which records undo history).
3555
+ */
3556
+ onTableCellCommit(event: {
3557
+ id: string;
3558
+ commit: TableCellCommit;
3559
+ }): void;
3560
+ /**
3561
+ * Persist a structural table change originating on the canvas (column / row
3562
+ * drag-resize) as one undoable history entry.
3563
+ */
3564
+ onTableChange(event: {
3565
+ id: string;
3566
+ tableData: PptxTableData;
3567
+ }): void;
3568
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerCanvasEditingService, never>;
3569
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerCanvasEditingService>;
3570
+ }
3571
+
3572
+ /** Live host accessors the cursor broadcast needs. */
3573
+ interface CollabCursorHost {
3574
+ readonly mainElement: () => HTMLElement | undefined;
3575
+ readonly zoom: () => number;
3576
+ readonly canvasSize: () => CanvasSize;
3577
+ readonly activeSlideIndex: () => number;
3578
+ }
3579
+ declare class ViewerCollabCursorService {
3580
+ private readonly collab;
3581
+ /** Timestamp of the last cursor broadcast (throttle gate). */
3582
+ private lastCursorBroadcast;
3583
+ private host;
3584
+ /** Wire the host accessors (called once from the component constructor). */
3585
+ bind(host: CollabCursorHost): void;
3586
+ private requireHost;
3587
+ /**
3588
+ * Remote cursors filtered to the slide the local user is viewing, so peers'
3589
+ * cursors only appear on the shared slide (mirrors React/Vue).
3590
+ */
3591
+ readonly cursors: _angular_core.Signal<pptx_angular_viewer.RemoteCursor[]>;
3592
+ /**
3593
+ * Publish the local cursor while the pointer moves over the canvas. Throttled
3594
+ * to {@link BROADCAST_THROTTLE_MS}; coordinates are mapped from client space
3595
+ * into unscaled slide space (dividing by zoom, matching the cursor overlay)
3596
+ * and clamped to the canvas bounds.
3597
+ */
3598
+ onPointerMove(event: PointerEvent): void;
3599
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerCollabCursorService, never>;
3600
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerCollabCursorService>;
3601
+ }
3602
+
3520
3603
  /** Seed values for the Share dialog's start form. */
3521
3604
  interface ShareDefaults$1 {
3522
3605
  roomId?: string;
@@ -3682,6 +3765,40 @@ declare class ViewerDialogsService {
3682
3765
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerDialogsService>;
3683
3766
  }
3684
3767
 
3768
+ /** Live host accessors the document-properties controller needs. */
3769
+ interface DocumentPropertiesHost {
3770
+ readonly canEdit: () => boolean;
3771
+ readonly selectedElement: () => PptxElement | null;
3772
+ readonly activeSlideIndex: () => number;
3773
+ readonly emitPropertiesChange: (patch: Partial<PptxCoreProperties>) => void;
3774
+ }
3775
+ declare class ViewerDocumentPropertiesService {
3776
+ private readonly editor;
3777
+ private readonly loader;
3778
+ /** Document-properties (Info) dialog visibility. */
3779
+ readonly showProperties: _angular_core.WritableSignal<boolean>;
3780
+ /** Hyperlink-edit dialog visibility. */
3781
+ readonly showHyperlink: _angular_core.WritableSignal<boolean>;
3782
+ /** Local overrides applied to document properties via the Info dialog. */
3783
+ private readonly coreOverride;
3784
+ /** Document core properties (loaded, with any in-session edits merged in). */
3785
+ readonly coreProperties: _angular_core.Signal<PptxCoreProperties>;
3786
+ private host;
3787
+ /** Wire the host accessors (called once from the component constructor). */
3788
+ bind(host: DocumentPropertiesHost): void;
3789
+ private requireHost;
3790
+ /**
3791
+ * Persist a document-properties edit from the Info dialog. Gated on
3792
+ * `canEdit`: viewers may inspect properties but not mutate them (mirrors the
3793
+ * comments / hyperlink edit paths).
3794
+ */
3795
+ onPropertiesSave(patch: Partial<PptxCoreProperties>): void;
3796
+ /** Apply a hyperlink edit to the selected element (one history entry). */
3797
+ onHyperlinkSave(patch: Partial<PptxElement>): void;
3798
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerDocumentPropertiesService, never>;
3799
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerDocumentPropertiesService>;
3800
+ }
3801
+
3685
3802
  /** Live accessors the export loop needs from the host component. */
3686
3803
  interface ExportHost {
3687
3804
  /** The component's active-slide index (read + written to flip the live stage). */
@@ -3744,6 +3861,52 @@ declare class ViewerExportService {
3744
3861
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerExportService>;
3745
3862
  }
3746
3863
 
3864
+ /** Live host accessors the file-IO controller needs. */
3865
+ interface FileIOHost {
3866
+ readonly canEdit: () => boolean;
3867
+ readonly content: () => Uint8Array | ArrayBuffer | null;
3868
+ readonly onOpenFile: () => (() => void) | undefined;
3869
+ readonly slides: () => readonly PptxSlide[];
3870
+ readonly templateElementsBySlideId: () => TemplateElementsBySlideId;
3871
+ readonly emitContentChange: (bytes: Uint8Array) => void;
3872
+ }
3873
+ declare class ViewerFileIOService {
3874
+ private readonly loader;
3875
+ private readonly exportSvc;
3876
+ /**
3877
+ * Built-in File ▸ Open override of the host `content` input. The native
3878
+ * picker sets this to swap the deck in place; a fresh `content` input clears
3879
+ * it so external reloads always win.
3880
+ */
3881
+ readonly contentOverride: _angular_core.WritableSignal<ArrayBuffer | Uint8Array<ArrayBufferLike> | null>;
3882
+ private host;
3883
+ /** Wire the host accessors (called once from the component constructor). */
3884
+ bind(host: FileIOHost): void;
3885
+ private requireHost;
3886
+ /** The currently active content: a picked-file override, else the host `content` input. */
3887
+ activeContent(): Uint8Array | ArrayBuffer | null;
3888
+ /** The loaded source `.pptx` bytes (for elected-writer write-back), if any. */
3889
+ sourceBytes(): Uint8Array | null;
3890
+ /**
3891
+ * Serialise the current presentation to `.pptx` bytes. When editing, this
3892
+ * serialises the editor's edited deck so changes persist.
3893
+ */
3894
+ getContent(): Promise<Uint8Array>;
3895
+ /**
3896
+ * Serialise the current deck and trigger a browser download of the `.pptx`.
3897
+ * Surfaced on the mobile toolbar so saving is reachable without the desktop
3898
+ * ribbon's File tab.
3899
+ */
3900
+ saveAsPptx(): Promise<void>;
3901
+ /**
3902
+ * File ▸ Open: host override (`onOpenFile` input) takes precedence; otherwise
3903
+ * a built-in native picker loads the chosen presentation in place.
3904
+ */
3905
+ openFile(): void;
3906
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerFileIOService, never>;
3907
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerFileIOService>;
3908
+ }
3909
+
3747
3910
  /** Emitted when the user changes the find query or the case-sensitive toggle. */
3748
3911
  interface FindEvent {
3749
3912
  query: string;
@@ -3878,70 +4041,292 @@ declare class ViewerFormatPainterService {
3878
4041
  }
3879
4042
 
3880
4043
  /**
3881
- * PowerPointViewerComponent: Angular port of the React `PowerPointViewer.tsx`
3882
- * and Vue `PowerPointViewer.vue`.
3883
- *
3884
- * Top-level orchestrator that loads `.pptx` bytes and renders the slides with
3885
- * navigation and zoom, composing the full editor (toolbar, inspector panels,
3886
- * dialogs, presentation mode, collaboration, export) like its React and Vue
3887
- * counterparts.
4044
+ * swipe-dismiss.ts: Reusable pointer-drag-to-dismiss gesture for a docked
4045
+ * sheet/panel that lives in normal document flow (not a fixed overlay), so a
4046
+ * downward swipe is surfaced as a live drag offset (applied via a CSS
4047
+ * transform) rather than relying on a native overlay's dismiss gesture.
3888
4048
  *
3889
- * Conventions vs. React/Vue:
3890
- * - React `forwardRef` handle / Vue `defineExpose` → public {@link getContent}
3891
- * method (reach it via a template ref or `viewChild`).
3892
- * - React callback props / Vue emits → Angular `output()` events.
3893
- * - React theme context / Vue provide-inject → `themeStyle` CSS vars applied to
3894
- * the root element (app-wide sharing via `provideViewerTheme`).
4049
+ * Shared by the mobile notes sheet and the mobile inspector host in
4050
+ * {@link PowerPointViewerComponent}, which both need identical drag-to-dismiss
4051
+ * behaviour, differing only in what happens once the drag clears the
4052
+ * threshold (their own `onDismiss` callback).
3895
4053
  */
3896
- declare class PowerPointViewerComponent {
3897
- /** PowerPoint content as Uint8Array (or ArrayBuffer). */
3898
- readonly content: _angular_core.InputSignal<ArrayBuffer | Uint8Array<ArrayBufferLike> | null>;
3899
- /** Whether editing actions are enabled. (Editor chrome not yet ported.) */
3900
- readonly canEdit: _angular_core.InputSignal<boolean>;
3901
- /** Optional class applied to the root element. */
3902
- readonly class: _angular_core.InputSignal<string>;
3903
- /** Theme configuration for customising the viewer's appearance. */
3904
- readonly theme: _angular_core.InputSignal<ViewerTheme | undefined>;
4054
+ /** Live drag state + pointer handlers for one swipe-to-dismiss surface. */
4055
+ interface SwipeDismissDrag {
4056
+ /** Live downward drag offset (px; 0 when idle). */
4057
+ readonly dragY: () => number;
4058
+ /** True while a drag is in progress (disables the snap-back transition). */
4059
+ readonly dragging: () => boolean;
4060
+ onPointerDown(event: PointerEvent): void;
4061
+ onPointerMove(event: PointerEvent): void;
4062
+ onPointerUp(event: PointerEvent): void;
4063
+ }
4064
+
4065
+ /** The explicit right-docked tool panels a ribbon/bottom-bar button can toggle. */
4066
+ type InspectorToolPanel = 'comments' | 'accessibility' | 'signatures' | 'selection';
4067
+ /** What the single inspector host is actually showing, after precedence rules. */
4068
+ type InspectorContent = InspectorToolPanel | 'element' | 'slide' | null;
4069
+ /** Live host accessors the content precedence needs. */
4070
+ interface InspectorPanelHost {
4071
+ readonly canEdit: () => boolean;
4072
+ readonly selectedElement: () => PptxElement | null;
4073
+ readonly activeSlide: () => PptxSlide | undefined;
4074
+ }
4075
+ declare class ViewerInspectorPanelService {
4076
+ private readonly mobile;
4077
+ /** Active right-docked tool panel (comments / accessibility / selection), or null. */
4078
+ readonly activePanel: _angular_core.WritableSignal<InspectorToolPanel | null>;
4079
+ /** True once the user swiped the inspector away on mobile (until reopened). */
4080
+ readonly mobileInspectorHidden: _angular_core.WritableSignal<boolean>;
3905
4081
  /**
3906
- * Host file path/identifier keying the version-history store. When omitted
3907
- * the version-history panel shows its empty state. Mirrors React's
3908
- * `filePath` prop.
4082
+ * Swipe-to-dismiss drag for the inspector host. The host docks in-flow below
4083
+ * the canvas on mobile (same keyboard-reachability reason as the notes
4084
+ * sheet), so the gesture is wired here rather than via a fixed-overlay
4085
+ * dismiss. Clearing the past-threshold drag also closes any open tool panel.
3909
4086
  */
3910
- readonly filePath: _angular_core.InputSignal<string | undefined>;
3911
- /** Optional real-time collaboration config; when set, connects and shows remote cursors. */
3912
- readonly collaboration: _angular_core.InputSignal<CollaborationConfig | undefined>;
4087
+ readonly inspectorDrag: SwipeDismissDrag;
4088
+ private host;
4089
+ /** Wire the host accessors (called once from the component constructor). */
4090
+ bind(host: InspectorPanelHost): void;
4091
+ private requireHost;
3913
4092
  /**
3914
- * Display name for the local user in collaboration/broadcast sessions and
3915
- * presence avatars. Falls back to "You" (cursors/avatars) and "Presenter"
3916
- * (broadcast owner) when omitted. Mirrors the React/Vue `authorName` prop.
4093
+ * Which panel the single inspector host should show, applying the original
4094
+ * first-match precedence (explicit tool panels element slide default).
4095
+ * `accessibility`/`signatures` render regardless of edit mode; the rest need
4096
+ * `canEdit`.
3917
4097
  */
3918
- readonly authorName: _angular_core.InputSignal<string | undefined>;
4098
+ readonly inspectorContent: _angular_core.Signal<InspectorContent>;
3919
4099
  /**
3920
- * Seed values for the Share dialog's start form (and the broadcast server
3921
- * URL). Lets the host pre-fill the room id / user name / server URL. Mirrors
3922
- * the React/Vue `shareDefaults` prop.
4100
+ * Whether the right-docked inspector is showing the format panel (element or
4101
+ * slide properties). Drives the top-bar inspector-toggle active state.
3923
4102
  */
3924
- readonly shareDefaults: _angular_core.InputSignal<{
3925
- roomId?: string;
3926
- userName?: string;
3927
- serverUrl?: string;
3928
- } | undefined>;
4103
+ readonly inspectorPaneOpen: _angular_core.Signal<boolean>;
4104
+ /** Inspector content, but null on mobile once the user has swiped it away. */
4105
+ readonly visibleInspectorKind: _angular_core.Signal<InspectorContent>;
4106
+ /** Accessible-label translation key for the inspector host, by active content. */
4107
+ readonly inspectorLabel: _angular_core.Signal<"" | "pptx.toolbar.comments" | "pptx.accessibility.title" | "pptx.viewer.digitalSignatures" | "pptx.selectionPane.title" | "pptx.viewer.elementProperties" | "pptx.viewer.slideProperties">;
4108
+ /** Toggle a right-docked tool panel (clicking the active one closes it). */
4109
+ togglePanel(panel: InspectorToolPanel): void;
4110
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerInspectorPanelService, never>;
4111
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerInspectorPanelService>;
4112
+ }
4113
+
4114
+ /** Live host accessors the mobile-insert action needs. */
4115
+ interface MobileSheetHost {
4116
+ readonly canEdit: () => boolean;
4117
+ readonly slideCount: () => number;
4118
+ readonly activeSlideIndex: () => number;
4119
+ }
4120
+ declare class ViewerMobileSheetService {
4121
+ private readonly editor;
4122
+ /** Open mobile bottom-sheet (slides / menu), or null. */
4123
+ readonly mobileSheet: _angular_core.WritableSignal<"menu" | "slides" | null>;
4124
+ /** Speaker-notes strip visibility. */
4125
+ readonly showNotes: _angular_core.WritableSignal<boolean>;
3929
4126
  /**
3930
- * Host override for the File ▸ Open action. When set, the built-in native
3931
- * file picker is bypassed and this is invoked instead; the host then supplies
3932
- * a new `content` value. When omitted, the viewer opens its own picker and
3933
- * loads the chosen presentation in place.
4127
+ * Swipe-to-dismiss drag for the notes sheet. The sheet stays in normal flow
4128
+ * (see template/CSS notes), so the drag gesture is wired here rather than
4129
+ * via a fixed-overlay dismiss.
3934
4130
  */
3935
- readonly onOpenFile: _angular_core.InputSignal<(() => void) | undefined>;
4131
+ readonly notesDrag: SwipeDismissDrag;
4132
+ private host;
4133
+ /** Wire the host accessors (called once from the component constructor). */
4134
+ bind(host: MobileSheetHost): void;
4135
+ private requireHost;
4136
+ /** Toggle the speaker-notes strip. */
4137
+ toggleNotes(): void;
3936
4138
  /**
3937
- * Opt in to the experimental Three.js SmartArt renderer. When `true`,
3938
- * SmartArt diagrams render as extruded 3D blocks on a WebGL canvas instead
3939
- * of flat SVG. Requires the optional `three` peer dependency; when it is not
3940
- * installed (or the diagram has no geometry), the viewer transparently falls
3941
- * back to the SVG SmartArt renderer. Default `false`.
4139
+ * Mobile quick-insert: drop a text box on the active slide. Mirrors React's
4140
+ * mobile bottom-bar "Insert" slot (a text box is the most common starter
4141
+ * element on a phone; the full Insert section lives in the top-bar menu).
3942
4142
  */
3943
- readonly smartArt3D: _angular_core.InputSignal<boolean>;
3944
- /** Fired when the active slide changes. */
4143
+ onMobileInsert(): void;
4144
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerMobileSheetService, never>;
4145
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerMobileSheetService>;
4146
+ }
4147
+
4148
+ /**
4149
+ * presentation-annotations-helpers.ts: Pure geometry helpers for presentation
4150
+ * ink annotations (pen, highlighter, eraser, laser).
4151
+ *
4152
+ * Ported from React:
4153
+ * packages/react/src/viewer/components/PresentationAnnotationOverlay.tsx
4154
+ * packages/react/src/viewer/hooks/usePresentationAnnotations.ts
4155
+ *
4156
+ * No Angular dependencies; all functions are pure so they can be unit-tested
4157
+ * without TestBed.
4158
+ */
4159
+ /** A single {x, y} coordinate in slide-space pixels. */
4160
+ interface AnnotationPoint {
4161
+ x: number;
4162
+ y: number;
4163
+ }
4164
+ /** An ink stroke: a sequence of points with visual properties. */
4165
+ interface AnnotationStroke {
4166
+ id: string;
4167
+ points: AnnotationPoint[];
4168
+ color: string;
4169
+ width: number;
4170
+ /** 1 = opaque (pen); 0.4 = semi-transparent (highlighter). */
4171
+ opacity: number;
4172
+ }
4173
+ /** The tool currently armed in presentation mode. */
4174
+ type PresentationTool = 'none' | 'pen' | 'highlighter' | 'eraser' | 'laser';
4175
+ /** Per-slide annotation storage: slide index → strokes. */
4176
+ type SlideAnnotationMap = Map<number, AnnotationStroke[]>;
4177
+ /** Transient laser-pointer position in slide-space pixels. */
4178
+ interface LaserPosition {
4179
+ x: number;
4180
+ y: number;
4181
+ }
4182
+
4183
+ /** Live host accessors the presentation-mode controller needs. */
4184
+ interface PresentationModeHost {
4185
+ readonly slideCount: () => number;
4186
+ readonly activeSlideIndex: () => number;
4187
+ readonly setActiveSlideIndex: (index: number) => void;
4188
+ readonly clearEditing: () => void;
4189
+ readonly clearSelection: () => void;
4190
+ readonly sourceContent: () => Uint8Array | ArrayBuffer | null;
4191
+ readonly canEdit: () => boolean;
4192
+ readonly promptKeepAnnotations: (map: SlideAnnotationMap) => void;
4193
+ }
4194
+ declare class ViewerPresentationModeService {
4195
+ private readonly loader;
4196
+ private readonly presenterWindow;
4197
+ private readonly customShowsCtl;
4198
+ /** Fullscreen presentation-mode overlay visibility. */
4199
+ readonly presenting: _angular_core.WritableSignal<boolean>;
4200
+ /** Presenter-view (speaker) overlay visibility. */
4201
+ readonly presentingPresenter: _angular_core.WritableSignal<boolean>;
4202
+ /** Epoch ms when presenter view started (drives the elapsed timer). */
4203
+ readonly presenterStartTime: _angular_core.WritableSignal<number | null>;
4204
+ private host;
4205
+ /** Wire the host accessors (called once from the component constructor). */
4206
+ bind(host: PresentationModeHost): void;
4207
+ private requireHost;
4208
+ /** Open the fullscreen presentation overlay from the current slide. */
4209
+ present(): void;
4210
+ /**
4211
+ * Map a presentation-overlay index back to the full-deck `activeSlideIndex`.
4212
+ * The overlay's index is relative to the (possibly custom-show-filtered)
4213
+ * presentation slides, so resolve by slide id to keep the editor selection
4214
+ * correct when the show closes.
4215
+ */
4216
+ onPresentationIndexChange(index: number): void;
4217
+ /**
4218
+ * Open a separate audience tab and hand off the deck via the shared
4219
+ * IndexedDB store. Mirrors React's presenter "open audience window".
4220
+ */
4221
+ openAudienceWindow(): void;
4222
+ /** Open the presenter (speaker) view: current+next slide, notes, timer. */
4223
+ presentPresenter(): void;
4224
+ /** Close the presenter view (and any audience overlay/window it opened). */
4225
+ exitPresenter(): void;
4226
+ /** Presentation exited with ink on it: offer the keep/discard prompt. */
4227
+ onPresentationAnnotationsExit(map: SlideAnnotationMap): void;
4228
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerPresentationModeService, never>;
4229
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerPresentationModeService>;
4230
+ }
4231
+
4232
+ declare class ViewerThemeGalleryService {
4233
+ private readonly editor;
4234
+ private readonly loader;
4235
+ /** Whether the theme-gallery overlay is visible (Design → Browse Themes). */
4236
+ readonly showThemeGallery: _angular_core.WritableSignal<boolean>;
4237
+ /** The `name` property of the loaded deck's theme (for check-mark in gallery). */
4238
+ readonly activeThemeName: _angular_core.Signal<string | undefined>;
4239
+ /**
4240
+ * Apply a built-in theme preset to the whole deck.
4241
+ *
4242
+ * Mirrors Vue's `applyThemePreset()`: re-resolves slide colours via core's
4243
+ * pure `applyThemeToData`, then writes the updated slides + theme metadata
4244
+ * into `EditorStateService` as a single undoable entry. Also refreshes the
4245
+ * `loader.themeColorMap` so subsequent theme switches start from the correct
4246
+ * baseline.
4247
+ */
4248
+ applyThemePreset(preset: PptxThemePreset): void;
4249
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerThemeGalleryService, never>;
4250
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerThemeGalleryService>;
4251
+ }
4252
+
4253
+ declare class ViewerZoomService {
4254
+ /** Current zoom multiplier applied to the main slide canvas (1 = 100%). */
4255
+ readonly zoom: _angular_core.WritableSignal<number>;
4256
+ /** {@link zoom} rounded to a whole percentage for display. */
4257
+ readonly zoomPercent: _angular_core.Signal<number>;
4258
+ zoomIn(): void;
4259
+ zoomOut(): void;
4260
+ zoomReset(): void;
4261
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewerZoomService, never>;
4262
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ViewerZoomService>;
4263
+ }
4264
+
4265
+ /**
4266
+ * PowerPointViewerComponent: Angular port of the React `PowerPointViewer.tsx`
4267
+ * and Vue `PowerPointViewer.vue`.
4268
+ *
4269
+ * Top-level orchestrator that loads `.pptx` bytes and renders the slides with
4270
+ * navigation and zoom, composing the full editor (toolbar, inspector panels,
4271
+ * dialogs, presentation mode, collaboration, export) like its React and Vue
4272
+ * counterparts.
4273
+ *
4274
+ * Conventions vs. React/Vue:
4275
+ * - React `forwardRef` handle / Vue `defineExpose` → public {@link getContent}
4276
+ * method (reach it via a template ref or `viewChild`).
4277
+ * - React callback props / Vue emits → Angular `output()` events.
4278
+ * - React theme context / Vue provide-inject → `themeStyle` CSS vars applied to
4279
+ * the root element (app-wide sharing via `provideViewerTheme`).
4280
+ */
4281
+ declare class PowerPointViewerComponent {
4282
+ /** PowerPoint content as Uint8Array (or ArrayBuffer). */
4283
+ readonly content: _angular_core.InputSignal<ArrayBuffer | Uint8Array<ArrayBufferLike> | null>;
4284
+ /** Whether editing actions are enabled. (Editor chrome not yet ported.) */
4285
+ readonly canEdit: _angular_core.InputSignal<boolean>;
4286
+ /** Optional class applied to the root element. */
4287
+ readonly class: _angular_core.InputSignal<string>;
4288
+ /** Theme configuration for customising the viewer's appearance. */
4289
+ readonly theme: _angular_core.InputSignal<ViewerTheme | undefined>;
4290
+ /**
4291
+ * Host file path/identifier keying the version-history store. When omitted
4292
+ * the version-history panel shows its empty state. Mirrors React's
4293
+ * `filePath` prop.
4294
+ */
4295
+ readonly filePath: _angular_core.InputSignal<string | undefined>;
4296
+ /** Optional real-time collaboration config; when set, connects and shows remote cursors. */
4297
+ readonly collaboration: _angular_core.InputSignal<CollaborationConfig | undefined>;
4298
+ /**
4299
+ * Display name for the local user in collaboration/broadcast sessions and
4300
+ * presence avatars. Falls back to "You" (cursors/avatars) and "Presenter"
4301
+ * (broadcast owner) when omitted. Mirrors the React/Vue `authorName` prop.
4302
+ */
4303
+ readonly authorName: _angular_core.InputSignal<string | undefined>;
4304
+ /**
4305
+ * Seed values for the Share dialog's start form (and the broadcast server
4306
+ * URL). Lets the host pre-fill the room id / user name / server URL. Mirrors
4307
+ * the React/Vue `shareDefaults` prop.
4308
+ */
4309
+ readonly shareDefaults: _angular_core.InputSignal<{
4310
+ roomId?: string;
4311
+ userName?: string;
4312
+ serverUrl?: string;
4313
+ } | undefined>;
4314
+ /**
4315
+ * Host override for the File ▸ Open action. When set, the built-in native
4316
+ * file picker is bypassed and this is invoked instead; the host then supplies
4317
+ * a new `content` value. When omitted, the viewer opens its own picker and
4318
+ * loads the chosen presentation in place.
4319
+ */
4320
+ readonly onOpenFile: _angular_core.InputSignal<(() => void) | undefined>;
4321
+ /**
4322
+ * Opt in to the experimental Three.js SmartArt renderer. When `true`,
4323
+ * SmartArt diagrams render as extruded 3D blocks on a WebGL canvas instead
4324
+ * of flat SVG. Requires the optional `three` peer dependency; when it is not
4325
+ * installed (or the diagram has no geometry), the viewer transparently falls
4326
+ * back to the SVG SmartArt renderer. Default `false`.
4327
+ */
4328
+ readonly smartArt3D: _angular_core.InputSignal<boolean>;
4329
+ /** Fired when the active slide changes. */
3945
4330
  readonly activeSlideChange: _angular_core.OutputEmitterRef<number>;
3946
4331
  /** Fired when the unsaved-changes flag toggles. */
3947
4332
  readonly dirtyChange: _angular_core.OutputEmitterRef<boolean>;
@@ -3958,7 +4343,6 @@ declare class PowerPointViewerComponent {
3958
4343
  /** Fired when the collaboration/broadcast session stops. Mirrors React/Vue. */
3959
4344
  readonly stopCollaboration: _angular_core.OutputEmitterRef<void>;
3960
4345
  protected readonly loader: LoadContentService;
3961
- private readonly exportSvc;
3962
4346
  protected readonly editor: EditorStateService;
3963
4347
  private readonly fonts;
3964
4348
  protected readonly collab: CollaborationService;
@@ -3976,6 +4360,16 @@ declare class PowerPointViewerComponent {
3976
4360
  protected readonly session: ViewerCollaborationSessionService;
3977
4361
  protected readonly formatPainter: ViewerFormatPainterService;
3978
4362
  private readonly keyboard;
4363
+ protected readonly zoomSvc: ViewerZoomService;
4364
+ private readonly touchGestures;
4365
+ protected readonly presentationMode: ViewerPresentationModeService;
4366
+ protected readonly mobileSheetSvc: ViewerMobileSheetService;
4367
+ protected readonly inspectorPanel: ViewerInspectorPanelService;
4368
+ protected readonly fileIO: ViewerFileIOService;
4369
+ protected readonly themeGallery: ViewerThemeGalleryService;
4370
+ protected readonly canvasEditing: ViewerCanvasEditingService;
4371
+ protected readonly collabCursor: ViewerCollabCursorService;
4372
+ protected readonly docProperties: ViewerDocumentPropertiesService;
3979
4373
  /** Handle on the secondary-dialog host (keep-annotations prompt). */
3980
4374
  private readonly extraDialogs;
3981
4375
  /** Surface the encrypted-file notice dialog alongside the inline fallback. */
@@ -3999,27 +4393,8 @@ declare class PowerPointViewerComponent {
3999
4393
  /** Inherited template (master/layout) elements for the active slide, when editing. */
4000
4394
  protected readonly activeTemplateElements: _angular_core.Signal<readonly PptxElement[]>;
4001
4395
  protected readonly rootStyle: _angular_core.Signal<Record<string, string>>;
4002
- protected readonly zoom: _angular_core.WritableSignal<number>;
4003
- protected readonly zoomPercent: _angular_core.Signal<number>;
4004
- /**
4005
- * Remote cursors filtered to the slide the local user is viewing, so peers'
4006
- * cursors only appear on the shared slide (mirrors React/Vue).
4007
- */
4008
- protected readonly collabCursors: _angular_core.Signal<pptx_angular_viewer.RemoteCursor[]>;
4009
- /** Timestamp of the last cursor broadcast (throttle gate). */
4010
- private lastCursorBroadcast;
4011
- /** Fullscreen presentation-mode overlay visibility. */
4012
- protected readonly presenting: _angular_core.WritableSignal<boolean>;
4013
- /** Presenter-view (speaker) overlay visibility. */
4014
- protected readonly presentingPresenter: _angular_core.WritableSignal<boolean>;
4015
- /** Epoch ms when presenter view started (drives the elapsed timer). */
4016
- protected readonly presenterStartTime: _angular_core.WritableSignal<number | null>;
4017
4396
  /** Slide-sorter grid overlay visibility. */
4018
4397
  protected readonly showSorter: _angular_core.WritableSignal<boolean>;
4019
- /** Open mobile bottom-sheet (slides / menu), or null. */
4020
- protected readonly mobileSheet: _angular_core.WritableSignal<"menu" | "slides" | null>;
4021
- /** Speaker-notes strip visibility. */
4022
- protected readonly showNotes: _angular_core.WritableSignal<boolean>;
4023
4398
  /** Whether the left slides panel is collapsed (top-bar sidebar toggle). */
4024
4399
  protected readonly slidesPanelCollapsed: _angular_core.WritableSignal<boolean>;
4025
4400
  /** Active drawing tool (from the ribbon Draw tab). */
@@ -4028,24 +4403,6 @@ declare class PowerPointViewerComponent {
4028
4403
  protected readonly activeDrawColor: _angular_core.WritableSignal<string>;
4029
4404
  /** Active ink stroke width in stage pixels. */
4030
4405
  protected readonly activeDrawWidth: _angular_core.WritableSignal<number>;
4031
- /** Active right-docked tool panel (comments / accessibility / selection), or null. */
4032
- protected readonly activePanel: _angular_core.WritableSignal<"comments" | "accessibility" | "signatures" | "selection" | null>;
4033
- /**
4034
- * Which panel the single inspector host should show, applying the original
4035
- * first-match precedence (explicit tool panels → element → slide default).
4036
- * `accessibility`/`signatures` render regardless of edit mode; the rest need
4037
- * `canEdit`.
4038
- */
4039
- protected readonly inspectorContent: _angular_core.Signal<"slide" | "element" | "comments" | "accessibility" | "signatures" | "selection" | null>;
4040
- /**
4041
- * Whether the right-docked inspector is showing the format panel (element or
4042
- * slide properties). Drives the top-bar inspector-toggle active state.
4043
- */
4044
- protected readonly inspectorPaneOpen: _angular_core.Signal<boolean>;
4045
- /** Inspector content, but null on mobile once the user has swiped it away. */
4046
- protected readonly visibleInspectorKind: _angular_core.Signal<"slide" | "element" | "comments" | "accessibility" | "signatures" | "selection" | null>;
4047
- /** Accessible-label translation key for the inspector host, by active content. */
4048
- protected readonly inspectorLabel: _angular_core.Signal<"" | "pptx.toolbar.comments" | "pptx.accessibility.title" | "pptx.viewer.digitalSignatures" | "pptx.selectionPane.title" | "pptx.viewer.elementProperties" | "pptx.viewer.slideProperties">;
4049
4406
  /**
4050
4407
  * Which mobile bottom-bar slot is currently "active" (highlighted). The
4051
4408
  * comments panel maps to the Comments slot; an open notes strip maps to
@@ -4053,23 +4410,8 @@ declare class PowerPointViewerComponent {
4053
4410
  * selected the inspector (Format) is showing inline so it maps to inspector.
4054
4411
  */
4055
4412
  protected readonly mobileBarSheet: _angular_core.Signal<MobileBarSheet>;
4056
- /** Document-properties (Info) dialog visibility. */
4057
- protected readonly showProperties: _angular_core.WritableSignal<boolean>;
4058
- /** Hyperlink-edit dialog visibility. */
4059
- protected readonly showHyperlink: _angular_core.WritableSignal<boolean>;
4060
- /** Local overrides applied to document properties via the Info dialog. */
4061
- private readonly coreOverride;
4062
4413
  /** Comments on the active slide. */
4063
4414
  protected readonly activeComments: _angular_core.Signal<PptxComment[]>;
4064
- /** Document core properties (loaded, with any in-session edits merged in). */
4065
- protected readonly coreProperties: _angular_core.Signal<PptxCoreProperties>;
4066
- /** Open editor context-menu position (client coords), or null. */
4067
- protected readonly contextMenuPos: _angular_core.WritableSignal<{
4068
- x: number;
4069
- y: number;
4070
- } | null>;
4071
- /** Id of the element being inline text-edited, or null. */
4072
- protected readonly editingId: _angular_core.WritableSignal<string | null>;
4073
4415
  /** Whether the dot-grid overlay is visible on the editor canvas. */
4074
4416
  protected readonly showGrid: _angular_core.WritableSignal<boolean>;
4075
4417
  /** Whether ruler strips are visible on the editor canvas. */
@@ -4078,12 +4420,8 @@ declare class PowerPointViewerComponent {
4078
4420
  protected readonly showGuides: _angular_core.WritableSignal<boolean>;
4079
4421
  /** Whether snap-to-grid is active on the editor canvas. */
4080
4422
  protected readonly snapToGrid: _angular_core.WritableSignal<boolean>;
4081
- /** Whether the theme-gallery overlay is visible (Design → Browse Themes). */
4082
- protected readonly showThemeGallery: _angular_core.WritableSignal<boolean>;
4083
4423
  /** Whether the Insert SmartArt gallery dialog is open. */
4084
4424
  protected readonly showSmartArtInsert: _angular_core.WritableSignal<boolean>;
4085
- /** The `name` property of the loaded deck's theme (for check-mark in gallery). */
4086
- protected readonly activeThemeName: _angular_core.Signal<string | undefined>;
4087
4425
  /**
4088
4426
  * Stable, always-truthy key for the slide-properties form. Changes only when
4089
4427
  * the active slide changes, so the `@if` recreates (and reseeds) the
@@ -4093,131 +4431,19 @@ declare class PowerPointViewerComponent {
4093
4431
  protected readonly slidePropsKey: _angular_core.Signal<string>;
4094
4432
  /** The single selected element on the active slide (for the inspector). */
4095
4433
  protected readonly selectedElement: _angular_core.Signal<PptxElement | null>;
4096
- /**
4097
- * Built-in File ▸ Open override of the `content` input. The native picker
4098
- * sets this to swap the deck in place; a fresh `content` input clears it so
4099
- * external reloads always win.
4100
- */
4101
- private readonly contentOverride;
4102
4434
  constructor();
4103
4435
  /**
4104
4436
  * Serialise the current presentation to `.pptx` bytes (imperative handle).
4105
4437
  * When editing, this serialises the editor's edited deck so changes persist.
4106
4438
  */
4107
4439
  getContent(): Promise<Uint8Array>;
4108
- /**
4109
- * Serialise the current deck and trigger a browser download of the `.pptx`.
4110
- * Surfaced on the mobile toolbar so saving is reachable without the desktop
4111
- * ribbon's File tab.
4112
- */
4113
- saveAsPptx(): Promise<void>;
4114
4440
  goTo(index: number): void;
4115
4441
  goPrev(): void;
4116
4442
  goNext(): void;
4117
- /**
4118
- * Publish the local cursor while the pointer moves over the canvas. Throttled
4119
- * to {@link BROADCAST_THROTTLE_MS}; coordinates are mapped from client space
4120
- * into unscaled slide space (dividing by zoom, matching the cursor overlay)
4121
- * and clamped to the canvas bounds.
4122
- */
4123
- protected onCollabPointerMove(event: PointerEvent): void;
4124
- /** The loaded source `.pptx` bytes (for elected-writer write-back), if any. */
4125
- private currentSourceBytes;
4126
- /**
4127
- * Apply a built-in theme preset to the whole deck.
4128
- *
4129
- * Mirrors Vue's `applyThemePreset()`: re-resolves slide colours via core's
4130
- * pure `applyThemeToData`, then writes the updated slides + theme metadata
4131
- * into `EditorStateService` as a single undoable entry. Also refreshes the
4132
- * `loader.themeColorMap` so subsequent theme switches start from the correct
4133
- * baseline.
4134
- */
4135
- applyThemePreset(preset: PptxThemePreset): void;
4136
- /**
4137
- * Wire the framework-agnostic touch-gesture recogniser to the `<main>` canvas
4138
- * host once it is in the DOM. Mirrors React's `useTouchGestures` wiring:
4139
- * - pinch-to-zoom always updates the zoom signal (clamped to the viewer
4140
- * range), with `preventDefault()` on the pinch path to suppress the
4141
- * browser's native pinch-zoom;
4142
- * - horizontal swipe navigates slides, but only when editing is off
4143
- * (`!canEdit()`): in edit mode single-finger gestures belong to element
4144
- * manipulation (move/resize/rotate), so we never hijack them. The large
4145
- * ‹ › buttons remain available for explicit navigation in all modes;
4146
- * - long-press in edit mode opens the editor context menu at the press
4147
- * point for the current selection (mirrors React's onLongPress path).
4148
- *
4149
- * The recogniser's swipe/long-press callbacks check the live `canEdit()` /
4150
- * selection state, so a single attach handles every mode without re-binding.
4151
- */
4152
- private setupTouchGestures;
4153
- zoomIn(): void;
4154
- zoomOut(): void;
4155
- zoomReset(): void;
4156
- /** Open the fullscreen presentation overlay from the current slide. */
4157
- present(): void;
4158
- /**
4159
- * Map a presentation-overlay index back to the full-deck `activeSlideIndex`.
4160
- * The overlay's index is relative to {@link presentationSlides} (a custom show
4161
- * may filter/reorder the deck), so resolve by slide id to keep the editor
4162
- * selection correct when the show closes.
4163
- */
4164
- onPresentationIndexChange(index: number): void;
4165
- /**
4166
- * Open a separate audience tab and hand off the deck via the shared
4167
- * IndexedDB store. Mirrors React's presenter "open audience window".
4168
- */
4169
- openAudienceWindow(): void;
4170
- /** Open the presenter (speaker) view: current+next slide, notes, timer. */
4171
- presentPresenter(): void;
4172
- /** Close the presenter view (and any audience overlay/window it opened). */
4173
- exitPresenter(): void;
4174
4443
  /** Review ▸ Compare: pick a `.pptx` and diff it against the current deck. */
4175
4444
  protected onOpenCompare(): void;
4176
- /**
4177
- * Double-click text edit entry: equations open the equation editor instead
4178
- * of the inline text editor (mirrors React's dbl-click-to-edit-equation).
4179
- */
4180
- protected onTextEditStart(id: string): void;
4181
- /** Apply a Ctrl/Cmd+B/I/U toggle from the inline editor (undoable). */
4182
- protected onTextFormat(event: {
4183
- id: string;
4184
- updates: Partial<TextStyle>;
4185
- }): void;
4186
- /** Presentation exited with ink on it: offer the keep/discard prompt. */
4187
- protected onPresentationAnnotationsExit(map: SlideAnnotationMap): void;
4188
4445
  /** Swap the deck for a restored version-history snapshot. */
4189
4446
  protected onRestoreVersion(bytes: Uint8Array): void;
4190
- /** Toggle the speaker-notes strip. */
4191
- toggleNotes(): void;
4192
- /**
4193
- * File ▸ Open: host override (`onOpenFile` input) takes precedence; otherwise
4194
- * a built-in native picker loads the chosen presentation in place.
4195
- */
4196
- openFile(): void;
4197
- /** Live downward drag offset for the notes sheet (px; 0 when idle). */
4198
- protected readonly notesDragY: _angular_core.WritableSignal<number>;
4199
- /** True while a notes-sheet drag is in progress (disables the snap-back transition). */
4200
- protected readonly notesDragging: _angular_core.WritableSignal<boolean>;
4201
- private notesDragStartY;
4202
- protected onNotesPointerDown(event: PointerEvent): void;
4203
- protected onNotesPointerMove(event: PointerEvent): void;
4204
- protected onNotesPointerUp(event: PointerEvent): void;
4205
- /** True once the user swiped the inspector away on mobile (until reopened). */
4206
- protected readonly mobileInspectorHidden: _angular_core.WritableSignal<boolean>;
4207
- /** Live downward drag offset for the inspector host (px; 0 when idle). */
4208
- protected readonly inspectorDragY: _angular_core.WritableSignal<number>;
4209
- /** True while an inspector-host drag is in progress. */
4210
- protected readonly inspectorDragging: _angular_core.WritableSignal<boolean>;
4211
- private inspectorDragStartY;
4212
- protected onInspectorPointerDown(event: PointerEvent): void;
4213
- protected onInspectorPointerMove(event: PointerEvent): void;
4214
- protected onInspectorPointerUp(event: PointerEvent): void;
4215
- /**
4216
- * Mobile quick-insert: drop a text box on the active slide. Mirrors React's
4217
- * mobile bottom-bar "Insert" slot (a text box is the most common starter
4218
- * element on a phone; the full Insert section lives in the top-bar menu).
4219
- */
4220
- protected onMobileInsert(): void;
4221
4447
  /**
4222
4448
  * Mobile "Format" slot: surface the inspector for the current selection. The
4223
4449
  * inspector renders inline (below the canvas) whenever an element is selected
@@ -4226,86 +4452,24 @@ declare class PowerPointViewerComponent {
4226
4452
  * instead).
4227
4453
  */
4228
4454
  protected onMobileFormat(): void;
4229
- /** Toggle a right-docked tool panel (clicking the active one closes it). */
4230
- togglePanel(panel: 'comments' | 'accessibility' | 'signatures' | 'selection'): void;
4231
4455
  /** Receive draw-tool state changes from the ribbon Draw tab. */
4232
4456
  protected onDrawToolChange(state: {
4233
4457
  tool: string;
4234
4458
  color: string;
4235
4459
  width: number;
4236
4460
  }): void;
4237
- /** Receive a completed ink stroke and append it to the active slide. */
4238
- protected onInkStrokeComplete(ink: InkPptxElement): void;
4239
- /** Receive an eraser hit and delete the targeted ink element. */
4240
- protected onEraserHit(id: string): void;
4241
4461
  /** Append a comment to the active slide (one history entry). */
4242
4462
  onCommentAdd(text: string): void;
4243
4463
  /** Remove a comment from the active slide. */
4244
4464
  onCommentRemove(id: string): void;
4245
4465
  /** Toggle a comment's resolved flag on the active slide. */
4246
4466
  onCommentResolve(id: string): void;
4247
- /**
4248
- * Persist a document-properties edit from the Info dialog. Gated on
4249
- * {@link canEdit}: viewers may inspect properties but not mutate them
4250
- * (mirrors the comments / hyperlink edit paths).
4251
- */
4252
- onPropertiesSave(patch: Partial<PptxCoreProperties>): void;
4253
- /** Apply a hyperlink edit to the selected element (one history entry). */
4254
- onHyperlinkSave(patch: Partial<PptxElement>): void;
4255
- /**
4256
- * Handle an element press from the canvas. Additive (Shift/Ctrl) toggles
4257
- * membership; a plain press selects the element (keeping it selected if it
4258
- * already was, so a subsequent drag works).
4259
- */
4260
- onElementSelect(event: {
4261
- id: string;
4262
- additive: boolean;
4263
- }): void;
4264
- /** Empty-stage press: disarm the painter if armed, else clear the selection. */
4265
- onBackgroundClick(): void;
4266
- /** Right-click: select the element under the cursor and open the menu. */
4267
- onContextMenu(event: {
4268
- id: string | null;
4269
- x: number;
4270
- y: number;
4271
- }): void;
4272
- /** Update the active slide's background colour. */
4273
- onSlideBackground(event: Event): void;
4274
- /** Update the active slide's speaker notes. */
4275
- onSlideNotes(event: Event): void;
4276
- /** Update the active slide's speaker notes from the editable NotesPanel. */
4277
- onNotesUpdate(notes: string): void;
4278
- onSelectionPaneBringForward(id: string): void;
4279
- onSelectionPaneSendBackward(id: string): void;
4280
- onToggleElementHidden(id: string): void;
4281
4467
  /**
4282
4468
  * Insert a new SmartArt element built from the dialog's chosen preset + item
4283
4469
  * texts. The element id is left empty so `EditorStateService.addElement`
4284
4470
  * assigns one; the insert is a single undo/redo history entry.
4285
4471
  */
4286
4472
  protected onInsertSmartArt(event: SmartArtInsertEvent): void;
4287
- /** Commit an inline text edit: replace the element's text (one history entry). */
4288
- onTextCommit(event: {
4289
- id: string;
4290
- text: string;
4291
- }): void;
4292
- /**
4293
- * Commit a table cell's inline text edit. Finds the table element on the
4294
- * active slide, rebuilds its `tableData` with the new cell text, and patches
4295
- * it through the editor (which records undo history).
4296
- */
4297
- protected onTableCellCommit(event: {
4298
- id: string;
4299
- commit: TableCellCommit;
4300
- }): void;
4301
- /**
4302
- * Persist a structural table change originating on the canvas (column / row
4303
- * drag-resize) as one undoable history entry.
4304
- */
4305
- protected onTableChange(event: {
4306
- id: string;
4307
- tableData: PptxTableData;
4308
- }): void;
4309
4473
  /**
4310
4474
  * Editing keyboard shortcuts (only when `canEdit` and not typing in a
4311
4475
  * field or presenting). The decorator must live on the component; the logic
@@ -4338,6 +4502,115 @@ declare function applyMove(start: Box, dx: number, dy: number): Box;
4338
4502
  */
4339
4503
  declare function applyResize(start: Box, handle: ResizeHandle, dx: number, dy: number, min?: number): Box;
4340
4504
 
4505
+ /**
4506
+ * selection-geometry.ts: Pure geometry helpers for `SlideCanvasComponent`'s
4507
+ * selection outline, resize/rotate/adjust handles, and the
4508
+ * pointer-target-to-interactive-element resolution. No Angular dependency, so
4509
+ * these are trivially unit-testable and reusable outside the component.
4510
+ */
4511
+
4512
+ /** A selected element's bounding box (stage coords), keyed by id. */
4513
+ interface SelectionBox extends Box {
4514
+ id: string;
4515
+ }
4516
+ /** A resize-handle's render box (stage coords) plus its cursor style. */
4517
+ interface HandleBox {
4518
+ handle: ResizeHandle;
4519
+ left: number;
4520
+ top: number;
4521
+ size: number;
4522
+ cursor: string;
4523
+ }
4524
+ /** A rotate/adjust handle's render box (stage coords). */
4525
+ interface CornerHandleBox {
4526
+ left: number;
4527
+ top: number;
4528
+ size: number;
4529
+ }
4530
+
4531
+ /** The draw tools `SlideCanvasComponent` forwards from the ribbon Draw tab. */
4532
+ type DrawTool = 'select' | 'pen' | 'highlighter' | 'eraser' | 'freeform';
4533
+ /** Live host accessors + emitters the ink-drawing controller needs. */
4534
+ interface InkDrawingHost {
4535
+ readonly stageElement: () => HTMLElement | undefined;
4536
+ readonly effectiveScale: () => number;
4537
+ readonly elements: () => readonly PptxElement[];
4538
+ readonly drawTool: () => DrawTool;
4539
+ readonly drawColor: () => string;
4540
+ readonly drawWidth: () => number;
4541
+ readonly emitInkStrokeComplete: (ink: InkPptxElement) => void;
4542
+ readonly emitEraserHit: (id: string) => void;
4543
+ }
4544
+ declare class InkDrawingService {
4545
+ /** Whether a freehand stroke is in progress. Signal for template reactivity. */
4546
+ readonly active: _angular_core.WritableSignal<boolean>;
4547
+ /** SVG path `d` for the live stroke preview (updated on every pointer move). */
4548
+ readonly liveInkPath: _angular_core.WritableSignal<string>;
4549
+ /** Accumulated points for the stroke currently being drawn. */
4550
+ private points;
4551
+ private host;
4552
+ /** Wire the host accessors/emitters (called once from the component constructor). */
4553
+ bind(host: InkDrawingHost): void;
4554
+ private requireHost;
4555
+ /** True when a draw tool (anything but 'select') should own the current gesture. */
4556
+ isDrawToolActive(): boolean;
4557
+ /**
4558
+ * Handle a stage pointerdown while a draw tool is active: eraser hit-tests
4559
+ * against ink elements (topmost wins); pen/highlighter/freeform begin a new
4560
+ * stroke.
4561
+ */
4562
+ handleStagePointerDown(event: PointerEvent): void;
4563
+ /** Append a point to the in-progress stroke. Returns false when no stroke is active (caller should fall through). */
4564
+ handlePointerMove(event: PointerEvent): boolean;
4565
+ /** Finalise the in-progress stroke and emit it. Returns false when no stroke was active (caller should fall through). */
4566
+ handlePointerUp(): boolean;
4567
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<InkDrawingService, never>;
4568
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<InkDrawingService>;
4569
+ }
4570
+
4571
+ /** A user-created guide line dragged from a ruler strip. */
4572
+ interface RulerGuide {
4573
+ id: string;
4574
+ axis: 'x' | 'y';
4575
+ pos: number;
4576
+ }
4577
+ /** Live host accessors the guide controller needs. */
4578
+ interface RulerGuidesHost {
4579
+ readonly editable: () => boolean;
4580
+ readonly stageElement: () => HTMLElement | undefined;
4581
+ readonly effectiveScale: () => number;
4582
+ readonly canvasSize: () => CanvasSize;
4583
+ }
4584
+ declare class RulerGuidesService {
4585
+ /**
4586
+ * User-created guide lines (dragged from rulers or added from toolbar).
4587
+ * axis:'x' -> vertical line at x=pos; axis:'y' -> horizontal line at y=pos.
4588
+ */
4589
+ readonly rulerGuides: _angular_core.WritableSignal<readonly RulerGuide[]>;
4590
+ /** Active guide-drag state (id + axis only), or null when nothing is being dragged. */
4591
+ private guideDrag;
4592
+ private host;
4593
+ /** Wire the host accessors (called once from the component constructor). */
4594
+ bind(host: RulerGuidesHost): void;
4595
+ private requireHost;
4596
+ /** True while an existing or just-created guide is being dragged. */
4597
+ isDragging(): boolean;
4598
+ /** Begin dragging an existing guide. Called from the guide handle pointerdown. */
4599
+ onGuidePointerDown(event: PointerEvent, id: string, axis: RulerGuide['axis']): void;
4600
+ /** Remove a guide (called on guide handle double-click). */
4601
+ onGuideDoubleClick(event: MouseEvent, id: string): void;
4602
+ /** Drag from the horizontal ruler to create a new horizontal guide (axis:'y'). */
4603
+ onHRulerPointerDown(event: PointerEvent): void;
4604
+ /** Drag from the vertical ruler to create a new vertical guide (axis:'x'). */
4605
+ onVRulerPointerDown(event: PointerEvent): void;
4606
+ /** Update the dragged guide's position. Returns false when no guide drag is in progress (caller should fall through). */
4607
+ handlePointerMove(event: PointerEvent): boolean;
4608
+ /** End the guide drag. Returns false when no guide drag was in progress (caller should fall through). */
4609
+ handlePointerUp(): boolean;
4610
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RulerGuidesService, never>;
4611
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<RulerGuidesService>;
4612
+ }
4613
+
4341
4614
  /** A single tick mark on a ruler strip. */
4342
4615
  interface RulerTick {
4343
4616
  /** Position in screen pixels along the ruler. */
@@ -4347,12 +4620,7 @@ interface RulerTick {
4347
4620
  /** Label to display (only on major ticks, every N inches). */
4348
4621
  label: string | null;
4349
4622
  }
4350
- /** A user-created guide line dragged from a ruler strip. */
4351
- interface RulerGuide {
4352
- id: string;
4353
- axis: 'x' | 'y';
4354
- pos: number;
4355
- }
4623
+
4356
4624
  /**
4357
4625
  * SlideCanvasComponent: Angular port of the React `SlideCanvas.tsx` and Vue
4358
4626
  * `SlideCanvas.vue`.
@@ -4497,8 +4765,6 @@ declare class SlideCanvasComponent {
4497
4765
  }>;
4498
4766
  private drag;
4499
4767
  private editCancelled;
4500
- /** Active guide-drag state (id + axis only), or null when nothing is being dragged. */
4501
- private guideDrag;
4502
4768
  private marquee;
4503
4769
  /** Live marquee rectangle (stage coords) while rubber-band selecting. */
4504
4770
  readonly marqueeRect: _angular_core.WritableSignal<{
@@ -4509,28 +4775,15 @@ declare class SlideCanvasComponent {
4509
4775
  } | null>;
4510
4776
  /** Live alignment-snap guide lines (stage coords) during a move. */
4511
4777
  readonly snapGuides: _angular_core.WritableSignal<readonly SnapGuide[]>;
4512
- /**
4513
- * User-created guide lines (dragged from rulers or added from toolbar).
4514
- * axis:'x' vertical line at x=pos; axis:'y' → horizontal line at y=pos.
4515
- */
4516
- readonly rulerGuides: _angular_core.WritableSignal<readonly RulerGuide[]>;
4517
- /** Accumulated points for the stroke currently being drawn. */
4518
- private inkPoints;
4519
- /** Whether a freehand stroke is in progress. Signal for template reactivity. */
4520
- readonly inkActiveSignal: _angular_core.WritableSignal<boolean>;
4521
- /** SVG path `d` for the live stroke preview (updated on every pointer move). */
4522
- readonly liveInkPath: _angular_core.WritableSignal<string>;
4778
+ /** Per-instance pen/highlighter/freeform/eraser drawing controller. */
4779
+ protected readonly inkDrawing: InkDrawingService;
4780
+ /** Per-instance user-created ruler-guide controller. */
4781
+ protected readonly rulerGuidesSvc: RulerGuidesService;
4523
4782
  private readonly textEditor;
4524
4783
  private readonly stageRef;
4525
4784
  private readonly viewportRef;
4526
- /**
4527
- * Auto-fit scale (≤ 1): how much the fixed-size slide must shrink to fit the
4528
- * scroll viewport. The authored slide is e.g. 1280×720, which overflows a
4529
- * phone; without this it renders off-screen at `zoom=1`. Mirrors the React
4530
- * viewer's `fitScale * scale` model (useZoomViewport.ts) so "100%" means "fit
4531
- * to viewport". Measured from the viewport via ResizeObserver below.
4532
- */
4533
- private readonly fitScale;
4785
+ /** Per-instance auto-fit scale measurement (see {@link CanvasFitService}). */
4786
+ private readonly canvasFit;
4534
4787
  /**
4535
4788
  * The on-screen scale used for ALL rendering and pointer→stage coordinate
4536
4789
  * math: the user's zoom folded with the auto-fit. The parent keeps showing the
@@ -4542,11 +4795,6 @@ declare class SlideCanvasComponent {
4542
4795
  /** Last-tap timestamp + element id, for synthetic double-tap detection on touch. */
4543
4796
  private lastTap;
4544
4797
  constructor();
4545
- /**
4546
- * Compute the largest scale (≤ 1) at which the whole slide fits the viewport,
4547
- * reserving the 1rem gutter + drop shadow. Sets fitScale to 1 when unmeasured.
4548
- */
4549
- private recomputeFit;
4550
4798
  readonly elements: _angular_core.Signal<PptxElement[]>;
4551
4799
  /**
4552
4800
  * Template elements + the slide's own elements, template first (behind). Used
@@ -4574,48 +4822,25 @@ declare class SlideCanvasComponent {
4574
4822
  height: number;
4575
4823
  }[]>;
4576
4824
  /** Bounding boxes (stage coords) for the selected elements. */
4577
- readonly selectionBoxes: _angular_core.Signal<{
4578
- id: string;
4579
- x: number;
4580
- y: number;
4581
- width: number;
4582
- height: number;
4583
- }[]>;
4825
+ readonly selectionBoxes: _angular_core.Signal<SelectionBox[]>;
4584
4826
  /** The single selected element's box, or null when 0 or >1 are selected. */
4585
4827
  readonly singleSelected: _angular_core.Signal<(Box & {
4586
4828
  id: string;
4587
4829
  }) | null>;
4588
4830
  /** Resize-handle render boxes (stage coords) for the single selection. */
4589
- readonly handleBoxes: _angular_core.Signal<{
4590
- handle: ResizeHandleId;
4591
- left: number;
4592
- top: number;
4593
- size: number;
4594
- cursor: string;
4595
- }[]>;
4831
+ readonly handleBoxes: _angular_core.Signal<HandleBox[]>;
4596
4832
  /** Rotation-handle box (stage coords) above the single selection, or null. */
4597
- readonly rotateHandle: _angular_core.Signal<{
4598
- left: number;
4599
- top: number;
4600
- size: number;
4601
- } | null>;
4833
+ readonly rotateHandle: _angular_core.Signal<CornerHandleBox | null>;
4602
4834
  /**
4603
4835
  * Shape-adjustment-handle box (stage coords) for the single selection, or
4604
4836
  * null. Sits just outside the top-left corner so it never collides with the
4605
4837
  * resize/rotate handles. Selection-only + editable-only, so it vanishes in
4606
4838
  * presentation alongside the rest of the edit chrome.
4607
4839
  */
4608
- readonly adjustHandle: _angular_core.Signal<{
4609
- left: number;
4610
- top: number;
4611
- size: number;
4612
- } | null>;
4840
+ readonly adjustHandle: _angular_core.Signal<CornerHandleBox | null>;
4613
4841
  /**
4614
4842
  * Resolve the id of the interactive element under a pointer target, or null.
4615
- * An element host carries `data-element-id`, but template (master/layout)
4616
- * elements are only interactive while editTemplateMode is on; when off they
4617
- * are reported as null so the canvas treats them as background (no
4618
- * select/drag/context-menu/inline-edit).
4843
+ * See {@link resolveInteractiveElementId}.
4619
4844
  */
4620
4845
  private interactiveElementIdAt;
4621
4846
  onStagePointerDown(event: PointerEvent): void;
@@ -4647,14 +4872,6 @@ declare class SlideCanvasComponent {
4647
4872
  onRotatePointerDown(event: PointerEvent): void;
4648
4873
  onPointerMove(event: PointerEvent): void;
4649
4874
  onPointerUp(): void;
4650
- /** Begin dragging an existing guide. Called from the guide handle pointerdown. */
4651
- onGuidePointerDown(event: PointerEvent, id: string, axis: RulerGuide['axis']): void;
4652
- /** Remove a guide (called on guide handle double-click). */
4653
- onGuideDoubleClick(event: MouseEvent, id: string): void;
4654
- /** Drag from the horizontal ruler to create a new horizontal guide (axis:'y'). */
4655
- onHRulerPointerDown(event: PointerEvent): void;
4656
- /** Drag from the vertical ruler to create a new vertical guide (axis:'x'). */
4657
- onVRulerPointerDown(event: PointerEvent): void;
4658
4875
  readonly wrapperStyle: _angular_core.Signal<StyleMap>;
4659
4876
  /**
4660
4877
  * Public accessor of the internal effective scale for ruler/overlay sizing
@@ -5170,6 +5387,14 @@ declare class SmartArtRendererComponent {
5170
5387
  private readonly nodeEditor;
5171
5388
  /** Container div ref used to project hover rects into local coordinates. */
5172
5389
  private readonly smartartContainer;
5390
+ /**
5391
+ * The mounted style-bar popover, if any. Mousemove events landing inside it
5392
+ * must not clear hover state, or the popover would unmount as soon as the
5393
+ * pointer reaches the swatches it needs to be clicked.
5394
+ */
5395
+ private readonly styleBar;
5396
+ /** Pending "leave" timeout: grace period for the pointer to reach the style bar. */
5397
+ private hideTimeout;
5173
5398
  /**
5174
5399
  * Guards against a cancel-triggered DOM-removal blur committing the edit.
5175
5400
  * Set to true before programmatic cancellation; reset to false on each new edit.
@@ -5241,11 +5466,24 @@ declare class SmartArtRendererComponent {
5241
5466
  private rawNodeText;
5242
5467
  /** Commit edited text through the shared editor state (one history entry). */
5243
5468
  private applyCommit;
5469
+ /** Approximate rendered size of the style bar (6 swatches + padding/border). */
5470
+ private static readonly STYLE_BAR_WIDTH;
5471
+ private static readonly STYLE_BAR_HEIGHT;
5472
+ /** Grace period (ms) before clearing hover state once the pointer leaves the
5473
+ * node, so it can cross the small visual gap to the style bar. */
5474
+ private static readonly HIDE_GRACE_MS;
5244
5475
  protected readonly styleBarStyle: _angular_core.Signal<Record<string, string> | null>;
5245
5476
  protected onMouseMove(event: MouseEvent): void;
5246
5477
  protected onMouseLeave(): void;
5478
+ private cancelPendingHide;
5247
5479
  private findNodeEl;
5248
5480
  protected handleChangeNodeStyle(nodeId: string, fill: string): void;
5481
+ /**
5482
+ * Reflow `drawingShapes` back from the shared layout engine when an edit op
5483
+ * cleared them (every text/style edit does) -- otherwise the renderer falls
5484
+ * back to the generic SVG layout for every node, not just the edited one.
5485
+ */
5486
+ private reflow;
5249
5487
  readonly isEmpty: _angular_core.Signal<boolean>;
5250
5488
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SmartArtRendererComponent, never>;
5251
5489
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<SmartArtRendererComponent, "pptx-smart-art-renderer", never, { "element": { "alias": "element"; "required": true; "isSignal": true; }; "zIndex": { "alias": "zIndex"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;