pptx-angular-viewer 2.1.0 → 2.2.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.
- package/CHANGELOG.md +8 -0
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-DuJ08L_k.mjs → pptx-angular-viewer-chat-history-idb-Cf56VuqS.mjs} +2 -2
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-DuJ08L_k.mjs.map → pptx-angular-viewer-chat-history-idb-Cf56VuqS.mjs.map} +1 -1
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-X944WpER.mjs → pptx-angular-viewer-pptx-angular-viewer-CA_G8slL.mjs} +610 -305
- package/fesm2022/pptx-angular-viewer-pptx-angular-viewer-CA_G8slL.mjs.map +1 -0
- package/fesm2022/pptx-angular-viewer.mjs +1 -1
- package/package.json +1 -1
- package/types/pptx-angular-viewer.d.ts +129 -150
- package/fesm2022/pptx-angular-viewer-pptx-angular-viewer-X944WpER.mjs.map +0 -1
|
@@ -38393,6 +38393,165 @@ function formatSlideCounter(currentSlide, totalSlides) {
|
|
|
38393
38393
|
return `${currentSlide + 1} / ${totalSlides}`;
|
|
38394
38394
|
}
|
|
38395
38395
|
|
|
38396
|
+
/**
|
|
38397
|
+
* PowerPoint-accurate slide-show keyboard map.
|
|
38398
|
+
*
|
|
38399
|
+
* Single source of truth for what a key press does while a slide show is
|
|
38400
|
+
* running, shared by every binding so React / Vue / Angular / Svelte / Vanilla
|
|
38401
|
+
* cannot drift apart. Modelled on Microsoft's published shortcut list for
|
|
38402
|
+
* "Use keyboard shortcuts to deliver your presentation":
|
|
38403
|
+
*
|
|
38404
|
+
* next N, Enter, Page Down, Right, Down, Spacebar
|
|
38405
|
+
* previous P, Page Up, Left, Up, Backspace
|
|
38406
|
+
* goto type a slide number, then Enter
|
|
38407
|
+
* first Home last End
|
|
38408
|
+
* black B or `.` white W or `,`
|
|
38409
|
+
* laser Ctrl+L pen Ctrl+P
|
|
38410
|
+
* arrow Ctrl+A eraser Ctrl+E
|
|
38411
|
+
* erase-all E ink markup Ctrl+M
|
|
38412
|
+
* hide UI Ctrl+H all slides Ctrl+S
|
|
38413
|
+
* menu Shift+F10 end Esc or `-`
|
|
38414
|
+
*
|
|
38415
|
+
* Note the deliberate collisions with editor shortcuts: during a show `Ctrl+S`
|
|
38416
|
+
* is "All Slides", not save, and `Ctrl+A` is "arrow pointer", not select-all.
|
|
38417
|
+
* That is PowerPoint's behaviour and callers should not re-add editor handling
|
|
38418
|
+
* on top.
|
|
38419
|
+
*/
|
|
38420
|
+
/** A fresh, empty digit buffer. */
|
|
38421
|
+
function createPresentationKeyBuffer() {
|
|
38422
|
+
return { digits: '' };
|
|
38423
|
+
}
|
|
38424
|
+
// ---------------------------------------------------------------------------
|
|
38425
|
+
// Key sets
|
|
38426
|
+
// ---------------------------------------------------------------------------
|
|
38427
|
+
const NEXT_KEYS = new Set(['Enter', 'PageDown', 'ArrowRight', 'ArrowDown', ' ', 'Spacebar']);
|
|
38428
|
+
const PREVIOUS_KEYS = new Set(['PageUp', 'ArrowLeft', 'ArrowUp', 'Backspace']);
|
|
38429
|
+
/**
|
|
38430
|
+
* True when the modifier state means "no chord": PowerPoint's bare-letter
|
|
38431
|
+
* shortcuts (N, P, B, W, E) must not fire while Ctrl/Cmd/Alt is held.
|
|
38432
|
+
*/
|
|
38433
|
+
function isBare(input) {
|
|
38434
|
+
return !input.ctrlKey && !input.metaKey && !input.altKey;
|
|
38435
|
+
}
|
|
38436
|
+
/** True when Ctrl (Windows) or Cmd (macOS) is held, without Alt. */
|
|
38437
|
+
function isControlChord(input) {
|
|
38438
|
+
return Boolean(input.ctrlKey || input.metaKey) && !input.altKey;
|
|
38439
|
+
}
|
|
38440
|
+
// ---------------------------------------------------------------------------
|
|
38441
|
+
// Mapping
|
|
38442
|
+
// ---------------------------------------------------------------------------
|
|
38443
|
+
/**
|
|
38444
|
+
* Map one key press to a slide-show action.
|
|
38445
|
+
*
|
|
38446
|
+
* `buffer` is mutated in place to track a partially typed slide number. Pass
|
|
38447
|
+
* the same buffer for the lifetime of the show; {@link createPresentationKeyBuffer}
|
|
38448
|
+
* makes one. Digits return `buffering` so callers can show the pending number;
|
|
38449
|
+
* the following Enter resolves to `goto`.
|
|
38450
|
+
*/
|
|
38451
|
+
function mapPresentationKey(input, buffer = createPresentationKeyBuffer()) {
|
|
38452
|
+
const { key } = input;
|
|
38453
|
+
// -- Ctrl/Cmd chords ----------------------------------------------------
|
|
38454
|
+
// Checked first: Ctrl+P is the pen, while a bare P is "previous slide".
|
|
38455
|
+
if (isControlChord(input)) {
|
|
38456
|
+
switch (key.toLowerCase()) {
|
|
38457
|
+
case 'l':
|
|
38458
|
+
return { action: 'pointerTool', tool: 'laser' };
|
|
38459
|
+
case 'p':
|
|
38460
|
+
return { action: 'pointerTool', tool: 'pen' };
|
|
38461
|
+
case 'a':
|
|
38462
|
+
return { action: 'pointerTool', tool: 'arrow' };
|
|
38463
|
+
case 'e':
|
|
38464
|
+
return { action: 'pointerTool', tool: 'eraser' };
|
|
38465
|
+
case 'm':
|
|
38466
|
+
return { action: 'toggleInkMarkup' };
|
|
38467
|
+
case 'h':
|
|
38468
|
+
return { action: 'toggleChrome' };
|
|
38469
|
+
case 's':
|
|
38470
|
+
return { action: 'showAllSlides' };
|
|
38471
|
+
default:
|
|
38472
|
+
return { action: 'none' };
|
|
38473
|
+
}
|
|
38474
|
+
}
|
|
38475
|
+
// -- Context menu -------------------------------------------------------
|
|
38476
|
+
if ((key === 'F10' && input.shiftKey) || key === 'ContextMenu') {
|
|
38477
|
+
return { action: 'contextMenu' };
|
|
38478
|
+
}
|
|
38479
|
+
// -- Digit buffer (type a slide number, then Enter) ---------------------
|
|
38480
|
+
if (isBare(input) && key.length === 1 && key >= '0' && key <= '9') {
|
|
38481
|
+
// Cap the buffer so a leaning keyboard can't build an unbounded string.
|
|
38482
|
+
buffer.digits = (buffer.digits + key).slice(-4);
|
|
38483
|
+
return { action: 'buffering', buffer: buffer.digits };
|
|
38484
|
+
}
|
|
38485
|
+
// -- Navigation ---------------------------------------------------------
|
|
38486
|
+
if (NEXT_KEYS.has(key)) {
|
|
38487
|
+
// Enter resolves a pending slide number instead of advancing.
|
|
38488
|
+
if (key === 'Enter' && buffer.digits) {
|
|
38489
|
+
const slideNumber = Number.parseInt(buffer.digits, 10);
|
|
38490
|
+
buffer.digits = '';
|
|
38491
|
+
if (Number.isFinite(slideNumber) && slideNumber > 0) {
|
|
38492
|
+
return { action: 'goto', slideNumber };
|
|
38493
|
+
}
|
|
38494
|
+
return { action: 'none' };
|
|
38495
|
+
}
|
|
38496
|
+
buffer.digits = '';
|
|
38497
|
+
return { action: 'next' };
|
|
38498
|
+
}
|
|
38499
|
+
if (PREVIOUS_KEYS.has(key)) {
|
|
38500
|
+
buffer.digits = '';
|
|
38501
|
+
return { action: 'previous' };
|
|
38502
|
+
}
|
|
38503
|
+
if (key === 'Home') {
|
|
38504
|
+
buffer.digits = '';
|
|
38505
|
+
return { action: 'first' };
|
|
38506
|
+
}
|
|
38507
|
+
if (key === 'End') {
|
|
38508
|
+
buffer.digits = '';
|
|
38509
|
+
return { action: 'last' };
|
|
38510
|
+
}
|
|
38511
|
+
// -- Bare letters / punctuation -----------------------------------------
|
|
38512
|
+
if (isBare(input)) {
|
|
38513
|
+
switch (key) {
|
|
38514
|
+
case 'n':
|
|
38515
|
+
case 'N':
|
|
38516
|
+
buffer.digits = '';
|
|
38517
|
+
return { action: 'next' };
|
|
38518
|
+
case 'p':
|
|
38519
|
+
case 'P':
|
|
38520
|
+
buffer.digits = '';
|
|
38521
|
+
return { action: 'previous' };
|
|
38522
|
+
case 'b':
|
|
38523
|
+
case 'B':
|
|
38524
|
+
case '.':
|
|
38525
|
+
return { action: 'toggleBlackScreen' };
|
|
38526
|
+
case 'w':
|
|
38527
|
+
case 'W':
|
|
38528
|
+
case ',':
|
|
38529
|
+
return { action: 'toggleWhiteScreen' };
|
|
38530
|
+
case 'e':
|
|
38531
|
+
case 'E':
|
|
38532
|
+
return { action: 'eraseAnnotations' };
|
|
38533
|
+
case 'Escape':
|
|
38534
|
+
case '-':
|
|
38535
|
+
buffer.digits = '';
|
|
38536
|
+
return { action: 'end' };
|
|
38537
|
+
default:
|
|
38538
|
+
break;
|
|
38539
|
+
}
|
|
38540
|
+
}
|
|
38541
|
+
return { action: 'none' };
|
|
38542
|
+
}
|
|
38543
|
+
/**
|
|
38544
|
+
* True when the action changes which slide is shown, so callers can gate
|
|
38545
|
+
* side effects (rehearsal timing capture, audience sync) on real navigation.
|
|
38546
|
+
*/
|
|
38547
|
+
function isNavigationAction(action) {
|
|
38548
|
+
return (action.action === 'next' ||
|
|
38549
|
+
action.action === 'previous' ||
|
|
38550
|
+
action.action === 'first' ||
|
|
38551
|
+
action.action === 'last' ||
|
|
38552
|
+
action.action === 'goto');
|
|
38553
|
+
}
|
|
38554
|
+
|
|
38396
38555
|
/**
|
|
38397
38556
|
* action-buttons.ts: Action-button insertion (Insert > Action) shared across
|
|
38398
38557
|
* bindings. Builds the OOXML built-in action-button shapes: a labelled
|
|
@@ -47392,6 +47551,7 @@ const translationsEn = {
|
|
|
47392
47551
|
'pptx.presentation.highlighter': 'Highlighter',
|
|
47393
47552
|
'pptx.presentation.eraser': 'Eraser',
|
|
47394
47553
|
'pptx.presentation.laserPointer': 'Laser Pointer',
|
|
47554
|
+
'pptx.presentation.pointerTools': 'Pointer Options',
|
|
47395
47555
|
// Selection pane
|
|
47396
47556
|
'pptx.selectionPane.title': 'Selection Pane',
|
|
47397
47557
|
'pptx.selectionPane.empty': 'No elements',
|
|
@@ -48118,9 +48278,17 @@ const translationsEn = {
|
|
|
48118
48278
|
'pptx.presenter.nextSlide': 'Next Slide',
|
|
48119
48279
|
'pptx.presenter.noSlides': 'No slides',
|
|
48120
48280
|
'pptx.presenter.openAudienceWindow': 'Open Audience Window',
|
|
48121
|
-
'pptx.presenter.presenterView': 'Presenter View
|
|
48281
|
+
'pptx.presenter.presenterView': 'Presenter View',
|
|
48122
48282
|
'pptx.presenter.prev': 'Prev',
|
|
48123
48283
|
'pptx.presenter.previousSlide': 'Previous Slide',
|
|
48284
|
+
'pptx.presenter.seeAllSlides': 'See All Slides',
|
|
48285
|
+
'pptx.presenter.screen': 'Screen',
|
|
48286
|
+
'pptx.presenter.blackScreen': 'Black Screen',
|
|
48287
|
+
'pptx.presenter.whiteScreen': 'White Screen',
|
|
48288
|
+
'pptx.presenter.pointerArrow': 'Arrow',
|
|
48289
|
+
'pptx.presenter.pointerPen': 'Pen',
|
|
48290
|
+
'pptx.presenter.pointerHighlighter': 'Highlighter',
|
|
48291
|
+
'pptx.presenter.eraseAllInk': 'Erase All Ink on Slide',
|
|
48124
48292
|
'pptx.presenter.slideLabel': 'Slide {{current}} of {{total}}',
|
|
48125
48293
|
'pptx.presenter.timerProgress': 'Timer Progress',
|
|
48126
48294
|
// Print dialog
|
|
@@ -51743,7 +51911,7 @@ function createLocalStorageBackend(namespace) {
|
|
|
51743
51911
|
/** Try IndexedDB first; fall back to localStorage on any failure. */
|
|
51744
51912
|
async function resolveBackend(dbName, namespace) {
|
|
51745
51913
|
try {
|
|
51746
|
-
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-
|
|
51914
|
+
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-Cf56VuqS.mjs');
|
|
51747
51915
|
const db = await openChatDb(dbName);
|
|
51748
51916
|
return createIdbBackend(db);
|
|
51749
51917
|
}
|
|
@@ -53974,54 +54142,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
53974
54142
|
`, styles: [":host{position:absolute;inset:0;pointer-events:none;overflow:visible;z-index:9999}.pptx-ng-collab-cursor{position:absolute;top:0;left:0;pointer-events:none;will-change:transform;transition:transform 90ms linear}.pptx-ng-collab-pointer{display:block;filter:drop-shadow(0 1px 1px rgba(0,0,0,.35))}.pptx-ng-collab-label{position:absolute;top:16px;left:12px;max-width:150px;padding:2px 6px;border-radius:4px;color:#fff;font-family:system-ui,sans-serif;font-size:10px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-shadow:0 1px 2px #0000004d}\n"] }]
|
|
53975
54143
|
}], propDecorators: { cursors: [{ type: i0.Input, args: [{ isSignal: true, alias: "cursors", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }] } });
|
|
53976
54144
|
|
|
53977
|
-
/**
|
|
53978
|
-
* collaboration-local-presence.ts: publishes the local user's presence into a
|
|
53979
|
-
* single awareness `presence` field.
|
|
53980
|
-
*
|
|
53981
|
-
* Cursor, selection, and active-slide updates all merge into one record so a
|
|
53982
|
-
* later cursor move never clobbers the selection (or vice versa). The remote
|
|
53983
|
-
* side reads this shape via the shared `derivePresenceList`.
|
|
53984
|
-
*/
|
|
53985
|
-
class LocalPresencePublisher {
|
|
53986
|
-
awareness;
|
|
53987
|
-
identity;
|
|
53988
|
-
activeSlide = 0;
|
|
53989
|
-
cursor = { x: 0, y: 0 };
|
|
53990
|
-
selection;
|
|
53991
|
-
constructor(awareness, identity) {
|
|
53992
|
-
this.awareness = awareness;
|
|
53993
|
-
this.identity = identity;
|
|
53994
|
-
}
|
|
53995
|
-
/** Re-emit the merged presence record (also used as a heartbeat). */
|
|
53996
|
-
publish() {
|
|
53997
|
-
this.awareness.setLocalStateField('presence', {
|
|
53998
|
-
userName: this.identity.userName,
|
|
53999
|
-
userColor: this.identity.userColor,
|
|
54000
|
-
userAvatar: this.identity.userAvatar,
|
|
54001
|
-
role: this.identity.role,
|
|
54002
|
-
activeSlideIndex: this.activeSlide,
|
|
54003
|
-
cursorX: this.cursor.x,
|
|
54004
|
-
cursorY: this.cursor.y,
|
|
54005
|
-
selectedElementId: this.selection,
|
|
54006
|
-
lastUpdated: new Date().toISOString(),
|
|
54007
|
-
});
|
|
54008
|
-
}
|
|
54009
|
-
setCursor(x, y, activeSlideIndex = this.activeSlide) {
|
|
54010
|
-
this.cursor = { x, y };
|
|
54011
|
-
this.activeSlide = activeSlideIndex;
|
|
54012
|
-
this.publish();
|
|
54013
|
-
}
|
|
54014
|
-
setSelection(selectedElementId, activeSlideIndex = this.activeSlide) {
|
|
54015
|
-
this.selection = selectedElementId;
|
|
54016
|
-
this.activeSlide = activeSlideIndex;
|
|
54017
|
-
this.publish();
|
|
54018
|
-
}
|
|
54019
|
-
setActiveSlide(index) {
|
|
54020
|
-
this.activeSlide = Math.max(0, Math.floor(index));
|
|
54021
|
-
this.publish();
|
|
54022
|
-
}
|
|
54023
|
-
}
|
|
54024
|
-
|
|
54025
54145
|
/**
|
|
54026
54146
|
* collaboration-providers.ts: transport factories for the Angular
|
|
54027
54147
|
* collaboration service.
|
|
@@ -54083,6 +54203,346 @@ async function createWebrtcBundle(config) {
|
|
|
54083
54203
|
};
|
|
54084
54204
|
}
|
|
54085
54205
|
|
|
54206
|
+
/**
|
|
54207
|
+
* collaboration-connection.ts: provider connection-status wiring for the
|
|
54208
|
+
* Angular `CollaborationService`. Owns the websocket connect timeout and drives
|
|
54209
|
+
* the status transitions plus the sync-gate re-arm on drops; extracted from
|
|
54210
|
+
* `collaboration.service.ts` so the service stays within the file-size budget.
|
|
54211
|
+
*
|
|
54212
|
+
* Mirrors the vanilla binding's module of the same name, but over Angular's
|
|
54213
|
+
* `ProviderLike` event surface (`on('status', payload)` with
|
|
54214
|
+
* `payload.connected` for webrtc / `payload.status` for websocket).
|
|
54215
|
+
*/
|
|
54216
|
+
/**
|
|
54217
|
+
* Subscribe to the provider's status events, driving `setStatus` and the
|
|
54218
|
+
* sync-gate re-arm. For websocket transports this also arms a one-shot connect
|
|
54219
|
+
* timeout when the socket is not open yet.
|
|
54220
|
+
*/
|
|
54221
|
+
function wireConnectionStatus(deps) {
|
|
54222
|
+
const { provider, transport } = deps;
|
|
54223
|
+
let connectTimer = null;
|
|
54224
|
+
function cancelConnectTimer() {
|
|
54225
|
+
if (connectTimer !== null) {
|
|
54226
|
+
clearTimeout(connectTimer);
|
|
54227
|
+
connectTimer = null;
|
|
54228
|
+
}
|
|
54229
|
+
}
|
|
54230
|
+
if (transport === 'webrtc') {
|
|
54231
|
+
// P2P: no server round-trip to wait on. Treat "created" as connected, and
|
|
54232
|
+
// reflect explicit disconnect events (re-arming the gate on a drop).
|
|
54233
|
+
deps.setStatus('connected');
|
|
54234
|
+
provider.on('status', (payload) => {
|
|
54235
|
+
if (payload.connected === false && deps.isActive()) {
|
|
54236
|
+
deps.setStatus('disconnected');
|
|
54237
|
+
deps.reArmGate();
|
|
54238
|
+
}
|
|
54239
|
+
else if (payload.connected === true) {
|
|
54240
|
+
deps.setStatus('connected');
|
|
54241
|
+
}
|
|
54242
|
+
});
|
|
54243
|
+
return { cancelConnectTimer };
|
|
54244
|
+
}
|
|
54245
|
+
provider.on('status', (payload) => {
|
|
54246
|
+
if (payload.status === 'connected') {
|
|
54247
|
+
cancelConnectTimer();
|
|
54248
|
+
deps.setStatus('connected');
|
|
54249
|
+
}
|
|
54250
|
+
else if (payload.status === 'disconnected' && deps.isActive()) {
|
|
54251
|
+
deps.setStatus('disconnected');
|
|
54252
|
+
deps.reArmGate();
|
|
54253
|
+
}
|
|
54254
|
+
});
|
|
54255
|
+
if (provider.wsconnected) {
|
|
54256
|
+
deps.setStatus('connected');
|
|
54257
|
+
return { cancelConnectTimer };
|
|
54258
|
+
}
|
|
54259
|
+
connectTimer = setTimeout(() => {
|
|
54260
|
+
connectTimer = null;
|
|
54261
|
+
if (deps.getStatus() !== 'connected') {
|
|
54262
|
+
deps.onConnectTimeout();
|
|
54263
|
+
}
|
|
54264
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
54265
|
+
return { cancelConnectTimer };
|
|
54266
|
+
}
|
|
54267
|
+
|
|
54268
|
+
/**
|
|
54269
|
+
* collaboration-local-presence.ts: publishes the local user's presence into a
|
|
54270
|
+
* single awareness `presence` field.
|
|
54271
|
+
*
|
|
54272
|
+
* Cursor, selection, and active-slide updates all merge into one record so a
|
|
54273
|
+
* later cursor move never clobbers the selection (or vice versa). The remote
|
|
54274
|
+
* side reads this shape via the shared `derivePresenceList`.
|
|
54275
|
+
*/
|
|
54276
|
+
class LocalPresencePublisher {
|
|
54277
|
+
awareness;
|
|
54278
|
+
identity;
|
|
54279
|
+
activeSlide = 0;
|
|
54280
|
+
cursor = { x: 0, y: 0 };
|
|
54281
|
+
selection;
|
|
54282
|
+
constructor(awareness, identity) {
|
|
54283
|
+
this.awareness = awareness;
|
|
54284
|
+
this.identity = identity;
|
|
54285
|
+
}
|
|
54286
|
+
/** Re-emit the merged presence record (also used as a heartbeat). */
|
|
54287
|
+
publish() {
|
|
54288
|
+
this.awareness.setLocalStateField('presence', {
|
|
54289
|
+
userName: this.identity.userName,
|
|
54290
|
+
userColor: this.identity.userColor,
|
|
54291
|
+
userAvatar: this.identity.userAvatar,
|
|
54292
|
+
role: this.identity.role,
|
|
54293
|
+
activeSlideIndex: this.activeSlide,
|
|
54294
|
+
cursorX: this.cursor.x,
|
|
54295
|
+
cursorY: this.cursor.y,
|
|
54296
|
+
selectedElementId: this.selection,
|
|
54297
|
+
lastUpdated: new Date().toISOString(),
|
|
54298
|
+
});
|
|
54299
|
+
}
|
|
54300
|
+
setCursor(x, y, activeSlideIndex = this.activeSlide) {
|
|
54301
|
+
this.cursor = { x, y };
|
|
54302
|
+
this.activeSlide = activeSlideIndex;
|
|
54303
|
+
this.publish();
|
|
54304
|
+
}
|
|
54305
|
+
setSelection(selectedElementId, activeSlideIndex = this.activeSlide) {
|
|
54306
|
+
this.selection = selectedElementId;
|
|
54307
|
+
this.activeSlide = activeSlideIndex;
|
|
54308
|
+
this.publish();
|
|
54309
|
+
}
|
|
54310
|
+
setActiveSlide(index) {
|
|
54311
|
+
this.activeSlide = Math.max(0, Math.floor(index));
|
|
54312
|
+
this.publish();
|
|
54313
|
+
}
|
|
54314
|
+
}
|
|
54315
|
+
|
|
54316
|
+
/**
|
|
54317
|
+
* collaboration-session-setup.ts: build up and tear down a live collaboration
|
|
54318
|
+
* session for the Angular `CollaborationService`.
|
|
54319
|
+
*
|
|
54320
|
+
* Extracted from `collaboration.service.ts` so the service keeps only its
|
|
54321
|
+
* reactive state and public API. `activateSession` wires a freshly-created
|
|
54322
|
+
* provider bundle into a running session and returns the {@link ActiveSession}
|
|
54323
|
+
* the service holds as a single atomic handle; `teardownSession` disposes it.
|
|
54324
|
+
*/
|
|
54325
|
+
/**
|
|
54326
|
+
* Wire `bundle` into an active session: configure the live-patch channel, bind
|
|
54327
|
+
* the slide-sync engine, publish local presence, subscribe to awareness +
|
|
54328
|
+
* connection-status + remote-slide changes, then open the first-write gate.
|
|
54329
|
+
*/
|
|
54330
|
+
function activateSession(bundle, config, transport, deps) {
|
|
54331
|
+
deps.livePatcher.configure(bundle.doc, bundle.factories);
|
|
54332
|
+
deps.slideSync.bind({
|
|
54333
|
+
ydoc: bundle.doc,
|
|
54334
|
+
factories: bundle.factories,
|
|
54335
|
+
onRemoteSlides: deps.onRemoteSlides,
|
|
54336
|
+
scheduleWriteBack: deps.scheduleWriteBack,
|
|
54337
|
+
});
|
|
54338
|
+
const localPresence = new LocalPresencePublisher(bundle.awareness, {
|
|
54339
|
+
userName: config.userName,
|
|
54340
|
+
userColor: config.userColor ?? DEFAULT_CURSOR_COLOR,
|
|
54341
|
+
userAvatar: config.userAvatar,
|
|
54342
|
+
role: config.role,
|
|
54343
|
+
});
|
|
54344
|
+
localPresence.publish();
|
|
54345
|
+
bundle.awareness.on('change', deps.refreshPresence);
|
|
54346
|
+
bundle.awareness.on('update', deps.refreshPresence);
|
|
54347
|
+
deps.slideSync.gate.reset();
|
|
54348
|
+
const connection = wireConnectionStatus({
|
|
54349
|
+
provider: bundle.provider,
|
|
54350
|
+
transport,
|
|
54351
|
+
setStatus: deps.setStatus,
|
|
54352
|
+
getStatus: deps.getStatus,
|
|
54353
|
+
isActive: deps.isActive,
|
|
54354
|
+
reArmGate: () => {
|
|
54355
|
+
deps.slideSync.gate.reset();
|
|
54356
|
+
deps.slideSync.gate.arm();
|
|
54357
|
+
},
|
|
54358
|
+
onConnectTimeout: deps.failConnection,
|
|
54359
|
+
});
|
|
54360
|
+
deps.slideSync.wireSynced(bundle.provider);
|
|
54361
|
+
const unobserve = observeYDocSlides(bundle.doc, (_events, transaction) => deps.slideSync.onRemoteChange(transaction));
|
|
54362
|
+
return {
|
|
54363
|
+
ydoc: bundle.doc,
|
|
54364
|
+
provider: bundle.provider,
|
|
54365
|
+
awareness: bundle.awareness,
|
|
54366
|
+
departure: bundle.departure,
|
|
54367
|
+
factories: bundle.factories,
|
|
54368
|
+
selfId: bundle.awareness.clientID ?? -1,
|
|
54369
|
+
localPresence,
|
|
54370
|
+
connection,
|
|
54371
|
+
unobserve,
|
|
54372
|
+
};
|
|
54373
|
+
}
|
|
54374
|
+
/**
|
|
54375
|
+
* Dispose a live session. Announces the departure synchronously first: the
|
|
54376
|
+
* provider's own awareness removal is broadcast a microtask later and would be
|
|
54377
|
+
* dropped when this runs from a document being destroyed, leaving a ghost
|
|
54378
|
+
* collaborator until the 30s awareness timeout.
|
|
54379
|
+
*/
|
|
54380
|
+
function teardownSession(session, refreshPresence) {
|
|
54381
|
+
session.connection.cancelConnectTimer();
|
|
54382
|
+
session.unobserve();
|
|
54383
|
+
session.awareness.off?.('change', refreshPresence);
|
|
54384
|
+
session.awareness.off?.('update', refreshPresence);
|
|
54385
|
+
session.departure.announce();
|
|
54386
|
+
session.departure.dispose();
|
|
54387
|
+
clearLocalAwareness(session.awareness);
|
|
54388
|
+
session.provider.disconnect();
|
|
54389
|
+
session.provider.destroy();
|
|
54390
|
+
session.ydoc.destroy();
|
|
54391
|
+
}
|
|
54392
|
+
|
|
54393
|
+
/**
|
|
54394
|
+
* collaboration-slide-sync.ts: the granular local<->doc slide sync engine for
|
|
54395
|
+
* the Angular `CollaborationService`, extracted so the service stays within the
|
|
54396
|
+
* repo's 300 LOC ceiling.
|
|
54397
|
+
*
|
|
54398
|
+
* Owns the first-write gate plus the echo-dedupe bookkeeping (`lastSynced`,
|
|
54399
|
+
* `applyingRemote`, `pendingBroadcast`) and the broadcast / remote-apply /
|
|
54400
|
+
* post-load-adoption logic. The service keeps provider + awareness ownership
|
|
54401
|
+
* and binds this engine to the live doc for the duration of a session.
|
|
54402
|
+
*/
|
|
54403
|
+
class SlideSyncEngine {
|
|
54404
|
+
/**
|
|
54405
|
+
* First-write gate: local broadcasts are suppressed (captured as pending)
|
|
54406
|
+
* until the provider confirms its initial sync or the grace period lifts the
|
|
54407
|
+
* gate, so a late joiner never seeds its placeholder deck into a room whose
|
|
54408
|
+
* real content has not arrived yet.
|
|
54409
|
+
*/
|
|
54410
|
+
gate = createSyncGate(() => this.#flushPending());
|
|
54411
|
+
#binding = null;
|
|
54412
|
+
#lastSynced = '';
|
|
54413
|
+
#applyingRemote = false;
|
|
54414
|
+
#pending = null;
|
|
54415
|
+
/** Attach the engine to a freshly connected session's doc. */
|
|
54416
|
+
bind(binding) {
|
|
54417
|
+
this.#binding = binding;
|
|
54418
|
+
}
|
|
54419
|
+
/** Clear all per-session state (call on disconnect). */
|
|
54420
|
+
reset() {
|
|
54421
|
+
this.gate.reset();
|
|
54422
|
+
this.#binding = null;
|
|
54423
|
+
this.#pending = null;
|
|
54424
|
+
this.#lastSynced = '';
|
|
54425
|
+
this.#applyingRemote = false;
|
|
54426
|
+
}
|
|
54427
|
+
/**
|
|
54428
|
+
* Record the current local deck as the sync baseline so the first (unchanged)
|
|
54429
|
+
* broadcast after connecting is suppressed. Call right after connect for a
|
|
54430
|
+
* joiner whose local deck is a placeholder awaiting remote sync, so it never
|
|
54431
|
+
* overwrites the shared document before receiving it.
|
|
54432
|
+
*/
|
|
54433
|
+
seedBaseline(slides) {
|
|
54434
|
+
this.#lastSynced = JSON.stringify(slides);
|
|
54435
|
+
}
|
|
54436
|
+
/**
|
|
54437
|
+
* Open the first-write gate on the provider's initial-sync confirmation.
|
|
54438
|
+
* y-websocket emits 'sync' with a boolean; y-webrtc emits 'synced' with an
|
|
54439
|
+
* object carrying a `synced` flag (and only once a peer syncs, hence the
|
|
54440
|
+
* grace timer). Listen to both; opening is idempotent.
|
|
54441
|
+
*/
|
|
54442
|
+
wireSynced(provider) {
|
|
54443
|
+
const handle = (payload) => {
|
|
54444
|
+
const flag = payload;
|
|
54445
|
+
const isSynced = typeof flag === 'boolean' ? flag : flag?.synced !== false;
|
|
54446
|
+
if (isSynced) {
|
|
54447
|
+
this.gate.open();
|
|
54448
|
+
}
|
|
54449
|
+
};
|
|
54450
|
+
provider.on('sync', handle);
|
|
54451
|
+
provider.on('synced', handle);
|
|
54452
|
+
if (provider.synced === true) {
|
|
54453
|
+
this.gate.open();
|
|
54454
|
+
}
|
|
54455
|
+
else {
|
|
54456
|
+
this.gate.arm();
|
|
54457
|
+
}
|
|
54458
|
+
}
|
|
54459
|
+
/**
|
|
54460
|
+
* Broadcast the local slide set to peers, reconciling only what changed into
|
|
54461
|
+
* the pptx:slides Y.Array. An empty deck is never written (so a late-joiner
|
|
54462
|
+
* that has not yet received the doc cannot clobber it), an unchanged deck is
|
|
54463
|
+
* skipped, and while the gate is shut the deck is held as pending.
|
|
54464
|
+
*/
|
|
54465
|
+
broadcast(slides) {
|
|
54466
|
+
const b = this.#binding;
|
|
54467
|
+
if (!b || this.#applyingRemote || slides.length === 0) {
|
|
54468
|
+
return;
|
|
54469
|
+
}
|
|
54470
|
+
if (!this.gate.isOpen()) {
|
|
54471
|
+
this.#pending = slides;
|
|
54472
|
+
return;
|
|
54473
|
+
}
|
|
54474
|
+
const s = JSON.stringify(slides);
|
|
54475
|
+
if (s === this.#lastSynced) {
|
|
54476
|
+
return;
|
|
54477
|
+
}
|
|
54478
|
+
this.#lastSynced = s;
|
|
54479
|
+
reconcileSlidesInYDoc([...slides], b.ydoc, b.factories, LOCAL_SYNC_ORIGIN);
|
|
54480
|
+
b.scheduleWriteBack();
|
|
54481
|
+
}
|
|
54482
|
+
/** Handle a remote Y.Doc change, skipping our own local-origin transactions. */
|
|
54483
|
+
onRemoteChange(transaction) {
|
|
54484
|
+
const b = this.#binding;
|
|
54485
|
+
if (transaction?.origin === LOCAL_SYNC_ORIGIN || this.#applyingRemote || !b) {
|
|
54486
|
+
return;
|
|
54487
|
+
}
|
|
54488
|
+
const remote = readSlidesFromYDoc(b.ydoc);
|
|
54489
|
+
if (remote.length === 0) {
|
|
54490
|
+
return;
|
|
54491
|
+
}
|
|
54492
|
+
// Suppress the echo: record what we just applied so the subsequent local
|
|
54493
|
+
// broadcast (driven by the editor signal) is a no-op.
|
|
54494
|
+
this.#lastSynced = JSON.stringify(remote);
|
|
54495
|
+
this.#applyingRemote = true;
|
|
54496
|
+
b.onRemoteSlides?.(remote);
|
|
54497
|
+
this.#applyingRemote = false;
|
|
54498
|
+
b.scheduleWriteBack();
|
|
54499
|
+
}
|
|
54500
|
+
/**
|
|
54501
|
+
* Re-adopt the shared document's slides after a local content load committed
|
|
54502
|
+
* a parsed deck to viewer state. The load pipeline applies its deck
|
|
54503
|
+
* unconditionally, so a load finishing AFTER the room's slides were applied
|
|
54504
|
+
* (a late joiner's bootstrap deck parsing slower than the doc sync) would
|
|
54505
|
+
* clobber the synced state, and with the doc itself unchanged the observer
|
|
54506
|
+
* never re-fires. When the room already has slides they win, re-applied
|
|
54507
|
+
* through `onRemoteSlides` and recorded as the baseline (bypassing the JSON
|
|
54508
|
+
* dedupe) so the follow-up local broadcast is a no-op. An empty room means
|
|
54509
|
+
* this client is the seeder. Returns true when the doc was adopted.
|
|
54510
|
+
*/
|
|
54511
|
+
adoptDocAfterLoad() {
|
|
54512
|
+
const b = this.#binding;
|
|
54513
|
+
if (!b) {
|
|
54514
|
+
return false;
|
|
54515
|
+
}
|
|
54516
|
+
const docSlides = readSlidesFromYDoc(b.ydoc);
|
|
54517
|
+
if (docSlides.length === 0) {
|
|
54518
|
+
return false;
|
|
54519
|
+
}
|
|
54520
|
+
this.#lastSynced = JSON.stringify(docSlides);
|
|
54521
|
+
this.#applyingRemote = true;
|
|
54522
|
+
b.onRemoteSlides?.(docSlides);
|
|
54523
|
+
this.#applyingRemote = false;
|
|
54524
|
+
return true;
|
|
54525
|
+
}
|
|
54526
|
+
/**
|
|
54527
|
+
* Perform the deferred first broadcast once the gate opens. When the doc is
|
|
54528
|
+
* still empty (fresh room, or nobody else present), clear the baseline so the
|
|
54529
|
+
* pending deck actually seeds it; when remote content already arrived, the
|
|
54530
|
+
* pending deck matches the applied baseline and the write is a no-op.
|
|
54531
|
+
*/
|
|
54532
|
+
#flushPending() {
|
|
54533
|
+
const b = this.#binding;
|
|
54534
|
+
const pending = this.#pending;
|
|
54535
|
+
this.#pending = null;
|
|
54536
|
+
if (!pending || !b) {
|
|
54537
|
+
return;
|
|
54538
|
+
}
|
|
54539
|
+
if (b.ydoc.getArray(YDOC_SLIDES_KEY).length === 0) {
|
|
54540
|
+
this.#lastSynced = '';
|
|
54541
|
+
}
|
|
54542
|
+
this.broadcast(pending);
|
|
54543
|
+
}
|
|
54544
|
+
}
|
|
54545
|
+
|
|
54086
54546
|
/**
|
|
54087
54547
|
* Split inherited template (master/layout) elements out of each loaded slide.
|
|
54088
54548
|
*
|
|
@@ -54217,6 +54677,7 @@ class WriteBackScheduler {
|
|
|
54217
54677
|
*
|
|
54218
54678
|
* Provide at the component level: `@Component({ providers: [CollaborationService] })`.
|
|
54219
54679
|
*/
|
|
54680
|
+
/** Sentinel canvas bound used until the host reports real dimensions. */
|
|
54220
54681
|
const DEFAULT_CANVAS_BOUND = 100_000;
|
|
54221
54682
|
class CollaborationService {
|
|
54222
54683
|
// Reactive state
|
|
@@ -54256,26 +54717,13 @@ class CollaborationService {
|
|
|
54256
54717
|
* as it happens instead of on commit. Dormant outside a session.
|
|
54257
54718
|
*/
|
|
54258
54719
|
livePatcher = createCollaborationLivePatcher();
|
|
54259
|
-
//
|
|
54260
|
-
|
|
54261
|
-
|
|
54262
|
-
|
|
54263
|
-
departure = null;
|
|
54264
|
-
selfId = -1;
|
|
54265
|
-
applyingRemote = false;
|
|
54266
|
-
yFactories = null;
|
|
54267
|
-
lastSynced = '';
|
|
54268
|
-
connectTimer = null;
|
|
54269
|
-
unobserveSlides = null;
|
|
54720
|
+
// The live session's transport objects + wiring handles, owned as one atomic
|
|
54721
|
+
// unit: null when disconnected, assigned by connect(), disposed by
|
|
54722
|
+
// disconnect(). See collaboration-session-setup.ts.
|
|
54723
|
+
session = null;
|
|
54270
54724
|
writeBack = new WriteBackScheduler();
|
|
54271
|
-
/**
|
|
54272
|
-
|
|
54273
|
-
* until the provider confirms its initial sync or the grace period lifts
|
|
54274
|
-
* the gate, so a late joiner never seeds its placeholder deck into a room
|
|
54275
|
-
* whose real content has not arrived yet.
|
|
54276
|
-
*/
|
|
54277
|
-
syncGate = createSyncGate(() => this.flushPendingBroadcast());
|
|
54278
|
-
pendingBroadcast = null;
|
|
54725
|
+
/** Granular local<->doc slide sync (gate + echo dedupe + broadcast/adopt). */
|
|
54726
|
+
slideSync = new SlideSyncEngine();
|
|
54279
54727
|
onRemoteSlides = null;
|
|
54280
54728
|
canvasWidth = DEFAULT_CANVAS_BOUND;
|
|
54281
54729
|
canvasHeight = DEFAULT_CANVAS_BOUND;
|
|
@@ -54284,7 +54732,6 @@ class CollaborationService {
|
|
|
54284
54732
|
currentConfig = null;
|
|
54285
54733
|
lastConfig = null;
|
|
54286
54734
|
lastOptions = {};
|
|
54287
|
-
localPresence = null;
|
|
54288
54735
|
/**
|
|
54289
54736
|
* Reentrancy token for {@link connect}: bumped by every connect() and
|
|
54290
54737
|
* disconnect(). A connect() whose token no longer matches after an await was
|
|
@@ -54294,11 +54741,12 @@ class CollaborationService {
|
|
|
54294
54741
|
*/
|
|
54295
54742
|
connectToken = 0;
|
|
54296
54743
|
refreshPresence = () => {
|
|
54297
|
-
|
|
54744
|
+
const s = this.session;
|
|
54745
|
+
if (!s) {
|
|
54298
54746
|
this.presence.set([]);
|
|
54299
54747
|
return;
|
|
54300
54748
|
}
|
|
54301
|
-
this.presence.set(derivePresenceList(
|
|
54749
|
+
this.presence.set(derivePresenceList(s.awareness.getStates(), s.selfId, this.canvasWidth, this.canvasHeight));
|
|
54302
54750
|
};
|
|
54303
54751
|
constructor() {
|
|
54304
54752
|
// Service destruction is not the only way a session ends: a tab close, a
|
|
@@ -54359,26 +54807,20 @@ class CollaborationService {
|
|
|
54359
54807
|
bundle.doc.destroy();
|
|
54360
54808
|
return;
|
|
54361
54809
|
}
|
|
54362
|
-
this.
|
|
54363
|
-
|
|
54364
|
-
|
|
54365
|
-
|
|
54366
|
-
|
|
54367
|
-
|
|
54368
|
-
|
|
54369
|
-
|
|
54370
|
-
|
|
54371
|
-
|
|
54372
|
-
|
|
54373
|
-
|
|
54810
|
+
this.session = activateSession(bundle, config, transport, {
|
|
54811
|
+
slideSync: this.slideSync,
|
|
54812
|
+
livePatcher: this.livePatcher,
|
|
54813
|
+
onRemoteSlides: this.onRemoteSlides,
|
|
54814
|
+
refreshPresence: this.refreshPresence,
|
|
54815
|
+
scheduleWriteBack: () => this.scheduleWriteBack(),
|
|
54816
|
+
setStatus: (status) => this.status.set(status),
|
|
54817
|
+
getStatus: () => this.status(),
|
|
54818
|
+
isActive: () => this.active(),
|
|
54819
|
+
failConnection: () => {
|
|
54820
|
+
this.disconnect();
|
|
54821
|
+
this.status.set('error');
|
|
54822
|
+
},
|
|
54374
54823
|
});
|
|
54375
|
-
this.localPresence.publish();
|
|
54376
|
-
this.awareness.on('change', this.refreshPresence);
|
|
54377
|
-
this.awareness.on('update', this.refreshPresence);
|
|
54378
|
-
this.wireStatus(transport);
|
|
54379
|
-
this.syncGate.reset();
|
|
54380
|
-
this.wireSynced();
|
|
54381
|
-
this.unobserveSlides = observeYDocSlides(this.ydoc, (_events, transaction) => this.onRemoteChange(transaction));
|
|
54382
54824
|
this.active.set(true);
|
|
54383
54825
|
this.refreshPresence();
|
|
54384
54826
|
}
|
|
@@ -54397,147 +54839,16 @@ class CollaborationService {
|
|
|
54397
54839
|
await this.connect(this.lastConfig, this.lastOptions);
|
|
54398
54840
|
}
|
|
54399
54841
|
}
|
|
54400
|
-
/** Wire the provider status events + (websocket-only) connection timeout. */
|
|
54401
|
-
wireStatus(transport) {
|
|
54402
|
-
const provider = this.provider;
|
|
54403
|
-
if (!provider) {
|
|
54404
|
-
return;
|
|
54405
|
-
}
|
|
54406
|
-
if (transport === 'webrtc') {
|
|
54407
|
-
// P2P: no server round-trip to wait on. Treat "created" as connected,
|
|
54408
|
-
// and reflect explicit disconnect events.
|
|
54409
|
-
this.status.set('connected');
|
|
54410
|
-
provider.on('status', (payload) => {
|
|
54411
|
-
if (payload.connected === false && this.active()) {
|
|
54412
|
-
this.status.set('disconnected');
|
|
54413
|
-
// Re-arm on (re)connect: without this, a peer that drops and
|
|
54414
|
-
// rejoins keeps the gate permanently open from the first
|
|
54415
|
-
// connection and can clobber the room with a stale local doc.
|
|
54416
|
-
this.syncGate.reset();
|
|
54417
|
-
this.syncGate.arm();
|
|
54418
|
-
}
|
|
54419
|
-
else if (payload.connected === true) {
|
|
54420
|
-
this.status.set('connected');
|
|
54421
|
-
}
|
|
54422
|
-
});
|
|
54423
|
-
return;
|
|
54424
|
-
}
|
|
54425
|
-
provider.on('status', (payload) => {
|
|
54426
|
-
if (payload.status === 'connected') {
|
|
54427
|
-
this.clearConnectTimer();
|
|
54428
|
-
this.status.set('connected');
|
|
54429
|
-
}
|
|
54430
|
-
else if (payload.status === 'disconnected' && this.active()) {
|
|
54431
|
-
this.status.set('disconnected');
|
|
54432
|
-
this.syncGate.reset();
|
|
54433
|
-
this.syncGate.arm();
|
|
54434
|
-
}
|
|
54435
|
-
});
|
|
54436
|
-
if (provider.wsconnected) {
|
|
54437
|
-
this.status.set('connected');
|
|
54438
|
-
return;
|
|
54439
|
-
}
|
|
54440
|
-
this.connectTimer = setTimeout(() => {
|
|
54441
|
-
this.connectTimer = null;
|
|
54442
|
-
if (this.status() !== 'connected') {
|
|
54443
|
-
this.disconnect();
|
|
54444
|
-
this.status.set('error');
|
|
54445
|
-
}
|
|
54446
|
-
}, CONNECTION_TIMEOUT_MS);
|
|
54447
|
-
}
|
|
54448
|
-
/**
|
|
54449
|
-
* Open the first-write gate on the provider's initial-sync confirmation.
|
|
54450
|
-
* y-websocket emits 'sync' with a boolean; y-webrtc emits 'synced' with an
|
|
54451
|
-
* object carrying a `synced` flag (and only once a peer syncs, hence the
|
|
54452
|
-
* grace timer). Listen to both; opening is idempotent.
|
|
54453
|
-
*/
|
|
54454
|
-
wireSynced() {
|
|
54455
|
-
const provider = this.provider;
|
|
54456
|
-
if (!provider) {
|
|
54457
|
-
return;
|
|
54458
|
-
}
|
|
54459
|
-
const handle = (payload) => {
|
|
54460
|
-
const flag = payload;
|
|
54461
|
-
const isSynced = typeof flag === 'boolean' ? flag : flag?.synced !== false;
|
|
54462
|
-
if (isSynced) {
|
|
54463
|
-
this.syncGate.open();
|
|
54464
|
-
}
|
|
54465
|
-
};
|
|
54466
|
-
provider.on('sync', handle);
|
|
54467
|
-
provider.on('synced', handle);
|
|
54468
|
-
if (provider.synced === true) {
|
|
54469
|
-
this.syncGate.open();
|
|
54470
|
-
}
|
|
54471
|
-
else {
|
|
54472
|
-
this.syncGate.arm();
|
|
54473
|
-
}
|
|
54474
|
-
}
|
|
54475
|
-
/**
|
|
54476
|
-
* Perform the deferred first broadcast once the gate opens. When the doc is
|
|
54477
|
-
* still empty (fresh room, or nobody else present), clear the baseline so
|
|
54478
|
-
* the pending deck actually seeds it; when remote content already arrived,
|
|
54479
|
-
* the pending deck matches the applied baseline and the write is a no-op.
|
|
54480
|
-
*/
|
|
54481
|
-
flushPendingBroadcast() {
|
|
54482
|
-
const pending = this.pendingBroadcast;
|
|
54483
|
-
this.pendingBroadcast = null;
|
|
54484
|
-
if (!pending || !this.ydoc || !this.yFactories) {
|
|
54485
|
-
return;
|
|
54486
|
-
}
|
|
54487
|
-
if (this.ydoc.getArray(YDOC_SLIDES_KEY).length === 0) {
|
|
54488
|
-
this.lastSynced = '';
|
|
54489
|
-
}
|
|
54490
|
-
this.broadcastSlides(pending);
|
|
54491
|
-
}
|
|
54492
|
-
/** Handle a remote Y.Doc change, skipping our own local-origin transactions. */
|
|
54493
|
-
onRemoteChange(transaction) {
|
|
54494
|
-
if (transaction?.origin === LOCAL_SYNC_ORIGIN || this.applyingRemote || !this.ydoc) {
|
|
54495
|
-
return;
|
|
54496
|
-
}
|
|
54497
|
-
const remote = readSlidesFromYDoc(this.ydoc);
|
|
54498
|
-
if (remote.length === 0) {
|
|
54499
|
-
return;
|
|
54500
|
-
}
|
|
54501
|
-
// Suppress the echo: record what we just applied so the subsequent local
|
|
54502
|
-
// broadcast (driven by the editor signal) is a no-op.
|
|
54503
|
-
this.lastSynced = JSON.stringify(remote);
|
|
54504
|
-
this.applyingRemote = true;
|
|
54505
|
-
this.onRemoteSlides?.(remote);
|
|
54506
|
-
this.applyingRemote = false;
|
|
54507
|
-
this.scheduleWriteBack();
|
|
54508
|
-
}
|
|
54509
54842
|
disconnect() {
|
|
54510
54843
|
// Invalidate any in-flight connect() so it discards its bundle on resume.
|
|
54511
54844
|
this.connectToken += 1;
|
|
54512
|
-
this.
|
|
54513
|
-
this.syncGate.reset();
|
|
54514
|
-
this.pendingBroadcast = null;
|
|
54845
|
+
this.slideSync.reset();
|
|
54515
54846
|
this.writeBack.cancel();
|
|
54516
|
-
this.
|
|
54517
|
-
|
|
54518
|
-
|
|
54519
|
-
|
|
54520
|
-
// Announce first: it is synchronous, so it still reaches same-browser
|
|
54521
|
-
// peers when this runs from a document that is being destroyed. The
|
|
54522
|
-
// provider's own awareness removal is broadcast a microtask later and
|
|
54523
|
-
// would be dropped, leaving us a ghost collaborator until the 30s
|
|
54524
|
-
// awareness timeout.
|
|
54525
|
-
this.departure?.announce();
|
|
54526
|
-
this.departure?.dispose();
|
|
54527
|
-
this.departure = null;
|
|
54528
|
-
clearLocalAwareness(this.awareness);
|
|
54529
|
-
this.provider?.disconnect();
|
|
54530
|
-
this.provider?.destroy();
|
|
54531
|
-
this.ydoc?.destroy();
|
|
54532
|
-
this.provider = null;
|
|
54533
|
-
this.ydoc = null;
|
|
54534
|
-
this.awareness = null;
|
|
54535
|
-
this.localPresence = null;
|
|
54536
|
-
this.selfId = -1;
|
|
54537
|
-
this.applyingRemote = false;
|
|
54538
|
-
this.yFactories = null;
|
|
54847
|
+
if (this.session) {
|
|
54848
|
+
teardownSession(this.session, this.refreshPresence);
|
|
54849
|
+
this.session = null;
|
|
54850
|
+
}
|
|
54539
54851
|
this.livePatcher.configure(null, null);
|
|
54540
|
-
this.lastSynced = '';
|
|
54541
54852
|
this.onRemoteSlides = null;
|
|
54542
54853
|
this.currentConfig = null;
|
|
54543
54854
|
this.status.set('disconnected');
|
|
@@ -54546,88 +54857,46 @@ class CollaborationService {
|
|
|
54546
54857
|
this.presence.set([]);
|
|
54547
54858
|
this.followedClientId.set(null);
|
|
54548
54859
|
}
|
|
54549
|
-
/**
|
|
54550
|
-
* Broadcast the local slide set to peers, reconciling only what changed into
|
|
54551
|
-
* the pptx:slides Y.Array. An empty deck is never written (so a late-joiner
|
|
54552
|
-
* that has not yet received the doc cannot clobber it), and an unchanged deck
|
|
54553
|
-
* is skipped.
|
|
54554
|
-
*/
|
|
54555
54860
|
/**
|
|
54556
54861
|
* Record the current local deck as the sync baseline so the first (unchanged)
|
|
54557
54862
|
* broadcast after connecting is suppressed. Call right after {@link connect}
|
|
54558
|
-
* for a joiner whose local deck is a placeholder awaiting remote sync
|
|
54559
|
-
* never overwrites the shared document before receiving it.
|
|
54863
|
+
* for a joiner whose local deck is a placeholder awaiting remote sync.
|
|
54560
54864
|
*/
|
|
54561
54865
|
seedBaseline(slides) {
|
|
54562
|
-
this.
|
|
54866
|
+
this.slideSync.seedBaseline(slides);
|
|
54563
54867
|
}
|
|
54564
54868
|
/**
|
|
54565
|
-
* Re-adopt the shared document's slides after a local content load
|
|
54566
|
-
*
|
|
54567
|
-
*
|
|
54568
|
-
* already applied (a late joiner's bootstrap deck parsing slower than the
|
|
54569
|
-
* doc sync) silently clobbers the synced state and, with the doc itself
|
|
54570
|
-
* unchanged, the remote observer never re-fires. When the room already has
|
|
54571
|
-
* slides they win: the doc content is re-applied through `onRemoteSlides`
|
|
54572
|
-
* and recorded as the sync baseline (bypassing the usual JSON dedupe) so
|
|
54573
|
-
* the follow-up local broadcast of the adopted deck is a no-op. An empty
|
|
54574
|
-
* room means this client is the seeder and the loaded deck stands, written
|
|
54575
|
-
* by the normal gated broadcast path. Returns true when the doc was adopted.
|
|
54869
|
+
* Re-adopt the shared document's slides after a local content load committed
|
|
54870
|
+
* a parsed deck to viewer state (see {@link SlideSyncEngine.adoptDocAfterLoad}).
|
|
54871
|
+
* Returns true when the room's slides were adopted over the loaded deck.
|
|
54576
54872
|
*/
|
|
54577
54873
|
adoptDocSlidesAfterLoad() {
|
|
54578
|
-
|
|
54579
|
-
return false;
|
|
54580
|
-
}
|
|
54581
|
-
const docSlides = readSlidesFromYDoc(this.ydoc);
|
|
54582
|
-
if (docSlides.length === 0) {
|
|
54583
|
-
return false;
|
|
54584
|
-
}
|
|
54585
|
-
this.lastSynced = JSON.stringify(docSlides);
|
|
54586
|
-
this.applyingRemote = true;
|
|
54587
|
-
this.onRemoteSlides?.(docSlides);
|
|
54588
|
-
this.applyingRemote = false;
|
|
54589
|
-
return true;
|
|
54874
|
+
return this.connected() ? this.slideSync.adoptDocAfterLoad() : false;
|
|
54590
54875
|
}
|
|
54876
|
+
/**
|
|
54877
|
+
* Broadcast the local slide set to peers, reconciling only what changed into
|
|
54878
|
+
* the pptx:slides Y.Array. Empty/unchanged decks are skipped; while the gate
|
|
54879
|
+
* is shut the deck is held pending until the initial sync confirms.
|
|
54880
|
+
*/
|
|
54591
54881
|
broadcastSlides(slides) {
|
|
54592
|
-
|
|
54593
|
-
return;
|
|
54594
|
-
}
|
|
54595
|
-
if (!this.syncGate.isOpen()) {
|
|
54596
|
-
// Defer until the initial sync confirms; the gate flushes the latest
|
|
54597
|
-
// pending deck when it opens.
|
|
54598
|
-
this.pendingBroadcast = slides;
|
|
54599
|
-
return;
|
|
54600
|
-
}
|
|
54601
|
-
const s = JSON.stringify(slides);
|
|
54602
|
-
if (s === this.lastSynced) {
|
|
54603
|
-
return;
|
|
54604
|
-
}
|
|
54605
|
-
this.lastSynced = s;
|
|
54606
|
-
reconcileSlidesInYDoc([...slides], this.ydoc, this.yFactories, LOCAL_SYNC_ORIGIN);
|
|
54607
|
-
this.scheduleWriteBack();
|
|
54882
|
+
this.slideSync.broadcast(slides);
|
|
54608
54883
|
}
|
|
54609
54884
|
setCursor(x, y, activeSlideIndex) {
|
|
54610
|
-
this.localPresence
|
|
54885
|
+
this.session?.localPresence.setCursor(x, y, activeSlideIndex);
|
|
54611
54886
|
}
|
|
54612
54887
|
setSelection(selectedElementId, activeSlideIndex) {
|
|
54613
|
-
this.localPresence
|
|
54888
|
+
this.session?.localPresence.setSelection(selectedElementId, activeSlideIndex);
|
|
54614
54889
|
}
|
|
54615
54890
|
/** Publish the local active-slide index (drives follow-along). */
|
|
54616
54891
|
setActiveSlide(index) {
|
|
54617
|
-
this.localPresence
|
|
54892
|
+
this.session?.localPresence.setActiveSlide(index);
|
|
54618
54893
|
}
|
|
54619
54894
|
/** Follow the given peer's active slide, or `null` to stop following. */
|
|
54620
54895
|
followUser(clientId) {
|
|
54621
54896
|
this.followedClientId.set(clientId);
|
|
54622
54897
|
}
|
|
54623
|
-
clearConnectTimer() {
|
|
54624
|
-
if (this.connectTimer !== null) {
|
|
54625
|
-
clearTimeout(this.connectTimer);
|
|
54626
|
-
this.connectTimer = null;
|
|
54627
|
-
}
|
|
54628
|
-
}
|
|
54629
54898
|
scheduleWriteBack() {
|
|
54630
|
-
this.writeBack.schedule(this.currentConfig, this.ydoc, this.getSourceBytes, this.getTemplateElements);
|
|
54899
|
+
this.writeBack.schedule(this.currentConfig, this.session?.ydoc ?? null, this.getSourceBytes, this.getTemplateElements);
|
|
54631
54900
|
}
|
|
54632
54901
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CollaborationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
54633
54902
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CollaborationService });
|
|
@@ -78227,6 +78496,9 @@ class PresentationOverlayComponent {
|
|
|
78227
78496
|
/** Zero-based index into `slides()`. */
|
|
78228
78497
|
currentIndex = signal(0, /* @ts-ignore */
|
|
78229
78498
|
...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
|
|
78499
|
+
/** PowerPoint's Ctrl+M: hide ink markup without discarding the strokes. */
|
|
78500
|
+
inkMarkupVisible = signal(true, /* @ts-ignore */
|
|
78501
|
+
...(ngDevMode ? [{ debugName: "inkMarkupVisible" }] : /* istanbul ignore next */ []));
|
|
78230
78502
|
syncExternalIndex = effect(() => {
|
|
78231
78503
|
const count = this.slides().length;
|
|
78232
78504
|
if (count === 0) {
|
|
@@ -78592,35 +78864,62 @@ class PresentationOverlayComponent {
|
|
|
78592
78864
|
// ------------------------------------------------------------------
|
|
78593
78865
|
// Keyboard navigation (document-level: works even when nothing is focused)
|
|
78594
78866
|
// ------------------------------------------------------------------
|
|
78867
|
+
/** Digit buffer backing PowerPoint's "type a slide number, then Enter" jump. */
|
|
78868
|
+
keyBuffer = createPresentationKeyBuffer();
|
|
78595
78869
|
onKeyDown(event) {
|
|
78596
|
-
|
|
78597
|
-
|
|
78598
|
-
|
|
78599
|
-
|
|
78600
|
-
|
|
78870
|
+
const mapped = mapPresentationKey(event, this.keyBuffer);
|
|
78871
|
+
if (mapped.action === 'none') {
|
|
78872
|
+
return;
|
|
78873
|
+
}
|
|
78874
|
+
event.preventDefault();
|
|
78875
|
+
switch (mapped.action) {
|
|
78876
|
+
case 'next':
|
|
78601
78877
|
this.navigate('next');
|
|
78602
78878
|
break;
|
|
78603
|
-
case '
|
|
78604
|
-
case 'PageUp':
|
|
78605
|
-
event.preventDefault();
|
|
78879
|
+
case 'previous':
|
|
78606
78880
|
this.navigate('prev');
|
|
78607
78881
|
break;
|
|
78608
|
-
case '
|
|
78609
|
-
event.preventDefault();
|
|
78882
|
+
case 'first':
|
|
78610
78883
|
this.navigate('first');
|
|
78611
78884
|
break;
|
|
78612
|
-
case '
|
|
78613
|
-
event.preventDefault();
|
|
78885
|
+
case 'last':
|
|
78614
78886
|
this.navigate('last');
|
|
78615
78887
|
break;
|
|
78616
|
-
case '
|
|
78617
|
-
|
|
78888
|
+
case 'goto': {
|
|
78889
|
+
const index = mapped.slideNumber - 1;
|
|
78890
|
+
if (index >= 0 && index < this.slides().length) {
|
|
78891
|
+
this.goToSlide(index);
|
|
78892
|
+
}
|
|
78893
|
+
break;
|
|
78894
|
+
}
|
|
78895
|
+
case 'end':
|
|
78618
78896
|
this.emitClosed();
|
|
78619
78897
|
break;
|
|
78898
|
+
case 'pointerTool':
|
|
78899
|
+
// PowerPoint's Ctrl+A "arrow" is the plain pointer: no active tool.
|
|
78900
|
+
this.annotations.setTool(mapped.tool === 'arrow' ? 'none' : mapped.tool);
|
|
78901
|
+
break;
|
|
78902
|
+
case 'eraseAnnotations':
|
|
78903
|
+
this.annotations.clearAnnotations();
|
|
78904
|
+
break;
|
|
78905
|
+
case 'toggleInkMarkup':
|
|
78906
|
+
this.inkMarkupVisible.update((visible) => !visible);
|
|
78907
|
+
break;
|
|
78908
|
+
case 'toggleBlackScreen':
|
|
78909
|
+
this.toggleBlank('black');
|
|
78910
|
+
break;
|
|
78911
|
+
case 'toggleWhiteScreen':
|
|
78912
|
+
this.toggleBlank('white');
|
|
78913
|
+
break;
|
|
78620
78914
|
default:
|
|
78621
78915
|
break;
|
|
78622
78916
|
}
|
|
78623
78917
|
}
|
|
78918
|
+
/** Toggle PowerPoint's blank black/white screen (B/W, or `.`/`,`). */
|
|
78919
|
+
toggleBlank(value) {
|
|
78920
|
+
const current = this.presenterWindow.snapshot().blackout;
|
|
78921
|
+
this.presenterWindow.updateSnapshot({ blackout: current === value ? 'none' : value });
|
|
78922
|
+
}
|
|
78624
78923
|
// ------------------------------------------------------------------
|
|
78625
78924
|
// Click handling
|
|
78626
78925
|
// ------------------------------------------------------------------
|
|
@@ -78821,8 +79120,11 @@ class PresentationOverlayComponent {
|
|
|
78821
79120
|
/>
|
|
78822
79121
|
}
|
|
78823
79122
|
|
|
78824
|
-
<!-- Ink annotation overlay (pen/highlighter/eraser/laser).
|
|
78825
|
-
|
|
79123
|
+
<!-- Ink annotation overlay (pen/highlighter/eraser/laser). Ctrl+M
|
|
79124
|
+
hides the markup without discarding the strokes. -->
|
|
79125
|
+
@if (inkMarkupVisible()) {
|
|
79126
|
+
<pptx-presentation-annotation-overlay [canvasSize]="canvasSize()" [zoom]="zoom()" />
|
|
79127
|
+
}
|
|
78826
79128
|
</div>
|
|
78827
79129
|
@if (presenterWindow.snapshot().blackout !== 'none') {
|
|
78828
79130
|
<div class="presenter-blank" [style.background]="presenterWindow.snapshot().blackout"></div>
|
|
@@ -78990,8 +79292,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
78990
79292
|
/>
|
|
78991
79293
|
}
|
|
78992
79294
|
|
|
78993
|
-
<!-- Ink annotation overlay (pen/highlighter/eraser/laser).
|
|
78994
|
-
|
|
79295
|
+
<!-- Ink annotation overlay (pen/highlighter/eraser/laser). Ctrl+M
|
|
79296
|
+
hides the markup without discarding the strokes. -->
|
|
79297
|
+
@if (inkMarkupVisible()) {
|
|
79298
|
+
<pptx-presentation-annotation-overlay [canvasSize]="canvasSize()" [zoom]="zoom()" />
|
|
79299
|
+
}
|
|
78995
79300
|
</div>
|
|
78996
79301
|
@if (presenterWindow.snapshot().blackout !== 'none') {
|
|
78997
79302
|
<div class="presenter-blank" [style.background]="presenterWindow.snapshot().blackout"></div>
|
|
@@ -83739,7 +84044,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
83739
84044
|
}], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }] } });
|
|
83740
84045
|
|
|
83741
84046
|
// Generated by scripts/inline-shared.mjs from package.json. Do not edit.
|
|
83742
|
-
const PPTX_ANGULAR_VIEWER_VERSION = "2.
|
|
84047
|
+
const PPTX_ANGULAR_VIEWER_VERSION = "2.1.1";
|
|
83743
84048
|
|
|
83744
84049
|
/**
|
|
83745
84050
|
* account-page.component.ts: File > Account content.
|
|
@@ -112553,4 +112858,4 @@ function cn(...values) {
|
|
|
112553
112858
|
*/
|
|
112554
112859
|
|
|
112555
112860
|
export { DATA_TABLE_KEY_W as $, ALIGN_OPTIONS as A, BroadcastDialogComponent as B, CHART_EDITOR_STYLES as C, ChartAxisOptionsComponent as D, ChartAxisStyleOptionsComponent as E, ChartComboTypeOptionsComponent as F, ChartDataEditorComponent as G, ChartDataLabelOptionsComponent as H, ChartDatapointOptionsComponent as I, ChartDisplayOptionsComponent as J, ChartElementViewComponent as K, ChartErrorBarOptionsComponent as L, ChartMarkerOptionsComponent as M, ChartPartSelectionService as N, ChartPrimitivesComponent as O, ChartRendererComponent as P, ChartTrendlineOptionsComponent as Q, CollaborationCursorsComponent as R, CollaborationService as S, ColorChangedImageComponent as T, CommentsPanelComponent as U, CommentsService as V, ComparePanelComponent as W, ConnectorRendererComponent as X, ConnectorTextOverlayComponent as Y, CustomShowsComponent as Z, DATA_TABLE_HEADER_H as _, AUDIENCE_HASH as a, MIN_ZOOM_SCALE as a$, DATA_TABLE_PADDING as a0, DATA_TABLE_ROW_H as a1, DEFAULT_BOUNDS as a2, DEFAULT_BROADCAST_SERVER_URL as a3, DEFAULT_CANVAS_HEIGHT as a4, DEFAULT_CANVAS_WIDTH as a5, DEFAULT_COLOR_SCHEME as a6, DEFAULT_FILL_COLOR as a7, DEFAULT_LAYOUT as a8, DEFAULT_PALETTE$1 as a9, ExportProgressModalComponent as aA, ExportService as aB, FieldContextService as aC, FindBarComponent as aD, FindReplaceBarComponent as aE, FollowModeBarComponent as aF, FontEmbeddingListComponent as aG, FontEmbeddingPanelComponent as aH, GALLERY_THEME_PRESETS as aI, GradientPickerComponent as aJ, HANDOUT_OPTIONS as aK, HeaderFooterDialogComponent as aL, HyperlinkDialogComponent as aM, ImagePropertiesPanelComponent as aN, InkDrawingService as aO, InkRendererComponent as aP, InsertSmartArtDialogComponent as aQ, InspectorPaneHeaderComponent as aR, InspectorPanelComponent as aS, IsMobileService as aT, KeepAnnotationsDialogComponent as aU, LOCALE_CATALOG as aV, LONG_PRESS_DURATION_MS as aW, LONG_PRESS_MOVE_TOLERANCE_PX as aX, LoadContentService as aY, LocalPresencePublisher as aZ, MAX_ZOOM_SCALE as a_, DEFAULT_PRINT_SETTINGS as aa, DEFAULT_SLIDE_BACKGROUND as ab, DEFAULT_STROKE_COLOR as ac, DEFAULT_STYLE as ad, DEFAULT_TABLE_ROW_HEIGHT as ae, DEFAULT_TEXT_COLOR$1 as af, DEFAULT_VIEWER_PROFILE as ag, DIRECTIONAL_PRESETS as ah, DIRECTION_OPTIONS as ai, DocumentPropertiesCardComponent as aj, EMBEDDED_FONTS_STYLE_ID as ak, EMPHASIS_PRESETS as al, ENTRANCE_PRESETS as am, TEMPLATES as an, EXIT_PRESETS as ao, EditorContextMenuComponent as ap, EditorHistory as aq, EditorStateService as ar, EditorToolbarComponent as as, EffectsPanelComponent as at, ElementRendererComponent as au, EmbeddedFontsService as av, EncryptedFileDialogComponent as aw, EquationEditorDialogComponent as ax, EquationRendererComponent as ay, EquationTemplateGalleryComponent as az, AUDIENCE_NONCE_KEY as b, SLIDE_PX_PER_INCH as b$, MediaPreviewComponent as b0, MediaPropertiesPanelComponent as b1, MediaRendererComponent as b2, MediaTrimTimelineComponent as b3, MobileBottomBarComponent as b4, MobileMenuSheetComponent as b5, MobilePresenterViewComponent as b6, MobileSheetComponent as b7, MobileSlidesSheetComponent as b8, MobileToolbarComponent as b9, RESIZE_HANDLES as bA, RULER_THICKNESS as bB, RemoteSelectionOverlayComponent as bC, RibbonAnimationsSectionComponent as bD, RibbonArrangeSectionComponent as bE, RibbonColorPopoverComponent as bF, RibbonComponent as bG, RibbonDesignSectionComponent as bH, RibbonDrawSectionComponent as bI, RibbonDrawingGroupComponent as bJ, RibbonEditingSectionComponent as bK, RibbonFileSectionComponent as bL, RibbonFontControlsComponent as bM, RibbonHomeSectionComponent as bN, RibbonInsertFieldsComponent as bO, RibbonInsertSectionComponent as bP, RibbonParagraphControlsComponent as bQ, RibbonPrimaryRowComponent as bR, RibbonReviewSectionComponent as bS, RibbonSlideshowSectionComponent as bT, RibbonTransitionsSectionComponent as bU, RibbonViewSectionComponent as bV, RulerGuidesService as bW, SEQUENCE_OPTIONS as bX, SEVERITY_GROUPS as bY, SEVERITY_LABELS as bZ, SHORTCUT_REFERENCE_ITEMS as b_, ModalDialogComponent as ba, Model3DRendererComponent as bb, NotesHandoutCardComponent as bc, NotesPanelComponent as bd, NotesToolbarComponent as be, OleRendererComponent as bf, POWER_POINT_VIEWER_PROVIDERS as bg, PRESENTER_CHANNEL_NAME as bh, PRESENTER_MSG_ORIGIN as bi, PasswordProtectionDialogComponent as bj, PasswordStrengthMeterComponent as bk, PowerPointViewerComponent as bl, PresentationAnnotationOverlayComponent as bm, PresentationAnnotationsService as bn, PresentationOverlayComponent as bo, PresentationPropertiesPanelComponent as bp, PresentationSettingsCardComponent as bq, PresentationSubtitleBarComponent as br, PresentationTransitionOverlayComponent as bs, PresenterViewComponent as bt, PresenterWindowService as bu, PrintDialogComponent as bv, PrintService as bw, PrintSettingsPanelComponent as bx, PropertiesDialogComponent as by, REPEAT_MODE_OPTIONS as bz, AVATAR_COLOR_SWATCHES as c, ViewerDocumentPropertiesService as c$, SLIDE_TRANSITION_KEYFRAMES as c0, DEFAULT_PALETTE as c1, PALETTES$1 as c2, SMART_ART_COLOR_SCHEMES as c3, SMART_ART_STYLE_OPTIONS as c4, SUB_ITEM_LABEL as c5, SVG_WARP_PRESETS as c6, SWIPE_MAX_VERTICAL_PX as c7, SWIPE_THRESHOLD_PX as c8, SelectionPaneComponent as c9, TABLE_STRUCTURE_TOGGLES as cA, TEXT_DIRECTION_OPTIONS$1 as cB, THEME_CATALOG as cC, TIMING_CURVE_OPTIONS as cD, TRIGGER_OPTIONS as cE, TYPE_LABELS as cF, TableCellAdvancedFillComponent as cG, TableCellFormattingComponent as cH, TableDataEditorComponent as cI, TablePropertiesComponent as cJ, TableRendererComponent as cK, TableResizeOverlayComponent as cL, TableSelectionService as cM, TextAdvancedPanelComponent as cN, ThemeEditorFieldsComponent as cO, ThemeGalleryComponent as cP, ThemeSelectorCardComponent as cQ, TitleBarComponent as cR, VALIGN_OPTIONS as cS, VIEWER_THEME as cT, VersionHistoryPanelComponent as cU, ViewerCanvasEditingService as cV, ViewerCollabCursorService as cW, ViewerCollaborationSessionService as cX, ViewerCompareService as cY, ViewerCustomShowsService as cZ, ViewerDialogsService as c_, SetUpSlideShowDialogComponent as ca, SettingsAppearanceTabComponent as cb, SettingsDialogComponent as cc, SettingsLanguageTabComponent as cd, ShareDialogComponent as ce, ShortcutPanelComponent as cf, ShowOptionsFieldsetComponent as cg, ShowSlidesFieldsetComponent as ch, SignatureStrippedDialogComponent as ci, SignaturesPanelComponent as cj, SignaturesService as ck, SlideCanvasComponent as cl, SlideDefaultInspectorComponent as cm, SlideDiffChangesComponent as cn, SlideDiffRowComponent as co, SlideDiffThumbnailsComponent as cp, SlideSizeCardComponent as cq, SlideSorterOverlayComponent as cr, SlideThemeOverridePanelComponent as cs, SlidesPanelComponent as ct, SmartArt3DRendererComponent as cu, SmartArt3DService as cv, SmartArtPreviewComponent as cw, SmartArtPropertiesComponent as cx, SmartArtRendererComponent as cy, StatusBarComponent as cz, AccessibilityPanelComponent as d, buildFallbackViewModel as d$, ViewerExportService as d0, ViewerExtraDialogsComponent as d1, ViewerFileIOService as d2, ViewerFindReplaceService as d3, ViewerFormatPainterService as d4, ViewerInspectorPanelService as d5, ViewerKeyboardService as d6, ViewerMobileSheetService as d7, ViewerPresentationModeService as d8, ViewerThemeGalleryService as d9, asMediaElement as dA, assignUserColor as dB, attachTouchGestures as dC, beginNodeEdit as dD, boolFromEvent as dE, bringForward as dF, bringToFront as dG, buildBarActions as dH, buildBroadcastConfig as dI, buildBroadcastViewerUrl as dJ, buildCategoryLabels as dK, buildCellParagraphs as dL, buildChartViewModel as dM, buildChatLogExport as dN, buildChatLogMarkdown as dO, buildChromeStyle as dP, buildClearHyperlinkPatch as dQ, buildClickGroups as dR, buildColStyles as dS, buildCollaborationConfig as dT, buildComboViewModel as dU, buildCssGradientFromShapeStyle as dV, buildDuotoneFilter as dW, buildDuotoneFilterId as dX, buildEmbeddedFontStyles as dY, buildEquationElement as dZ, buildEquationSegment as d_, ViewerTouchGesturesService as da, ViewerZoomService as db, WEBM_MIME_CANDIDATES as dc, WriteBackScheduler as dd, ZoomNavigationService as de, ZoomRendererComponent as df, ZoomTargetService as dg, addCategory as dh, addCommentToList as di, addGradientStopPatch as dj, addItem as dk, addSeries as dl, addSubItem as dm, advanceStep as dn, aiToggleVisible as dp, alignPatch as dq, animationFor as dr, annotationMapToInkInserts as ds, applyAcceptedDiff as dt, applyAnimationPreset as du, applyFindReplacements as dv, applyFormatToElement as dw, applyMove as dx, applyResize as dy, applyTableStylePreset as dz, AccessibilityService as e, computeDataTablePrimitives as e$, buildFontFaceRule as e0, buildGradientFillCss as e1, buildGridlinesAndLabels as e2, buildHyperlinkPatch as e3, buildInkContainerStyle as e4, buildInkStrokes as e5, buildLegend as e6, buildModel3DContainerStyle as e7, buildModel3DViewModel as e8, buildOleActionModel as e9, cellStyleToStyleMap as eA, cellTdStyle as eB, changeCountLabel as eC, changeIcon as eD, characterSpacingPatch as eE, checkFontAvailable as eF, clampCursorPosition as eG, clampGifDimensions as eH, clampIndex as eI, clampNotesFontSize as eJ, clampScale as eK, clampStep as eL, clearAllLocalViewerData as eM, clearAudienceContent as eN, cn as eO, collectAccessibilityIssues as eP, collectElementText as eQ, collectSlideText as eR, collectStoredChats as eS, collectUsedFontFamilies as eT, columnWidthStyle as eU, commitNodeText as eV, computeAlign as eW, computeAxisTitlePrimitives as eX, computeBarRects as eY, computeBubbleRadius as eZ, computeCornerHandle as e_, buildOleInfoRows as ea, buildPatternFillCss as eb, buildPrintHtmlDocument as ec, buildPropertiesPatch as ed, buildRegionMapViewModel as ee, buildSaveSlides as ef, buildShareUrl as eg, buildSmartArtInsertElement as eh, buildSmartArtNodes as ei, buildStockViewModel as ej, buildSurfaceViewModel as ek, buildTableViewModel as el, buildTreemapViewModel as em, buildTrimFragment as en, buildWaterfallViewModel as eo, buildZeroLine as ep, buildZoomContainerStyle as eq, buildZoomViewModel as er, bulletIndentPx as es, canAddTopLevelNode as et, canRemoveTopLevelNode as eu, canStartBroadcast as ev, canStartShare as ew, canUseClipboard as ex, captionDisplayText as ey, cellRunStyle as ez, AccountPageComponent as f, encodeGif as f$, computeDistribute as f0, computeDrawingViewBox as f1, computeErrorBarPrimitives as f2, computeFocusTargets as f3, computeHandleBoxes as f4, computeHandoutLayout as f5, computeIsMobile as f6, computeIsTablet as f7, computeLinePoints as f8, computeLinearRegression as f9, createWebsocketBundle as fA, cssObjectToStyleMap as fB, currentColorScheme as fC, currentLayout as fD, currentStyle as fE, defaultCssVars as fF, defaultRadius as fG, defaultThemeColors as fH, deleteElementsByIds as fI, deleteVersion as fJ, demoteNode as fK, deriveModel3DBlobUrl as fL, derivePresenceList as fM, describeSmartArtBounds as fN, disableGlowPatch as fO, disableInnerShadowPatch as fP, disableOuterShadowPatch as fQ, disableReflectionPatch as fR, disableSoftEdgePatch as fS, duplicateElementById as fT, durationOf as fU, effectsStateOf as fV, enableGlowPatch as fW, enableInnerShadowPatch as fX, enableOuterShadowPatch as fY, enableReflectionPatch as fZ, enableSoftEdgePatch as f_, computePageCount as fa, computePieLayout as fb, computePieSlicePath as fc, computePieSlices as fd, computePlotLayout as fe, computeRSquared as ff, computeRadarPoints as fg, computeScatterDots as fh, computeSelectionBoxes as fi, computeSingleSelected as fj, computeSlideIndices as fk, computeSnap as fl, computeStackedBarRects as fm, computeStackedValueRange as fn, computeTextLines as fo, computeTimerProgress as fp, computeTrendlinePrimitives as fq, computeValueRange as fr, convertOmmlToMathMl as fs, copyFormatFromElement as ft, countAccessibilityIssues as fu, countAnnotationStrokes as fv, createAngularAiBridge as fw, createCustomShow as fx, createSwipeDismissDrag as fy, createWebrtcBundle as fz, ActionSettingsPanelComponent as g, hasAnimation as g$, estimatePageCount as g0, evenColumnWidths as g1, evenRowHeights as g2, exitPresentationFullscreen as g3, exportAiChatLogs as g4, extractPathPoints as g5, eyedropperAvailable as g6, fillColorOf as g7, findInSlides as g8, findOwningSlideIndex as g9, getOleBadgeLabel as gA, getOleDisplayName as gB, getOleDownloadFileName as gC, getOleTypeColor as gD, getOleTypeLabel as gE, getPasswordStrength as gF, getPatternSvg as gG, getPlaceholderStyle as gH, getVersions as gI, getResolvedShapeClipPath as gJ, getResolvedShapeClipPathFor as gK, getShapeFillStrokeStyle as gL, getSlideBackgroundStyle as gM, getSlideTransitionAnimations as gN, getSmartArtNodeBounds as gO, getSpeechRecognitionCtor as gP, getTextBlockStyle as gQ, getTextWarp as gR, getTouchDistance as gS, getWarpCategory as gT, getWarpPath as gU, gradientStateFromStyle as gV, gradientStateOf as gW, gradientStatePatch as gX, gridColumns as gY, groupElements as gZ, groupIssuesBySeverity as g_, findSlideIndexByElementId as ga, fitPolynomial as gb, fitZoom as gc, focusTargetChips as gd, fontMimeForFormat as ge, fontSizeOf as gf, formatAutoNumber as gg, formatAxisValue as gh, formatBytes as gi, formatCursorLabel as gj, formatElapsed as gk, formatFileSize as gl, formatPropertyDate as gm, formatTime as gn, fpsToFrameIntervalMs as go, generateBroadcastRoomId as gp, generateCommentId as gq, generateCustomShowId as gr, generatePressureCircles as gs, generateRulerTicks as gt, getClrChangeParams as gu, getContainerStyle as gv, getDuotoneFilterDef as gw, getImageSrc as gx, getLocalStorageUsageSummary as gy, getOleAriaLabel as gz, AdvancedChartEditorComponent as h, numFromEvent as h$, hasCopyableFormat as h0, hasExistingLink as h1, hasExitedFullscreen as h2, hasGradientFill as h3, hasPressureVariation as h4, headerLabel as h5, inkViewBox as h6, insertColumn as h7, insertRow as h8, interpolateWidth as h9, mergeRight as hA, mergeSelection as hB, moveElementBy as hC, moveNodeDown as hD, moveNodeUp as hE, msToFrameDelayCs as hF, narrowToCircle as hG, narrowToPolygon as hH, narrowToRect as hI, newChartElement as hJ, newEquationElement as hK, newPresetShapeElement as hL, newShapeElement as hM, newSmartArtElement as hN, newTableElement as hO, newTextElement as hP, nextVisibleIndex as hQ, nodeBold as hR, nodeEditBox as hS, nodeFillColor as hT, nodeFontColor as hU, nodeIdFromKey as hV, nodeItalic as hW, nodeStyle as hX, normalizeFontFormat as hY, normalizeSlidesPerPage as hZ, normalizeValue as h_, isAudienceTab as ha, isBold as hb, isBrowserOpenableMime as hc, isChildNode as hd, isElementInteractive as he, isInjectableUrl as hf, isItalic as hg, isPpactionUrl as hh, isPresenterMessage as hi, isSigned as hj, isTextElement as hk, isTwoTableFocus as hl, isUnderline as hm, isUrlSafe as hn, isValidRoomId as ho, isViewportBackgroundPressTarget as hp, isZoomActivationKey as hq, issueTrackKey as hr, issueTypeLabel as hs, keyToLabel as ht, latexToMathml as hu, linePointsToSvgString as hv, lineSpacingPatch as hw, loadAudienceContent as hx, mergeCaptionResults as hy, mergeDown as hz, AiChangeOverlayComponent as i, routeOrthogonalConnector as i$, ommlToMathml as i0, ooxmlDashToCssBorderStyle as i1, openNativeEyeDropper as i2, overallStatus as i3, paletteColor as i4, parseAudienceNonce as i5, parseNodeTextarea as i6, partitionSlides as i7, patchChartData as i8, patchChartStyle as i9, removeElementAnimation as iA, removeGradientStopPatch as iB, removeNode as iC, removeRow as iD, removeSeries as iE, renderToCanvas as iF, reorderAnimationDown as iG, reorderAnimationUp as iH, replaceInSlides as iI, replaceMatch as iJ, requestPresentationFullscreen as iK, resizeElement as iL, resolveCaptionTracks as iM, resolveChartKind as iN, resolveFontVariant as iO, resolveHyperlinkHref as iP, resolveInteractiveElementId as iQ, resolveMediaSrc as iR, resolveOleType as iS, resolveParagraphBullet as iT, resolvePresenterNotes as iU, resolveProfileInitial as iV, resolveRegionCode as iW, resolvePalette as iX, resolveThemeCatalogEntry as iY, resolveTransitionDuration as iZ, revealedElementStyles as i_, patchTableData as ia, patchTextStyle as ib, pendingElementStyles as ic, pickColorByClickFallback as id, pickSupportedMimeType as ie, planGifFrames as ig, planVideoSegments as ih, pointsToSvgPathD as ii, presenceToCursors as ij, presetByLayout as ik, presetsForCategory as il, pressuresToWidths as im, prevVisibleIndex as io, projectDrawingShapes as ip, promoteNode as iq, provideViewerTheme as ir, radarAngle as is, radarRingPoints as it, recordWebm as iu, redistributeColumnWidth as iv, removeAnimation as iw, removeCategory as ix, removeColumn as iy, removeCommentFromList as iz, AiChatPanelComponent as j, signatureKey as j$, rowStyle as j0, sampleColorFromSlide as j1, sanitizeColor as j2, sanitizeSlideIndex as j3, sanitizeUserName as j4, saveViewerProfile as j5, scanAvailableFonts as j6, searchSlides as j7, seedBroadcastFields as j8, seedHyperlinkDraft as j9, setGridlineStyle as jA, setLayout as jB, setLegend as jC, setNodeStyle as jD, setNodeText as jE, setRepeatCount as jF, setRepeatMode as jG, setSequence as jH, setSeriesChartType as jI, setSeriesColor as jJ, setSeriesErrorBars as jK, setSeriesMarker as jL, setSeriesName as jM, setSeriesTrendline as jN, setSeriesValue as jO, setStyle as jP, setTimingCurve as jQ, setTitle as jR, setTrigger as jS, setTriggerShapeId as jT, shapeStylePatch as jU, sheetAfterNavigate as jV, shouldBlockClickAdvance as jW, shouldUseSvgWarp as jX, showDirectionPicker as jY, showsTemplateAffordance as jZ, signatureCountLabel as j_, seedPropertiesDraft as ja, seedShareFields as jb, segmentFrameCount as jc, selectValue$2 as jd, sendBackward as je, sendToBack as jf, sequentialColorScale as jg, serializeWriteBack as jh, seriesColor as ji, setAnimationEmphasis as jj, setAnimationEntrance as jk, setAnimationExit as jl, setAxis as jm, setAxisLogScale as jn, setAxisTitleStyle as jo, setCategoryLabel as jp, setCellText as jq, setColorScheme as jr, setDataLabels as js, setDataPointExplosion as jt, setDataPointFill as ju, setDataPointLabel as jv, setDelay as jw, setDirection as jx, setDuration as jy, setElementPosition as jz, AiChatService as k, signatureTimestamp as k0, signerName as k1, statusLabel as k2, slideNumberOf as k3, smartArtNodes as k4, paletteColour as k5, snapToGridStep as k6, splitCursorCell as k7, splitMergedCell as k8, statusKind as k9, updateGlowPatch as kA, updateGradientStopPatch as kB, updateInnerShadowPatch as kC, updateOuterShadowPatch as kD, updateReflectionPatch as kE, vAlignPatch as kF, validatePassword as kG, validatePrintSettings as kH, validateRoomId as kI, valueToY as kJ, vermilionDarkColors as kK, vermilionDarkTheme as kL, vermilionLightColors as kM, vermilionLightTheme as kN, vermilionRadius as kO, waypointsToPathD as kP, worstStatus as kQ, zoomTargetSlideIndex as kR, statusLabel$1 as ka, storeAudienceContent as kb, stringFromEvent$5 as kc, strokeColorOf as kd, strokeToInkElement as ke, styleShadowFilter as kf, textAdvancedPatch as kg, textAdvancedStateFromStyle as kh, textAdvancedStateOf as ki, textColorOf as kj, textDirectionPatch as kk, textStyleOf as kl, textStylePatch as km, themeStyle as kn, themeToCssVars as ko, thumbnailHeight as kp, thumbnailZoom as kq, toggleCommentResolvedInList as kr, toggleNodeBold as ks, toggleNodeItalic as kt, toggleSheet as ku, topLevelNodeCount as kv, transformSelectedTextCase as kw, translationsEn as kx, ungroupElements as ky, updateElementById as kz, AiComposerComponent as l, AiFocusBarComponent as m, AiFocusHighlightOverlayComponent as n, AiMessageListComponent as o, AiPanelStore as p, AiProposalCardComponent as q, AiSettingsSectionComponent as r, AiToolCallCardComponent as s, toChatSummary as t, AnimationAuthorPanelComponent as u, AnimationPanelComponent as v, AnimationPlaybackService as w, AutosaveService as x, CURSOR_PALETTE as y, CanvasFitService as z };
|
|
112556
|
-
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-
|
|
112861
|
+
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-CA_G8slL.mjs.map
|