kritzel-stencil 0.4.21 → 0.4.22

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.
Files changed (29) hide show
  1. package/dist/cjs/index.cjs.js +50 -1
  2. package/dist/cjs/kritzel-active-users_47.cjs.entry.js +128 -31
  3. package/dist/collection/classes/providers/sync/hocuspocus-sync-provider.class.js +21 -0
  4. package/dist/collection/classes/providers/sync/websocket-sync-provider.class.js +29 -1
  5. package/dist/collection/classes/structures/app-state-map.structure.js +4 -1
  6. package/dist/collection/classes/structures/object-map.structure.js +66 -4
  7. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +42 -9
  8. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +44 -17
  9. package/dist/collection/constants/version.js +1 -1
  10. package/dist/components/index.js +51 -2
  11. package/dist/components/kritzel-editor.js +16 -11
  12. package/dist/components/kritzel-engine.js +1 -1
  13. package/dist/components/kritzel-settings.js +1 -1
  14. package/dist/components/{p-C_f_QElb.js → p-BNeE3SiZ.js} +113 -21
  15. package/dist/components/{p-BdCrcQ0_.js → p-Dx5NfJ0U.js} +1 -1
  16. package/dist/esm/index.js +50 -1
  17. package/dist/esm/kritzel-active-users_47.entry.js +128 -31
  18. package/dist/stencil/index.esm.js +50 -1
  19. package/dist/stencil/{p-832b80e9.entry.js → p-ab5c4292.entry.js} +128 -31
  20. package/dist/stencil/stencil.esm.js +1 -1
  21. package/dist/types/classes/providers/sync/hocuspocus-sync-provider.class.d.ts +5 -0
  22. package/dist/types/classes/providers/sync/websocket-sync-provider.class.d.ts +6 -0
  23. package/dist/types/classes/structures/object-map.structure.d.ts +26 -2
  24. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +3 -0
  25. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +9 -1
  26. package/dist/types/components.d.ts +8 -1
  27. package/dist/types/constants/version.d.ts +1 -1
  28. package/dist/types/interfaces/sync-provider.interface.d.ts +13 -0
  29. package/package.json +1 -1
@@ -37,6 +37,10 @@ export class KritzelObjectMap {
37
37
  _awarenessChangeHandler = null;
38
38
  _awarenessChangeCallbacks = [];
39
39
  _objectsChangeCallbacks = [];
40
+ _remoteChangesCallbacks = [];
41
+ _docUpdateHandler = null;
42
+ _remoteOrigins = new Set();
43
+ _appliedRemoteChanges = false;
40
44
  _lastAwarenessEmitTime = 0;
41
45
  _awarenessEmitTimeout = null;
42
46
  AWARENESS_THROTTLE_INTERVAL = 100; // milliseconds
@@ -60,12 +64,44 @@ export class KritzelObjectMap {
60
64
  return this._isReady;
61
65
  }
62
66
  /**
63
- * Promise that settles when network providers finish connecting.
64
- * Resolves immediately if no network providers. Awaited by the loading overlay.
67
+ * Promise that settles when network providers finish connecting and complete
68
+ * their Yjs sync handshake. Resolves immediately if no network providers.
65
69
  */
66
70
  whenNetworkSynced() {
67
71
  return this._networkSyncPromise ?? Promise.resolve();
68
72
  }
73
+ /**
74
+ * Whether any configured provider synchronizes over the network.
75
+ */
76
+ get hasNetworkSyncProvider() {
77
+ return this._providers.some(provider => provider.type === 'network');
78
+ }
79
+ /**
80
+ * Whether remote-originated changes can be told apart from local writes.
81
+ * Requires every network provider to expose its Yjs update origin.
82
+ */
83
+ get supportsRemoteChangeDetection() {
84
+ const networkProviders = this._providers.filter(provider => provider.type === 'network');
85
+ return networkProviders.length > 0 && networkProviders.every(provider => provider.updateOrigin !== undefined);
86
+ }
87
+ /**
88
+ * Whether at least one update originating from a network provider has been
89
+ * applied to this document. Stays `false` when the remote held no data the
90
+ * local document was missing, because Yjs suppresses empty updates.
91
+ */
92
+ get hasAppliedRemoteChanges() {
93
+ return this._appliedRemoteChanges;
94
+ }
95
+ /**
96
+ * Registers a callback invoked whenever a network provider applies changes to this document.
97
+ * @returns A function that unregisters the callback.
98
+ */
99
+ onRemoteChangesApplied(callback) {
100
+ this._remoteChangesCallbacks.push(callback);
101
+ return () => {
102
+ this._remoteChangesCallbacks = this._remoteChangesCallbacks.filter(registered => registered !== callback);
103
+ };
104
+ }
69
105
  /**
70
106
  * Returns the Yjs Awareness instance, if a network provider is available.
71
107
  */
@@ -308,6 +344,22 @@ export class KritzelObjectMap {
308
344
  const providerList = this._providers.map(p => `${p.constructor.name} (${p.type})`).join(', ');
309
345
  console.info(`[Kritzel] Workspace sync providers initialized for ${docName}: ${providerList || 'none'}`);
310
346
  }
347
+ // Registered before any provider connects so no remote update can be missed.
348
+ for (const provider of this._providers) {
349
+ if (provider.type === 'network' && provider.updateOrigin !== undefined) {
350
+ this._remoteOrigins.add(provider.updateOrigin);
351
+ }
352
+ }
353
+ this._docUpdateHandler = (_update, origin) => {
354
+ if (!this._remoteOrigins.has(origin)) {
355
+ return;
356
+ }
357
+ this._appliedRemoteChanges = true;
358
+ for (const callback of [...this._remoteChangesCallbacks]) {
359
+ callback();
360
+ }
361
+ };
362
+ this._ydoc.on('update', this._docUpdateHandler);
311
363
  // captureTimeout is effectively infinite — undo-step boundaries are
312
364
  // marked explicitly via markUndoBoundary() in insert/remove/reset and
313
365
  // at stroke/gesture boundaries in the tools. This prevents a single
@@ -361,12 +413,15 @@ export class KritzelObjectMap {
361
413
  if (showSyncProviderInfo && networkProviders.length > 0) {
362
414
  console.info(`[Kritzel] Workspace network providers connecting in background: ${networkProviders.length}`);
363
415
  }
364
- const networkConnectPromises = networkProviders.map(provider => provider.connect().catch(err => {
416
+ const networkConnectPromises = networkProviders.map(provider => provider
417
+ .connect()
418
+ .then(() => provider.whenSynced?.())
419
+ .catch(err => {
365
420
  if (showSyncProviderInfo) {
366
421
  console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
367
422
  }
368
423
  }));
369
- // Network sync awaited by loading overlay to show remote workspace hydration
424
+ // Settles once remote data has been reconciled, not merely once the transport connected
370
425
  this._networkSyncPromise = networkConnectPromises.length > 0 ? Promise.allSettled(networkConnectPromises).then(() => undefined) : null;
371
426
  this._isReady = true;
372
427
  // Find the first provider that exposes awareness (network providers)
@@ -1031,6 +1086,13 @@ export class KritzelObjectMap {
1031
1086
  this._objectsMap.unobserve(this._objectsObserver);
1032
1087
  this._objectsObserver = null;
1033
1088
  }
1089
+ if (this._ydoc && this._docUpdateHandler) {
1090
+ this._ydoc.off('update', this._docUpdateHandler);
1091
+ }
1092
+ this._docUpdateHandler = null;
1093
+ this._remoteOrigins.clear();
1094
+ this._remoteChangesCallbacks = [];
1095
+ this._appliedRemoteChanges = false;
1034
1096
  // Remove undo manager event listeners
1035
1097
  if (this._undoManager) {
1036
1098
  if (this._stackItemAddedHandler) {
@@ -266,6 +266,9 @@ export class KritzelEditor {
266
266
  login;
267
267
  isPublicChange;
268
268
  awarenessChange;
269
+ /** Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new. */
270
+ syncingChange;
271
+ loadingChange;
269
272
  isEngineReady = false;
270
273
  isControlsReady = false;
271
274
  isWorkspaceManagerReady = false;
@@ -1084,40 +1087,40 @@ export class KritzelEditor {
1084
1087
  const isLoggedIn = this.isLoggedIn;
1085
1088
  const shouldShowCurrentUser = isLoggedIn;
1086
1089
  const shouldShowLoginButton = this.isReady && !!this.loginConfig && !isLoggedIn;
1087
- return (h(Host, { key: 'd9d87b5ca40972d7ab2c5986f340278347f78c6e' }, h("div", { key: 'd8da2ed24d19c550420689c6720dc491fe0d3a22', class: "editor-content", style: {
1090
+ return (h(Host, { key: 'ff7d505697bd4c4bd840dadcb04a934b7e243a01' }, h("div", { key: 'b40076c3c79ad3f879e80c58b412b79266de2ea8', class: "editor-content", style: {
1088
1091
  opacity: this.isEditorVisible ? '1' : '0',
1089
1092
  visibility: this.isEditorVisible ? 'visible' : 'hidden',
1090
1093
  transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
1091
- } }, h("div", { key: '82a5d216e7c95307b1bdbd1f06fdfe881c1c50d4', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: 'de13673384b44bb6bd309a927a2866a9b78b519e', 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: 'f68610d7d91713f7a0ec5f0135d208769f7fab9c', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), this.activeNotification && (h("div", { key: '9d1f972fb67050b1f996e3088710713fa1219b9a', class: "top-center-notification-layer", role: "presentation" }, h("div", { key: '295eff0a02f7cc79ad8e80cda23baa3c576ce156', class: { 'top-center-notification-container': true, 'is-dismissing': this.isNotificationDismissing }, role: "presentation" }, h("kritzel-notification-card", { key: '1d1bf140ddfb7b927d30fb1d77e7b887240d7deb', 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: '1062bc9554e5834ef2002fbfe953f7938866fb84', class: "context-menu", items: this.contextMenuState.items, objects: this.contextMenuState.objects, style: {
1094
+ } }, 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: {
1092
1095
  position: 'absolute',
1093
1096
  left: `${this.contextMenuState.position.x}px`,
1094
1097
  top: `${this.contextMenuState.position.y}px`,
1095
1098
  zIndex: '10002',
1096
- }, onActionSelected: event => this.handleContextMenuActionSelected(event), onClose: () => this.hideContextMenu() })), h("kritzel-sync-indicator", { key: '0094a026828473988f857d82cfccb811f36f049c', visible: this.isSyncing, text: "Syncing..." }), h("kritzel-loading-overlay", { key: '680096e20b6175104a866b9a92ca4c1d0087c93a', visible: this.isLoadingOverlayVisible, text: this.resolvedTerms['editor.loading'] ?? 'Loading...' }), h("kritzel-engine", { key: '0698a44fcbf2d6ea786d53502350ac0c9ffb9d66', ref: el => {
1099
+ }, 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 => {
1097
1100
  if (el) {
1098
1101
  this.engineRef = el;
1099
1102
  }
1100
- }, 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: '765a670807111ad68e9f8d14bdca2651a33704d4', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
1103
+ }, 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 => {
1101
1104
  if (el) {
1102
1105
  this.controlsRef = el;
1103
1106
  }
1104
- }, controls: this.normalizedControls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: 'e7c5ebab4a40b7ab4fa5d19d203129326c0009ed', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: '0aa3891b553d2abcd06e635227fd6bb6656ed739', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: '824244bbd78784c1d78ab89cc12e206ca61d84cc', class: "top-right-buttons" }, h("kritzel-settings", { key: 'baf4830b6b9ccb6241b1a1baa5cc6ff9d0295fb9', ref: el => {
1107
+ }, 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 => {
1105
1108
  if (el) {
1106
1109
  this.settingsRef = el;
1107
1110
  }
1108
- }, 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: '57e2682450c71803cd49bfe65f16e02d9d3358bc', ref: el => {
1111
+ }, 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 => {
1109
1112
  if (el) {
1110
1113
  this.exportRef = el;
1111
1114
  }
1112
- }, 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: '955d2a61b98d937f8eec39ab19f1b73e702c9fd7', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: 'c0885e0c8cab1a4cb50c8f6d4b17e03f32cc2b87', user: this.user, terms: this.resolvedTerms, onClick: () => this.currentUserDialogRef?.open() }), shouldShowCurrentUser && (h("kritzel-current-user-dialog", { key: 'fc70555061d2aeaebdbe981bdbe8cf501bd8120e', ref: el => {
1115
+ }, 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 => {
1113
1116
  if (el) {
1114
1117
  this.currentUserDialogRef = el;
1115
1118
  }
1116
- }, user: this.user, terms: this.resolvedTerms, onLogoutRequest: this.handleCurrentUserLogout })), shouldShowLoginButton && h("kritzel-button", { key: '9666163a2a36a64ecb641659dca515ae30409d0f', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in'), h("kritzel-more-menu", { key: '167ac17ea93897597e83d4fe8b3b561fac848bb6', items: this.resolvedMoreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: '10cfb0b794d613ec39299f06ac1a3402d1ad3084', ref: el => {
1119
+ }, 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 => {
1117
1120
  if (el) {
1118
1121
  this.shareDialogRef = el;
1119
1122
  }
1120
- }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '99dcb9051382982ec5a8975bdd991bae57745bb9', ref: el => {
1123
+ }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'a7d52bad9e752ad546adf80d257c6a46de031e76', ref: el => {
1121
1124
  if (el) {
1122
1125
  this.loginDialogRef = el;
1123
1126
  }
@@ -2290,6 +2293,36 @@ export class KritzelEditor {
2290
2293
  }
2291
2294
  }
2292
2295
  }
2296
+ }, {
2297
+ "method": "syncingChange",
2298
+ "name": "syncingChange",
2299
+ "bubbles": true,
2300
+ "cancelable": true,
2301
+ "composed": true,
2302
+ "docs": {
2303
+ "tags": [],
2304
+ "text": "Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new."
2305
+ },
2306
+ "complexType": {
2307
+ "original": "boolean",
2308
+ "resolved": "boolean",
2309
+ "references": {}
2310
+ }
2311
+ }, {
2312
+ "method": "loadingChange",
2313
+ "name": "loadingChange",
2314
+ "bubbles": true,
2315
+ "cancelable": true,
2316
+ "composed": true,
2317
+ "docs": {
2318
+ "tags": [],
2319
+ "text": ""
2320
+ },
2321
+ "complexType": {
2322
+ "original": "boolean",
2323
+ "resolved": "boolean",
2324
+ "references": {}
2325
+ }
2293
2326
  }];
2294
2327
  }
2295
2328
  static get methods() {
@@ -258,7 +258,7 @@ export class KritzelEngine {
258
258
  contextMenuStateChange;
259
259
  /** Emitted when the combined loading state (external `isLoading` prop or internal workspace loading) changes. */
260
260
  loadingChange;
261
- /** Emitted when remote workspace synchronization is in progress. */
261
+ /** Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new. */
262
262
  syncingChange;
263
263
  forceUpdate = 0;
264
264
  isSyncing = false;
@@ -1845,6 +1845,7 @@ export class KritzelEngine {
1845
1845
  _sceneBootstrapOpacity = 1;
1846
1846
  _sceneBootstrapTransitionEnabled = false;
1847
1847
  _sceneBootstrapTransitionTimer = null;
1848
+ _remoteChangesUnsubscribe = null;
1848
1849
  _defaultUndoState = {
1849
1850
  canUndo: false,
1850
1851
  canRedo: false,
@@ -1862,9 +1863,16 @@ export class KritzelEngine {
1862
1863
  this.loadingChange.emit(this.core.store.state.isLoading);
1863
1864
  }
1864
1865
  syncSyncingState(isSyncing) {
1866
+ if (this.isSyncing === isSyncing) {
1867
+ return;
1868
+ }
1865
1869
  this.isSyncing = isSyncing;
1866
1870
  this.syncingChange.emit(isSyncing);
1867
1871
  }
1872
+ clearRemoteChangesSubscription() {
1873
+ this._remoteChangesUnsubscribe?.();
1874
+ this._remoteChangesUnsubscribe = null;
1875
+ }
1868
1876
  isNetworkAvailable() {
1869
1877
  return typeof navigator !== 'undefined' && navigator.onLine;
1870
1878
  }
@@ -1957,6 +1965,7 @@ export class KritzelEngine {
1957
1965
  disconnectedCallback() {
1958
1966
  this.throttledPointerMoveMulti.cancel();
1959
1967
  this._revealedImageKeys.clear();
1968
+ this.clearRemoteChangesSubscription();
1960
1969
  if (this._sceneBootstrapTransitionTimer !== null) {
1961
1970
  clearTimeout(this._sceneBootstrapTransitionTimer);
1962
1971
  this._sceneBootstrapTransitionTimer = null;
@@ -2211,21 +2220,7 @@ export class KritzelEngine {
2211
2220
  this._workspaceInitializationTargetKey = targetKey;
2212
2221
  try {
2213
2222
  await initializationPromise;
2214
- // Track remote sync in the background without blocking the loading overlay.
2215
- const initializedObjects = this.core.store.objects;
2216
- const networkSyncPromise = initializedObjects?.whenNetworkSynced();
2217
- if (networkSyncPromise && this.isNetworkAvailable()) {
2218
- this.syncSyncingState(true);
2219
- void networkSyncPromise.finally(() => {
2220
- // Only clear syncing for the currently active object map.
2221
- if (this.core.store.objects === initializedObjects) {
2222
- this.syncSyncingState(false);
2223
- }
2224
- });
2225
- }
2226
- else {
2227
- this.syncSyncingState(false);
2228
- }
2223
+ this.trackRemoteSynchronization();
2229
2224
  }
2230
2225
  finally {
2231
2226
  // Only the most recent init clears loading state to prevent race conditions
@@ -2237,6 +2232,38 @@ export class KritzelEngine {
2237
2232
  }
2238
2233
  }
2239
2234
  }
2235
+ /**
2236
+ * Reports syncing only while remote data is actually being reconciled into the
2237
+ * active workspace. Yjs suppresses updates that carry nothing new, so a workspace
2238
+ * that already matches the remote never reports syncing at all.
2239
+ */
2240
+ trackRemoteSynchronization() {
2241
+ this.clearRemoteChangesSubscription();
2242
+ this.syncSyncingState(false);
2243
+ const initializedObjects = this.core.store.objects;
2244
+ if (!initializedObjects?.hasNetworkSyncProvider || !this.isNetworkAvailable()) {
2245
+ return;
2246
+ }
2247
+ if (initializedObjects.supportsRemoteChangeDetection) {
2248
+ this._remoteChangesUnsubscribe = initializedObjects.onRemoteChangesApplied(() => {
2249
+ if (this.core.store.objects !== initializedObjects) {
2250
+ return;
2251
+ }
2252
+ this.syncSyncingState(true);
2253
+ });
2254
+ }
2255
+ else {
2256
+ // Custom providers without an update origin cannot be diffed, so fall back to reporting the whole handshake.
2257
+ this.syncSyncingState(true);
2258
+ }
2259
+ void initializedObjects.whenNetworkSynced().finally(() => {
2260
+ if (this.core.store.objects !== initializedObjects) {
2261
+ return;
2262
+ }
2263
+ this.clearRemoteChangesSubscription();
2264
+ this.syncSyncingState(false);
2265
+ });
2266
+ }
2240
2267
  emitObjectsChange() {
2241
2268
  const objectsMap = this.core.store.objects;
2242
2269
  if (!objectsMap) {
@@ -3860,7 +3887,7 @@ export class KritzelEngine {
3860
3887
  "composed": true,
3861
3888
  "docs": {
3862
3889
  "tags": [],
3863
- "text": "Emitted when remote workspace synchronization is in progress."
3890
+ "text": "Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new."
3864
3891
  },
3865
3892
  "complexType": {
3866
3893
  "original": "boolean",
@@ -3,4 +3,4 @@
3
3
  * This file is auto-generated by the version bump scripts.
4
4
  * Do not modify manually.
5
5
  */
6
- export const KRITZEL_VERSION = '0.4.21';
6
+ export const KRITZEL_VERSION = '0.4.22';
@@ -1,6 +1,6 @@
1
1
  export { g as getAssetPath, r as render, s as setAssetPath, a as setNonce, b as setPlatformOptions } from './p-B43upypT.js';
2
2
  export { K as KritzelBaseObject, b as KritzelLine, a as KritzelPath } from './p-bAacqNHq.js';
3
- export { u as APP_STATE_MIGRATIONS, A as AssetNotFoundError, D as DE_LOCALE, E as EN_LOCALE, F as FR_LOCALE, I as IndexedDBAssetProvider, s as KritzelAlignment, o as KritzelAnchorManager, n as KritzelAssetResolver, e as KritzelBaseTool, f as KritzelBrushTool, l as KritzelCursorHelper, d as KritzelCustomElement, r as KritzelCustomElementRendererRegistry, h as KritzelEraserTool, b as KritzelGroup, a as KritzelImage, i as KritzelImageTool, q as KritzelLicenseManager, g as KritzelLineTool, p as KritzelLocalizationManager, m as KritzelSelectionTool, c as KritzelShape, k as KritzelShapeTool, K as KritzelText, j as KritzelTextTool, S as ShapeType, W as WORKSPACE_MIGRATIONS, t as runMigrations } from './p-C_f_QElb.js';
3
+ export { u as APP_STATE_MIGRATIONS, A as AssetNotFoundError, D as DE_LOCALE, E as EN_LOCALE, F as FR_LOCALE, I as IndexedDBAssetProvider, s as KritzelAlignment, o as KritzelAnchorManager, n as KritzelAssetResolver, e as KritzelBaseTool, f as KritzelBrushTool, l as KritzelCursorHelper, d as KritzelCustomElement, r as KritzelCustomElementRendererRegistry, h as KritzelEraserTool, b as KritzelGroup, a as KritzelImage, i as KritzelImageTool, q as KritzelLicenseManager, g as KritzelLineTool, p as KritzelLocalizationManager, m as KritzelSelectionTool, c as KritzelShape, k as KritzelShapeTool, K as KritzelText, j as KritzelTextTool, S as ShapeType, W as WORKSPACE_MIGRATIONS, t as runMigrations } from './p-BNeE3SiZ.js';
4
4
  import * as Y from 'yjs';
5
5
  import { IndexeddbPersistence } from 'y-indexeddb';
6
6
  import { WebsocketProvider } from 'y-websocket';
@@ -603,9 +603,15 @@ class WebSocketSyncProvider {
603
603
  provider;
604
604
  isConnected = false;
605
605
  _quiet = false;
606
+ _isSynced = false;
607
+ _syncedResolvers = [];
606
608
  get awareness() {
607
609
  return this.provider.awareness;
608
610
  }
611
+ /** y-websocket applies remote updates with the underlying provider as the Yjs transaction origin. */
612
+ get updateOrigin() {
613
+ return this.provider;
614
+ }
609
615
  constructor(docName, doc, options) {
610
616
  const url = options?.url || 'ws://localhost:1234';
611
617
  const roomName = options?.roomName || docName;
@@ -652,11 +658,29 @@ class WebSocketSyncProvider {
652
658
  }
653
659
  });
654
660
  this.provider.on('sync', (synced) => {
655
- if (synced && !this._quiet) {
661
+ if (!synced) {
662
+ return;
663
+ }
664
+ this._isSynced = true;
665
+ this.releaseSyncedWaiters();
666
+ if (!this._quiet) {
656
667
  console.info('WebSocket synced');
657
668
  }
658
669
  });
659
670
  }
671
+ releaseSyncedWaiters() {
672
+ const resolvers = this._syncedResolvers;
673
+ this._syncedResolvers = [];
674
+ resolvers.forEach(resolve => resolve());
675
+ }
676
+ async whenSynced() {
677
+ if (this._isSynced || this.provider.synced) {
678
+ return;
679
+ }
680
+ return new Promise(resolve => {
681
+ this._syncedResolvers.push(resolve);
682
+ });
683
+ }
660
684
  async connect() {
661
685
  if (this.isConnected) {
662
686
  return;
@@ -688,6 +712,8 @@ class WebSocketSyncProvider {
688
712
  this.provider.disconnect();
689
713
  }
690
714
  this.isConnected = false;
715
+ this._isSynced = false;
716
+ this.releaseSyncedWaiters();
691
717
  }
692
718
  async reconnect() {
693
719
  this.disconnect();
@@ -698,6 +724,8 @@ class WebSocketSyncProvider {
698
724
  this.provider.destroy();
699
725
  }
700
726
  this.isConnected = false;
727
+ this._isSynced = false;
728
+ this.releaseSyncedWaiters();
701
729
  }
702
730
  }
703
731
 
@@ -719,9 +747,14 @@ class HocuspocusSyncProvider {
719
747
  _connectionStatus = 'disconnected';
720
748
  visibilityHandler = null;
721
749
  onlineHandler = null;
750
+ syncedResolvers = [];
722
751
  get awareness() {
723
752
  return this.provider.awareness;
724
753
  }
754
+ /** HocuspocusProvider applies remote updates with itself as the Yjs transaction origin. */
755
+ get updateOrigin() {
756
+ return this.provider;
757
+ }
725
758
  get connectionStatus() {
726
759
  return this._connectionStatus;
727
760
  }
@@ -778,6 +811,7 @@ class HocuspocusSyncProvider {
778
811
  }
779
812
  this.isSynced = true;
780
813
  this._connectionStatus = 'synced';
814
+ this.releaseSyncedWaiters();
781
815
  if (!options?.quiet) {
782
816
  console.info(`Hocuspocus synced: ${name}`);
783
817
  }
@@ -988,6 +1022,19 @@ class HocuspocusSyncProvider {
988
1022
  this.disconnect();
989
1023
  return this.connect();
990
1024
  }
1025
+ releaseSyncedWaiters() {
1026
+ const resolvers = this.syncedResolvers;
1027
+ this.syncedResolvers = [];
1028
+ resolvers.forEach(resolve => resolve());
1029
+ }
1030
+ async whenSynced() {
1031
+ if (this.isSynced || this.isDestroyed || this.provider.isSynced) {
1032
+ return;
1033
+ }
1034
+ return new Promise(resolve => {
1035
+ this.syncedResolvers.push(resolve);
1036
+ });
1037
+ }
991
1038
  disconnect() {
992
1039
  // Cancel any pending connection attempt
993
1040
  if (this.connectTimeout) {
@@ -1009,6 +1056,7 @@ class HocuspocusSyncProvider {
1009
1056
  this.isConnected = false;
1010
1057
  this.isSynced = false;
1011
1058
  this._connectionStatus = 'disconnected';
1059
+ this.releaseSyncedWaiters();
1012
1060
  }
1013
1061
  destroy() {
1014
1062
  // Mark as destroyed first to prevent any callbacks from doing work
@@ -1028,6 +1076,7 @@ class HocuspocusSyncProvider {
1028
1076
  this.isConnected = false;
1029
1077
  this.isSynced = false;
1030
1078
  this._connectionStatus = 'disconnected';
1079
+ this.releaseSyncedWaiters();
1031
1080
  }
1032
1081
  }
1033
1082
 
@@ -1,6 +1,6 @@
1
1
  import { p as proxyCustomElement, H, c as createEvent, h, d as Host, t as transformTag } from './p-B43upypT.js';
2
2
  import { K as KritzelIconRegistry, d as defineCustomElement$s } from './p-CSuU8frA.js';
3
- import { v as DEFAULT_STROKE_SIZES, w as DEFAULT_FONT_SIZES, S as ShapeType, m as KritzelSelectionTool, f as KritzelBrushTool, h as KritzelEraserTool, g as KritzelLineTool, k as KritzelShapeTool, j as KritzelTextTool, i as KritzelImageTool, s as KritzelAlignment, x as DEFAULT_SYNC_CONFIG, y as KritzelSelectionGroup, z as KritzelSelectionBox, B as KritzelKeyboardHelper, C as defineCustomElement$x } from './p-C_f_QElb.js';
3
+ import { v as DEFAULT_STROKE_SIZES, w as DEFAULT_FONT_SIZES, S as ShapeType, m as KritzelSelectionTool, f as KritzelBrushTool, h as KritzelEraserTool, g as KritzelLineTool, k as KritzelShapeTool, j as KritzelTextTool, i as KritzelImageTool, s as KritzelAlignment, x as DEFAULT_SYNC_CONFIG, y as KritzelSelectionGroup, z as KritzelSelectionBox, B as KritzelKeyboardHelper, C as defineCustomElement$x } from './p-BNeE3SiZ.js';
4
4
  import { D as DEFAULT_COLOR_PALETTE, T as ThemeHelper, d as darkTheme, l as lightTheme } from './p-7I4Uzjrw.js';
5
5
  import { D as DEFAULT_TEXT_TOOL_AVAILABLE_FONTS, r as resolveTextToolAvailableFonts, K as KritzelFontRegistry } from './p-DJkKlUyZ.js';
6
6
  import { A as ABSOLUTE_SCALE_MAX, a as ABSOLUTE_SCALE_MIN, d as defineCustomElement$4 } from './p-DtnMdWWB.js';
@@ -39,7 +39,7 @@ import { d as defineCustomElement$i } from './p-DKGg-76-.js';
39
39
  import { d as defineCustomElement$h } from './p-CrHvaLRV.js';
40
40
  import { d as defineCustomElement$g } from './p-jIKskj3S.js';
41
41
  import { d as defineCustomElement$f } from './p-BPsKYMH6.js';
42
- import { d as defineCustomElement$e } from './p-BdCrcQ0_.js';
42
+ import { d as defineCustomElement$e } from './p-Dx5NfJ0U.js';
43
43
  import { d as defineCustomElement$d } from './p-BFYtCsZu.js';
44
44
  import { d as defineCustomElement$c } from './p-CwZ3KKFq.js';
45
45
  import { d as defineCustomElement$b } from './p-BozrueXV.js';
@@ -2957,6 +2957,8 @@ const KritzelEditor$1 = /*@__PURE__*/ proxyCustomElement(class KritzelEditor ext
2957
2957
  this.login = createEvent(this, "login");
2958
2958
  this.isPublicChange = createEvent(this, "isPublicChange");
2959
2959
  this.awarenessChange = createEvent(this, "awarenessChange");
2960
+ this.syncingChange = createEvent(this, "syncingChange");
2961
+ this.loadingChange = createEvent(this, "loadingChange");
2960
2962
  }
2961
2963
  get host() { return this; }
2962
2964
  scaleMax = ABSOLUTE_SCALE_MAX;
@@ -3196,6 +3198,9 @@ const KritzelEditor$1 = /*@__PURE__*/ proxyCustomElement(class KritzelEditor ext
3196
3198
  login;
3197
3199
  isPublicChange;
3198
3200
  awarenessChange;
3201
+ /** Emitted while remote changes are being applied to the active workspace. Stays silent when the remote holds nothing new. */
3202
+ syncingChange;
3203
+ loadingChange;
3199
3204
  isEngineReady = false;
3200
3205
  isControlsReady = false;
3201
3206
  isWorkspaceManagerReady = false;
@@ -4014,40 +4019,40 @@ const KritzelEditor$1 = /*@__PURE__*/ proxyCustomElement(class KritzelEditor ext
4014
4019
  const isLoggedIn = this.isLoggedIn;
4015
4020
  const shouldShowCurrentUser = isLoggedIn;
4016
4021
  const shouldShowLoginButton = this.isReady && !!this.loginConfig && !isLoggedIn;
4017
- return (h(Host, { key: 'd9d87b5ca40972d7ab2c5986f340278347f78c6e' }, h("div", { key: 'd8da2ed24d19c550420689c6720dc491fe0d3a22', class: "editor-content", style: {
4022
+ return (h(Host, { key: 'ff7d505697bd4c4bd840dadcb04a934b7e243a01' }, h("div", { key: 'b40076c3c79ad3f879e80c58b412b79266de2ea8', class: "editor-content", style: {
4018
4023
  opacity: this.isEditorVisible ? '1' : '0',
4019
4024
  visibility: this.isEditorVisible ? 'visible' : 'hidden',
4020
4025
  transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
4021
- } }, h("div", { key: '82a5d216e7c95307b1bdbd1f06fdfe881c1c50d4', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: 'de13673384b44bb6bd309a927a2866a9b78b519e', 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: 'f68610d7d91713f7a0ec5f0135d208769f7fab9c', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), this.activeNotification && (h("div", { key: '9d1f972fb67050b1f996e3088710713fa1219b9a', class: "top-center-notification-layer", role: "presentation" }, h("div", { key: '295eff0a02f7cc79ad8e80cda23baa3c576ce156', class: { 'top-center-notification-container': true, 'is-dismissing': this.isNotificationDismissing }, role: "presentation" }, h("kritzel-notification-card", { key: '1d1bf140ddfb7b927d30fb1d77e7b887240d7deb', 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: '1062bc9554e5834ef2002fbfe953f7938866fb84', class: "context-menu", items: this.contextMenuState.items, objects: this.contextMenuState.objects, style: {
4026
+ } }, 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: {
4022
4027
  position: 'absolute',
4023
4028
  left: `${this.contextMenuState.position.x}px`,
4024
4029
  top: `${this.contextMenuState.position.y}px`,
4025
4030
  zIndex: '10002',
4026
- }, onActionSelected: event => this.handleContextMenuActionSelected(event), onClose: () => this.hideContextMenu() })), h("kritzel-sync-indicator", { key: '0094a026828473988f857d82cfccb811f36f049c', visible: this.isSyncing, text: "Syncing..." }), h("kritzel-loading-overlay", { key: '680096e20b6175104a866b9a92ca4c1d0087c93a', visible: this.isLoadingOverlayVisible, text: this.resolvedTerms['editor.loading'] ?? 'Loading...' }), h("kritzel-engine", { key: '0698a44fcbf2d6ea786d53502350ac0c9ffb9d66', ref: el => {
4031
+ }, 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 => {
4027
4032
  if (el) {
4028
4033
  this.engineRef = el;
4029
4034
  }
4030
- }, 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: '765a670807111ad68e9f8d14bdca2651a33704d4', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
4035
+ }, 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 => {
4031
4036
  if (el) {
4032
4037
  this.controlsRef = el;
4033
4038
  }
4034
- }, controls: this.normalizedControls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: 'e7c5ebab4a40b7ab4fa5d19d203129326c0009ed', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: '0aa3891b553d2abcd06e635227fd6bb6656ed739', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: '824244bbd78784c1d78ab89cc12e206ca61d84cc', class: "top-right-buttons" }, h("kritzel-settings", { key: 'baf4830b6b9ccb6241b1a1baa5cc6ff9d0295fb9', ref: el => {
4039
+ }, 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 => {
4035
4040
  if (el) {
4036
4041
  this.settingsRef = el;
4037
4042
  }
4038
- }, 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: '57e2682450c71803cd49bfe65f16e02d9d3358bc', ref: el => {
4043
+ }, 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 => {
4039
4044
  if (el) {
4040
4045
  this.exportRef = el;
4041
4046
  }
4042
- }, 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: '955d2a61b98d937f8eec39ab19f1b73e702c9fd7', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: 'c0885e0c8cab1a4cb50c8f6d4b17e03f32cc2b87', user: this.user, terms: this.resolvedTerms, onClick: () => this.currentUserDialogRef?.open() }), shouldShowCurrentUser && (h("kritzel-current-user-dialog", { key: 'fc70555061d2aeaebdbe981bdbe8cf501bd8120e', ref: el => {
4047
+ }, 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 => {
4043
4048
  if (el) {
4044
4049
  this.currentUserDialogRef = el;
4045
4050
  }
4046
- }, user: this.user, terms: this.resolvedTerms, onLogoutRequest: this.handleCurrentUserLogout })), shouldShowLoginButton && h("kritzel-button", { key: '9666163a2a36a64ecb641659dca515ae30409d0f', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in'), h("kritzel-more-menu", { key: '167ac17ea93897597e83d4fe8b3b561fac848bb6', items: this.resolvedMoreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: '10cfb0b794d613ec39299f06ac1a3402d1ad3084', ref: el => {
4051
+ }, 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 => {
4047
4052
  if (el) {
4048
4053
  this.shareDialogRef = el;
4049
4054
  }
4050
- }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '99dcb9051382982ec5a8975bdd991bae57745bb9', ref: el => {
4055
+ }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'a7d52bad9e752ad546adf80d257c6a46de031e76', ref: el => {
4051
4056
  if (el) {
4052
4057
  this.loginDialogRef = el;
4053
4058
  }
@@ -1,4 +1,4 @@
1
- import { G as KritzelEngine$1, C as defineCustomElement$1 } from './p-C_f_QElb.js';
1
+ import { G as KritzelEngine$1, C as defineCustomElement$1 } from './p-BNeE3SiZ.js';
2
2
 
3
3
  const KritzelEngine = KritzelEngine$1;
4
4
  const defineCustomElement = defineCustomElement$1;
@@ -1,4 +1,4 @@
1
- import { K as KritzelSettings$1, d as defineCustomElement$1 } from './p-BdCrcQ0_.js';
1
+ import { K as KritzelSettings$1, d as defineCustomElement$1 } from './p-Dx5NfJ0U.js';
2
2
 
3
3
  const KritzelSettings = KritzelSettings$1;
4
4
  const defineCustomElement = defineCustomElement$1;