kritzel-stencil 0.4.21 → 0.4.23
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/dist/cjs/index.cjs.js +50 -1
- package/dist/cjs/kritzel-active-users_47.cjs.entry.js +135 -31
- package/dist/collection/classes/providers/sync/hocuspocus-sync-provider.class.js +21 -0
- package/dist/collection/classes/providers/sync/websocket-sync-provider.class.js +29 -1
- package/dist/collection/classes/structures/app-state-map.structure.js +4 -1
- package/dist/collection/classes/structures/object-map.structure.js +66 -4
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +42 -9
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +51 -17
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +51 -2
- package/dist/components/kritzel-editor.js +16 -11
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/{p-BdCrcQ0_.js → p-Cb-xlpsJ.js} +1 -1
- package/dist/components/{p-C_f_QElb.js → p-EijwLEZx.js} +120 -21
- package/dist/esm/index.js +50 -1
- package/dist/esm/kritzel-active-users_47.entry.js +135 -31
- package/dist/stencil/index.esm.js +50 -1
- package/dist/stencil/{p-832b80e9.entry.js → p-9e38407e.entry.js} +135 -31
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/providers/sync/hocuspocus-sync-provider.class.d.ts +5 -0
- package/dist/types/classes/providers/sync/websocket-sync-provider.class.d.ts +6 -0
- package/dist/types/classes/structures/object-map.structure.d.ts +26 -2
- package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +3 -0
- package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +9 -1
- package/dist/types/components.d.ts +8 -1
- package/dist/types/constants/version.d.ts +1 -1
- package/dist/types/interfaces/sync-provider.interface.d.ts +13 -0
- package/package.json +1 -1
|
@@ -43319,6 +43319,10 @@ class KritzelObjectMap {
|
|
|
43319
43319
|
_awarenessChangeHandler = null;
|
|
43320
43320
|
_awarenessChangeCallbacks = [];
|
|
43321
43321
|
_objectsChangeCallbacks = [];
|
|
43322
|
+
_remoteChangesCallbacks = [];
|
|
43323
|
+
_docUpdateHandler = null;
|
|
43324
|
+
_remoteOrigins = new Set();
|
|
43325
|
+
_appliedRemoteChanges = false;
|
|
43322
43326
|
_lastAwarenessEmitTime = 0;
|
|
43323
43327
|
_awarenessEmitTimeout = null;
|
|
43324
43328
|
AWARENESS_THROTTLE_INTERVAL = 100; // milliseconds
|
|
@@ -43342,12 +43346,44 @@ class KritzelObjectMap {
|
|
|
43342
43346
|
return this._isReady;
|
|
43343
43347
|
}
|
|
43344
43348
|
/**
|
|
43345
|
-
* Promise that settles when network providers finish connecting
|
|
43346
|
-
* Resolves immediately if no network providers.
|
|
43349
|
+
* Promise that settles when network providers finish connecting and complete
|
|
43350
|
+
* their Yjs sync handshake. Resolves immediately if no network providers.
|
|
43347
43351
|
*/
|
|
43348
43352
|
whenNetworkSynced() {
|
|
43349
43353
|
return this._networkSyncPromise ?? Promise.resolve();
|
|
43350
43354
|
}
|
|
43355
|
+
/**
|
|
43356
|
+
* Whether any configured provider synchronizes over the network.
|
|
43357
|
+
*/
|
|
43358
|
+
get hasNetworkSyncProvider() {
|
|
43359
|
+
return this._providers.some(provider => provider.type === 'network');
|
|
43360
|
+
}
|
|
43361
|
+
/**
|
|
43362
|
+
* Whether remote-originated changes can be told apart from local writes.
|
|
43363
|
+
* Requires every network provider to expose its Yjs update origin.
|
|
43364
|
+
*/
|
|
43365
|
+
get supportsRemoteChangeDetection() {
|
|
43366
|
+
const networkProviders = this._providers.filter(provider => provider.type === 'network');
|
|
43367
|
+
return networkProviders.length > 0 && networkProviders.every(provider => provider.updateOrigin !== undefined);
|
|
43368
|
+
}
|
|
43369
|
+
/**
|
|
43370
|
+
* Whether at least one update originating from a network provider has been
|
|
43371
|
+
* applied to this document. Stays `false` when the remote held no data the
|
|
43372
|
+
* local document was missing, because Yjs suppresses empty updates.
|
|
43373
|
+
*/
|
|
43374
|
+
get hasAppliedRemoteChanges() {
|
|
43375
|
+
return this._appliedRemoteChanges;
|
|
43376
|
+
}
|
|
43377
|
+
/**
|
|
43378
|
+
* Registers a callback invoked whenever a network provider applies changes to this document.
|
|
43379
|
+
* @returns A function that unregisters the callback.
|
|
43380
|
+
*/
|
|
43381
|
+
onRemoteChangesApplied(callback) {
|
|
43382
|
+
this._remoteChangesCallbacks.push(callback);
|
|
43383
|
+
return () => {
|
|
43384
|
+
this._remoteChangesCallbacks = this._remoteChangesCallbacks.filter(registered => registered !== callback);
|
|
43385
|
+
};
|
|
43386
|
+
}
|
|
43351
43387
|
/**
|
|
43352
43388
|
* Returns the Yjs Awareness instance, if a network provider is available.
|
|
43353
43389
|
*/
|
|
@@ -43590,6 +43626,22 @@ class KritzelObjectMap {
|
|
|
43590
43626
|
const providerList = this._providers.map(p => `${p.constructor.name} (${p.type})`).join(', ');
|
|
43591
43627
|
console.info(`[Kritzel] Workspace sync providers initialized for ${docName}: ${providerList || 'none'}`);
|
|
43592
43628
|
}
|
|
43629
|
+
// Registered before any provider connects so no remote update can be missed.
|
|
43630
|
+
for (const provider of this._providers) {
|
|
43631
|
+
if (provider.type === 'network' && provider.updateOrigin !== undefined) {
|
|
43632
|
+
this._remoteOrigins.add(provider.updateOrigin);
|
|
43633
|
+
}
|
|
43634
|
+
}
|
|
43635
|
+
this._docUpdateHandler = (_update, origin) => {
|
|
43636
|
+
if (!this._remoteOrigins.has(origin)) {
|
|
43637
|
+
return;
|
|
43638
|
+
}
|
|
43639
|
+
this._appliedRemoteChanges = true;
|
|
43640
|
+
for (const callback of [...this._remoteChangesCallbacks]) {
|
|
43641
|
+
callback();
|
|
43642
|
+
}
|
|
43643
|
+
};
|
|
43644
|
+
this._ydoc.on('update', this._docUpdateHandler);
|
|
43593
43645
|
// captureTimeout is effectively infinite — undo-step boundaries are
|
|
43594
43646
|
// marked explicitly via markUndoBoundary() in insert/remove/reset and
|
|
43595
43647
|
// at stroke/gesture boundaries in the tools. This prevents a single
|
|
@@ -43643,12 +43695,15 @@ class KritzelObjectMap {
|
|
|
43643
43695
|
if (showSyncProviderInfo && networkProviders.length > 0) {
|
|
43644
43696
|
console.info(`[Kritzel] Workspace network providers connecting in background: ${networkProviders.length}`);
|
|
43645
43697
|
}
|
|
43646
|
-
const networkConnectPromises = networkProviders.map(provider => provider
|
|
43698
|
+
const networkConnectPromises = networkProviders.map(provider => provider
|
|
43699
|
+
.connect()
|
|
43700
|
+
.then(() => provider.whenSynced?.())
|
|
43701
|
+
.catch(err => {
|
|
43647
43702
|
if (showSyncProviderInfo) {
|
|
43648
43703
|
console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
|
|
43649
43704
|
}
|
|
43650
43705
|
}));
|
|
43651
|
-
//
|
|
43706
|
+
// Settles once remote data has been reconciled, not merely once the transport connected
|
|
43652
43707
|
this._networkSyncPromise = networkConnectPromises.length > 0 ? Promise.allSettled(networkConnectPromises).then(() => undefined) : null;
|
|
43653
43708
|
this._isReady = true;
|
|
43654
43709
|
// Find the first provider that exposes awareness (network providers)
|
|
@@ -44306,6 +44361,13 @@ class KritzelObjectMap {
|
|
|
44306
44361
|
this._objectsMap.unobserve(this._objectsObserver);
|
|
44307
44362
|
this._objectsObserver = null;
|
|
44308
44363
|
}
|
|
44364
|
+
if (this._ydoc && this._docUpdateHandler) {
|
|
44365
|
+
this._ydoc.off('update', this._docUpdateHandler);
|
|
44366
|
+
}
|
|
44367
|
+
this._docUpdateHandler = null;
|
|
44368
|
+
this._remoteOrigins.clear();
|
|
44369
|
+
this._remoteChangesCallbacks = [];
|
|
44370
|
+
this._appliedRemoteChanges = false;
|
|
44309
44371
|
// Remove undo manager event listeners
|
|
44310
44372
|
if (this._undoManager) {
|
|
44311
44373
|
if (this._stackItemAddedHandler) {
|
|
@@ -44801,7 +44863,10 @@ class KritzelAppStateMap {
|
|
|
44801
44863
|
if (showSyncProviderInfo && networkProviders.length > 0) {
|
|
44802
44864
|
console.info(`[Kritzel] App-state network providers connecting in background: ${networkProviders.length}`);
|
|
44803
44865
|
}
|
|
44804
|
-
const networkConnectPromises = networkProviders.map(provider => provider
|
|
44866
|
+
const networkConnectPromises = networkProviders.map(provider => provider
|
|
44867
|
+
.connect()
|
|
44868
|
+
.then(() => provider.whenSynced?.())
|
|
44869
|
+
.catch(err => {
|
|
44805
44870
|
if (showSyncProviderInfo) {
|
|
44806
44871
|
console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
|
|
44807
44872
|
}
|
|
@@ -48905,7 +48970,7 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
48905
48970
|
contextMenuStateChange;
|
|
48906
48971
|
/** Emitted when the combined loading state (external `isLoading` prop or internal workspace loading) changes. */
|
|
48907
48972
|
loadingChange;
|
|
48908
|
-
/** Emitted
|
|
48973
|
+
/** Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new. */
|
|
48909
48974
|
syncingChange;
|
|
48910
48975
|
forceUpdate = 0;
|
|
48911
48976
|
isSyncing = false;
|
|
@@ -50492,6 +50557,7 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
50492
50557
|
_sceneBootstrapOpacity = 1;
|
|
50493
50558
|
_sceneBootstrapTransitionEnabled = false;
|
|
50494
50559
|
_sceneBootstrapTransitionTimer = null;
|
|
50560
|
+
_remoteChangesUnsubscribe = null;
|
|
50495
50561
|
_defaultUndoState = {
|
|
50496
50562
|
canUndo: false,
|
|
50497
50563
|
canRedo: false,
|
|
@@ -50509,9 +50575,16 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
50509
50575
|
this.loadingChange.emit(this.core.store.state.isLoading);
|
|
50510
50576
|
}
|
|
50511
50577
|
syncSyncingState(isSyncing) {
|
|
50578
|
+
if (this.isSyncing === isSyncing) {
|
|
50579
|
+
return;
|
|
50580
|
+
}
|
|
50512
50581
|
this.isSyncing = isSyncing;
|
|
50513
50582
|
this.syncingChange.emit(isSyncing);
|
|
50514
50583
|
}
|
|
50584
|
+
clearRemoteChangesSubscription() {
|
|
50585
|
+
this._remoteChangesUnsubscribe?.();
|
|
50586
|
+
this._remoteChangesUnsubscribe = null;
|
|
50587
|
+
}
|
|
50515
50588
|
isNetworkAvailable() {
|
|
50516
50589
|
return typeof navigator !== 'undefined' && navigator.onLine;
|
|
50517
50590
|
}
|
|
@@ -50627,6 +50700,7 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
50627
50700
|
disconnectedCallback() {
|
|
50628
50701
|
this.throttledPointerMoveMulti.cancel();
|
|
50629
50702
|
this._revealedImageKeys.clear();
|
|
50703
|
+
this.clearRemoteChangesSubscription();
|
|
50630
50704
|
if (this._sceneBootstrapTransitionTimer !== null) {
|
|
50631
50705
|
clearTimeout(this._sceneBootstrapTransitionTimer);
|
|
50632
50706
|
this._sceneBootstrapTransitionTimer = null;
|
|
@@ -50881,21 +50955,7 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
50881
50955
|
this._workspaceInitializationTargetKey = targetKey;
|
|
50882
50956
|
try {
|
|
50883
50957
|
await initializationPromise;
|
|
50884
|
-
|
|
50885
|
-
const initializedObjects = this.core.store.objects;
|
|
50886
|
-
const networkSyncPromise = initializedObjects?.whenNetworkSynced();
|
|
50887
|
-
if (networkSyncPromise && this.isNetworkAvailable()) {
|
|
50888
|
-
this.syncSyncingState(true);
|
|
50889
|
-
void networkSyncPromise.finally(() => {
|
|
50890
|
-
// Only clear syncing for the currently active object map.
|
|
50891
|
-
if (this.core.store.objects === initializedObjects) {
|
|
50892
|
-
this.syncSyncingState(false);
|
|
50893
|
-
}
|
|
50894
|
-
});
|
|
50895
|
-
}
|
|
50896
|
-
else {
|
|
50897
|
-
this.syncSyncingState(false);
|
|
50898
|
-
}
|
|
50958
|
+
this.trackRemoteSynchronization();
|
|
50899
50959
|
}
|
|
50900
50960
|
finally {
|
|
50901
50961
|
// Only the most recent init clears loading state to prevent race conditions
|
|
@@ -50907,6 +50967,45 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
|
|
|
50907
50967
|
}
|
|
50908
50968
|
}
|
|
50909
50969
|
}
|
|
50970
|
+
/**
|
|
50971
|
+
* Reports syncing only while remote data is actually being reconciled into the
|
|
50972
|
+
* active workspace. Yjs suppresses updates that carry nothing new, so a workspace
|
|
50973
|
+
* that already matches the remote never reports syncing at all.
|
|
50974
|
+
*/
|
|
50975
|
+
trackRemoteSynchronization() {
|
|
50976
|
+
this.clearRemoteChangesSubscription();
|
|
50977
|
+
this.syncSyncingState(false);
|
|
50978
|
+
const initializedObjects = this.core.store.objects;
|
|
50979
|
+
if (!initializedObjects?.hasNetworkSyncProvider || !this.isNetworkAvailable()) {
|
|
50980
|
+
return;
|
|
50981
|
+
}
|
|
50982
|
+
if (initializedObjects.supportsRemoteChangeDetection) {
|
|
50983
|
+
this._remoteChangesUnsubscribe = initializedObjects.onRemoteChangesApplied(() => {
|
|
50984
|
+
if (this.core.store.objects !== initializedObjects) {
|
|
50985
|
+
return;
|
|
50986
|
+
}
|
|
50987
|
+
this.syncSyncingState(true);
|
|
50988
|
+
});
|
|
50989
|
+
// The network provider may have already applied remote changes between
|
|
50990
|
+
// object-map initialization and this subscription (e.g. an already-connected
|
|
50991
|
+
// shared socket resolves the sync handshake almost immediately). Without this
|
|
50992
|
+
// check that update is missed entirely and the indicator never appears.
|
|
50993
|
+
if (initializedObjects.hasAppliedRemoteChanges) {
|
|
50994
|
+
this.syncSyncingState(true);
|
|
50995
|
+
}
|
|
50996
|
+
}
|
|
50997
|
+
else {
|
|
50998
|
+
// Custom providers without an update origin cannot be diffed, so fall back to reporting the whole handshake.
|
|
50999
|
+
this.syncSyncingState(true);
|
|
51000
|
+
}
|
|
51001
|
+
void initializedObjects.whenNetworkSynced().finally(() => {
|
|
51002
|
+
if (this.core.store.objects !== initializedObjects) {
|
|
51003
|
+
return;
|
|
51004
|
+
}
|
|
51005
|
+
this.clearRemoteChangesSubscription();
|
|
51006
|
+
this.syncSyncingState(false);
|
|
51007
|
+
});
|
|
51008
|
+
}
|
|
50910
51009
|
emitObjectsChange() {
|
|
50911
51010
|
const objectsMap = this.core.store.objects;
|
|
50912
51011
|
if (!objectsMap) {
|
package/dist/esm/index.js
CHANGED
|
@@ -549,9 +549,15 @@ class WebSocketSyncProvider {
|
|
|
549
549
|
provider;
|
|
550
550
|
isConnected = false;
|
|
551
551
|
_quiet = false;
|
|
552
|
+
_isSynced = false;
|
|
553
|
+
_syncedResolvers = [];
|
|
552
554
|
get awareness() {
|
|
553
555
|
return this.provider.awareness;
|
|
554
556
|
}
|
|
557
|
+
/** y-websocket applies remote updates with the underlying provider as the Yjs transaction origin. */
|
|
558
|
+
get updateOrigin() {
|
|
559
|
+
return this.provider;
|
|
560
|
+
}
|
|
555
561
|
constructor(docName, doc, options) {
|
|
556
562
|
const url = options?.url || 'ws://localhost:1234';
|
|
557
563
|
const roomName = options?.roomName || docName;
|
|
@@ -598,11 +604,29 @@ class WebSocketSyncProvider {
|
|
|
598
604
|
}
|
|
599
605
|
});
|
|
600
606
|
this.provider.on('sync', (synced) => {
|
|
601
|
-
if (synced
|
|
607
|
+
if (!synced) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
this._isSynced = true;
|
|
611
|
+
this.releaseSyncedWaiters();
|
|
612
|
+
if (!this._quiet) {
|
|
602
613
|
console.info('WebSocket synced');
|
|
603
614
|
}
|
|
604
615
|
});
|
|
605
616
|
}
|
|
617
|
+
releaseSyncedWaiters() {
|
|
618
|
+
const resolvers = this._syncedResolvers;
|
|
619
|
+
this._syncedResolvers = [];
|
|
620
|
+
resolvers.forEach(resolve => resolve());
|
|
621
|
+
}
|
|
622
|
+
async whenSynced() {
|
|
623
|
+
if (this._isSynced || this.provider.synced) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
return new Promise(resolve => {
|
|
627
|
+
this._syncedResolvers.push(resolve);
|
|
628
|
+
});
|
|
629
|
+
}
|
|
606
630
|
async connect() {
|
|
607
631
|
if (this.isConnected) {
|
|
608
632
|
return;
|
|
@@ -634,6 +658,8 @@ class WebSocketSyncProvider {
|
|
|
634
658
|
this.provider.disconnect();
|
|
635
659
|
}
|
|
636
660
|
this.isConnected = false;
|
|
661
|
+
this._isSynced = false;
|
|
662
|
+
this.releaseSyncedWaiters();
|
|
637
663
|
}
|
|
638
664
|
async reconnect() {
|
|
639
665
|
this.disconnect();
|
|
@@ -644,6 +670,8 @@ class WebSocketSyncProvider {
|
|
|
644
670
|
this.provider.destroy();
|
|
645
671
|
}
|
|
646
672
|
this.isConnected = false;
|
|
673
|
+
this._isSynced = false;
|
|
674
|
+
this.releaseSyncedWaiters();
|
|
647
675
|
}
|
|
648
676
|
}
|
|
649
677
|
|
|
@@ -665,9 +693,14 @@ class HocuspocusSyncProvider {
|
|
|
665
693
|
_connectionStatus = 'disconnected';
|
|
666
694
|
visibilityHandler = null;
|
|
667
695
|
onlineHandler = null;
|
|
696
|
+
syncedResolvers = [];
|
|
668
697
|
get awareness() {
|
|
669
698
|
return this.provider.awareness;
|
|
670
699
|
}
|
|
700
|
+
/** HocuspocusProvider applies remote updates with itself as the Yjs transaction origin. */
|
|
701
|
+
get updateOrigin() {
|
|
702
|
+
return this.provider;
|
|
703
|
+
}
|
|
671
704
|
get connectionStatus() {
|
|
672
705
|
return this._connectionStatus;
|
|
673
706
|
}
|
|
@@ -724,6 +757,7 @@ class HocuspocusSyncProvider {
|
|
|
724
757
|
}
|
|
725
758
|
this.isSynced = true;
|
|
726
759
|
this._connectionStatus = 'synced';
|
|
760
|
+
this.releaseSyncedWaiters();
|
|
727
761
|
if (!options?.quiet) {
|
|
728
762
|
console.info(`Hocuspocus synced: ${name}`);
|
|
729
763
|
}
|
|
@@ -934,6 +968,19 @@ class HocuspocusSyncProvider {
|
|
|
934
968
|
this.disconnect();
|
|
935
969
|
return this.connect();
|
|
936
970
|
}
|
|
971
|
+
releaseSyncedWaiters() {
|
|
972
|
+
const resolvers = this.syncedResolvers;
|
|
973
|
+
this.syncedResolvers = [];
|
|
974
|
+
resolvers.forEach(resolve => resolve());
|
|
975
|
+
}
|
|
976
|
+
async whenSynced() {
|
|
977
|
+
if (this.isSynced || this.isDestroyed || this.provider.isSynced) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
return new Promise(resolve => {
|
|
981
|
+
this.syncedResolvers.push(resolve);
|
|
982
|
+
});
|
|
983
|
+
}
|
|
937
984
|
disconnect() {
|
|
938
985
|
// Cancel any pending connection attempt
|
|
939
986
|
if (this.connectTimeout) {
|
|
@@ -955,6 +1002,7 @@ class HocuspocusSyncProvider {
|
|
|
955
1002
|
this.isConnected = false;
|
|
956
1003
|
this.isSynced = false;
|
|
957
1004
|
this._connectionStatus = 'disconnected';
|
|
1005
|
+
this.releaseSyncedWaiters();
|
|
958
1006
|
}
|
|
959
1007
|
destroy() {
|
|
960
1008
|
// Mark as destroyed first to prevent any callbacks from doing work
|
|
@@ -974,6 +1022,7 @@ class HocuspocusSyncProvider {
|
|
|
974
1022
|
this.isConnected = false;
|
|
975
1023
|
this.isSynced = false;
|
|
976
1024
|
this._connectionStatus = 'disconnected';
|
|
1025
|
+
this.releaseSyncedWaiters();
|
|
977
1026
|
}
|
|
978
1027
|
}
|
|
979
1028
|
|