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
|
@@ -2146,6 +2146,8 @@ const KritzelEditor = class {
|
|
|
2146
2146
|
this.login = createEvent(this, "login");
|
|
2147
2147
|
this.isPublicChange = createEvent(this, "isPublicChange");
|
|
2148
2148
|
this.awarenessChange = createEvent(this, "awarenessChange");
|
|
2149
|
+
this.syncingChange = createEvent(this, "syncingChange");
|
|
2150
|
+
this.loadingChange = createEvent(this, "loadingChange");
|
|
2149
2151
|
}
|
|
2150
2152
|
get host() { return getElement(this); }
|
|
2151
2153
|
scaleMax = ABSOLUTE_SCALE_MAX;
|
|
@@ -2385,6 +2387,9 @@ const KritzelEditor = class {
|
|
|
2385
2387
|
login;
|
|
2386
2388
|
isPublicChange;
|
|
2387
2389
|
awarenessChange;
|
|
2390
|
+
/** Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new. */
|
|
2391
|
+
syncingChange;
|
|
2392
|
+
loadingChange;
|
|
2388
2393
|
isEngineReady = false;
|
|
2389
2394
|
isControlsReady = false;
|
|
2390
2395
|
isWorkspaceManagerReady = false;
|
|
@@ -3203,40 +3208,40 @@ const KritzelEditor = class {
|
|
|
3203
3208
|
const isLoggedIn = this.isLoggedIn;
|
|
3204
3209
|
const shouldShowCurrentUser = isLoggedIn;
|
|
3205
3210
|
const shouldShowLoginButton = this.isReady && !!this.loginConfig && !isLoggedIn;
|
|
3206
|
-
return (h(Host, { key: '
|
|
3211
|
+
return (h(Host, { key: 'ff7d505697bd4c4bd840dadcb04a934b7e243a01' }, h("div", { key: 'b40076c3c79ad3f879e80c58b412b79266de2ea8', class: "editor-content", style: {
|
|
3207
3212
|
opacity: this.isEditorVisible ? '1' : '0',
|
|
3208
3213
|
visibility: this.isEditorVisible ? 'visible' : 'hidden',
|
|
3209
3214
|
transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
|
|
3210
|
-
} }, h("div", { key: '
|
|
3215
|
+
} }, h("div", { key: '1b1171eea60a36ad06da55a3da564bc356526266', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '48b59f3f57ace57debc19a295d4fb642e8c3b23d', visible: this.isWorkspaceManagerVisible, workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, terms: this.resolvedTerms, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: 'c66a6faae8bf7115dcce47630ad820af9ba76eb0', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), this.activeNotification && (h("div", { key: '834baedc5f20328deb4ee586f693313306895a71', class: "top-center-notification-layer", role: "presentation" }, h("div", { key: 'f800508a2e076ee3e89820ffff4df81842b175b3', class: { 'top-center-notification-container': true, 'is-dismissing': this.isNotificationDismissing }, role: "presentation" }, h("kritzel-notification-card", { key: '2835ea84e8fa92c41637adaa29d9005f209b2da9', notification: this.activeNotification, locale: this.locale, onDismiss: this.dismissNotification, onHoverChange: event => this.handleNotificationHoverChange(event) })))), this.contextMenuState && this.contextMenuState.items.length > 0 && (h("kritzel-context-menu", { key: '6c49e899b7f933d3f06cd1f8696a590d542214a7', class: "context-menu", items: this.contextMenuState.items, objects: this.contextMenuState.objects, style: {
|
|
3211
3216
|
position: 'absolute',
|
|
3212
3217
|
left: `${this.contextMenuState.position.x}px`,
|
|
3213
3218
|
top: `${this.contextMenuState.position.y}px`,
|
|
3214
3219
|
zIndex: '10002',
|
|
3215
|
-
}, onActionSelected: event => this.handleContextMenuActionSelected(event), onClose: () => this.hideContextMenu() })), h("kritzel-sync-indicator", { key: '
|
|
3220
|
+
}, onActionSelected: event => this.handleContextMenuActionSelected(event), onClose: () => this.hideContextMenu() })), h("kritzel-sync-indicator", { key: '9622af1634a939d6689d5a133496d536f5d7865f', visible: this.isSyncing, text: "Syncing..." }), h("kritzel-loading-overlay", { key: '00f142a3eb8b23fbd0bdb6ec53ae95855434516c', visible: this.isLoadingOverlayVisible, text: this.resolvedTerms['editor.loading'] ?? 'Loading...' }), h("kritzel-engine", { key: '39b1d7bfce0a5347fec60cd135d6db8fb4250bfa', ref: el => {
|
|
3216
3221
|
if (el) {
|
|
3217
3222
|
this.engineRef = el;
|
|
3218
3223
|
}
|
|
3219
|
-
}, workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, assetStorageConfig: this.assetStorageConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, isObjectDistanceFadingActive: this.isObjectDistanceFadingActive, scaleMin: this.scaleMin, cursorTarget: this.cursorTarget, isLoading: this.isLoading, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, isPanningEnabled: this.isPanningEnabled, isZoomingEnabled: this.isZoomingEnabled, theme: this.theme, themes: this.themes, licenseKey: this.licenseKey, locale: this.locale, locales: this.locales, fallbackLocale: this.fallbackLocale, debugInfo: this.debugInfo, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onActiveWorkspaceChange: event => this.handleActiveWorkspaceChange(event), onObjectsChange: event => this.handleObjectsChange(event), onObjectsAdded: event => this.handleObjectsAdded(event), onObjectsRemoved: event => this.handleObjectsRemoved(event), onObjectsUpdated: event => this.handleObjectsUpdated(event), onUndoStateChange: event => this.handleUndoStateChange(event), onObjectsInViewportChange: event => this.handleObjectsInViewportChange(event), onViewportChange: event => this.handleViewportChange(event), onAwarenessChange: event => this.handleAwarenessChange(event), onNotificationsChange: event => this.handleNotificationsChange(event), onContextMenuStateChange: event => this.handleContextMenuStateChange(event), onSyncingChange: event => (this.isSyncing = event.detail), onLoadingChange: event => (this.isLoadingOverlayVisible = event.detail) }), h("kritzel-controls", { key: '
|
|
3224
|
+
}, workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, assetStorageConfig: this.assetStorageConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, isObjectDistanceFadingActive: this.isObjectDistanceFadingActive, scaleMin: this.scaleMin, cursorTarget: this.cursorTarget, isLoading: this.isLoading, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, isPanningEnabled: this.isPanningEnabled, isZoomingEnabled: this.isZoomingEnabled, theme: this.theme, themes: this.themes, licenseKey: this.licenseKey, locale: this.locale, locales: this.locales, fallbackLocale: this.fallbackLocale, debugInfo: this.debugInfo, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onActiveWorkspaceChange: event => this.handleActiveWorkspaceChange(event), onObjectsChange: event => this.handleObjectsChange(event), onObjectsAdded: event => this.handleObjectsAdded(event), onObjectsRemoved: event => this.handleObjectsRemoved(event), onObjectsUpdated: event => this.handleObjectsUpdated(event), onUndoStateChange: event => this.handleUndoStateChange(event), onObjectsInViewportChange: event => this.handleObjectsInViewportChange(event), onViewportChange: event => this.handleViewportChange(event), onAwarenessChange: event => this.handleAwarenessChange(event), onNotificationsChange: event => this.handleNotificationsChange(event), onContextMenuStateChange: event => this.handleContextMenuStateChange(event), onSyncingChange: event => (this.isSyncing = event.detail), onLoadingChange: event => (this.isLoadingOverlayVisible = event.detail) }), h("kritzel-controls", { key: '9a708f21ab92ecc6953a35f5db8001b2403f330e', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
|
|
3220
3225
|
if (el) {
|
|
3221
3226
|
this.controlsRef = el;
|
|
3222
3227
|
}
|
|
3223
|
-
}, controls: this.normalizedControls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: '
|
|
3228
|
+
}, controls: this.normalizedControls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: '6a2b1cd72099db86952ac31aad9655693d0a6a0d', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: 'b3756514bf8de6de8ac7586c626f9759da7f231d', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: 'd5e118bc4d60b867006bac287c5516baf3aa890e', class: "top-right-buttons" }, h("kritzel-settings", { key: 'e4065d36beb9283cdbbf3ae9800f4f8011c0b4f0', ref: el => {
|
|
3224
3229
|
if (el) {
|
|
3225
3230
|
this.settingsRef = el;
|
|
3226
3231
|
}
|
|
3227
|
-
}, shortcuts: this.shortcuts, availableThemes: this.themes && this.themes.length > 0 ? this.themes.map(t => t.name) : ['light', 'dark'], availableLocales: this.availableLocaleOptions, settings: this.currentSettingsConfig, terms: this.resolvedTerms, onSettingsChange: event => this.handleSettingsChange(event) }), h("kritzel-export", { key: '
|
|
3232
|
+
}, shortcuts: this.shortcuts, availableThemes: this.themes && this.themes.length > 0 ? this.themes.map(t => t.name) : ['light', 'dark'], availableLocales: this.availableLocaleOptions, settings: this.currentSettingsConfig, terms: this.resolvedTerms, onSettingsChange: event => this.handleSettingsChange(event) }), h("kritzel-export", { key: '4e18d9b42c9e7fef9433577b983c508559461da2', ref: el => {
|
|
3228
3233
|
if (el) {
|
|
3229
3234
|
this.exportRef = el;
|
|
3230
3235
|
}
|
|
3231
|
-
}, workspaceName: this.activeWorkspace?.name || 'workspace', terms: this.resolvedTerms, onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: '
|
|
3236
|
+
}, workspaceName: this.activeWorkspace?.name || 'workspace', terms: this.resolvedTerms, onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: '56e6b29c9c3dcd7b68af4a1ef7d34224d592ce34', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '3b90bcc7e583fcf3c6845d222ad181ccf234a423', user: this.user, terms: this.resolvedTerms, onClick: () => this.currentUserDialogRef?.open() }), shouldShowCurrentUser && (h("kritzel-current-user-dialog", { key: 'f87702c5a708b6afdb2bc3760895e3e3650a4cb5', ref: el => {
|
|
3232
3237
|
if (el) {
|
|
3233
3238
|
this.currentUserDialogRef = el;
|
|
3234
3239
|
}
|
|
3235
|
-
}, user: this.user, terms: this.resolvedTerms, onLogoutRequest: this.handleCurrentUserLogout })), shouldShowLoginButton && h("kritzel-button", { key: '
|
|
3240
|
+
}, user: this.user, terms: this.resolvedTerms, onLogoutRequest: this.handleCurrentUserLogout })), shouldShowLoginButton && h("kritzel-button", { key: '015bc0f4c66352136de66d35d28c20070366933f', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in'), h("kritzel-more-menu", { key: 'b3fdf9fdb2a34508002e82dfdef9e9bdbbe57ecc', items: this.resolvedMoreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: 'bac4fbbfa04c261e2b128e766cc43c1062aec2ff', ref: el => {
|
|
3236
3241
|
if (el) {
|
|
3237
3242
|
this.shareDialogRef = el;
|
|
3238
3243
|
}
|
|
3239
|
-
}, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '
|
|
3244
|
+
}, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'a7d52bad9e752ad546adf80d257c6a46de031e76', ref: el => {
|
|
3240
3245
|
if (el) {
|
|
3241
3246
|
this.loginDialogRef = el;
|
|
3242
3247
|
}
|
|
@@ -22279,6 +22284,10 @@ class KritzelObjectMap {
|
|
|
22279
22284
|
_awarenessChangeHandler = null;
|
|
22280
22285
|
_awarenessChangeCallbacks = [];
|
|
22281
22286
|
_objectsChangeCallbacks = [];
|
|
22287
|
+
_remoteChangesCallbacks = [];
|
|
22288
|
+
_docUpdateHandler = null;
|
|
22289
|
+
_remoteOrigins = new Set();
|
|
22290
|
+
_appliedRemoteChanges = false;
|
|
22282
22291
|
_lastAwarenessEmitTime = 0;
|
|
22283
22292
|
_awarenessEmitTimeout = null;
|
|
22284
22293
|
AWARENESS_THROTTLE_INTERVAL = 100; // milliseconds
|
|
@@ -22302,12 +22311,44 @@ class KritzelObjectMap {
|
|
|
22302
22311
|
return this._isReady;
|
|
22303
22312
|
}
|
|
22304
22313
|
/**
|
|
22305
|
-
* Promise that settles when network providers finish connecting
|
|
22306
|
-
* Resolves immediately if no network providers.
|
|
22314
|
+
* Promise that settles when network providers finish connecting and complete
|
|
22315
|
+
* their Yjs sync handshake. Resolves immediately if no network providers.
|
|
22307
22316
|
*/
|
|
22308
22317
|
whenNetworkSynced() {
|
|
22309
22318
|
return this._networkSyncPromise ?? Promise.resolve();
|
|
22310
22319
|
}
|
|
22320
|
+
/**
|
|
22321
|
+
* Whether any configured provider synchronizes over the network.
|
|
22322
|
+
*/
|
|
22323
|
+
get hasNetworkSyncProvider() {
|
|
22324
|
+
return this._providers.some(provider => provider.type === 'network');
|
|
22325
|
+
}
|
|
22326
|
+
/**
|
|
22327
|
+
* Whether remote-originated changes can be told apart from local writes.
|
|
22328
|
+
* Requires every network provider to expose its Yjs update origin.
|
|
22329
|
+
*/
|
|
22330
|
+
get supportsRemoteChangeDetection() {
|
|
22331
|
+
const networkProviders = this._providers.filter(provider => provider.type === 'network');
|
|
22332
|
+
return networkProviders.length > 0 && networkProviders.every(provider => provider.updateOrigin !== undefined);
|
|
22333
|
+
}
|
|
22334
|
+
/**
|
|
22335
|
+
* Whether at least one update originating from a network provider has been
|
|
22336
|
+
* applied to this document. Stays `false` when the remote held no data the
|
|
22337
|
+
* local document was missing, because Yjs suppresses empty updates.
|
|
22338
|
+
*/
|
|
22339
|
+
get hasAppliedRemoteChanges() {
|
|
22340
|
+
return this._appliedRemoteChanges;
|
|
22341
|
+
}
|
|
22342
|
+
/**
|
|
22343
|
+
* Registers a callback invoked whenever a network provider applies changes to this document.
|
|
22344
|
+
* @returns A function that unregisters the callback.
|
|
22345
|
+
*/
|
|
22346
|
+
onRemoteChangesApplied(callback) {
|
|
22347
|
+
this._remoteChangesCallbacks.push(callback);
|
|
22348
|
+
return () => {
|
|
22349
|
+
this._remoteChangesCallbacks = this._remoteChangesCallbacks.filter(registered => registered !== callback);
|
|
22350
|
+
};
|
|
22351
|
+
}
|
|
22311
22352
|
/**
|
|
22312
22353
|
* Returns the Yjs Awareness instance, if a network provider is available.
|
|
22313
22354
|
*/
|
|
@@ -22550,6 +22591,22 @@ class KritzelObjectMap {
|
|
|
22550
22591
|
const providerList = this._providers.map(p => `${p.constructor.name} (${p.type})`).join(', ');
|
|
22551
22592
|
console.info(`[Kritzel] Workspace sync providers initialized for ${docName}: ${providerList || 'none'}`);
|
|
22552
22593
|
}
|
|
22594
|
+
// Registered before any provider connects so no remote update can be missed.
|
|
22595
|
+
for (const provider of this._providers) {
|
|
22596
|
+
if (provider.type === 'network' && provider.updateOrigin !== undefined) {
|
|
22597
|
+
this._remoteOrigins.add(provider.updateOrigin);
|
|
22598
|
+
}
|
|
22599
|
+
}
|
|
22600
|
+
this._docUpdateHandler = (_update, origin) => {
|
|
22601
|
+
if (!this._remoteOrigins.has(origin)) {
|
|
22602
|
+
return;
|
|
22603
|
+
}
|
|
22604
|
+
this._appliedRemoteChanges = true;
|
|
22605
|
+
for (const callback of [...this._remoteChangesCallbacks]) {
|
|
22606
|
+
callback();
|
|
22607
|
+
}
|
|
22608
|
+
};
|
|
22609
|
+
this._ydoc.on('update', this._docUpdateHandler);
|
|
22553
22610
|
// captureTimeout is effectively infinite — undo-step boundaries are
|
|
22554
22611
|
// marked explicitly via markUndoBoundary() in insert/remove/reset and
|
|
22555
22612
|
// at stroke/gesture boundaries in the tools. This prevents a single
|
|
@@ -22603,12 +22660,15 @@ class KritzelObjectMap {
|
|
|
22603
22660
|
if (showSyncProviderInfo && networkProviders.length > 0) {
|
|
22604
22661
|
console.info(`[Kritzel] Workspace network providers connecting in background: ${networkProviders.length}`);
|
|
22605
22662
|
}
|
|
22606
|
-
const networkConnectPromises = networkProviders.map(provider => provider
|
|
22663
|
+
const networkConnectPromises = networkProviders.map(provider => provider
|
|
22664
|
+
.connect()
|
|
22665
|
+
.then(() => provider.whenSynced?.())
|
|
22666
|
+
.catch(err => {
|
|
22607
22667
|
if (showSyncProviderInfo) {
|
|
22608
22668
|
console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
|
|
22609
22669
|
}
|
|
22610
22670
|
}));
|
|
22611
|
-
//
|
|
22671
|
+
// Settles once remote data has been reconciled, not merely once the transport connected
|
|
22612
22672
|
this._networkSyncPromise = networkConnectPromises.length > 0 ? Promise.allSettled(networkConnectPromises).then(() => undefined) : null;
|
|
22613
22673
|
this._isReady = true;
|
|
22614
22674
|
// Find the first provider that exposes awareness (network providers)
|
|
@@ -23266,6 +23326,13 @@ class KritzelObjectMap {
|
|
|
23266
23326
|
this._objectsMap.unobserve(this._objectsObserver);
|
|
23267
23327
|
this._objectsObserver = null;
|
|
23268
23328
|
}
|
|
23329
|
+
if (this._ydoc && this._docUpdateHandler) {
|
|
23330
|
+
this._ydoc.off('update', this._docUpdateHandler);
|
|
23331
|
+
}
|
|
23332
|
+
this._docUpdateHandler = null;
|
|
23333
|
+
this._remoteOrigins.clear();
|
|
23334
|
+
this._remoteChangesCallbacks = [];
|
|
23335
|
+
this._appliedRemoteChanges = false;
|
|
23269
23336
|
// Remove undo manager event listeners
|
|
23270
23337
|
if (this._undoManager) {
|
|
23271
23338
|
if (this._stackItemAddedHandler) {
|
|
@@ -23761,7 +23828,10 @@ class KritzelAppStateMap {
|
|
|
23761
23828
|
if (showSyncProviderInfo && networkProviders.length > 0) {
|
|
23762
23829
|
console.info(`[Kritzel] App-state network providers connecting in background: ${networkProviders.length}`);
|
|
23763
23830
|
}
|
|
23764
|
-
const networkConnectPromises = networkProviders.map(provider => provider
|
|
23831
|
+
const networkConnectPromises = networkProviders.map(provider => provider
|
|
23832
|
+
.connect()
|
|
23833
|
+
.then(() => provider.whenSynced?.())
|
|
23834
|
+
.catch(err => {
|
|
23765
23835
|
if (showSyncProviderInfo) {
|
|
23766
23836
|
console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
|
|
23767
23837
|
}
|
|
@@ -27865,7 +27935,7 @@ const KritzelEngine = class {
|
|
|
27865
27935
|
contextMenuStateChange;
|
|
27866
27936
|
/** Emitted when the combined loading state (external `isLoading` prop or internal workspace loading) changes. */
|
|
27867
27937
|
loadingChange;
|
|
27868
|
-
/** Emitted
|
|
27938
|
+
/** Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new. */
|
|
27869
27939
|
syncingChange;
|
|
27870
27940
|
forceUpdate = 0;
|
|
27871
27941
|
isSyncing = false;
|
|
@@ -29452,6 +29522,7 @@ const KritzelEngine = class {
|
|
|
29452
29522
|
_sceneBootstrapOpacity = 1;
|
|
29453
29523
|
_sceneBootstrapTransitionEnabled = false;
|
|
29454
29524
|
_sceneBootstrapTransitionTimer = null;
|
|
29525
|
+
_remoteChangesUnsubscribe = null;
|
|
29455
29526
|
_defaultUndoState = {
|
|
29456
29527
|
canUndo: false,
|
|
29457
29528
|
canRedo: false,
|
|
@@ -29469,9 +29540,16 @@ const KritzelEngine = class {
|
|
|
29469
29540
|
this.loadingChange.emit(this.core.store.state.isLoading);
|
|
29470
29541
|
}
|
|
29471
29542
|
syncSyncingState(isSyncing) {
|
|
29543
|
+
if (this.isSyncing === isSyncing) {
|
|
29544
|
+
return;
|
|
29545
|
+
}
|
|
29472
29546
|
this.isSyncing = isSyncing;
|
|
29473
29547
|
this.syncingChange.emit(isSyncing);
|
|
29474
29548
|
}
|
|
29549
|
+
clearRemoteChangesSubscription() {
|
|
29550
|
+
this._remoteChangesUnsubscribe?.();
|
|
29551
|
+
this._remoteChangesUnsubscribe = null;
|
|
29552
|
+
}
|
|
29475
29553
|
isNetworkAvailable() {
|
|
29476
29554
|
return typeof navigator !== 'undefined' && navigator.onLine;
|
|
29477
29555
|
}
|
|
@@ -29583,6 +29661,7 @@ const KritzelEngine = class {
|
|
|
29583
29661
|
disconnectedCallback() {
|
|
29584
29662
|
this.throttledPointerMoveMulti.cancel();
|
|
29585
29663
|
this._revealedImageKeys.clear();
|
|
29664
|
+
this.clearRemoteChangesSubscription();
|
|
29586
29665
|
if (this._sceneBootstrapTransitionTimer !== null) {
|
|
29587
29666
|
clearTimeout(this._sceneBootstrapTransitionTimer);
|
|
29588
29667
|
this._sceneBootstrapTransitionTimer = null;
|
|
@@ -29837,21 +29916,7 @@ const KritzelEngine = class {
|
|
|
29837
29916
|
this._workspaceInitializationTargetKey = targetKey;
|
|
29838
29917
|
try {
|
|
29839
29918
|
await initializationPromise;
|
|
29840
|
-
|
|
29841
|
-
const initializedObjects = this.core.store.objects;
|
|
29842
|
-
const networkSyncPromise = initializedObjects?.whenNetworkSynced();
|
|
29843
|
-
if (networkSyncPromise && this.isNetworkAvailable()) {
|
|
29844
|
-
this.syncSyncingState(true);
|
|
29845
|
-
void networkSyncPromise.finally(() => {
|
|
29846
|
-
// Only clear syncing for the currently active object map.
|
|
29847
|
-
if (this.core.store.objects === initializedObjects) {
|
|
29848
|
-
this.syncSyncingState(false);
|
|
29849
|
-
}
|
|
29850
|
-
});
|
|
29851
|
-
}
|
|
29852
|
-
else {
|
|
29853
|
-
this.syncSyncingState(false);
|
|
29854
|
-
}
|
|
29919
|
+
this.trackRemoteSynchronization();
|
|
29855
29920
|
}
|
|
29856
29921
|
finally {
|
|
29857
29922
|
// Only the most recent init clears loading state to prevent race conditions
|
|
@@ -29863,6 +29928,45 @@ const KritzelEngine = class {
|
|
|
29863
29928
|
}
|
|
29864
29929
|
}
|
|
29865
29930
|
}
|
|
29931
|
+
/**
|
|
29932
|
+
* Reports syncing only while remote data is actually being reconciled into the
|
|
29933
|
+
* active workspace. Yjs suppresses updates that carry nothing new, so a workspace
|
|
29934
|
+
* that already matches the remote never reports syncing at all.
|
|
29935
|
+
*/
|
|
29936
|
+
trackRemoteSynchronization() {
|
|
29937
|
+
this.clearRemoteChangesSubscription();
|
|
29938
|
+
this.syncSyncingState(false);
|
|
29939
|
+
const initializedObjects = this.core.store.objects;
|
|
29940
|
+
if (!initializedObjects?.hasNetworkSyncProvider || !this.isNetworkAvailable()) {
|
|
29941
|
+
return;
|
|
29942
|
+
}
|
|
29943
|
+
if (initializedObjects.supportsRemoteChangeDetection) {
|
|
29944
|
+
this._remoteChangesUnsubscribe = initializedObjects.onRemoteChangesApplied(() => {
|
|
29945
|
+
if (this.core.store.objects !== initializedObjects) {
|
|
29946
|
+
return;
|
|
29947
|
+
}
|
|
29948
|
+
this.syncSyncingState(true);
|
|
29949
|
+
});
|
|
29950
|
+
// The network provider may have already applied remote changes between
|
|
29951
|
+
// object-map initialization and this subscription (e.g. an already-connected
|
|
29952
|
+
// shared socket resolves the sync handshake almost immediately). Without this
|
|
29953
|
+
// check that update is missed entirely and the indicator never appears.
|
|
29954
|
+
if (initializedObjects.hasAppliedRemoteChanges) {
|
|
29955
|
+
this.syncSyncingState(true);
|
|
29956
|
+
}
|
|
29957
|
+
}
|
|
29958
|
+
else {
|
|
29959
|
+
// Custom providers without an update origin cannot be diffed, so fall back to reporting the whole handshake.
|
|
29960
|
+
this.syncSyncingState(true);
|
|
29961
|
+
}
|
|
29962
|
+
void initializedObjects.whenNetworkSynced().finally(() => {
|
|
29963
|
+
if (this.core.store.objects !== initializedObjects) {
|
|
29964
|
+
return;
|
|
29965
|
+
}
|
|
29966
|
+
this.clearRemoteChangesSubscription();
|
|
29967
|
+
this.syncSyncingState(false);
|
|
29968
|
+
});
|
|
29969
|
+
}
|
|
29866
29970
|
emitObjectsChange() {
|
|
29867
29971
|
const objectsMap = this.core.store.objects;
|
|
29868
29972
|
if (!objectsMap) {
|
|
@@ -32007,7 +32111,7 @@ const KritzelPortal = class {
|
|
|
32007
32111
|
* This file is auto-generated by the version bump scripts.
|
|
32008
32112
|
* Do not modify manually.
|
|
32009
32113
|
*/
|
|
32010
|
-
const KRITZEL_VERSION = '0.4.
|
|
32114
|
+
const KRITZEL_VERSION = '0.4.23';
|
|
32011
32115
|
|
|
32012
32116
|
const kritzelSettingsCss = () => `:host{display:contents}kritzel-dialog{--kritzel-dialog-body-padding:0;--kritzel-dialog-width-large:800px;--kritzel-dialog-height-large:500px}.footer-button{padding:8px 16px;border-radius:6px;cursor:pointer;font-size:14px}.cancel-button{border:1px solid #ebebeb;background:#fff;color:inherit}.cancel-button:hover{background:#f5f5f5}.settings-tab-panel{display:none}.settings-tab-panel.is-active{display:block}.settings-content{padding:0}.settings-content h3{margin:0 0 16px 0;font-size:18px;font-weight:600;color:var(--kritzel-settings-content-heading-color, #333333)}.settings-content p{margin:0;font-size:14px;color:var(--kritzel-settings-content-text-color, #666666);line-height:1.5}.settings-group{display:flex;flex-direction:column;gap:24px}.settings-item{display:flex;flex-direction:column;gap:8px}.settings-row{display:flex;align-items:center;justify-content:space-between;gap:16px}.settings-label{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.settings-description{font-size:12px;color:var(--kritzel-settings-description-color, #888888);margin:0;line-height:1.4}.shortcuts-list{display:flex;flex-direction:column;gap:24px}.shortcuts-category{display:flex;flex-direction:column;gap:8px}.shortcuts-category-title{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.shortcuts-group{display:flex;flex-direction:column;gap:4px}.shortcut-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-item-bg, rgba(0, 0, 0, 0.02))}.shortcut-label{font-size:14px;color:var(--kritzel-settings-content-text-color, #666666)}.shortcut-key{font-family:monospace;font-size:12px;padding:2px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-key-bg, #f0f0f0);color:var(--kritzel-settings-shortcut-key-color, #333333);border:1px solid var(--kritzel-settings-shortcut-key-border, #ddd)}`;
|
|
32013
32117
|
|
|
@@ -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
|
|