pptx-angular-viewer 1.1.59 → 1.1.60
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
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
|
|
5
5
|
by [git-cliff](https://git-cliff.org); do not edit it by hand.
|
|
6
6
|
|
|
7
|
+
## [1.1.59](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.1.59) - 2026-07-03
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- **angular:** Wire collaboration end-to-end (by @ChristopherVR) ([0498cea](https://github.com/ChristopherVR/pptx-viewer/commit/0498cea40ac10e08069f560be0a1cea6f92a8721))
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
|
|
15
|
+
- **deps:** Declare yjs, y-websocket, and y-webrtc across bindings (by @ChristopherVR) ([27a2849](https://github.com/ChristopherVR/pptx-viewer/commit/27a2849da755a0902296dcd59557c1329a1cbadf))
|
|
16
|
+
|
|
7
17
|
## [1.1.57](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.1.57) - 2026-07-03
|
|
8
18
|
|
|
9
19
|
### Features
|
|
@@ -22215,6 +22215,14 @@ function getDraggedShapeAdjustmentValue(state, deltaX) {
|
|
|
22215
22215
|
*/
|
|
22216
22216
|
/** Maximum time (ms) to wait for an initial WebSocket connection before giving up. */
|
|
22217
22217
|
const CONNECTION_TIMEOUT_MS = 30_000;
|
|
22218
|
+
/**
|
|
22219
|
+
* Grace period (ms) before the first local doc write when the provider has not
|
|
22220
|
+
* signalled initial sync. Websocket providers emit 'synced' reliably; webrtc
|
|
22221
|
+
* only syncs once a peer is present, so a lone (fresh-room) peer seeds the doc
|
|
22222
|
+
* after this delay instead. Gating the first write prevents a late joiner's
|
|
22223
|
+
* bootstrap deck from merging into a room whose real content has not arrived.
|
|
22224
|
+
*/
|
|
22225
|
+
const INITIAL_SYNC_GRACE_MS = 3_000;
|
|
22218
22226
|
/** Heartbeat interval (ms): re-publish presence so peers don't time us out. */
|
|
22219
22227
|
const PRESENCE_HEARTBEAT_MS = 10_000;
|
|
22220
22228
|
/** Minimum interval (ms) between outgoing presence broadcasts (rate limiting). */
|
|
@@ -23174,6 +23182,58 @@ function reconcileSlidesInYDoc(slides, ydoc, factories, origin = LOCAL_SYNC_ORIG
|
|
|
23174
23182
|
}, origin);
|
|
23175
23183
|
}
|
|
23176
23184
|
|
|
23185
|
+
/**
|
|
23186
|
+
* collaboration-sync-gate.ts: first-write gate for collaborative sessions.
|
|
23187
|
+
*
|
|
23188
|
+
* Until a provider confirms its initial document sync, local state must not
|
|
23189
|
+
* be written into the shared doc: a late joiner bootstrapped with a
|
|
23190
|
+
* placeholder deck would otherwise merge that placeholder into the room's
|
|
23191
|
+
* real content. Websocket providers emit a reliable 'synced' event; y-webrtc
|
|
23192
|
+
* only syncs when a peer is present, so a lone fresh-room peer opens the gate
|
|
23193
|
+
* after {@link INITIAL_SYNC_GRACE_MS} instead and seeds the empty doc.
|
|
23194
|
+
*
|
|
23195
|
+
* Usage per binding: create the gate with an `onOpen` callback that performs
|
|
23196
|
+
* the deferred first write, call `arm()` when the provider is created (and on
|
|
23197
|
+
* (re)connect), `open()` from the provider's sync event, gate local writes on
|
|
23198
|
+
* `isOpen()`, and `reset()` on session teardown.
|
|
23199
|
+
*/
|
|
23200
|
+
/**
|
|
23201
|
+
* Create a first-write gate. `onOpen` fires exactly once per session (until
|
|
23202
|
+
* `reset()`), from either the provider sync signal or the grace timer.
|
|
23203
|
+
*/
|
|
23204
|
+
function createSyncGate(onOpen, graceMs = INITIAL_SYNC_GRACE_MS) {
|
|
23205
|
+
let opened = false;
|
|
23206
|
+
let timer = null;
|
|
23207
|
+
const clear = () => {
|
|
23208
|
+
if (timer !== null) {
|
|
23209
|
+
clearTimeout(timer);
|
|
23210
|
+
timer = null;
|
|
23211
|
+
}
|
|
23212
|
+
};
|
|
23213
|
+
const open = () => {
|
|
23214
|
+
clear();
|
|
23215
|
+
if (opened) {
|
|
23216
|
+
return;
|
|
23217
|
+
}
|
|
23218
|
+
opened = true;
|
|
23219
|
+
onOpen();
|
|
23220
|
+
};
|
|
23221
|
+
return {
|
|
23222
|
+
isOpen: () => opened,
|
|
23223
|
+
arm: () => {
|
|
23224
|
+
clear();
|
|
23225
|
+
if (!opened) {
|
|
23226
|
+
timer = setTimeout(open, graceMs);
|
|
23227
|
+
}
|
|
23228
|
+
},
|
|
23229
|
+
open,
|
|
23230
|
+
reset: () => {
|
|
23231
|
+
clear();
|
|
23232
|
+
opened = false;
|
|
23233
|
+
},
|
|
23234
|
+
};
|
|
23235
|
+
}
|
|
23236
|
+
|
|
23177
23237
|
// ---------------------------------------------------------------------------
|
|
23178
23238
|
// Tuning constants (px tolerances for position / size changes)
|
|
23179
23239
|
// ---------------------------------------------------------------------------
|
|
@@ -35818,6 +35878,14 @@ class CollaborationService {
|
|
|
35818
35878
|
connectTimer = null;
|
|
35819
35879
|
unobserveSlides = null;
|
|
35820
35880
|
writeBack = new WriteBackScheduler();
|
|
35881
|
+
/**
|
|
35882
|
+
* First-write gate: local broadcasts are suppressed (captured as pending)
|
|
35883
|
+
* until the provider confirms its initial sync or the grace period lifts
|
|
35884
|
+
* the gate, so a late joiner never seeds its placeholder deck into a room
|
|
35885
|
+
* whose real content has not arrived yet.
|
|
35886
|
+
*/
|
|
35887
|
+
syncGate = createSyncGate(() => this.flushPendingBroadcast());
|
|
35888
|
+
pendingBroadcast = null;
|
|
35821
35889
|
onRemoteSlides = null;
|
|
35822
35890
|
canvasWidth = DEFAULT_CANVAS_BOUND;
|
|
35823
35891
|
canvasHeight = DEFAULT_CANVAS_BOUND;
|
|
@@ -35881,6 +35949,8 @@ class CollaborationService {
|
|
|
35881
35949
|
this.awareness.on('change', this.refreshPresence);
|
|
35882
35950
|
this.awareness.on('update', this.refreshPresence);
|
|
35883
35951
|
this.wireStatus(config);
|
|
35952
|
+
this.syncGate.reset();
|
|
35953
|
+
this.wireSynced();
|
|
35884
35954
|
this.unobserveSlides = observeYDocSlides(this.ydoc, (_events, transaction) => this.onRemoteChange(transaction));
|
|
35885
35955
|
this.active.set(true);
|
|
35886
35956
|
this.refreshPresence();
|
|
@@ -35937,6 +36007,50 @@ class CollaborationService {
|
|
|
35937
36007
|
}
|
|
35938
36008
|
}, CONNECTION_TIMEOUT_MS);
|
|
35939
36009
|
}
|
|
36010
|
+
/**
|
|
36011
|
+
* Open the first-write gate on the provider's initial-sync confirmation.
|
|
36012
|
+
* y-websocket emits 'sync' with a boolean; y-webrtc emits 'synced' with an
|
|
36013
|
+
* object carrying a `synced` flag (and only once a peer syncs, hence the
|
|
36014
|
+
* grace timer). Listen to both; opening is idempotent.
|
|
36015
|
+
*/
|
|
36016
|
+
wireSynced() {
|
|
36017
|
+
const provider = this.provider;
|
|
36018
|
+
if (!provider) {
|
|
36019
|
+
return;
|
|
36020
|
+
}
|
|
36021
|
+
const handle = (payload) => {
|
|
36022
|
+
const flag = payload;
|
|
36023
|
+
const isSynced = typeof flag === 'boolean' ? flag : flag?.synced !== false;
|
|
36024
|
+
if (isSynced) {
|
|
36025
|
+
this.syncGate.open();
|
|
36026
|
+
}
|
|
36027
|
+
};
|
|
36028
|
+
provider.on('sync', handle);
|
|
36029
|
+
provider.on('synced', handle);
|
|
36030
|
+
if (provider.synced === true) {
|
|
36031
|
+
this.syncGate.open();
|
|
36032
|
+
}
|
|
36033
|
+
else {
|
|
36034
|
+
this.syncGate.arm();
|
|
36035
|
+
}
|
|
36036
|
+
}
|
|
36037
|
+
/**
|
|
36038
|
+
* Perform the deferred first broadcast once the gate opens. When the doc is
|
|
36039
|
+
* still empty (fresh room, or nobody else present), clear the baseline so
|
|
36040
|
+
* the pending deck actually seeds it; when remote content already arrived,
|
|
36041
|
+
* the pending deck matches the applied baseline and the write is a no-op.
|
|
36042
|
+
*/
|
|
36043
|
+
flushPendingBroadcast() {
|
|
36044
|
+
const pending = this.pendingBroadcast;
|
|
36045
|
+
this.pendingBroadcast = null;
|
|
36046
|
+
if (!pending || !this.ydoc || !this.yFactories) {
|
|
36047
|
+
return;
|
|
36048
|
+
}
|
|
36049
|
+
if (this.ydoc.getArray(YDOC_SLIDES_KEY).length === 0) {
|
|
36050
|
+
this.lastSynced = '';
|
|
36051
|
+
}
|
|
36052
|
+
this.broadcastSlides(pending);
|
|
36053
|
+
}
|
|
35940
36054
|
/** Handle a remote Y.Doc change, skipping our own local-origin transactions. */
|
|
35941
36055
|
onRemoteChange(transaction) {
|
|
35942
36056
|
if (transaction?.origin === LOCAL_SYNC_ORIGIN || this.applyingRemote || !this.ydoc) {
|
|
@@ -35956,6 +36070,8 @@ class CollaborationService {
|
|
|
35956
36070
|
}
|
|
35957
36071
|
disconnect() {
|
|
35958
36072
|
this.clearConnectTimer();
|
|
36073
|
+
this.syncGate.reset();
|
|
36074
|
+
this.pendingBroadcast = null;
|
|
35959
36075
|
this.writeBack.cancel();
|
|
35960
36076
|
this.unobserveSlides?.();
|
|
35961
36077
|
this.unobserveSlides = null;
|
|
@@ -35999,6 +36115,12 @@ class CollaborationService {
|
|
|
35999
36115
|
if (!this.ydoc || !this.yFactories || this.applyingRemote || slides.length === 0) {
|
|
36000
36116
|
return;
|
|
36001
36117
|
}
|
|
36118
|
+
if (!this.syncGate.isOpen()) {
|
|
36119
|
+
// Defer until the initial sync confirms; the gate flushes the latest
|
|
36120
|
+
// pending deck when it opens.
|
|
36121
|
+
this.pendingBroadcast = slides;
|
|
36122
|
+
return;
|
|
36123
|
+
}
|
|
36002
36124
|
const s = JSON.stringify(slides);
|
|
36003
36125
|
if (s === this.lastSynced) {
|
|
36004
36126
|
return;
|
|
@@ -57243,7 +57365,7 @@ class ElementRendererComponent {
|
|
|
57243
57365
|
}, /* @ts-ignore */
|
|
57244
57366
|
...(ngDevMode ? [{ debugName: "placeholderLabel" }] : /* istanbul ignore next */ []));
|
|
57245
57367
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: ElementRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
57246
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: ElementRendererComponent, isStandalone: true, selector: "pptx-element-renderer", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: true, transformFunction: null }, mediaDataUrls: { classPropertyName: "mediaDataUrls", publicName: "mediaDataUrls", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, obstacles: { classPropertyName: "obstacles", publicName: "obstacles", isSignal: true, isRequired: false, transformFunction: null }, canvasWidth: { classPropertyName: "canvasWidth", publicName: "canvasWidth", isSignal: true, isRequired: false, transformFunction: null }, canvasHeight: { classPropertyName: "canvasHeight", publicName: "canvasHeight", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, fieldContext: { classPropertyName: "fieldContext", publicName: "fieldContext", isSignal: true, isRequired: false, transformFunction: null }, editTemplateMode: { classPropertyName: "editTemplateMode", publicName: "editTemplateMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellCommit: "cellCommit", tableChange: "tableChange" }, ngImport: i0, template: `
|
|
57368
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: ElementRendererComponent, isStandalone: true, selector: "pptx-element-renderer", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: true, transformFunction: null }, mediaDataUrls: { classPropertyName: "mediaDataUrls", publicName: "mediaDataUrls", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, obstacles: { classPropertyName: "obstacles", publicName: "obstacles", isSignal: true, isRequired: false, transformFunction: null }, canvasWidth: { classPropertyName: "canvasWidth", publicName: "canvasWidth", isSignal: true, isRequired: false, transformFunction: null }, canvasHeight: { classPropertyName: "canvasHeight", publicName: "canvasHeight", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, fieldContext: { classPropertyName: "fieldContext", publicName: "fieldContext", isSignal: true, isRequired: false, transformFunction: null }, editTemplateMode: { classPropertyName: "editTemplateMode", publicName: "editTemplateMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellCommit: "cellCommit", tableChange: "tableChange" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
|
|
57247
57369
|
@switch (true) {
|
|
57248
57370
|
@case (element().type === 'connector') {
|
|
57249
57371
|
<pptx-connector-renderer
|
|
@@ -57517,6 +57639,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
57517
57639
|
selector: 'pptx-element-renderer',
|
|
57518
57640
|
standalone: true,
|
|
57519
57641
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
57642
|
+
host: { class: 'contents' },
|
|
57520
57643
|
imports: [
|
|
57521
57644
|
NgStyle,
|
|
57522
57645
|
ConnectorRendererComponent,
|
|
@@ -74119,7 +74242,7 @@ class FontEmbeddingListComponent {
|
|
|
74119
74242
|
missingCount = input(0, /* @ts-ignore */
|
|
74120
74243
|
...(ngDevMode ? [{ debugName: "missingCount" }] : /* istanbul ignore next */ []));
|
|
74121
74244
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: FontEmbeddingListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
74122
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: FontEmbeddingListComponent, isStandalone: true, selector: "pptx-font-embedding-list", inputs: { usedFontFamilies: { classPropertyName: "usedFontFamilies", publicName: "usedFontFamilies", isSignal: true, isRequired: false, transformFunction: null }, availableFamilies: { classPropertyName: "availableFamilies", publicName: "availableFamilies", isSignal: true, isRequired: false, transformFunction: null }, embeddedSet: { classPropertyName: "embeddedSet", publicName: "embeddedSet", isSignal: true, isRequired: false, transformFunction: null }, scanning: { classPropertyName: "scanning", publicName: "scanning", isSignal: true, isRequired: false, transformFunction: null }, missingCount: { classPropertyName: "missingCount", publicName: "missingCount", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
74245
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: FontEmbeddingListComponent, isStandalone: true, selector: "pptx-font-embedding-list", inputs: { usedFontFamilies: { classPropertyName: "usedFontFamilies", publicName: "usedFontFamilies", isSignal: true, isRequired: false, transformFunction: null }, availableFamilies: { classPropertyName: "availableFamilies", publicName: "availableFamilies", isSignal: true, isRequired: false, transformFunction: null }, embeddedSet: { classPropertyName: "embeddedSet", publicName: "embeddedSet", isSignal: true, isRequired: false, transformFunction: null }, scanning: { classPropertyName: "scanning", publicName: "scanning", isSignal: true, isRequired: false, transformFunction: null }, missingCount: { classPropertyName: "missingCount", publicName: "missingCount", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
|
|
74123
74246
|
<div class="pptx-ng-fonts-section">
|
|
74124
74247
|
<h3 class="pptx-ng-fonts-section-title">
|
|
74125
74248
|
{{ 'pptx.fontEmbedding.usedFonts' | translate: { count: usedFontFamilies().length } }}
|
|
@@ -74168,7 +74291,7 @@ class FontEmbeddingListComponent {
|
|
|
74168
74291
|
}
|
|
74169
74292
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: FontEmbeddingListComponent, decorators: [{
|
|
74170
74293
|
type: Component,
|
|
74171
|
-
args: [{ selector: 'pptx-font-embedding-list', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
74294
|
+
args: [{ selector: 'pptx-font-embedding-list', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'contents' }, template: `
|
|
74172
74295
|
<div class="pptx-ng-fonts-section">
|
|
74173
74296
|
<h3 class="pptx-ng-fonts-section-title">
|
|
74174
74297
|
{{ 'pptx.fontEmbedding.usedFonts' | translate: { count: usedFontFamilies().length } }}
|
|
@@ -76039,7 +76162,7 @@ class ViewerExtraDialogsComponent {
|
|
|
76039
76162
|
this.svc.showPassword.set(false);
|
|
76040
76163
|
}
|
|
76041
76164
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: ViewerExtraDialogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
76042
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: ViewerExtraDialogsComponent, isStandalone: true, selector: "pptx-viewer-extra-dialogs", inputs: { activeSlideIndex: { classPropertyName: "activeSlideIndex", publicName: "activeSlideIndex", isSignal: true, isRequired: false, transformFunction: null }, selectedElementId: { classPropertyName: "selectedElementId", publicName: "selectedElementId", isSignal: true, isRequired: false, transformFunction: null }, filePath: { classPropertyName: "filePath", publicName: "filePath", isSignal: true, isRequired: false, transformFunction: null }, customShows: { classPropertyName: "customShows", publicName: "customShows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { restoreContent: "restoreContent" }, ngImport: i0, template: `
|
|
76165
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: ViewerExtraDialogsComponent, isStandalone: true, selector: "pptx-viewer-extra-dialogs", inputs: { activeSlideIndex: { classPropertyName: "activeSlideIndex", publicName: "activeSlideIndex", isSignal: true, isRequired: false, transformFunction: null }, selectedElementId: { classPropertyName: "selectedElementId", publicName: "selectedElementId", isSignal: true, isRequired: false, transformFunction: null }, filePath: { classPropertyName: "filePath", publicName: "filePath", isSignal: true, isRequired: false, transformFunction: null }, customShows: { classPropertyName: "customShows", publicName: "customShows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { restoreContent: "restoreContent" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
|
|
76043
76166
|
<pptx-equation-editor-dialog
|
|
76044
76167
|
[open]="svc.showEquation()"
|
|
76045
76168
|
[existingOmml]="svc.editingEquationOmml()"
|
|
@@ -76120,6 +76243,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
76120
76243
|
selector: 'pptx-viewer-extra-dialogs',
|
|
76121
76244
|
standalone: true,
|
|
76122
76245
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
76246
|
+
host: { class: 'contents' },
|
|
76123
76247
|
imports: [
|
|
76124
76248
|
EquationEditorDialogComponent,
|
|
76125
76249
|
SetUpSlideShowDialogComponent,
|