kritzel-stencil 0.1.71 → 0.1.73
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 +58 -18
- package/dist/cjs/kritzel-active-users_41.cjs.entry.js +38 -10
- package/dist/cjs/{workspace.migrations-Cma2Vh5E.js → workspace.migrations-DcwqsqPC.js} +7 -0
- package/dist/collection/classes/core/core.class.js +0 -2
- package/dist/collection/classes/core/workspace.class.js +7 -0
- package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js +57 -17
- package/dist/collection/classes/structures/object-map.structure.js +34 -5
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +2 -1
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-editor.js +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/kritzel-workspace-manager.js +1 -1
- package/dist/components/{p-BB6jZPvJ.js → p-BgDhcDNw.js} +1 -1
- package/dist/components/{p-MrsvScCa.js → p-By3NOY-k.js} +2 -2
- package/dist/components/{p-D5rvyCKa.js → p-DhMlShij.js} +1 -1
- package/dist/components/{p-D_Rh1g93.js → p-Dp8idtVD.js} +1 -1
- package/dist/esm/index.js +59 -19
- package/dist/esm/kritzel-active-users_41.entry.js +38 -10
- package/dist/esm/{workspace.migrations-BeeAeDP0.js → workspace.migrations-BGixvB76.js} +7 -0
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-67c79d75.entry.js +9 -0
- package/dist/stencil/{p-BeeAeDP0.js → p-BGixvB76.js} +1 -1
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/core/workspace.class.d.ts +5 -0
- package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +3 -0
- package/dist/types/classes/structures/object-map.structure.d.ts +3 -0
- package/dist/types/constants/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/stencil/p-47cfd58a.entry.js +0 -9
package/dist/cjs/index.cjs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var workspace_migrations = require('./workspace.migrations-
|
|
3
|
+
var workspace_migrations = require('./workspace.migrations-DcwqsqPC.js');
|
|
4
4
|
var Y = require('yjs');
|
|
5
5
|
var yWebsocket = require('y-websocket');
|
|
6
6
|
require('y-indexeddb');
|
|
@@ -556,6 +556,9 @@ class HocuspocusSyncProvider {
|
|
|
556
556
|
isConnected = false;
|
|
557
557
|
isSynced = false;
|
|
558
558
|
usesSharedSocket = false;
|
|
559
|
+
isDestroyed = false;
|
|
560
|
+
connectTimeout = null;
|
|
561
|
+
pendingConnectReject = null;
|
|
559
562
|
get awareness() {
|
|
560
563
|
return this.provider.awareness;
|
|
561
564
|
}
|
|
@@ -574,8 +577,13 @@ class HocuspocusSyncProvider {
|
|
|
574
577
|
name,
|
|
575
578
|
document: doc,
|
|
576
579
|
token: options?.token || null,
|
|
580
|
+
onStatus: (data) => {
|
|
581
|
+
if (options?.onStatus) {
|
|
582
|
+
options.onStatus(data);
|
|
583
|
+
}
|
|
584
|
+
},
|
|
577
585
|
onConnect: () => {
|
|
578
|
-
if (this.isConnected) {
|
|
586
|
+
if (this.isConnected || this.isDestroyed) {
|
|
579
587
|
return;
|
|
580
588
|
}
|
|
581
589
|
this.isConnected = true;
|
|
@@ -587,7 +595,7 @@ class HocuspocusSyncProvider {
|
|
|
587
595
|
}
|
|
588
596
|
},
|
|
589
597
|
onDisconnect: () => {
|
|
590
|
-
if (!this.isConnected && !this.isSynced) {
|
|
598
|
+
if (this.isDestroyed || (!this.isConnected && !this.isSynced)) {
|
|
591
599
|
return;
|
|
592
600
|
}
|
|
593
601
|
this.isConnected = false;
|
|
@@ -600,7 +608,7 @@ class HocuspocusSyncProvider {
|
|
|
600
608
|
}
|
|
601
609
|
},
|
|
602
610
|
onSynced: () => {
|
|
603
|
-
if (this.isSynced) {
|
|
611
|
+
if (this.isSynced || this.isDestroyed) {
|
|
604
612
|
return;
|
|
605
613
|
}
|
|
606
614
|
this.isSynced = true;
|
|
@@ -619,9 +627,6 @@ class HocuspocusSyncProvider {
|
|
|
619
627
|
if (options?.onAuthenticationFailed) {
|
|
620
628
|
config.onAuthenticationFailed = options.onAuthenticationFailed;
|
|
621
629
|
}
|
|
622
|
-
if (options?.onStatus) {
|
|
623
|
-
config.onStatus = options.onStatus;
|
|
624
|
-
}
|
|
625
630
|
this.provider = new workspace_migrations.HocuspocusProvider(config);
|
|
626
631
|
// Must call attach() explicitly when using shared socket
|
|
627
632
|
this.provider.attach();
|
|
@@ -637,8 +642,14 @@ class HocuspocusSyncProvider {
|
|
|
637
642
|
name,
|
|
638
643
|
document: doc,
|
|
639
644
|
token: options?.token || null,
|
|
645
|
+
autoConnect: false,
|
|
646
|
+
onStatus: (data) => {
|
|
647
|
+
if (options?.onStatus) {
|
|
648
|
+
options.onStatus(data);
|
|
649
|
+
}
|
|
650
|
+
},
|
|
640
651
|
onConnect: () => {
|
|
641
|
-
if (this.isConnected) {
|
|
652
|
+
if (this.isConnected || this.isDestroyed) {
|
|
642
653
|
return;
|
|
643
654
|
}
|
|
644
655
|
this.isConnected = true;
|
|
@@ -650,7 +661,7 @@ class HocuspocusSyncProvider {
|
|
|
650
661
|
}
|
|
651
662
|
},
|
|
652
663
|
onDisconnect: () => {
|
|
653
|
-
if (!this.isConnected && !this.isSynced) {
|
|
664
|
+
if (this.isDestroyed || (!this.isConnected && !this.isSynced)) {
|
|
654
665
|
return;
|
|
655
666
|
}
|
|
656
667
|
this.isConnected = false;
|
|
@@ -663,7 +674,7 @@ class HocuspocusSyncProvider {
|
|
|
663
674
|
}
|
|
664
675
|
},
|
|
665
676
|
onSynced: () => {
|
|
666
|
-
if (this.isSynced) {
|
|
677
|
+
if (this.isSynced || this.isDestroyed) {
|
|
667
678
|
return;
|
|
668
679
|
}
|
|
669
680
|
this.isSynced = true;
|
|
@@ -682,9 +693,6 @@ class HocuspocusSyncProvider {
|
|
|
682
693
|
if (options?.onAuthenticationFailed) {
|
|
683
694
|
config.onAuthenticationFailed = options.onAuthenticationFailed;
|
|
684
695
|
}
|
|
685
|
-
if (options?.onStatus) {
|
|
686
|
-
config.onStatus = options.onStatus;
|
|
687
|
-
}
|
|
688
696
|
if (options?.WebSocketPolyfill) {
|
|
689
697
|
config.WebSocketPolyfill = options.WebSocketPolyfill;
|
|
690
698
|
}
|
|
@@ -752,22 +760,36 @@ class HocuspocusSyncProvider {
|
|
|
752
760
|
};
|
|
753
761
|
}
|
|
754
762
|
async connect() {
|
|
755
|
-
if (this.isSynced) {
|
|
763
|
+
if (this.isSynced || this.isDestroyed) {
|
|
756
764
|
return;
|
|
757
765
|
}
|
|
758
766
|
return new Promise((resolve, reject) => {
|
|
759
|
-
|
|
767
|
+
// Store reject function so we can cancel the connection if destroyed
|
|
768
|
+
this.pendingConnectReject = reject;
|
|
769
|
+
this.connectTimeout = setTimeout(() => {
|
|
770
|
+
this.pendingConnectReject = null;
|
|
771
|
+
this.connectTimeout = null;
|
|
760
772
|
reject(new Error('Hocuspocus connection timeout'));
|
|
761
773
|
}, 10000); // 10 second timeout
|
|
762
774
|
const syncHandler = () => {
|
|
763
|
-
|
|
775
|
+
if (this.connectTimeout) {
|
|
776
|
+
clearTimeout(this.connectTimeout);
|
|
777
|
+
this.connectTimeout = null;
|
|
778
|
+
}
|
|
779
|
+
this.pendingConnectReject = null;
|
|
764
780
|
this.provider.off('synced', syncHandler);
|
|
765
|
-
|
|
781
|
+
if (!this.isDestroyed) {
|
|
782
|
+
resolve();
|
|
783
|
+
}
|
|
766
784
|
};
|
|
767
785
|
this.provider.on('synced', syncHandler);
|
|
768
786
|
// If already synced, resolve immediately
|
|
769
787
|
if (this.provider.isSynced) {
|
|
770
|
-
|
|
788
|
+
if (this.connectTimeout) {
|
|
789
|
+
clearTimeout(this.connectTimeout);
|
|
790
|
+
this.connectTimeout = null;
|
|
791
|
+
}
|
|
792
|
+
this.pendingConnectReject = null;
|
|
771
793
|
this.provider.off('synced', syncHandler);
|
|
772
794
|
resolve();
|
|
773
795
|
return;
|
|
@@ -779,6 +801,14 @@ class HocuspocusSyncProvider {
|
|
|
779
801
|
});
|
|
780
802
|
}
|
|
781
803
|
disconnect() {
|
|
804
|
+
// Cancel any pending connection attempt
|
|
805
|
+
if (this.connectTimeout) {
|
|
806
|
+
clearTimeout(this.connectTimeout);
|
|
807
|
+
this.connectTimeout = null;
|
|
808
|
+
}
|
|
809
|
+
if (this.pendingConnectReject) {
|
|
810
|
+
this.pendingConnectReject = null; // Don't reject, just abandon the promise
|
|
811
|
+
}
|
|
782
812
|
if (this.provider) {
|
|
783
813
|
if (this.usesSharedSocket) {
|
|
784
814
|
// Detach from shared socket instead of disconnecting
|
|
@@ -792,6 +822,16 @@ class HocuspocusSyncProvider {
|
|
|
792
822
|
this.isSynced = false;
|
|
793
823
|
}
|
|
794
824
|
destroy() {
|
|
825
|
+
// Mark as destroyed first to prevent any callbacks from doing work
|
|
826
|
+
this.isDestroyed = true;
|
|
827
|
+
// Cancel any pending connection attempt
|
|
828
|
+
if (this.connectTimeout) {
|
|
829
|
+
clearTimeout(this.connectTimeout);
|
|
830
|
+
this.connectTimeout = null;
|
|
831
|
+
}
|
|
832
|
+
if (this.pendingConnectReject) {
|
|
833
|
+
this.pendingConnectReject = null; // Don't reject, just abandon the promise
|
|
834
|
+
}
|
|
795
835
|
if (this.provider) {
|
|
796
836
|
this.provider.destroy();
|
|
797
837
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var index = require('./index-Dc7LOVhs.js');
|
|
4
|
-
var workspace_migrations = require('./workspace.migrations-
|
|
4
|
+
var workspace_migrations = require('./workspace.migrations-DcwqsqPC.js');
|
|
5
5
|
var Y = require('yjs');
|
|
6
6
|
require('y-websocket');
|
|
7
7
|
require('y-indexeddb');
|
|
@@ -2175,6 +2175,7 @@ const KritzelEditor = class {
|
|
|
2175
2175
|
const activeWorkspace = await this.engineRef.getActiveWorkspace();
|
|
2176
2176
|
activeWorkspace.isPublic = isPublic;
|
|
2177
2177
|
await this.engineRef.updateWorkspace(activeWorkspace);
|
|
2178
|
+
this.activeWorkspace = activeWorkspace.clone();
|
|
2178
2179
|
this.isPublicChange.emit({ isPublic, workspaceId: this.activeWorkspace?.id });
|
|
2179
2180
|
};
|
|
2180
2181
|
/**
|
|
@@ -2288,7 +2289,7 @@ const KritzelEditor = class {
|
|
|
2288
2289
|
const isLoggedIn = this.isLoggedIn;
|
|
2289
2290
|
const shouldShowCurrentUser = isLoggedIn;
|
|
2290
2291
|
const shouldShowLoginButton = !!this.loginConfig && !isLoggedIn;
|
|
2291
|
-
return (index.h(index.Host, { key: '
|
|
2292
|
+
return (index.h(index.Host, { key: '84231308ca4b12863139aeeafa754d544345df3f' }, index.h("div", { key: 'b8cdd2862e9bd892dfeed356e85d608759307bf7', class: "top-left-buttons" }, index.h("kritzel-workspace-manager", { key: '2bce2b9564f5a083ba32667718685310d060f23a', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), index.h("kritzel-back-to-content", { key: 'b013213fc04fe86eefcc324911693fcffb8eea51', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), index.h("kritzel-engine", { key: '7338446462bfdc3995c0897704e9594a875895e1', ref: el => (this.engineRef = el), workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, scaleMin: this.scaleMin, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, wheelEnabled: this.wheelEnabled, theme: this.currentTheme, 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) }), index.h("kritzel-controls", { key: '97b9d8805446afc101f01d99bc96bc24a7054722', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => (this.controlsRef = el), controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState, theme: this.currentTheme, onIsControlsReady: () => (this.isControlsReady = true) }), index.h("div", { key: '0986b1e82b8fbf989e716fe07a401faca618f642', class: "top-right-buttons" }, index.h("kritzel-settings", { key: '6f4cfe92bb54f0de291b0a0482742b859e86ecf3', ref: el => (this.settingsRef = el), shortcuts: this.shortcuts, editorId: this.editorId, onSettingsChange: event => this.handleSettingsChange(event) }), index.h("kritzel-export", { key: 'd669ecae49f721e765d1f8d2f6725633c28cd635', ref: el => (this.exportRef = el), workspaceName: this.activeWorkspace?.name || 'workspace', onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), index.h("kritzel-active-users", { key: 'e44843dcdc9c0ff1e760aadac3f2ec79a7371aba', users: this.activeUsers }), shouldShowCurrentUser && index.h("kritzel-current-user", { key: '364e09a58fd147f29ce7402406907c6c633124bc', user: this.user }), shouldShowLoginButton && index.h("kritzel-button", { key: '79705ba4c5f991229ae08b90a3397bd3bac56333', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), index.h("kritzel-more-menu", { key: '95bd3a7f755aa941bd3d9ffdbc2c7f433e5beaaa', items: this.moreMenuItems }), index.h("kritzel-share-dialog", { key: '94f6cfdb67b82d35c6e86f37c88c2ec323234360', ref: el => (this.shareDialogRef = el), isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (index.h("kritzel-login-dialog", { key: 'bc4fd1ec6e61f4d4e61c043ad33a0b0e2c3c8792', ref: el => (this.loginDialogRef = el), providers: this.loginConfig.providers, dialogTitle: this.loginConfig.title, subtitle: this.loginConfig.subtitle, onProviderLogin: this.handleProviderLogin })))));
|
|
2292
2293
|
}
|
|
2293
2294
|
static get watchers() { return {
|
|
2294
2295
|
"isEngineReady": [{
|
|
@@ -21001,6 +21002,9 @@ class KritzelObjectMap {
|
|
|
21001
21002
|
_stackItemPoppedHandler = null;
|
|
21002
21003
|
_awarenessChangeHandler = null;
|
|
21003
21004
|
_awarenessChangeCallbacks = [];
|
|
21005
|
+
_lastAwarenessEmitTime = 0;
|
|
21006
|
+
_awarenessEmitTimeout = null;
|
|
21007
|
+
AWARENESS_THROTTLE_INTERVAL = 100; // milliseconds
|
|
21004
21008
|
/**
|
|
21005
21009
|
* Indicates whether the object map has been initialized and is ready for use.
|
|
21006
21010
|
* @returns `true` if providers are connected and the map is operational
|
|
@@ -21190,9 +21194,32 @@ class KritzelObjectMap {
|
|
|
21190
21194
|
// Subscribe to awareness changes
|
|
21191
21195
|
if (this._awareness) {
|
|
21192
21196
|
this._awarenessChangeHandler = () => {
|
|
21193
|
-
const
|
|
21194
|
-
|
|
21195
|
-
|
|
21197
|
+
const now = Date.now();
|
|
21198
|
+
const timeSinceLastEmit = now - this._lastAwarenessEmitTime;
|
|
21199
|
+
// Clear any pending timeout since we have a new event
|
|
21200
|
+
if (this._awarenessEmitTimeout !== null) {
|
|
21201
|
+
clearTimeout(this._awarenessEmitTimeout);
|
|
21202
|
+
this._awarenessEmitTimeout = null;
|
|
21203
|
+
}
|
|
21204
|
+
if (timeSinceLastEmit >= this.AWARENESS_THROTTLE_INTERVAL) {
|
|
21205
|
+
// Enough time has passed, emit immediately
|
|
21206
|
+
this._lastAwarenessEmitTime = now;
|
|
21207
|
+
const states = this._awareness.getStates();
|
|
21208
|
+
for (const callback of this._awarenessChangeCallbacks) {
|
|
21209
|
+
callback(states);
|
|
21210
|
+
}
|
|
21211
|
+
}
|
|
21212
|
+
else {
|
|
21213
|
+
// Schedule emission for the remaining time
|
|
21214
|
+
const delayMs = this.AWARENESS_THROTTLE_INTERVAL - timeSinceLastEmit;
|
|
21215
|
+
this._awarenessEmitTimeout = setTimeout(() => {
|
|
21216
|
+
this._lastAwarenessEmitTime = Date.now();
|
|
21217
|
+
this._awarenessEmitTimeout = null;
|
|
21218
|
+
const states = this._awareness.getStates();
|
|
21219
|
+
for (const callback of this._awarenessChangeCallbacks) {
|
|
21220
|
+
callback(states);
|
|
21221
|
+
}
|
|
21222
|
+
}, delayMs);
|
|
21196
21223
|
}
|
|
21197
21224
|
};
|
|
21198
21225
|
this._awareness.on('change', this._awarenessChangeHandler);
|
|
@@ -21712,11 +21739,14 @@ class KritzelObjectMap {
|
|
|
21712
21739
|
this._awareness.off('change', this._awarenessChangeHandler);
|
|
21713
21740
|
this._awarenessChangeHandler = null;
|
|
21714
21741
|
}
|
|
21742
|
+
if (this._awarenessEmitTimeout !== null) {
|
|
21743
|
+
clearTimeout(this._awarenessEmitTimeout);
|
|
21744
|
+
this._awarenessEmitTimeout = null;
|
|
21745
|
+
}
|
|
21715
21746
|
this._awareness = null;
|
|
21716
21747
|
this._awarenessChangeCallbacks = [];
|
|
21717
|
-
//
|
|
21748
|
+
// Destroy providers (destroy handles disconnection internally)
|
|
21718
21749
|
this._providers.forEach(p => {
|
|
21719
|
-
p.disconnect();
|
|
21720
21750
|
p.destroy();
|
|
21721
21751
|
});
|
|
21722
21752
|
this._providers = [];
|
|
@@ -22879,13 +22909,11 @@ class KritzelCore {
|
|
|
22879
22909
|
* @param workspace - The workspace to create
|
|
22880
22910
|
*/
|
|
22881
22911
|
createWorkspace(workspace) {
|
|
22882
|
-
console.log('Creating workspace:', workspace);
|
|
22883
22912
|
workspace._core = this;
|
|
22884
22913
|
workspace.createdAt = new Date();
|
|
22885
22914
|
workspace.updatedAt = new Date();
|
|
22886
22915
|
this.saveWorkspaceToAppState(workspace);
|
|
22887
22916
|
this._store.state.workspaces = this.loadWorkspacesFromAppState();
|
|
22888
|
-
console.log('Current workspaces after creation:', this._store.state.workspaces);
|
|
22889
22917
|
}
|
|
22890
22918
|
/**
|
|
22891
22919
|
* Updates an existing workspace in the app state.
|
|
@@ -27994,7 +28022,7 @@ const KritzelPortal = class {
|
|
|
27994
28022
|
* This file is auto-generated by the version bump scripts.
|
|
27995
28023
|
* Do not modify manually.
|
|
27996
28024
|
*/
|
|
27997
|
-
const KRITZEL_VERSION = '0.1.
|
|
28025
|
+
const KRITZEL_VERSION = '0.1.73';
|
|
27998
28026
|
|
|
27999
28027
|
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-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)}`;
|
|
28000
28028
|
|
|
@@ -25254,6 +25254,13 @@ class KritzelWorkspace {
|
|
|
25254
25254
|
}
|
|
25255
25255
|
return serialized;
|
|
25256
25256
|
}
|
|
25257
|
+
/**
|
|
25258
|
+
* Creates a shallow clone of this workspace, preserving the prototype chain.
|
|
25259
|
+
* Useful when Stencil needs a new object reference to detect property changes.
|
|
25260
|
+
*/
|
|
25261
|
+
clone() {
|
|
25262
|
+
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
25263
|
+
}
|
|
25257
25264
|
/**
|
|
25258
25265
|
* Deserializes a plain object into this workspace instance.
|
|
25259
25266
|
* Converts ISO string dates back to Date objects.
|
|
@@ -339,13 +339,11 @@ export class KritzelCore {
|
|
|
339
339
|
* @param workspace - The workspace to create
|
|
340
340
|
*/
|
|
341
341
|
createWorkspace(workspace) {
|
|
342
|
-
console.log('Creating workspace:', workspace);
|
|
343
342
|
workspace._core = this;
|
|
344
343
|
workspace.createdAt = new Date();
|
|
345
344
|
workspace.updatedAt = new Date();
|
|
346
345
|
this.saveWorkspaceToAppState(workspace);
|
|
347
346
|
this._store.state.workspaces = this.loadWorkspacesFromAppState();
|
|
348
|
-
console.log('Current workspaces after creation:', this._store.state.workspaces);
|
|
349
347
|
}
|
|
350
348
|
/**
|
|
351
349
|
* Updates an existing workspace in the app state.
|
|
@@ -77,6 +77,13 @@ export class KritzelWorkspace {
|
|
|
77
77
|
}
|
|
78
78
|
return serialized;
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Creates a shallow clone of this workspace, preserving the prototype chain.
|
|
82
|
+
* Useful when Stencil needs a new object reference to detect property changes.
|
|
83
|
+
*/
|
|
84
|
+
clone() {
|
|
85
|
+
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
86
|
+
}
|
|
80
87
|
/**
|
|
81
88
|
* Deserializes a plain object into this workspace instance.
|
|
82
89
|
* Converts ISO string dates back to Date objects.
|
|
@@ -8,6 +8,9 @@ export class HocuspocusSyncProvider {
|
|
|
8
8
|
isConnected = false;
|
|
9
9
|
isSynced = false;
|
|
10
10
|
usesSharedSocket = false;
|
|
11
|
+
isDestroyed = false;
|
|
12
|
+
connectTimeout = null;
|
|
13
|
+
pendingConnectReject = null;
|
|
11
14
|
get awareness() {
|
|
12
15
|
return this.provider.awareness;
|
|
13
16
|
}
|
|
@@ -26,8 +29,13 @@ export class HocuspocusSyncProvider {
|
|
|
26
29
|
name,
|
|
27
30
|
document: doc,
|
|
28
31
|
token: options?.token || null,
|
|
32
|
+
onStatus: (data) => {
|
|
33
|
+
if (options?.onStatus) {
|
|
34
|
+
options.onStatus(data);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
29
37
|
onConnect: () => {
|
|
30
|
-
if (this.isConnected) {
|
|
38
|
+
if (this.isConnected || this.isDestroyed) {
|
|
31
39
|
return;
|
|
32
40
|
}
|
|
33
41
|
this.isConnected = true;
|
|
@@ -39,7 +47,7 @@ export class HocuspocusSyncProvider {
|
|
|
39
47
|
}
|
|
40
48
|
},
|
|
41
49
|
onDisconnect: () => {
|
|
42
|
-
if (!this.isConnected && !this.isSynced) {
|
|
50
|
+
if (this.isDestroyed || (!this.isConnected && !this.isSynced)) {
|
|
43
51
|
return;
|
|
44
52
|
}
|
|
45
53
|
this.isConnected = false;
|
|
@@ -52,7 +60,7 @@ export class HocuspocusSyncProvider {
|
|
|
52
60
|
}
|
|
53
61
|
},
|
|
54
62
|
onSynced: () => {
|
|
55
|
-
if (this.isSynced) {
|
|
63
|
+
if (this.isSynced || this.isDestroyed) {
|
|
56
64
|
return;
|
|
57
65
|
}
|
|
58
66
|
this.isSynced = true;
|
|
@@ -71,9 +79,6 @@ export class HocuspocusSyncProvider {
|
|
|
71
79
|
if (options?.onAuthenticationFailed) {
|
|
72
80
|
config.onAuthenticationFailed = options.onAuthenticationFailed;
|
|
73
81
|
}
|
|
74
|
-
if (options?.onStatus) {
|
|
75
|
-
config.onStatus = options.onStatus;
|
|
76
|
-
}
|
|
77
82
|
this.provider = new HocuspocusProvider(config);
|
|
78
83
|
// Must call attach() explicitly when using shared socket
|
|
79
84
|
this.provider.attach();
|
|
@@ -89,8 +94,14 @@ export class HocuspocusSyncProvider {
|
|
|
89
94
|
name,
|
|
90
95
|
document: doc,
|
|
91
96
|
token: options?.token || null,
|
|
97
|
+
autoConnect: false,
|
|
98
|
+
onStatus: (data) => {
|
|
99
|
+
if (options?.onStatus) {
|
|
100
|
+
options.onStatus(data);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
92
103
|
onConnect: () => {
|
|
93
|
-
if (this.isConnected) {
|
|
104
|
+
if (this.isConnected || this.isDestroyed) {
|
|
94
105
|
return;
|
|
95
106
|
}
|
|
96
107
|
this.isConnected = true;
|
|
@@ -102,7 +113,7 @@ export class HocuspocusSyncProvider {
|
|
|
102
113
|
}
|
|
103
114
|
},
|
|
104
115
|
onDisconnect: () => {
|
|
105
|
-
if (!this.isConnected && !this.isSynced) {
|
|
116
|
+
if (this.isDestroyed || (!this.isConnected && !this.isSynced)) {
|
|
106
117
|
return;
|
|
107
118
|
}
|
|
108
119
|
this.isConnected = false;
|
|
@@ -115,7 +126,7 @@ export class HocuspocusSyncProvider {
|
|
|
115
126
|
}
|
|
116
127
|
},
|
|
117
128
|
onSynced: () => {
|
|
118
|
-
if (this.isSynced) {
|
|
129
|
+
if (this.isSynced || this.isDestroyed) {
|
|
119
130
|
return;
|
|
120
131
|
}
|
|
121
132
|
this.isSynced = true;
|
|
@@ -134,9 +145,6 @@ export class HocuspocusSyncProvider {
|
|
|
134
145
|
if (options?.onAuthenticationFailed) {
|
|
135
146
|
config.onAuthenticationFailed = options.onAuthenticationFailed;
|
|
136
147
|
}
|
|
137
|
-
if (options?.onStatus) {
|
|
138
|
-
config.onStatus = options.onStatus;
|
|
139
|
-
}
|
|
140
148
|
if (options?.WebSocketPolyfill) {
|
|
141
149
|
config.WebSocketPolyfill = options.WebSocketPolyfill;
|
|
142
150
|
}
|
|
@@ -204,22 +212,36 @@ export class HocuspocusSyncProvider {
|
|
|
204
212
|
};
|
|
205
213
|
}
|
|
206
214
|
async connect() {
|
|
207
|
-
if (this.isSynced) {
|
|
215
|
+
if (this.isSynced || this.isDestroyed) {
|
|
208
216
|
return;
|
|
209
217
|
}
|
|
210
218
|
return new Promise((resolve, reject) => {
|
|
211
|
-
|
|
219
|
+
// Store reject function so we can cancel the connection if destroyed
|
|
220
|
+
this.pendingConnectReject = reject;
|
|
221
|
+
this.connectTimeout = setTimeout(() => {
|
|
222
|
+
this.pendingConnectReject = null;
|
|
223
|
+
this.connectTimeout = null;
|
|
212
224
|
reject(new Error('Hocuspocus connection timeout'));
|
|
213
225
|
}, 10000); // 10 second timeout
|
|
214
226
|
const syncHandler = () => {
|
|
215
|
-
|
|
227
|
+
if (this.connectTimeout) {
|
|
228
|
+
clearTimeout(this.connectTimeout);
|
|
229
|
+
this.connectTimeout = null;
|
|
230
|
+
}
|
|
231
|
+
this.pendingConnectReject = null;
|
|
216
232
|
this.provider.off('synced', syncHandler);
|
|
217
|
-
|
|
233
|
+
if (!this.isDestroyed) {
|
|
234
|
+
resolve();
|
|
235
|
+
}
|
|
218
236
|
};
|
|
219
237
|
this.provider.on('synced', syncHandler);
|
|
220
238
|
// If already synced, resolve immediately
|
|
221
239
|
if (this.provider.isSynced) {
|
|
222
|
-
|
|
240
|
+
if (this.connectTimeout) {
|
|
241
|
+
clearTimeout(this.connectTimeout);
|
|
242
|
+
this.connectTimeout = null;
|
|
243
|
+
}
|
|
244
|
+
this.pendingConnectReject = null;
|
|
223
245
|
this.provider.off('synced', syncHandler);
|
|
224
246
|
resolve();
|
|
225
247
|
return;
|
|
@@ -231,6 +253,14 @@ export class HocuspocusSyncProvider {
|
|
|
231
253
|
});
|
|
232
254
|
}
|
|
233
255
|
disconnect() {
|
|
256
|
+
// Cancel any pending connection attempt
|
|
257
|
+
if (this.connectTimeout) {
|
|
258
|
+
clearTimeout(this.connectTimeout);
|
|
259
|
+
this.connectTimeout = null;
|
|
260
|
+
}
|
|
261
|
+
if (this.pendingConnectReject) {
|
|
262
|
+
this.pendingConnectReject = null; // Don't reject, just abandon the promise
|
|
263
|
+
}
|
|
234
264
|
if (this.provider) {
|
|
235
265
|
if (this.usesSharedSocket) {
|
|
236
266
|
// Detach from shared socket instead of disconnecting
|
|
@@ -244,6 +274,16 @@ export class HocuspocusSyncProvider {
|
|
|
244
274
|
this.isSynced = false;
|
|
245
275
|
}
|
|
246
276
|
destroy() {
|
|
277
|
+
// Mark as destroyed first to prevent any callbacks from doing work
|
|
278
|
+
this.isDestroyed = true;
|
|
279
|
+
// Cancel any pending connection attempt
|
|
280
|
+
if (this.connectTimeout) {
|
|
281
|
+
clearTimeout(this.connectTimeout);
|
|
282
|
+
this.connectTimeout = null;
|
|
283
|
+
}
|
|
284
|
+
if (this.pendingConnectReject) {
|
|
285
|
+
this.pendingConnectReject = null; // Don't reject, just abandon the promise
|
|
286
|
+
}
|
|
247
287
|
if (this.provider) {
|
|
248
288
|
this.provider.destroy();
|
|
249
289
|
}
|
|
@@ -34,6 +34,9 @@ export class KritzelObjectMap {
|
|
|
34
34
|
_stackItemPoppedHandler = null;
|
|
35
35
|
_awarenessChangeHandler = null;
|
|
36
36
|
_awarenessChangeCallbacks = [];
|
|
37
|
+
_lastAwarenessEmitTime = 0;
|
|
38
|
+
_awarenessEmitTimeout = null;
|
|
39
|
+
AWARENESS_THROTTLE_INTERVAL = 100; // milliseconds
|
|
37
40
|
/**
|
|
38
41
|
* Indicates whether the object map has been initialized and is ready for use.
|
|
39
42
|
* @returns `true` if providers are connected and the map is operational
|
|
@@ -223,9 +226,32 @@ export class KritzelObjectMap {
|
|
|
223
226
|
// Subscribe to awareness changes
|
|
224
227
|
if (this._awareness) {
|
|
225
228
|
this._awarenessChangeHandler = () => {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
const now = Date.now();
|
|
230
|
+
const timeSinceLastEmit = now - this._lastAwarenessEmitTime;
|
|
231
|
+
// Clear any pending timeout since we have a new event
|
|
232
|
+
if (this._awarenessEmitTimeout !== null) {
|
|
233
|
+
clearTimeout(this._awarenessEmitTimeout);
|
|
234
|
+
this._awarenessEmitTimeout = null;
|
|
235
|
+
}
|
|
236
|
+
if (timeSinceLastEmit >= this.AWARENESS_THROTTLE_INTERVAL) {
|
|
237
|
+
// Enough time has passed, emit immediately
|
|
238
|
+
this._lastAwarenessEmitTime = now;
|
|
239
|
+
const states = this._awareness.getStates();
|
|
240
|
+
for (const callback of this._awarenessChangeCallbacks) {
|
|
241
|
+
callback(states);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Schedule emission for the remaining time
|
|
246
|
+
const delayMs = this.AWARENESS_THROTTLE_INTERVAL - timeSinceLastEmit;
|
|
247
|
+
this._awarenessEmitTimeout = setTimeout(() => {
|
|
248
|
+
this._lastAwarenessEmitTime = Date.now();
|
|
249
|
+
this._awarenessEmitTimeout = null;
|
|
250
|
+
const states = this._awareness.getStates();
|
|
251
|
+
for (const callback of this._awarenessChangeCallbacks) {
|
|
252
|
+
callback(states);
|
|
253
|
+
}
|
|
254
|
+
}, delayMs);
|
|
229
255
|
}
|
|
230
256
|
};
|
|
231
257
|
this._awareness.on('change', this._awarenessChangeHandler);
|
|
@@ -752,11 +778,14 @@ export class KritzelObjectMap {
|
|
|
752
778
|
this._awareness.off('change', this._awarenessChangeHandler);
|
|
753
779
|
this._awarenessChangeHandler = null;
|
|
754
780
|
}
|
|
781
|
+
if (this._awarenessEmitTimeout !== null) {
|
|
782
|
+
clearTimeout(this._awarenessEmitTimeout);
|
|
783
|
+
this._awarenessEmitTimeout = null;
|
|
784
|
+
}
|
|
755
785
|
this._awareness = null;
|
|
756
786
|
this._awarenessChangeCallbacks = [];
|
|
757
|
-
//
|
|
787
|
+
// Destroy providers (destroy handles disconnection internally)
|
|
758
788
|
this._providers.forEach(p => {
|
|
759
|
-
p.disconnect();
|
|
760
789
|
p.destroy();
|
|
761
790
|
});
|
|
762
791
|
this._providers = [];
|
|
@@ -583,6 +583,7 @@ export class KritzelEditor {
|
|
|
583
583
|
const activeWorkspace = await this.engineRef.getActiveWorkspace();
|
|
584
584
|
activeWorkspace.isPublic = isPublic;
|
|
585
585
|
await this.engineRef.updateWorkspace(activeWorkspace);
|
|
586
|
+
this.activeWorkspace = activeWorkspace.clone();
|
|
586
587
|
this.isPublicChange.emit({ isPublic, workspaceId: this.activeWorkspace?.id });
|
|
587
588
|
};
|
|
588
589
|
/**
|
|
@@ -696,7 +697,7 @@ export class KritzelEditor {
|
|
|
696
697
|
const isLoggedIn = this.isLoggedIn;
|
|
697
698
|
const shouldShowCurrentUser = isLoggedIn;
|
|
698
699
|
const shouldShowLoginButton = !!this.loginConfig && !isLoggedIn;
|
|
699
|
-
return (h(Host, { key: '
|
|
700
|
+
return (h(Host, { key: '84231308ca4b12863139aeeafa754d544345df3f' }, h("div", { key: 'b8cdd2862e9bd892dfeed356e85d608759307bf7', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '2bce2b9564f5a083ba32667718685310d060f23a', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: 'b013213fc04fe86eefcc324911693fcffb8eea51', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), h("kritzel-engine", { key: '7338446462bfdc3995c0897704e9594a875895e1', ref: el => (this.engineRef = el), workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, scaleMin: this.scaleMin, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, wheelEnabled: this.wheelEnabled, theme: this.currentTheme, 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) }), h("kritzel-controls", { key: '97b9d8805446afc101f01d99bc96bc24a7054722', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => (this.controlsRef = el), controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState, theme: this.currentTheme, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: '0986b1e82b8fbf989e716fe07a401faca618f642', class: "top-right-buttons" }, h("kritzel-settings", { key: '6f4cfe92bb54f0de291b0a0482742b859e86ecf3', ref: el => (this.settingsRef = el), shortcuts: this.shortcuts, editorId: this.editorId, onSettingsChange: event => this.handleSettingsChange(event) }), h("kritzel-export", { key: 'd669ecae49f721e765d1f8d2f6725633c28cd635', ref: el => (this.exportRef = el), workspaceName: this.activeWorkspace?.name || 'workspace', onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: 'e44843dcdc9c0ff1e760aadac3f2ec79a7371aba', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '364e09a58fd147f29ce7402406907c6c633124bc', user: this.user }), shouldShowLoginButton && h("kritzel-button", { key: '79705ba4c5f991229ae08b90a3397bd3bac56333', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), h("kritzel-more-menu", { key: '95bd3a7f755aa941bd3d9ffdbc2c7f433e5beaaa', items: this.moreMenuItems }), h("kritzel-share-dialog", { key: '94f6cfdb67b82d35c6e86f37c88c2ec323234360', ref: el => (this.shareDialogRef = el), isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'bc4fd1ec6e61f4d4e61c043ad33a0b0e2c3c8792', ref: el => (this.loginDialogRef = el), providers: this.loginConfig.providers, dialogTitle: this.loginConfig.title, subtitle: this.loginConfig.subtitle, onProviderLogin: this.handleProviderLogin })))));
|
|
700
701
|
}
|
|
701
702
|
static get is() { return "kritzel-editor"; }
|
|
702
703
|
static get originalStyleUrls() {
|