pptx-angular-viewer 1.1.62 → 1.1.64

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
 
@@ -2951,10 +2951,9 @@ declare class IsMobileService {
2951
2951
  * Provide it at the component level so its lifetime tracks the host viewer:
2952
2952
  * `@Component({ providers: [LoadContentService] })`.
2953
2953
  *
2954
- * This is the viewer-first subset; the React hook also populated ~25 extra
2955
- * pieces of presentation metadata (sections, custom shows, embedded fonts,
2956
- * digital signatures, …). Those are tracked in PORTING.md and added here as
2957
- * the corresponding features are ported.
2954
+ * Originally the viewer-first subset of the React hook; the extra pieces of
2955
+ * presentation metadata (sections, custom shows, embedded fonts, digital
2956
+ * signatures, …) were added alongside the corresponding features.
2958
2957
  */
2959
2958
  declare class LoadContentService {
2960
2959
  /** Parsed slides (with image Blob URLs patched in). */
@@ -3045,41 +3044,6 @@ declare class MobileBottomBarComponent {
3045
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>;
3046
3045
  }
3047
3046
 
3048
- /**
3049
- * presentation-annotations-helpers.ts: Pure geometry helpers for presentation
3050
- * ink annotations (pen, highlighter, eraser, laser).
3051
- *
3052
- * Ported from React:
3053
- * packages/react/src/viewer/components/PresentationAnnotationOverlay.tsx
3054
- * packages/react/src/viewer/hooks/usePresentationAnnotations.ts
3055
- *
3056
- * No Angular dependencies; all functions are pure so they can be unit-tested
3057
- * without TestBed.
3058
- */
3059
- /** A single {x, y} coordinate in slide-space pixels. */
3060
- interface AnnotationPoint {
3061
- x: number;
3062
- y: number;
3063
- }
3064
- /** An ink stroke: a sequence of points with visual properties. */
3065
- interface AnnotationStroke {
3066
- id: string;
3067
- points: AnnotationPoint[];
3068
- color: string;
3069
- width: number;
3070
- /** 1 = opaque (pen); 0.4 = semi-transparent (highlighter). */
3071
- opacity: number;
3072
- }
3073
- /** The tool currently armed in presentation mode. */
3074
- type PresentationTool = 'none' | 'pen' | 'highlighter' | 'eraser' | 'laser';
3075
- /** Per-slide annotation storage: slide index → strokes. */
3076
- type SlideAnnotationMap = Map<number, AnnotationStroke[]>;
3077
- /** Transient laser-pointer position in slide-space pixels. */
3078
- interface LaserPosition {
3079
- x: number;
3080
- y: number;
3081
- }
3082
-
3083
3047
  /** BroadcastChannel name shared between presenter and audience tabs. */
3084
3048
  declare const PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
3085
3049
  /** Unique origin identifier so we only react to our own messages. */
@@ -3355,7 +3319,8 @@ declare function buildDuotoneFilter(element: PptxElement): DuotoneFilterDef | un
3355
3319
  * and paint text boxes, basic preset shapes, images, and image/gradient fills
3356
3320
  * (the latter via the parser's prebuilt CSS gradient string). Advanced visuals
3357
3321
  * (the structured gradient builder, pattern fills, custom geometry clip-paths,
3358
- * shadows, 3D, image effects, text warp) are tracked in PORTING.md.
3322
+ * shadows, 3D, image effects, text warp) are handled by the shared render
3323
+ * modules (`pptx-viewer-shared`) consumed from the renderer components.
3359
3324
  *
3360
3325
  * Long term the *logic* here is a shared-extraction candidate; only the
3361
3326
  * return type (CSS map shape) differs per framework, so a future refactor
@@ -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,74 +4041,295 @@ 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. This is the viewer-first milestone of the port: the
3886
- * React component additionally composes a full editor (toolbar, inspector
3887
- * panels, dialogs, presentation mode, collaboration, export). The roadmap and
3888
- * per-area status live in `packages/angular/PORTING.md`.
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.
3889
4048
  *
3890
- * Conventions vs. React/Vue:
3891
- * - React `forwardRef` handle / Vue `defineExpose` → public {@link getContent}
3892
- * method (reach it via a template ref or `viewChild`).
3893
- * - React callback props / Vue emits → Angular `output()` events.
3894
- * - React theme context / Vue provide-inject → `themeStyle` CSS vars applied to
3895
- * 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).
3896
4053
  */
3897
- declare class PowerPointViewerComponent {
3898
- /** PowerPoint content as Uint8Array (or ArrayBuffer). */
3899
- readonly content: _angular_core.InputSignal<ArrayBuffer | Uint8Array<ArrayBufferLike> | null>;
3900
- /** Whether editing actions are enabled. (Editor chrome not yet ported.) */
3901
- readonly canEdit: _angular_core.InputSignal<boolean>;
3902
- /** Optional class applied to the root element. */
3903
- readonly class: _angular_core.InputSignal<string>;
3904
- /** Theme configuration for customising the viewer's appearance. */
3905
- 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>;
3906
4081
  /**
3907
- * Host file path/identifier keying the version-history store. When omitted
3908
- * the version-history panel shows its empty state. Mirrors React's
3909
- * `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.
3910
4086
  */
3911
- readonly filePath: _angular_core.InputSignal<string | undefined>;
3912
- /** Optional real-time collaboration config; when set, connects and shows remote cursors. */
3913
- 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;
3914
4092
  /**
3915
- * Display name for the local user in collaboration/broadcast sessions and
3916
- * presence avatars. Falls back to "You" (cursors/avatars) and "Presenter"
3917
- * (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`.
3918
4097
  */
3919
- readonly authorName: _angular_core.InputSignal<string | undefined>;
4098
+ readonly inspectorContent: _angular_core.Signal<InspectorContent>;
3920
4099
  /**
3921
- * Seed values for the Share dialog's start form (and the broadcast server
3922
- * URL). Lets the host pre-fill the room id / user name / server URL. Mirrors
3923
- * 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.
3924
4102
  */
3925
- readonly shareDefaults: _angular_core.InputSignal<{
3926
- roomId?: string;
3927
- userName?: string;
3928
- serverUrl?: string;
3929
- } | 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>;
3930
4126
  /**
3931
- * Host override for the File ▸ Open action. When set, the built-in native
3932
- * file picker is bypassed and this is invoked instead; the host then supplies
3933
- * a new `content` value. When omitted, the viewer opens its own picker and
3934
- * 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.
3935
4130
  */
3936
- 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;
3937
4138
  /**
3938
- * Opt in to the experimental Three.js SmartArt renderer. When `true`,
3939
- * SmartArt diagrams render as extruded 3D blocks on a WebGL canvas instead
3940
- * of flat SVG. Requires the optional `three` peer dependency; when it is not
3941
- * installed (or the diagram has no geometry), the viewer transparently falls
3942
- * 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).
3943
4142
  */
3944
- readonly smartArt3D: _angular_core.InputSignal<boolean>;
3945
- /** Fired when the active slide changes. */
3946
- readonly activeSlideChange: _angular_core.OutputEmitterRef<number>;
3947
- /** Fired when the unsaved-changes flag toggles. */
3948
- readonly dirtyChange: _angular_core.OutputEmitterRef<boolean>;
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. */
4330
+ readonly activeSlideChange: _angular_core.OutputEmitterRef<number>;
4331
+ /** Fired when the unsaved-changes flag toggles. */
4332
+ readonly dirtyChange: _angular_core.OutputEmitterRef<boolean>;
3949
4333
  /** Fired with freshly-serialised `.pptx` bytes whenever {@link getContent} materialises the deck. */
3950
4334
  readonly contentChange: _angular_core.OutputEmitterRef<Uint8Array<ArrayBufferLike>>;
3951
4335
  /** Fired when the user edits document properties in the Info dialog. */
@@ -3959,7 +4343,6 @@ declare class PowerPointViewerComponent {
3959
4343
  /** Fired when the collaboration/broadcast session stops. Mirrors React/Vue. */
3960
4344
  readonly stopCollaboration: _angular_core.OutputEmitterRef<void>;
3961
4345
  protected readonly loader: LoadContentService;
3962
- private readonly exportSvc;
3963
4346
  protected readonly editor: EditorStateService;
3964
4347
  private readonly fonts;
3965
4348
  protected readonly collab: CollaborationService;
@@ -3977,6 +4360,16 @@ declare class PowerPointViewerComponent {
3977
4360
  protected readonly session: ViewerCollaborationSessionService;
3978
4361
  protected readonly formatPainter: ViewerFormatPainterService;
3979
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;
3980
4373
  /** Handle on the secondary-dialog host (keep-annotations prompt). */
3981
4374
  private readonly extraDialogs;
3982
4375
  /** Surface the encrypted-file notice dialog alongside the inline fallback. */
@@ -4000,27 +4393,8 @@ declare class PowerPointViewerComponent {
4000
4393
  /** Inherited template (master/layout) elements for the active slide, when editing. */
4001
4394
  protected readonly activeTemplateElements: _angular_core.Signal<readonly PptxElement[]>;
4002
4395
  protected readonly rootStyle: _angular_core.Signal<Record<string, string>>;
4003
- protected readonly zoom: _angular_core.WritableSignal<number>;
4004
- protected readonly zoomPercent: _angular_core.Signal<number>;
4005
- /**
4006
- * Remote cursors filtered to the slide the local user is viewing, so peers'
4007
- * cursors only appear on the shared slide (mirrors React/Vue).
4008
- */
4009
- protected readonly collabCursors: _angular_core.Signal<pptx_angular_viewer.RemoteCursor[]>;
4010
- /** Timestamp of the last cursor broadcast (throttle gate). */
4011
- private lastCursorBroadcast;
4012
- /** Fullscreen presentation-mode overlay visibility. */
4013
- protected readonly presenting: _angular_core.WritableSignal<boolean>;
4014
- /** Presenter-view (speaker) overlay visibility. */
4015
- protected readonly presentingPresenter: _angular_core.WritableSignal<boolean>;
4016
- /** Epoch ms when presenter view started (drives the elapsed timer). */
4017
- protected readonly presenterStartTime: _angular_core.WritableSignal<number | null>;
4018
4396
  /** Slide-sorter grid overlay visibility. */
4019
4397
  protected readonly showSorter: _angular_core.WritableSignal<boolean>;
4020
- /** Open mobile bottom-sheet (slides / menu), or null. */
4021
- protected readonly mobileSheet: _angular_core.WritableSignal<"menu" | "slides" | null>;
4022
- /** Speaker-notes strip visibility. */
4023
- protected readonly showNotes: _angular_core.WritableSignal<boolean>;
4024
4398
  /** Whether the left slides panel is collapsed (top-bar sidebar toggle). */
4025
4399
  protected readonly slidesPanelCollapsed: _angular_core.WritableSignal<boolean>;
4026
4400
  /** Active drawing tool (from the ribbon Draw tab). */
@@ -4029,24 +4403,6 @@ declare class PowerPointViewerComponent {
4029
4403
  protected readonly activeDrawColor: _angular_core.WritableSignal<string>;
4030
4404
  /** Active ink stroke width in stage pixels. */
4031
4405
  protected readonly activeDrawWidth: _angular_core.WritableSignal<number>;
4032
- /** Active right-docked tool panel (comments / accessibility / selection), or null. */
4033
- protected readonly activePanel: _angular_core.WritableSignal<"comments" | "accessibility" | "signatures" | "selection" | null>;
4034
- /**
4035
- * Which panel the single inspector host should show, applying the original
4036
- * first-match precedence (explicit tool panels → element → slide default).
4037
- * `accessibility`/`signatures` render regardless of edit mode; the rest need
4038
- * `canEdit`.
4039
- */
4040
- protected readonly inspectorContent: _angular_core.Signal<"slide" | "element" | "comments" | "accessibility" | "signatures" | "selection" | null>;
4041
- /**
4042
- * Whether the right-docked inspector is showing the format panel (element or
4043
- * slide properties). Drives the top-bar inspector-toggle active state.
4044
- */
4045
- protected readonly inspectorPaneOpen: _angular_core.Signal<boolean>;
4046
- /** Inspector content, but null on mobile once the user has swiped it away. */
4047
- protected readonly visibleInspectorKind: _angular_core.Signal<"slide" | "element" | "comments" | "accessibility" | "signatures" | "selection" | null>;
4048
- /** Accessible-label translation key for the inspector host, by active content. */
4049
- protected readonly inspectorLabel: _angular_core.Signal<"" | "pptx.toolbar.comments" | "pptx.accessibility.title" | "pptx.viewer.digitalSignatures" | "pptx.selectionPane.title" | "pptx.viewer.elementProperties" | "pptx.viewer.slideProperties">;
4050
4406
  /**
4051
4407
  * Which mobile bottom-bar slot is currently "active" (highlighted). The
4052
4408
  * comments panel maps to the Comments slot; an open notes strip maps to
@@ -4054,23 +4410,8 @@ declare class PowerPointViewerComponent {
4054
4410
  * selected the inspector (Format) is showing inline so it maps to inspector.
4055
4411
  */
4056
4412
  protected readonly mobileBarSheet: _angular_core.Signal<MobileBarSheet>;
4057
- /** Document-properties (Info) dialog visibility. */
4058
- protected readonly showProperties: _angular_core.WritableSignal<boolean>;
4059
- /** Hyperlink-edit dialog visibility. */
4060
- protected readonly showHyperlink: _angular_core.WritableSignal<boolean>;
4061
- /** Local overrides applied to document properties via the Info dialog. */
4062
- private readonly coreOverride;
4063
4413
  /** Comments on the active slide. */
4064
4414
  protected readonly activeComments: _angular_core.Signal<PptxComment[]>;
4065
- /** Document core properties (loaded, with any in-session edits merged in). */
4066
- protected readonly coreProperties: _angular_core.Signal<PptxCoreProperties>;
4067
- /** Open editor context-menu position (client coords), or null. */
4068
- protected readonly contextMenuPos: _angular_core.WritableSignal<{
4069
- x: number;
4070
- y: number;
4071
- } | null>;
4072
- /** Id of the element being inline text-edited, or null. */
4073
- protected readonly editingId: _angular_core.WritableSignal<string | null>;
4074
4415
  /** Whether the dot-grid overlay is visible on the editor canvas. */
4075
4416
  protected readonly showGrid: _angular_core.WritableSignal<boolean>;
4076
4417
  /** Whether ruler strips are visible on the editor canvas. */
@@ -4079,12 +4420,8 @@ declare class PowerPointViewerComponent {
4079
4420
  protected readonly showGuides: _angular_core.WritableSignal<boolean>;
4080
4421
  /** Whether snap-to-grid is active on the editor canvas. */
4081
4422
  protected readonly snapToGrid: _angular_core.WritableSignal<boolean>;
4082
- /** Whether the theme-gallery overlay is visible (Design → Browse Themes). */
4083
- protected readonly showThemeGallery: _angular_core.WritableSignal<boolean>;
4084
4423
  /** Whether the Insert SmartArt gallery dialog is open. */
4085
4424
  protected readonly showSmartArtInsert: _angular_core.WritableSignal<boolean>;
4086
- /** The `name` property of the loaded deck's theme (for check-mark in gallery). */
4087
- protected readonly activeThemeName: _angular_core.Signal<string | undefined>;
4088
4425
  /**
4089
4426
  * Stable, always-truthy key for the slide-properties form. Changes only when
4090
4427
  * the active slide changes, so the `@if` recreates (and reseeds) the
@@ -4094,131 +4431,19 @@ declare class PowerPointViewerComponent {
4094
4431
  protected readonly slidePropsKey: _angular_core.Signal<string>;
4095
4432
  /** The single selected element on the active slide (for the inspector). */
4096
4433
  protected readonly selectedElement: _angular_core.Signal<PptxElement | null>;
4097
- /**
4098
- * Built-in File ▸ Open override of the `content` input. The native picker
4099
- * sets this to swap the deck in place; a fresh `content` input clears it so
4100
- * external reloads always win.
4101
- */
4102
- private readonly contentOverride;
4103
4434
  constructor();
4104
4435
  /**
4105
4436
  * Serialise the current presentation to `.pptx` bytes (imperative handle).
4106
4437
  * When editing, this serialises the editor's edited deck so changes persist.
4107
4438
  */
4108
4439
  getContent(): Promise<Uint8Array>;
4109
- /**
4110
- * Serialise the current deck and trigger a browser download of the `.pptx`.
4111
- * Surfaced on the mobile toolbar so saving is reachable without the desktop
4112
- * ribbon's File tab.
4113
- */
4114
- saveAsPptx(): Promise<void>;
4115
4440
  goTo(index: number): void;
4116
4441
  goPrev(): void;
4117
4442
  goNext(): void;
4118
- /**
4119
- * Publish the local cursor while the pointer moves over the canvas. Throttled
4120
- * to {@link BROADCAST_THROTTLE_MS}; coordinates are mapped from client space
4121
- * into unscaled slide space (dividing by zoom, matching the cursor overlay)
4122
- * and clamped to the canvas bounds.
4123
- */
4124
- protected onCollabPointerMove(event: PointerEvent): void;
4125
- /** The loaded source `.pptx` bytes (for elected-writer write-back), if any. */
4126
- private currentSourceBytes;
4127
- /**
4128
- * Apply a built-in theme preset to the whole deck.
4129
- *
4130
- * Mirrors Vue's `applyThemePreset()`: re-resolves slide colours via core's
4131
- * pure `applyThemeToData`, then writes the updated slides + theme metadata
4132
- * into `EditorStateService` as a single undoable entry. Also refreshes the
4133
- * `loader.themeColorMap` so subsequent theme switches start from the correct
4134
- * baseline.
4135
- */
4136
- applyThemePreset(preset: PptxThemePreset): void;
4137
- /**
4138
- * Wire the framework-agnostic touch-gesture recogniser to the `<main>` canvas
4139
- * host once it is in the DOM. Mirrors React's `useTouchGestures` wiring:
4140
- * - pinch-to-zoom always updates the zoom signal (clamped to the viewer
4141
- * range), with `preventDefault()` on the pinch path to suppress the
4142
- * browser's native pinch-zoom;
4143
- * - horizontal swipe navigates slides, but only when editing is off
4144
- * (`!canEdit()`): in edit mode single-finger gestures belong to element
4145
- * manipulation (move/resize/rotate), so we never hijack them. The large
4146
- * ‹ › buttons remain available for explicit navigation in all modes;
4147
- * - long-press in edit mode opens the editor context menu at the press
4148
- * point for the current selection (mirrors React's onLongPress path).
4149
- *
4150
- * The recogniser's swipe/long-press callbacks check the live `canEdit()` /
4151
- * selection state, so a single attach handles every mode without re-binding.
4152
- */
4153
- private setupTouchGestures;
4154
- zoomIn(): void;
4155
- zoomOut(): void;
4156
- zoomReset(): void;
4157
- /** Open the fullscreen presentation overlay from the current slide. */
4158
- present(): void;
4159
- /**
4160
- * Map a presentation-overlay index back to the full-deck `activeSlideIndex`.
4161
- * The overlay's index is relative to {@link presentationSlides} (a custom show
4162
- * may filter/reorder the deck), so resolve by slide id to keep the editor
4163
- * selection correct when the show closes.
4164
- */
4165
- onPresentationIndexChange(index: number): void;
4166
- /**
4167
- * Open a separate audience tab and hand off the deck via the shared
4168
- * IndexedDB store. Mirrors React's presenter "open audience window".
4169
- */
4170
- openAudienceWindow(): void;
4171
- /** Open the presenter (speaker) view: current+next slide, notes, timer. */
4172
- presentPresenter(): void;
4173
- /** Close the presenter view (and any audience overlay/window it opened). */
4174
- exitPresenter(): void;
4175
4443
  /** Review ▸ Compare: pick a `.pptx` and diff it against the current deck. */
4176
4444
  protected onOpenCompare(): void;
4177
- /**
4178
- * Double-click text edit entry: equations open the equation editor instead
4179
- * of the inline text editor (mirrors React's dbl-click-to-edit-equation).
4180
- */
4181
- protected onTextEditStart(id: string): void;
4182
- /** Apply a Ctrl/Cmd+B/I/U toggle from the inline editor (undoable). */
4183
- protected onTextFormat(event: {
4184
- id: string;
4185
- updates: Partial<TextStyle>;
4186
- }): void;
4187
- /** Presentation exited with ink on it: offer the keep/discard prompt. */
4188
- protected onPresentationAnnotationsExit(map: SlideAnnotationMap): void;
4189
4445
  /** Swap the deck for a restored version-history snapshot. */
4190
4446
  protected onRestoreVersion(bytes: Uint8Array): void;
4191
- /** Toggle the speaker-notes strip. */
4192
- toggleNotes(): void;
4193
- /**
4194
- * File ▸ Open: host override (`onOpenFile` input) takes precedence; otherwise
4195
- * a built-in native picker loads the chosen presentation in place.
4196
- */
4197
- openFile(): void;
4198
- /** Live downward drag offset for the notes sheet (px; 0 when idle). */
4199
- protected readonly notesDragY: _angular_core.WritableSignal<number>;
4200
- /** True while a notes-sheet drag is in progress (disables the snap-back transition). */
4201
- protected readonly notesDragging: _angular_core.WritableSignal<boolean>;
4202
- private notesDragStartY;
4203
- protected onNotesPointerDown(event: PointerEvent): void;
4204
- protected onNotesPointerMove(event: PointerEvent): void;
4205
- protected onNotesPointerUp(event: PointerEvent): void;
4206
- /** True once the user swiped the inspector away on mobile (until reopened). */
4207
- protected readonly mobileInspectorHidden: _angular_core.WritableSignal<boolean>;
4208
- /** Live downward drag offset for the inspector host (px; 0 when idle). */
4209
- protected readonly inspectorDragY: _angular_core.WritableSignal<number>;
4210
- /** True while an inspector-host drag is in progress. */
4211
- protected readonly inspectorDragging: _angular_core.WritableSignal<boolean>;
4212
- private inspectorDragStartY;
4213
- protected onInspectorPointerDown(event: PointerEvent): void;
4214
- protected onInspectorPointerMove(event: PointerEvent): void;
4215
- protected onInspectorPointerUp(event: PointerEvent): void;
4216
- /**
4217
- * Mobile quick-insert: drop a text box on the active slide. Mirrors React's
4218
- * mobile bottom-bar "Insert" slot (a text box is the most common starter
4219
- * element on a phone; the full Insert section lives in the top-bar menu).
4220
- */
4221
- protected onMobileInsert(): void;
4222
4447
  /**
4223
4448
  * Mobile "Format" slot: surface the inspector for the current selection. The
4224
4449
  * inspector renders inline (below the canvas) whenever an element is selected
@@ -4227,86 +4452,24 @@ declare class PowerPointViewerComponent {
4227
4452
  * instead).
4228
4453
  */
4229
4454
  protected onMobileFormat(): void;
4230
- /** Toggle a right-docked tool panel (clicking the active one closes it). */
4231
- togglePanel(panel: 'comments' | 'accessibility' | 'signatures' | 'selection'): void;
4232
4455
  /** Receive draw-tool state changes from the ribbon Draw tab. */
4233
4456
  protected onDrawToolChange(state: {
4234
4457
  tool: string;
4235
4458
  color: string;
4236
4459
  width: number;
4237
4460
  }): void;
4238
- /** Receive a completed ink stroke and append it to the active slide. */
4239
- protected onInkStrokeComplete(ink: InkPptxElement): void;
4240
- /** Receive an eraser hit and delete the targeted ink element. */
4241
- protected onEraserHit(id: string): void;
4242
4461
  /** Append a comment to the active slide (one history entry). */
4243
4462
  onCommentAdd(text: string): void;
4244
4463
  /** Remove a comment from the active slide. */
4245
4464
  onCommentRemove(id: string): void;
4246
4465
  /** Toggle a comment's resolved flag on the active slide. */
4247
4466
  onCommentResolve(id: string): void;
4248
- /**
4249
- * Persist a document-properties edit from the Info dialog. Gated on
4250
- * {@link canEdit}: viewers may inspect properties but not mutate them
4251
- * (mirrors the comments / hyperlink edit paths).
4252
- */
4253
- onPropertiesSave(patch: Partial<PptxCoreProperties>): void;
4254
- /** Apply a hyperlink edit to the selected element (one history entry). */
4255
- onHyperlinkSave(patch: Partial<PptxElement>): void;
4256
- /**
4257
- * Handle an element press from the canvas. Additive (Shift/Ctrl) toggles
4258
- * membership; a plain press selects the element (keeping it selected if it
4259
- * already was, so a subsequent drag works).
4260
- */
4261
- onElementSelect(event: {
4262
- id: string;
4263
- additive: boolean;
4264
- }): void;
4265
- /** Empty-stage press: disarm the painter if armed, else clear the selection. */
4266
- onBackgroundClick(): void;
4267
- /** Right-click: select the element under the cursor and open the menu. */
4268
- onContextMenu(event: {
4269
- id: string | null;
4270
- x: number;
4271
- y: number;
4272
- }): void;
4273
- /** Update the active slide's background colour. */
4274
- onSlideBackground(event: Event): void;
4275
- /** Update the active slide's speaker notes. */
4276
- onSlideNotes(event: Event): void;
4277
- /** Update the active slide's speaker notes from the editable NotesPanel. */
4278
- onNotesUpdate(notes: string): void;
4279
- onSelectionPaneBringForward(id: string): void;
4280
- onSelectionPaneSendBackward(id: string): void;
4281
- onToggleElementHidden(id: string): void;
4282
4467
  /**
4283
4468
  * Insert a new SmartArt element built from the dialog's chosen preset + item
4284
4469
  * texts. The element id is left empty so `EditorStateService.addElement`
4285
4470
  * assigns one; the insert is a single undo/redo history entry.
4286
4471
  */
4287
4472
  protected onInsertSmartArt(event: SmartArtInsertEvent): void;
4288
- /** Commit an inline text edit: replace the element's text (one history entry). */
4289
- onTextCommit(event: {
4290
- id: string;
4291
- text: string;
4292
- }): void;
4293
- /**
4294
- * Commit a table cell's inline text edit. Finds the table element on the
4295
- * active slide, rebuilds its `tableData` with the new cell text, and patches
4296
- * it through the editor (which records undo history).
4297
- */
4298
- protected onTableCellCommit(event: {
4299
- id: string;
4300
- commit: TableCellCommit;
4301
- }): void;
4302
- /**
4303
- * Persist a structural table change originating on the canvas (column / row
4304
- * drag-resize) as one undoable history entry.
4305
- */
4306
- protected onTableChange(event: {
4307
- id: string;
4308
- tableData: PptxTableData;
4309
- }): void;
4310
4473
  /**
4311
4474
  * Editing keyboard shortcuts (only when `canEdit` and not typing in a
4312
4475
  * field or presenting). The decorator must live on the component; the logic
@@ -4339,6 +4502,115 @@ declare function applyMove(start: Box, dx: number, dy: number): Box;
4339
4502
  */
4340
4503
  declare function applyResize(start: Box, handle: ResizeHandle, dx: number, dy: number, min?: number): Box;
4341
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
+
4342
4614
  /** A single tick mark on a ruler strip. */
4343
4615
  interface RulerTick {
4344
4616
  /** Position in screen pixels along the ruler. */
@@ -4348,12 +4620,7 @@ interface RulerTick {
4348
4620
  /** Label to display (only on major ticks, every N inches). */
4349
4621
  label: string | null;
4350
4622
  }
4351
- /** A user-created guide line dragged from a ruler strip. */
4352
- interface RulerGuide {
4353
- id: string;
4354
- axis: 'x' | 'y';
4355
- pos: number;
4356
- }
4623
+
4357
4624
  /**
4358
4625
  * SlideCanvasComponent: Angular port of the React `SlideCanvas.tsx` and Vue
4359
4626
  * `SlideCanvas.vue`.
@@ -4361,8 +4628,8 @@ interface RulerGuide {
4361
4628
  * Renders the active slide as a fixed-size stage scaled by `zoom`, with each
4362
4629
  * element absolutely positioned. When `editable`, supports click-to-select
4363
4630
  * (event delegation), selection outlines, and pointer drag-to-move / resize
4364
- * handles. Rulers, grid, guides, marquee, and collaboration overlays are
4365
- * tracked in PORTING.md.
4631
+ * handles, plus the rulers, grid, guides, marquee, and collaboration
4632
+ * overlays.
4366
4633
  */
4367
4634
  declare class SlideCanvasComponent {
4368
4635
  readonly slide: _angular_core.InputSignal<PptxSlide | undefined>;
@@ -4498,8 +4765,6 @@ declare class SlideCanvasComponent {
4498
4765
  }>;
4499
4766
  private drag;
4500
4767
  private editCancelled;
4501
- /** Active guide-drag state (id + axis only), or null when nothing is being dragged. */
4502
- private guideDrag;
4503
4768
  private marquee;
4504
4769
  /** Live marquee rectangle (stage coords) while rubber-band selecting. */
4505
4770
  readonly marqueeRect: _angular_core.WritableSignal<{
@@ -4510,28 +4775,15 @@ declare class SlideCanvasComponent {
4510
4775
  } | null>;
4511
4776
  /** Live alignment-snap guide lines (stage coords) during a move. */
4512
4777
  readonly snapGuides: _angular_core.WritableSignal<readonly SnapGuide[]>;
4513
- /**
4514
- * User-created guide lines (dragged from rulers or added from toolbar).
4515
- * axis:'x' vertical line at x=pos; axis:'y' → horizontal line at y=pos.
4516
- */
4517
- readonly rulerGuides: _angular_core.WritableSignal<readonly RulerGuide[]>;
4518
- /** Accumulated points for the stroke currently being drawn. */
4519
- private inkPoints;
4520
- /** Whether a freehand stroke is in progress. Signal for template reactivity. */
4521
- readonly inkActiveSignal: _angular_core.WritableSignal<boolean>;
4522
- /** SVG path `d` for the live stroke preview (updated on every pointer move). */
4523
- 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;
4524
4782
  private readonly textEditor;
4525
4783
  private readonly stageRef;
4526
4784
  private readonly viewportRef;
4527
- /**
4528
- * Auto-fit scale (≤ 1): how much the fixed-size slide must shrink to fit the
4529
- * scroll viewport. The authored slide is e.g. 1280×720, which overflows a
4530
- * phone; without this it renders off-screen at `zoom=1`. Mirrors the React
4531
- * viewer's `fitScale * scale` model (useZoomViewport.ts) so "100%" means "fit
4532
- * to viewport". Measured from the viewport via ResizeObserver below.
4533
- */
4534
- private readonly fitScale;
4785
+ /** Per-instance auto-fit scale measurement (see {@link CanvasFitService}). */
4786
+ private readonly canvasFit;
4535
4787
  /**
4536
4788
  * The on-screen scale used for ALL rendering and pointer→stage coordinate
4537
4789
  * math: the user's zoom folded with the auto-fit. The parent keeps showing the
@@ -4543,11 +4795,6 @@ declare class SlideCanvasComponent {
4543
4795
  /** Last-tap timestamp + element id, for synthetic double-tap detection on touch. */
4544
4796
  private lastTap;
4545
4797
  constructor();
4546
- /**
4547
- * Compute the largest scale (≤ 1) at which the whole slide fits the viewport,
4548
- * reserving the 1rem gutter + drop shadow. Sets fitScale to 1 when unmeasured.
4549
- */
4550
- private recomputeFit;
4551
4798
  readonly elements: _angular_core.Signal<PptxElement[]>;
4552
4799
  /**
4553
4800
  * Template elements + the slide's own elements, template first (behind). Used
@@ -4575,48 +4822,25 @@ declare class SlideCanvasComponent {
4575
4822
  height: number;
4576
4823
  }[]>;
4577
4824
  /** Bounding boxes (stage coords) for the selected elements. */
4578
- readonly selectionBoxes: _angular_core.Signal<{
4579
- id: string;
4580
- x: number;
4581
- y: number;
4582
- width: number;
4583
- height: number;
4584
- }[]>;
4825
+ readonly selectionBoxes: _angular_core.Signal<SelectionBox[]>;
4585
4826
  /** The single selected element's box, or null when 0 or >1 are selected. */
4586
4827
  readonly singleSelected: _angular_core.Signal<(Box & {
4587
4828
  id: string;
4588
4829
  }) | null>;
4589
4830
  /** Resize-handle render boxes (stage coords) for the single selection. */
4590
- readonly handleBoxes: _angular_core.Signal<{
4591
- handle: ResizeHandleId;
4592
- left: number;
4593
- top: number;
4594
- size: number;
4595
- cursor: string;
4596
- }[]>;
4831
+ readonly handleBoxes: _angular_core.Signal<HandleBox[]>;
4597
4832
  /** Rotation-handle box (stage coords) above the single selection, or null. */
4598
- readonly rotateHandle: _angular_core.Signal<{
4599
- left: number;
4600
- top: number;
4601
- size: number;
4602
- } | null>;
4833
+ readonly rotateHandle: _angular_core.Signal<CornerHandleBox | null>;
4603
4834
  /**
4604
4835
  * Shape-adjustment-handle box (stage coords) for the single selection, or
4605
4836
  * null. Sits just outside the top-left corner so it never collides with the
4606
4837
  * resize/rotate handles. Selection-only + editable-only, so it vanishes in
4607
4838
  * presentation alongside the rest of the edit chrome.
4608
4839
  */
4609
- readonly adjustHandle: _angular_core.Signal<{
4610
- left: number;
4611
- top: number;
4612
- size: number;
4613
- } | null>;
4840
+ readonly adjustHandle: _angular_core.Signal<CornerHandleBox | null>;
4614
4841
  /**
4615
4842
  * Resolve the id of the interactive element under a pointer target, or null.
4616
- * An element host carries `data-element-id`, but template (master/layout)
4617
- * elements are only interactive while editTemplateMode is on; when off they
4618
- * are reported as null so the canvas treats them as background (no
4619
- * select/drag/context-menu/inline-edit).
4843
+ * See {@link resolveInteractiveElementId}.
4620
4844
  */
4621
4845
  private interactiveElementIdAt;
4622
4846
  onStagePointerDown(event: PointerEvent): void;
@@ -4648,14 +4872,6 @@ declare class SlideCanvasComponent {
4648
4872
  onRotatePointerDown(event: PointerEvent): void;
4649
4873
  onPointerMove(event: PointerEvent): void;
4650
4874
  onPointerUp(): void;
4651
- /** Begin dragging an existing guide. Called from the guide handle pointerdown. */
4652
- onGuidePointerDown(event: PointerEvent, id: string, axis: RulerGuide['axis']): void;
4653
- /** Remove a guide (called on guide handle double-click). */
4654
- onGuideDoubleClick(event: MouseEvent, id: string): void;
4655
- /** Drag from the horizontal ruler to create a new horizontal guide (axis:'y'). */
4656
- onHRulerPointerDown(event: PointerEvent): void;
4657
- /** Drag from the vertical ruler to create a new vertical guide (axis:'x'). */
4658
- onVRulerPointerDown(event: PointerEvent): void;
4659
4875
  readonly wrapperStyle: _angular_core.Signal<StyleMap>;
4660
4876
  /**
4661
4877
  * Public accessor of the internal effective scale for ruler/overlay sizing
@@ -4833,8 +5049,7 @@ interface Paragraph {
4833
5049
  * ElementRendererComponent: Angular port of the React `ElementRenderer.tsx`
4834
5050
  * and the Vue `ElementRenderer.vue`.
4835
5051
  *
4836
- * Renders a single slide element by its `type` discriminant (viewer-first
4837
- * subset):
5052
+ * Renders a single slide element by its `type` discriminant:
4838
5053
  * - `text` / `shape` → positioned box with fill/stroke + rich text + effects
4839
5054
  * - `connector` → SVG straight/bent/curved connector
4840
5055
  * - `chart` → inline-SVG chart (bar/line/area/pie/scatter)
@@ -4842,14 +5057,13 @@ interface Paragraph {
4842
5057
  * - `smartArt` → SVG drawing-shapes / node-text fallback
4843
5058
  * - `ink` → SVG ink strokes
4844
5059
  * - `ole` → embedded-object preview / icon
4845
- * - `model3d` → poster / placeholder (no three.js)
5060
+ * - `model3d` → interactive three.js scene when the optional
5061
+ * `three` peer is present, else poster / placeholder
4846
5062
  * - `zoom` → slide/section zoom thumbnail
4847
5063
  * - `picture` / `image` → `<img>`
4848
5064
  * - `media` → native `<video>`/`<audio>` playback, poster fallback
4849
5065
  * - `group` → recursive children (self-referencing selector)
4850
- * - everything else → labelled placeholder (TODO, see PORTING.md)
4851
- *
4852
- * Interaction (selection, resize, inline editing) is not yet ported.
5066
+ * - everything else → labelled placeholder (defensive fallback)
4853
5067
  */
4854
5068
  declare class ElementRendererComponent {
4855
5069
  readonly element: _angular_core.InputSignal<PptxElement>;
@@ -4962,7 +5176,7 @@ declare class ElementRendererComponent {
4962
5176
  * Compound (double/triple) lines render as parallel strands and line caps map
4963
5177
  * from `a:ln/@cap`; both derive from the shared connector geometry.
4964
5178
  *
4965
- * Not yet ported (TODO, see PORTING.md): line shadows/glow.
5179
+ * Not yet ported (TODO): line shadows/glow.
4966
5180
  */
4967
5181
  declare class ConnectorRendererComponent {
4968
5182
  readonly element: _angular_core.InputSignal<PptxElement>;
@@ -5073,7 +5287,7 @@ declare class ChartRendererComponent {
5073
5287
  * its text directly on the diagram. Everything that can be expressed as a pure
5074
5288
  * function lives here so it can be unit-tested in plain vitest (the Angular
5075
5289
  * package's vitest setup has no Angular compiler, so component/TestBed tests are
5076
- * not available - see PORTING.md). The component
5290
+ * not available). The component
5077
5291
  * (`smart-art-renderer.component.ts`) stays thin: it owns only the edit-state
5078
5292
  * signal, the positioned `<textarea>`, and the call into the existing commit
5079
5293
  * path (`EditorStateService.updateElement`, the same channel the inspector's
@@ -5288,7 +5502,7 @@ interface InkStroke {
5288
5502
  * radii follow the per-point pressure, matching React's `renderInk`. Strokes
5289
5503
  * without pressure variation degrade to a plain constant-width `<path>`.
5290
5504
  *
5291
- * Not ported (TODO, see PORTING.md): ink replay animation and the
5505
+ * Not ported (TODO): ink replay animation and the
5292
5506
  * highlighter/eraser tool blend modes.
5293
5507
  *
5294
5508
  * All non-trivial pure computation lives in `ink-renderer-helpers.ts` (no
@@ -5916,7 +6130,6 @@ declare class PresentationOverlayComponent implements OnInit {
5916
6130
  * Escape or clicking the ✕ button emits `closed`.
5917
6131
  *
5918
6132
  * Viewer-first scope: no drag-reorder, no context menu, no section grouping.
5919
- * Those features are tracked in PORTING.md.
5920
6133
  *
5921
6134
  * Usage:
5922
6135
  * ```html