pptx-angular-viewer 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2911,6 +2911,82 @@ interface YjsFactories {
2911
2911
  createText: () => YTextLike;
2912
2912
  }
2913
2913
 
2914
+ /**
2915
+ * collaboration-live-patch-target.ts: locating an element inside the shared
2916
+ * Y.Doc and writing one interim patch into it.
2917
+ *
2918
+ * Split out of `collaboration-live-patch.ts` (which owns the throttled patcher
2919
+ * and the binding-facing helpers) to keep both files inside the repo's 300 LOC
2920
+ * ceiling. This half is pure: given a doc it finds the element's Y.Map through
2921
+ * the existing schema and applies geometry / text, with no timers or state.
2922
+ */
2923
+
2924
+ /** Interim geometry for an element mid-gesture. All fields optional. */
2925
+ interface LiveGeometryPatch {
2926
+ x?: number;
2927
+ y?: number;
2928
+ width?: number;
2929
+ height?: number;
2930
+ rotation?: number;
2931
+ }
2932
+ /** The element's pre-edit rich text, used to remap the interim plain text. */
2933
+ interface LiveTextSource {
2934
+ textSegments?: TextSegment[];
2935
+ textStyle?: TextStyle;
2936
+ }
2937
+
2938
+ /**
2939
+ * collaboration-live-patch.ts: interim ("live preview") writes straight into
2940
+ * the shared Y.Doc, bypassing each binding's slides state.
2941
+ *
2942
+ * Why: the editors deliberately keep gestures out of framework state. React's
2943
+ * drag/resize writes `style.left/top/width/height` on the DOM node and only
2944
+ * calls `updateElementById` on pointer-up; every binding's inline text editor
2945
+ * buffers the typed string and only commits it on blur. Because the Y.Doc sync
2946
+ * is driven off the slides state, remote peers saw nothing until the gesture
2947
+ * (or the edit) ended.
2948
+ *
2949
+ * This module patches the element's Y.Map directly, so the host keeps its
2950
+ * per-frame performance design while peers see the move/typing live:
2951
+ *
2952
+ * - geometry scalars (x / y / width / height / rotation) are set in place
2953
+ * - text goes through the SAME character-level Y.Text merge the reconcile
2954
+ * pass uses (`reconcileElementTextBody`), so concurrent typing on one
2955
+ * element still merges instead of last-write-wins
2956
+ * - writes are throttled (~1 per 50ms) with a trailing write, plus an
2957
+ * explicit `flush()` for gesture end
2958
+ * - every transaction is tagged with LOCAL_SYNC_ORIGIN, exactly like
2959
+ * `reconcileSlidesInYDoc`, so the local observer skips the echo
2960
+ *
2961
+ * It is a no-op until `configure()` is handed a live doc + factories, so
2962
+ * bindings can call it unconditionally.
2963
+ */
2964
+
2965
+ interface CollaborationLivePatcher {
2966
+ /** Attach a live doc (or `null` to go dormant). Pending patches are dropped. */
2967
+ configure: (doc: YDocLike | null, factories: YjsFactories | null) => void;
2968
+ /** True when a doc + factories are attached, i.e. patches will be written. */
2969
+ isActive: () => boolean;
2970
+ /** Queue interim geometry for an element. */
2971
+ patchGeometry: (slideId: string | undefined, elementId: string, geometry: LiveGeometryPatch) => void;
2972
+ /** Queue interim text for an element (remapped over `source`'s segments). */
2973
+ patchText: (slideId: string | undefined, elementId: string, text: string, source?: LiveTextSource) => void;
2974
+ /** Write everything queued right now (call on gesture end / edit commit). */
2975
+ flush: () => void;
2976
+ /** Drop pending work, cancel timers and detach the doc. */
2977
+ dispose: () => void;
2978
+ }
2979
+
2980
+ interface DepartureChannel {
2981
+ /**
2982
+ * Announce that this client is leaving `roomId`. Synchronous on purpose:
2983
+ * it must survive being called from a `pagehide` handler.
2984
+ */
2985
+ announce: () => void;
2986
+ /** Stop listening and release the channel. */
2987
+ dispose: () => void;
2988
+ }
2989
+
2914
2990
  /**
2915
2991
  * slide-compare — pure, framework-agnostic slide-diff engine shared by every
2916
2992
  * binding (React / Vue / Angular).
@@ -5773,9 +5849,16 @@ declare class CollaborationService {
5773
5849
  readonly followedSlideIndex: _angular_core.Signal<number | null>;
5774
5850
  /** Active-slide index of the first `owner` peer (the broadcaster), or null. */
5775
5851
  readonly broadcasterSlideIndex: _angular_core.Signal<number | null>;
5852
+ /**
5853
+ * Interim ("live preview") Y.Doc write channel: publishes in-flight inline
5854
+ * editor text that has not reached the slides state yet, so peers see typing
5855
+ * as it happens instead of on commit. Dormant outside a session.
5856
+ */
5857
+ readonly livePatcher: CollaborationLivePatcher;
5776
5858
  private ydoc;
5777
5859
  private provider;
5778
5860
  private awareness;
5861
+ private departure;
5779
5862
  private selfId;
5780
5863
  private applyingRemote;
5781
5864
  private yFactories;
@@ -5800,6 +5883,14 @@ declare class CollaborationService {
5800
5883
  private lastConfig;
5801
5884
  private lastOptions;
5802
5885
  private localPresence;
5886
+ /**
5887
+ * Reentrancy token for {@link connect}: bumped by every connect() and
5888
+ * disconnect(). A connect() whose token no longer matches after an await was
5889
+ * superseded, so it tears down whatever it just created and leaves the
5890
+ * service state to the newer call (a second provider join on the same room
5891
+ * would otherwise throw inside Yjs and kill the surviving session).
5892
+ */
5893
+ private connectToken;
5803
5894
  private readonly refreshPresence;
5804
5895
  constructor();
5805
5896
  connect(config: CollaborationConfig, options?: ConnectOptions): Promise<void>;
@@ -6253,8 +6344,6 @@ interface BarAction {
6253
6344
  key: NonNullable<MobileBarSheet> | 'insert';
6254
6345
  labelKey: string;
6255
6346
  ariaLabelKey?: string;
6256
- /** SVG path data for the icon (24 × 24 view-box). */
6257
- svgPath: string;
6258
6347
  disabled: boolean;
6259
6348
  active: boolean;
6260
6349
  badge?: number;
@@ -6812,6 +6901,7 @@ declare class ViewerCanvasEditingService {
6812
6901
  private readonly editor;
6813
6902
  private readonly dialogs;
6814
6903
  private readonly formatPainter;
6904
+ private readonly collab;
6815
6905
  /** Id of the element being inline text-edited, or null. */
6816
6906
  readonly editingId: _angular_core.WritableSignal<string | null>;
6817
6907
  /** Open editor context-menu position (client coords), or null. */
@@ -6833,6 +6923,16 @@ declare class ViewerCanvasEditingService {
6833
6923
  id: string;
6834
6924
  updates: Partial<TextStyle>;
6835
6925
  }): void;
6926
+ /**
6927
+ * Mirror an in-progress inline edit to collaborators. The typed text only
6928
+ * reaches the editor state (and therefore the Y.Doc reconcile) on commit, so
6929
+ * without this peers saw nothing until the editor blurred. No-op when not
6930
+ * collaborating.
6931
+ */
6932
+ onTextInput(event: {
6933
+ id: string;
6934
+ text: string;
6935
+ }): void;
6836
6936
  /** Commit an inline text edit: replace the element's text (one history entry). */
6837
6937
  onTextCommit(event: {
6838
6938
  id: string;
@@ -6957,6 +7057,14 @@ declare class ViewerCollaborationSessionService {
6957
7057
  */
6958
7058
  private readonly activeSession;
6959
7059
  private host;
7060
+ /**
7061
+ * Last host `collaboration` config handled by {@link syncHostConfig}
7062
+ * (reference-equal dedup). A re-invocation with the same object (e.g. a
7063
+ * spurious effect re-run) must not reconnect: a second provider join on the
7064
+ * same room throws inside Yjs and tears down the live session. Cleared on
7065
+ * the explicit stop paths so stop + restart with the same object works.
7066
+ */
7067
+ private lastSyncedConfig;
6960
7068
  /** Wire the host inputs/outputs (called once from the component constructor). */
6961
7069
  bind(host: CollaborationSessionHost): void;
6962
7070
  private requireHost;
@@ -7444,6 +7552,11 @@ declare class ViewerInspectorPanelService {
7444
7552
  readonly visibleInspectorKind: _angular_core.Signal<InspectorContent>;
7445
7553
  /** Accessible-label translation key for the inspector host, by active content. */
7446
7554
  readonly inspectorLabel: _angular_core.Signal<"" | "pptx.toolbar.comments" | "pptx.accessibility.title" | "pptx.viewer.digitalSignatures" | "pptx.selectionPane.title" | "pptx.inspector.properties">;
7555
+ /**
7556
+ * Dismiss the mobile bottom-sheet inspector (backdrop tap or a past-threshold
7557
+ * swipe-down): hide the host until reopened and clear any explicit tool panel.
7558
+ */
7559
+ dismissMobileInspector(): void;
7447
7560
  /** Toggle a right-docked tool panel (clicking the active one closes it). */
7448
7561
  togglePanel(panel: InspectorToolPanel): void;
7449
7562
  /**
@@ -9397,6 +9510,15 @@ declare class SlideCanvasComponent implements SlideContext {
9397
9510
  id: string;
9398
9511
  text: string;
9399
9512
  }>;
9513
+ /**
9514
+ * Emitted on EVERY keystroke while inline-editing. The commit path stays the
9515
+ * only thing that touches editor state/history; this feeds the collaboration
9516
+ * live preview so peers see typing before the edit commits.
9517
+ */
9518
+ readonly textInput: _angular_core.OutputEmitterRef<{
9519
+ id: string;
9520
+ text: string;
9521
+ }>;
9400
9522
  /** Emitted when an inline edit is cancelled (Escape). */
9401
9523
  readonly textCancel: _angular_core.OutputEmitterRef<void>;
9402
9524
  /** Emitted on Ctrl/Cmd+B/I/U while inline-editing (parity with React/Vue). */
@@ -9542,6 +9664,8 @@ declare class SlideCanvasComponent implements SlideContext {
9542
9664
  private trimTrailingSpaceBeforeCaret;
9543
9665
  /** Toggle bold/italic/underline for the element under inline edit. */
9544
9666
  private emitTextFormat;
9667
+ /** Mirror each keystroke out for the collaboration live preview. */
9668
+ onTextInput(event: Event, id: string): void;
9545
9669
  commitText(event: Event, id: string): void;
9546
9670
  onContextMenu(event: MouseEvent): void;
9547
9671
  onHandlePointerDown(event: PointerEvent, handle: ResizeHandle): void;
@@ -9571,7 +9695,7 @@ declare class SlideCanvasComponent implements SlideContext {
9571
9695
  readonly vRulerTicks: _angular_core.Signal<readonly RulerTick[]>;
9572
9696
  readonly stageStyle: _angular_core.Signal<StyleMap>;
9573
9697
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SlideCanvasComponent, never>;
9574
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SlideCanvasComponent, "pptx-slide-canvas", never, { "slide": { "alias": "slide"; "required": false; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "showGrid": { "alias": "showGrid"; "required": false; "isSignal": true; }; "showRulers": { "alias": "showRulers"; "required": false; "isSignal": true; }; "showGuides": { "alias": "showGuides"; "required": false; "isSignal": true; }; "snapToGrid": { "alias": "snapToGrid"; "required": false; "isSignal": true; }; "snapToShape": { "alias": "snapToShape"; "required": false; "isSignal": true; }; "guideCommand": { "alias": "guideCommand"; "required": false; "isSignal": true; }; "spellCheck": { "alias": "spellCheck"; "required": false; "isSignal": true; }; "snapToGuides": { "alias": "snapToGuides"; "required": false; "isSignal": true; }; "autoFit": { "alias": "autoFit"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "presenting": { "alias": "presenting"; "required": false; "isSignal": true; }; "selectedIds": { "alias": "selectedIds"; "required": false; "isSignal": true; }; "editingId": { "alias": "editingId"; "required": false; "isSignal": true; }; "editTemplateMode": { "alias": "editTemplateMode"; "required": false; "isSignal": true; }; "templateElements": { "alias": "templateElements"; "required": false; "isSignal": true; }; "aiHighlights": { "alias": "aiHighlights"; "required": false; "isSignal": true; }; "aiActive": { "alias": "aiActive"; "required": false; "isSignal": true; }; "aiActiveSlideIndex": { "alias": "aiActiveSlideIndex"; "required": false; "isSignal": true; }; "aiChangeBatch": { "alias": "aiChangeBatch"; "required": false; "isSignal": true; }; "aiPickMode": { "alias": "aiPickMode"; "required": false; "isSignal": true; }; "drawTool": { "alias": "drawTool"; "required": false; "isSignal": true; }; "drawColor": { "alias": "drawColor"; "required": false; "isSignal": true; }; "drawWidth": { "alias": "drawWidth"; "required": false; "isSignal": true; }; }, { "elementSelect": "elementSelect"; "backgroundClick": "backgroundClick"; "transformStart": "transformStart"; "transformUpdate": "transformUpdate"; "contextMenu": "contextMenu"; "textEditStart": "textEditStart"; "textCommit": "textCommit"; "textCancel": "textCancel"; "textFormat": "textFormat"; "rotateUpdate": "rotateUpdate"; "marqueeSelect": "marqueeSelect"; "inkStrokeComplete": "inkStrokeComplete"; "eraserHit": "eraserHit"; "cellCommit": "cellCommit"; "tableChange": "tableChange"; }, never, never, true, never>;
9698
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SlideCanvasComponent, "pptx-slide-canvas", never, { "slide": { "alias": "slide"; "required": false; "isSignal": true; }; "canvasSize": { "alias": "canvasSize"; "required": true; "isSignal": true; }; "mediaDataUrls": { "alias": "mediaDataUrls"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "showGrid": { "alias": "showGrid"; "required": false; "isSignal": true; }; "showRulers": { "alias": "showRulers"; "required": false; "isSignal": true; }; "showGuides": { "alias": "showGuides"; "required": false; "isSignal": true; }; "snapToGrid": { "alias": "snapToGrid"; "required": false; "isSignal": true; }; "snapToShape": { "alias": "snapToShape"; "required": false; "isSignal": true; }; "guideCommand": { "alias": "guideCommand"; "required": false; "isSignal": true; }; "spellCheck": { "alias": "spellCheck"; "required": false; "isSignal": true; }; "snapToGuides": { "alias": "snapToGuides"; "required": false; "isSignal": true; }; "autoFit": { "alias": "autoFit"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "presenting": { "alias": "presenting"; "required": false; "isSignal": true; }; "selectedIds": { "alias": "selectedIds"; "required": false; "isSignal": true; }; "editingId": { "alias": "editingId"; "required": false; "isSignal": true; }; "editTemplateMode": { "alias": "editTemplateMode"; "required": false; "isSignal": true; }; "templateElements": { "alias": "templateElements"; "required": false; "isSignal": true; }; "aiHighlights": { "alias": "aiHighlights"; "required": false; "isSignal": true; }; "aiActive": { "alias": "aiActive"; "required": false; "isSignal": true; }; "aiActiveSlideIndex": { "alias": "aiActiveSlideIndex"; "required": false; "isSignal": true; }; "aiChangeBatch": { "alias": "aiChangeBatch"; "required": false; "isSignal": true; }; "aiPickMode": { "alias": "aiPickMode"; "required": false; "isSignal": true; }; "drawTool": { "alias": "drawTool"; "required": false; "isSignal": true; }; "drawColor": { "alias": "drawColor"; "required": false; "isSignal": true; }; "drawWidth": { "alias": "drawWidth"; "required": false; "isSignal": true; }; }, { "elementSelect": "elementSelect"; "backgroundClick": "backgroundClick"; "transformStart": "transformStart"; "transformUpdate": "transformUpdate"; "contextMenu": "contextMenu"; "textEditStart": "textEditStart"; "textCommit": "textCommit"; "textInput": "textInput"; "textCancel": "textCancel"; "textFormat": "textFormat"; "rotateUpdate": "rotateUpdate"; "marqueeSelect": "marqueeSelect"; "inkStrokeComplete": "inkStrokeComplete"; "eraserHit": "eraserHit"; "cellCommit": "cellCommit"; "tableChange": "tableChange"; }, never, never, true, never>;
9575
9699
  }
9576
9700
 
9577
9701
  /**
@@ -12404,13 +12528,15 @@ declare class MobileToolbarComponent {
12404
12528
  readonly undo: _angular_core.OutputEmitterRef<void>;
12405
12529
  /** User tapped Redo. */
12406
12530
  readonly redo: _angular_core.OutputEmitterRef<void>;
12531
+ /** User tapped Share (opens the collaboration share dialog). */
12532
+ readonly share: _angular_core.OutputEmitterRef<void>;
12407
12533
  /** User tapped Save (download as .pptx). */
12408
12534
  readonly save: _angular_core.OutputEmitterRef<void>;
12409
12535
  /** User tapped Present. */
12410
12536
  readonly present: _angular_core.OutputEmitterRef<void>;
12411
12537
  protected readonly toolbar: ToolbarVisibility;
12412
12538
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileToolbarComponent, never>;
12413
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileToolbarComponent, "pptx-mobile-toolbar", never, { "canUndo": { "alias": "canUndo"; "required": false; "isSignal": true; }; "canRedo": { "alias": "canRedo"; "required": false; "isSignal": true; }; "canPresent": { "alias": "canPresent"; "required": false; "isSignal": true; }; "canEdit": { "alias": "canEdit"; "required": false; "isSignal": true; }; "menuOpen": { "alias": "menuOpen"; "required": false; "isSignal": true; }; "aiEnabled": { "alias": "aiEnabled"; "required": false; "isSignal": true; }; "aiPanelOpen": { "alias": "aiPanelOpen"; "required": false; "isSignal": true; }; "hiddenActions": { "alias": "hiddenActions"; "required": false; "isSignal": true; }; }, { "toggleMenu": "toggleMenu"; "toggleAiPanel": "toggleAiPanel"; "undo": "undo"; "redo": "redo"; "save": "save"; "present": "present"; }, never, never, true, never>;
12539
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileToolbarComponent, "pptx-mobile-toolbar", never, { "canUndo": { "alias": "canUndo"; "required": false; "isSignal": true; }; "canRedo": { "alias": "canRedo"; "required": false; "isSignal": true; }; "canPresent": { "alias": "canPresent"; "required": false; "isSignal": true; }; "canEdit": { "alias": "canEdit"; "required": false; "isSignal": true; }; "menuOpen": { "alias": "menuOpen"; "required": false; "isSignal": true; }; "aiEnabled": { "alias": "aiEnabled"; "required": false; "isSignal": true; }; "aiPanelOpen": { "alias": "aiPanelOpen"; "required": false; "isSignal": true; }; "hiddenActions": { "alias": "hiddenActions"; "required": false; "isSignal": true; }; }, { "toggleMenu": "toggleMenu"; "toggleAiPanel": "toggleAiPanel"; "undo": "undo"; "redo": "redo"; "share": "share"; "save": "save"; "present": "present"; }, never, never, true, never>;
12414
12540
  }
12415
12541
 
12416
12542
  declare class NotesPanelComponent {
@@ -13566,7 +13692,7 @@ declare class SetUpSlideShowDialogComponent {
13566
13692
  readonly close: _angular_core.OutputEmitterRef<void>;
13567
13693
  /** Working copy of the properties; seeded from `properties` on open. */
13568
13694
  readonly draft: _angular_core.WritableSignal<PptxPresentationProperties>;
13569
- readonly showType: _angular_core.Signal<"kiosk" | "browsed" | "presented">;
13695
+ readonly showType: _angular_core.Signal<"kiosk" | "presented" | "browsed">;
13570
13696
  readonly showSlidesMode: _angular_core.Signal<"range" | "all" | "customShow">;
13571
13697
  constructor();
13572
13698
  /** Merge a partial patch into the current draft. */
@@ -13955,7 +14081,7 @@ declare class AccountPageComponent {
13955
14081
  readonly accountAuth: _angular_core.InputSignal<AccountAuthConfig | undefined>;
13956
14082
  private readonly translate;
13957
14083
  protected readonly swatches: readonly string[];
13958
- protected readonly version = "1.31.5";
14084
+ protected readonly version = "2.0.1";
13959
14085
  protected readonly profile: _angular_core.WritableSignal<ViewerProfile>;
13960
14086
  protected readonly initial: _angular_core.Signal<string>;
13961
14087
  protected readonly usage: _angular_core.WritableSignal<LocalStorageUsageSummary | null>;
@@ -15960,6 +16086,8 @@ declare function createSwipeDismissDrag(onDismiss: () => void): SwipeDismissDrag
15960
16086
  /** Minimal awareness surface used by the service. */
15961
16087
  interface AwarenessLike {
15962
16088
  clientID?: number;
16089
+ /** Passing `null` withdraws the local presence (see `clearLocalAwareness`). */
16090
+ setLocalState?: (state: null) => void;
15963
16091
  setLocalStateField: (field: string, value: unknown) => void;
15964
16092
  getStates: () => Map<number, Record<string, unknown>>;
15965
16093
  on: (event: string, cb: () => void) => void;
@@ -15986,6 +16114,13 @@ interface ProviderBundle {
15986
16114
  provider: ProviderLike;
15987
16115
  awareness: AwarenessLike;
15988
16116
  factories: YjsFactories;
16117
+ /**
16118
+ * Synchronous "I am leaving" announcement. The provider's own awareness
16119
+ * removal is broadcast a microtask later, which never escapes a document
16120
+ * that is already being destroyed (see the shared collaboration-departure
16121
+ * module), so `disconnect()` announces through this first.
16122
+ */
16123
+ departure: DepartureChannel;
15989
16124
  }
15990
16125
  /** Create a y-websocket transport bundle for `config`. */
15991
16126
  declare function createWebsocketBundle(config: CollaborationConfig): Promise<ProviderBundle>;