kritzel-stencil 0.1.74 → 0.1.76
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 +131 -86
- package/dist/cjs/kritzel-active-users_42.cjs.entry.js +132 -19
- package/dist/cjs/{workspace.migrations-Dyt35LBC.js → workspace.migrations-DkmVO6dE.js} +106 -44
- package/dist/collection/classes/core/viewport.class.js +32 -3
- package/dist/collection/classes/managers/anchor.manager.js +101 -44
- package/dist/collection/classes/providers/broadcast-sync-provider.class.js +5 -0
- package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js +120 -85
- package/dist/collection/classes/providers/indexeddb-sync-provider.class.js +5 -0
- package/dist/collection/classes/providers/websocket-sync-provider.class.js +5 -0
- package/dist/collection/classes/structures/app-state-map.structure.js +15 -4
- package/dist/collection/classes/structures/object-map.structure.js +75 -7
- package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.css +2 -2
- package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +7 -2
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-awareness-cursors.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/{p-B4Oqnl55.js → p-31FVoNWR.js} +1 -1
- package/dist/components/p-jdYmu4SA.js +9 -0
- package/dist/components/p-xNwOWoiT.js +1 -0
- package/dist/esm/index.js +132 -87
- package/dist/esm/kritzel-active-users_42.entry.js +132 -19
- package/dist/esm/{workspace.migrations-B99F1MdT.js → workspace.migrations-D48_Bqvh.js} +106 -44
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-775a7246.entry.js +9 -0
- package/dist/stencil/{p-B99F1MdT.js → p-D48_Bqvh.js} +1 -1
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/core/viewport.class.d.ts +8 -0
- package/dist/types/classes/managers/anchor.manager.d.ts +4 -0
- package/dist/types/classes/providers/broadcast-sync-provider.class.d.ts +2 -0
- package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +37 -1
- package/dist/types/classes/providers/indexeddb-sync-provider.class.d.ts +2 -0
- package/dist/types/classes/providers/websocket-sync-provider.class.d.ts +2 -0
- package/dist/types/classes/structures/object-map.structure.d.ts +6 -0
- package/dist/types/constants/version.d.ts +1 -1
- package/dist/types/interfaces/remote-cursor.interface.d.ts +1 -0
- package/dist/types/interfaces/sync-provider.interface.d.ts +16 -0
- package/package.json +1 -1
- package/dist/components/p-BSipRoFx.js +0 -1
- package/dist/components/p-RJWe82kG.js +0 -9
- package/dist/stencil/p-2a60e1bc.entry.js +0 -9
|
@@ -4,6 +4,7 @@ import { HocuspocusProvider, HocuspocusProviderWebsocket } from "@hocuspocus/pro
|
|
|
4
4
|
* Supports multiplexing - multiple documents can share the same WebSocket connection
|
|
5
5
|
*/
|
|
6
6
|
export class HocuspocusSyncProvider {
|
|
7
|
+
type = 'network';
|
|
7
8
|
provider;
|
|
8
9
|
isConnected = false;
|
|
9
10
|
isSynced = false;
|
|
@@ -11,16 +12,87 @@ export class HocuspocusSyncProvider {
|
|
|
11
12
|
isDestroyed = false;
|
|
12
13
|
connectTimeout = null;
|
|
13
14
|
pendingConnectReject = null;
|
|
15
|
+
connectionTimeoutMs;
|
|
16
|
+
_connectionStatus = 'disconnected';
|
|
17
|
+
visibilityHandler = null;
|
|
18
|
+
onlineHandler = null;
|
|
14
19
|
get awareness() {
|
|
15
20
|
return this.provider.awareness;
|
|
16
21
|
}
|
|
22
|
+
get connectionStatus() {
|
|
23
|
+
return this._connectionStatus;
|
|
24
|
+
}
|
|
17
25
|
// Static shared WebSocket instance for multiplexing
|
|
18
26
|
static sharedWebSocketProvider = null;
|
|
19
27
|
constructor(docName, doc, options) {
|
|
20
28
|
const name = options?.name || docName;
|
|
21
29
|
const url = options?.url || 'ws://localhost:1234';
|
|
30
|
+
this.connectionTimeoutMs = options?.connectionTimeout ?? 10000;
|
|
22
31
|
// Use provided websocketProvider or the static shared one
|
|
23
32
|
const websocketProvider = options?.websocketProvider || HocuspocusSyncProvider.sharedWebSocketProvider;
|
|
33
|
+
// Build reconnect config from options
|
|
34
|
+
const reconnectConfig = {};
|
|
35
|
+
if (options?.delay !== undefined)
|
|
36
|
+
reconnectConfig.delay = options.delay;
|
|
37
|
+
if (options?.factor !== undefined)
|
|
38
|
+
reconnectConfig.factor = options.factor;
|
|
39
|
+
if (options?.maxAttempts !== undefined)
|
|
40
|
+
reconnectConfig.maxAttempts = options.maxAttempts;
|
|
41
|
+
if (options?.minDelay !== undefined)
|
|
42
|
+
reconnectConfig.minDelay = options.minDelay;
|
|
43
|
+
if (options?.maxDelay !== undefined)
|
|
44
|
+
reconnectConfig.maxDelay = options.maxDelay;
|
|
45
|
+
const onConnect = () => {
|
|
46
|
+
if (this.isDestroyed) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this.isConnected = true;
|
|
50
|
+
this._connectionStatus = 'connected';
|
|
51
|
+
if (!options?.quiet) {
|
|
52
|
+
console.info(`Hocuspocus connected: ${name}`);
|
|
53
|
+
}
|
|
54
|
+
if (options?.onConnect) {
|
|
55
|
+
options.onConnect();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const onDisconnect = () => {
|
|
59
|
+
if (this.isDestroyed) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.isConnected = false;
|
|
63
|
+
this.isSynced = false;
|
|
64
|
+
this._connectionStatus = 'disconnected';
|
|
65
|
+
if (!options?.quiet) {
|
|
66
|
+
console.info(`Hocuspocus disconnected: ${name}`);
|
|
67
|
+
}
|
|
68
|
+
if (options?.onDisconnect) {
|
|
69
|
+
options.onDisconnect();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const onSynced = () => {
|
|
73
|
+
if (this.isDestroyed) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.isSynced = true;
|
|
77
|
+
this._connectionStatus = 'synced';
|
|
78
|
+
if (!options?.quiet) {
|
|
79
|
+
console.info(`Hocuspocus synced: ${name}`);
|
|
80
|
+
}
|
|
81
|
+
if (options?.onSynced) {
|
|
82
|
+
options.onSynced();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const onStatus = (data) => {
|
|
86
|
+
if (this.isDestroyed) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (data.status === 'connecting') {
|
|
90
|
+
this._connectionStatus = 'connecting';
|
|
91
|
+
}
|
|
92
|
+
if (options?.onStatus) {
|
|
93
|
+
options.onStatus(data);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
24
96
|
if (websocketProvider) {
|
|
25
97
|
// Multiplexing mode - use shared WebSocket connection
|
|
26
98
|
this.usesSharedSocket = true;
|
|
@@ -29,48 +101,11 @@ export class HocuspocusSyncProvider {
|
|
|
29
101
|
name,
|
|
30
102
|
document: doc,
|
|
31
103
|
token: options?.token || null,
|
|
32
|
-
onStatus
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
onConnect: () => {
|
|
38
|
-
if (this.isConnected || this.isDestroyed) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
this.isConnected = true;
|
|
42
|
-
if (!options?.quiet) {
|
|
43
|
-
console.info(`Hocuspocus connected: ${name}`);
|
|
44
|
-
}
|
|
45
|
-
if (options?.onConnect) {
|
|
46
|
-
options.onConnect();
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
onDisconnect: () => {
|
|
50
|
-
if (this.isDestroyed || (!this.isConnected && !this.isSynced)) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
this.isConnected = false;
|
|
54
|
-
this.isSynced = false;
|
|
55
|
-
if (!options?.quiet) {
|
|
56
|
-
console.info(`Hocuspocus disconnected: ${name}`);
|
|
57
|
-
}
|
|
58
|
-
if (options?.onDisconnect) {
|
|
59
|
-
options.onDisconnect();
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
onSynced: () => {
|
|
63
|
-
if (this.isSynced || this.isDestroyed) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
this.isSynced = true;
|
|
67
|
-
if (!options?.quiet) {
|
|
68
|
-
console.info(`Hocuspocus synced: ${name}`);
|
|
69
|
-
}
|
|
70
|
-
if (options?.onSynced) {
|
|
71
|
-
options.onSynced();
|
|
72
|
-
}
|
|
73
|
-
},
|
|
104
|
+
onStatus,
|
|
105
|
+
onConnect,
|
|
106
|
+
onDisconnect,
|
|
107
|
+
onSynced,
|
|
108
|
+
...reconnectConfig,
|
|
74
109
|
};
|
|
75
110
|
// Add optional settings
|
|
76
111
|
if (options?.forceSyncInterval !== undefined) {
|
|
@@ -95,48 +130,11 @@ export class HocuspocusSyncProvider {
|
|
|
95
130
|
document: doc,
|
|
96
131
|
token: options?.token || null,
|
|
97
132
|
autoConnect: false,
|
|
98
|
-
onStatus
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
onConnect: () => {
|
|
104
|
-
if (this.isConnected || this.isDestroyed) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
this.isConnected = true;
|
|
108
|
-
if (!options?.quiet) {
|
|
109
|
-
console.info(`Hocuspocus connected: ${name}`);
|
|
110
|
-
}
|
|
111
|
-
if (options?.onConnect) {
|
|
112
|
-
options.onConnect();
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
onDisconnect: () => {
|
|
116
|
-
if (this.isDestroyed || (!this.isConnected && !this.isSynced)) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
this.isConnected = false;
|
|
120
|
-
this.isSynced = false;
|
|
121
|
-
if (!options?.quiet) {
|
|
122
|
-
console.info(`Hocuspocus disconnected: ${name}`);
|
|
123
|
-
}
|
|
124
|
-
if (options?.onDisconnect) {
|
|
125
|
-
options.onDisconnect();
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
onSynced: () => {
|
|
129
|
-
if (this.isSynced || this.isDestroyed) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
this.isSynced = true;
|
|
133
|
-
if (!options?.quiet) {
|
|
134
|
-
console.info(`Hocuspocus synced: ${name}`);
|
|
135
|
-
}
|
|
136
|
-
if (options?.onSynced) {
|
|
137
|
-
options.onSynced();
|
|
138
|
-
}
|
|
139
|
-
},
|
|
133
|
+
onStatus,
|
|
134
|
+
onConnect,
|
|
135
|
+
onDisconnect,
|
|
136
|
+
onSynced,
|
|
137
|
+
...reconnectConfig,
|
|
140
138
|
};
|
|
141
139
|
// Add optional settings
|
|
142
140
|
if (options?.forceSyncInterval !== undefined) {
|
|
@@ -153,6 +151,35 @@ export class HocuspocusSyncProvider {
|
|
|
153
151
|
console.info(`Hocuspocus Provider initialized: ${url}/${name}`);
|
|
154
152
|
}
|
|
155
153
|
}
|
|
154
|
+
this.setupBrowserEventListeners();
|
|
155
|
+
}
|
|
156
|
+
setupBrowserEventListeners() {
|
|
157
|
+
if (typeof document !== 'undefined') {
|
|
158
|
+
this.visibilityHandler = () => {
|
|
159
|
+
if (document.visibilityState === 'visible' && !this.isConnected && !this.isDestroyed) {
|
|
160
|
+
this.provider.connect();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
document.addEventListener('visibilitychange', this.visibilityHandler);
|
|
164
|
+
}
|
|
165
|
+
if (typeof window !== 'undefined') {
|
|
166
|
+
this.onlineHandler = () => {
|
|
167
|
+
if (!this.isConnected && !this.isDestroyed) {
|
|
168
|
+
this.provider.connect();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
window.addEventListener('online', this.onlineHandler);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
removeBrowserEventListeners() {
|
|
175
|
+
if (this.visibilityHandler && typeof document !== 'undefined') {
|
|
176
|
+
document.removeEventListener('visibilitychange', this.visibilityHandler);
|
|
177
|
+
this.visibilityHandler = null;
|
|
178
|
+
}
|
|
179
|
+
if (this.onlineHandler && typeof window !== 'undefined') {
|
|
180
|
+
window.removeEventListener('online', this.onlineHandler);
|
|
181
|
+
this.onlineHandler = null;
|
|
182
|
+
}
|
|
156
183
|
}
|
|
157
184
|
/**
|
|
158
185
|
* Create a shared WebSocket connection for multiplexing
|
|
@@ -215,6 +242,7 @@ export class HocuspocusSyncProvider {
|
|
|
215
242
|
if (this.isSynced || this.isDestroyed) {
|
|
216
243
|
return;
|
|
217
244
|
}
|
|
245
|
+
this._connectionStatus = 'connecting';
|
|
218
246
|
return new Promise((resolve, reject) => {
|
|
219
247
|
// Store reject function so we can cancel the connection if destroyed
|
|
220
248
|
this.pendingConnectReject = reject;
|
|
@@ -222,7 +250,7 @@ export class HocuspocusSyncProvider {
|
|
|
222
250
|
this.pendingConnectReject = null;
|
|
223
251
|
this.connectTimeout = null;
|
|
224
252
|
reject(new Error('Hocuspocus connection timeout'));
|
|
225
|
-
},
|
|
253
|
+
}, this.connectionTimeoutMs);
|
|
226
254
|
const syncHandler = () => {
|
|
227
255
|
if (this.connectTimeout) {
|
|
228
256
|
clearTimeout(this.connectTimeout);
|
|
@@ -252,6 +280,10 @@ export class HocuspocusSyncProvider {
|
|
|
252
280
|
}
|
|
253
281
|
});
|
|
254
282
|
}
|
|
283
|
+
async reconnect() {
|
|
284
|
+
this.disconnect();
|
|
285
|
+
return this.connect();
|
|
286
|
+
}
|
|
255
287
|
disconnect() {
|
|
256
288
|
// Cancel any pending connection attempt
|
|
257
289
|
if (this.connectTimeout) {
|
|
@@ -272,6 +304,7 @@ export class HocuspocusSyncProvider {
|
|
|
272
304
|
}
|
|
273
305
|
this.isConnected = false;
|
|
274
306
|
this.isSynced = false;
|
|
307
|
+
this._connectionStatus = 'disconnected';
|
|
275
308
|
}
|
|
276
309
|
destroy() {
|
|
277
310
|
// Mark as destroyed first to prevent any callbacks from doing work
|
|
@@ -284,10 +317,12 @@ export class HocuspocusSyncProvider {
|
|
|
284
317
|
if (this.pendingConnectReject) {
|
|
285
318
|
this.pendingConnectReject = null; // Don't reject, just abandon the promise
|
|
286
319
|
}
|
|
320
|
+
this.removeBrowserEventListeners();
|
|
287
321
|
if (this.provider) {
|
|
288
322
|
this.provider.destroy();
|
|
289
323
|
}
|
|
290
324
|
this.isConnected = false;
|
|
291
325
|
this.isSynced = false;
|
|
326
|
+
this._connectionStatus = 'disconnected';
|
|
292
327
|
}
|
|
293
328
|
}
|
|
@@ -3,6 +3,7 @@ import { IndexeddbPersistence } from "y-indexeddb";
|
|
|
3
3
|
* IndexedDB sync provider for local persistence
|
|
4
4
|
*/
|
|
5
5
|
export class IndexedDBSyncProvider {
|
|
6
|
+
type = 'local';
|
|
6
7
|
provider;
|
|
7
8
|
isConnected = false;
|
|
8
9
|
constructor(docName, doc, options) {
|
|
@@ -24,6 +25,10 @@ export class IndexedDBSyncProvider {
|
|
|
24
25
|
// IndexedDB doesn't need explicit disconnect
|
|
25
26
|
this.isConnected = false;
|
|
26
27
|
}
|
|
28
|
+
async reconnect() {
|
|
29
|
+
this.disconnect();
|
|
30
|
+
return this.connect();
|
|
31
|
+
}
|
|
27
32
|
destroy() {
|
|
28
33
|
if (this.provider) {
|
|
29
34
|
this.provider.destroy();
|
|
@@ -3,6 +3,7 @@ import { WebsocketProvider } from "y-websocket";
|
|
|
3
3
|
* WebSocket sync provider for real-time collaboration
|
|
4
4
|
*/
|
|
5
5
|
export class WebSocketSyncProvider {
|
|
6
|
+
type = 'network';
|
|
6
7
|
provider;
|
|
7
8
|
isConnected = false;
|
|
8
9
|
_quiet = false;
|
|
@@ -91,6 +92,10 @@ export class WebSocketSyncProvider {
|
|
|
91
92
|
}
|
|
92
93
|
this.isConnected = false;
|
|
93
94
|
}
|
|
95
|
+
async reconnect() {
|
|
96
|
+
this.disconnect();
|
|
97
|
+
return this.connect();
|
|
98
|
+
}
|
|
94
99
|
destroy() {
|
|
95
100
|
if (this.provider) {
|
|
96
101
|
this.provider.destroy();
|
|
@@ -79,13 +79,24 @@ export class KritzelAppStateMap {
|
|
|
79
79
|
this.handleWorkspacesChange(event);
|
|
80
80
|
};
|
|
81
81
|
this._workspacesMap.observe(this._workspacesObserver);
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
// Separate local providers (IndexedDB, BroadcastChannel) from network providers (Hocuspocus, WebSocket)
|
|
83
|
+
// Local providers are awaited so data is available immediately; network providers sync in the background
|
|
84
|
+
// via Yjs observers that are already registered above.
|
|
85
|
+
const localProviders = this._providers.filter(p => p.type === 'local');
|
|
86
|
+
const networkProviders = this._providers.filter(p => p.type === 'network');
|
|
87
|
+
// Await local providers for immediate data availability
|
|
88
|
+
const localResults = await Promise.allSettled(localProviders.map(p => p.connect()));
|
|
89
|
+
localResults.forEach((result, i) => {
|
|
85
90
|
if (result.status === 'rejected') {
|
|
86
|
-
console.error(`[Kritzel] Sync provider "${
|
|
91
|
+
console.error(`[Kritzel] Sync provider "${localProviders[i]?.constructor.name}" failed to connect:`, result.reason);
|
|
87
92
|
}
|
|
88
93
|
});
|
|
94
|
+
// Connect network providers in the background (remote data arrives via Yjs observers)
|
|
95
|
+
for (const provider of networkProviders) {
|
|
96
|
+
provider.connect().catch(err => {
|
|
97
|
+
console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
89
100
|
this._isReady = true;
|
|
90
101
|
// Run any pending schema migrations before loading data
|
|
91
102
|
const quietMigrations = !core.store.state.debugInfo.showMigrationInfo;
|
|
@@ -137,6 +137,42 @@ export class KritzelObjectMap {
|
|
|
137
137
|
}
|
|
138
138
|
this._awareness.setLocalStateField('selectionBox', null);
|
|
139
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Removes selection groups whose owner is no longer present in awareness.
|
|
142
|
+
* Called when remote clients disconnect to prevent orphaned selection groups
|
|
143
|
+
* from persisting in the workspace state.
|
|
144
|
+
*/
|
|
145
|
+
removeOrphanedSelectionGroups() {
|
|
146
|
+
if (!this._awareness) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const states = this._awareness.getStates();
|
|
150
|
+
const activeUserIds = new Set();
|
|
151
|
+
states.forEach(state => {
|
|
152
|
+
const userId = state.user?.id;
|
|
153
|
+
if (userId) {
|
|
154
|
+
activeUserIds.add(userId);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
const localUserId = this._core?.user?.id;
|
|
158
|
+
const orphanedGroups = this.quadtree.filter(o => o instanceof KritzelSelectionGroup
|
|
159
|
+
&& o.userId != null
|
|
160
|
+
&& o.userId !== localUserId
|
|
161
|
+
&& !activeUserIds.has(o.userId));
|
|
162
|
+
for (const group of orphanedGroups) {
|
|
163
|
+
this.quadtree.remove(o => o.id === group.id);
|
|
164
|
+
this._idMap.delete(group.id);
|
|
165
|
+
if (this._objectsMap) {
|
|
166
|
+
this._ydoc.transact(() => {
|
|
167
|
+
this._objectsMap.delete(group.id);
|
|
168
|
+
}, 'local');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (orphanedGroups.length > 0) {
|
|
172
|
+
this._core?.store.invalidateSelectionCache();
|
|
173
|
+
this._core?.rerender();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
140
176
|
/**
|
|
141
177
|
* Registers a callback to be invoked when the awareness state changes.
|
|
142
178
|
* The callback receives the full awareness states map.
|
|
@@ -270,16 +306,28 @@ export class KritzelObjectMap {
|
|
|
270
306
|
this.handleObjectsChange(event);
|
|
271
307
|
};
|
|
272
308
|
this._objectsMap.observe(this._objectsObserver);
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
309
|
+
// Separate local providers (IndexedDB, BroadcastChannel) from network providers (Hocuspocus, WebSocket)
|
|
310
|
+
// Local providers are awaited so data is available immediately; network providers sync in the background
|
|
311
|
+
// via Yjs observers that are already registered above.
|
|
312
|
+
const localProviders = this._providers.filter(p => p.type === 'local');
|
|
313
|
+
const networkProviders = this._providers.filter(p => p.type === 'network');
|
|
314
|
+
// Await local providers for immediate data availability
|
|
315
|
+
const localResults = await Promise.allSettled(localProviders.map(p => p.connect()));
|
|
316
|
+
localResults.forEach((result, i) => {
|
|
276
317
|
if (result.status === 'rejected') {
|
|
277
|
-
console.error(`[Kritzel] Sync provider "${
|
|
318
|
+
console.error(`[Kritzel] Sync provider "${localProviders[i]?.constructor.name}" failed to connect:`, result.reason);
|
|
278
319
|
}
|
|
279
320
|
});
|
|
321
|
+
// Connect network providers in the background (remote data arrives via Yjs observers)
|
|
322
|
+
for (const provider of networkProviders) {
|
|
323
|
+
provider.connect().catch(err => {
|
|
324
|
+
console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
280
327
|
this._isReady = true;
|
|
281
328
|
// Find the first provider that exposes awareness (network providers)
|
|
282
|
-
|
|
329
|
+
// Awareness is available immediately after provider construction, before connect() resolves
|
|
330
|
+
for (const provider of networkProviders) {
|
|
283
331
|
if (provider.awareness) {
|
|
284
332
|
this._awareness = provider.awareness;
|
|
285
333
|
break;
|
|
@@ -287,7 +335,11 @@ export class KritzelObjectMap {
|
|
|
287
335
|
}
|
|
288
336
|
// Subscribe to awareness changes
|
|
289
337
|
if (this._awareness) {
|
|
290
|
-
this._awarenessChangeHandler = () => {
|
|
338
|
+
this._awarenessChangeHandler = (change) => {
|
|
339
|
+
// Clean up selection groups belonging to disconnected users
|
|
340
|
+
if (change.removed.length > 0) {
|
|
341
|
+
this.removeOrphanedSelectionGroups();
|
|
342
|
+
}
|
|
291
343
|
const now = Date.now();
|
|
292
344
|
const timeSinceLastEmit = now - this._lastAwarenessEmitTime;
|
|
293
345
|
// Clear any pending timeout since we have a new event
|
|
@@ -550,11 +602,27 @@ export class KritzelObjectMap {
|
|
|
550
602
|
}
|
|
551
603
|
this.quadtree.reset();
|
|
552
604
|
this._idMap.clear();
|
|
553
|
-
this.
|
|
605
|
+
const localUserId = this._core?.user?.id;
|
|
606
|
+
const staleSelectionGroupIds = [];
|
|
607
|
+
this._objectsMap.forEach((serialized, key) => {
|
|
554
608
|
const object = this._reviver.revive(serialized);
|
|
609
|
+
// Remove remote selection groups on startup — they are transient UI state
|
|
610
|
+
// that should not survive an app restart. The owning user's session is gone.
|
|
611
|
+
if (object instanceof KritzelSelectionGroup && object.userId != null && object.userId !== localUserId) {
|
|
612
|
+
staleSelectionGroupIds.push(key);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
555
615
|
this.quadtree.insert(object);
|
|
556
616
|
this._idMap.set(object.id, object);
|
|
557
617
|
});
|
|
618
|
+
// Clean up stale remote selection groups from Yjs
|
|
619
|
+
if (staleSelectionGroupIds.length > 0) {
|
|
620
|
+
this._ydoc.transact(() => {
|
|
621
|
+
for (const id of staleSelectionGroupIds) {
|
|
622
|
+
this._objectsMap.delete(id);
|
|
623
|
+
}
|
|
624
|
+
}, 'local');
|
|
625
|
+
}
|
|
558
626
|
}
|
|
559
627
|
/**
|
|
560
628
|
* Resets the object map by clearing both the local quadtree and the Yjs objects map.
|
package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.css
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
.awareness-cursor.stale {
|
|
22
|
-
opacity: 0
|
|
22
|
+
opacity: 0;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.awareness-cursor.tracking-object {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
.edge-indicator.stale {
|
|
67
|
-
opacity: 0
|
|
67
|
+
opacity: 0;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
.edge-indicator.tracking-object {
|
package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js
CHANGED
|
@@ -47,6 +47,10 @@ export class KritzelAwarenessCursors {
|
|
|
47
47
|
const cursor = state.cursor;
|
|
48
48
|
const activeObjectId = state.activeObjectId || null;
|
|
49
49
|
const selectionBox = state.selectionBox || null;
|
|
50
|
+
const existing = updated.get(clientId);
|
|
51
|
+
const cursorMoved = !existing ||
|
|
52
|
+
!existing.cursor !== !cursor ||
|
|
53
|
+
(cursor && existing.cursor && (cursor.x !== existing.cursor.x || cursor.y !== existing.cursor.y));
|
|
50
54
|
updated.set(clientId, {
|
|
51
55
|
clientId,
|
|
52
56
|
user,
|
|
@@ -54,6 +58,7 @@ export class KritzelAwarenessCursors {
|
|
|
54
58
|
activeObjectId,
|
|
55
59
|
selectionBox,
|
|
56
60
|
lastUpdated: now,
|
|
61
|
+
lastCursorMove: cursorMoved ? now : (existing?.lastCursorMove ?? now),
|
|
57
62
|
});
|
|
58
63
|
});
|
|
59
64
|
// Remove cursors for disconnected clients
|
|
@@ -79,7 +84,7 @@ export class KritzelAwarenessCursors {
|
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
isStale(cursor) {
|
|
82
|
-
return Date.now() - cursor.
|
|
87
|
+
return Date.now() - cursor.lastCursorMove > STALE_THRESHOLD_MS;
|
|
83
88
|
}
|
|
84
89
|
hasActiveDrawingCursors() {
|
|
85
90
|
for (const cursor of this.remoteCursors.values()) {
|
|
@@ -177,7 +182,7 @@ export class KritzelAwarenessCursors {
|
|
|
177
182
|
void _ty;
|
|
178
183
|
void this.objectVersion;
|
|
179
184
|
const cursors = Array.from(this.remoteCursors.values());
|
|
180
|
-
return (h(Host, { key: '
|
|
185
|
+
return (h(Host, { key: '4dd962322c7e955b9038c55cb10f8ffda1e0b246' }, cursors.map(remoteCursor => {
|
|
181
186
|
if (!remoteCursor.cursor)
|
|
182
187
|
return null;
|
|
183
188
|
// When a remote user is actively drawing, derive cursor position from
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-pebXO4LU.js";export{d as KritzelBrushTool,b as KritzelGroup,a as KritzelImage,e as KritzelLineTool,h as KritzelSelectionTool,c as KritzelShape,g as KritzelShapeTool,K as KritzelText,f as KritzelTextTool,S as ShapeType}from"./p-CJ2eHeoV.js";export{a as KritzelLine,K as KritzelPath}from"./p-DXpYcAnT.js";export{A as APP_STATE_MIGRATIONS,I as IndexedDBSyncProvider,d as KritzelAlignment,c as KritzelAnchorManager,b as KritzelCursorHelper,K as KritzelEraserTool,a as KritzelImageTool,W as WORKSPACE_MIGRATIONS,r as runMigrations}from"./p-RJWe82kG.js";import*as t from"yjs";import{WebsocketProvider as i}from"y-websocket";import{H as o,a as n}from"./kritzel-editor.js";export{D as DEFAULT_BRUSH_CONFIG,c as DEFAULT_LINE_TOOL_CONFIG,b as DEFAULT_TEXT_CONFIG,KritzelEditor,defineCustomElement as defineCustomElementKritzelEditor}from"./kritzel-editor.js";export{K as KritzelWorkspace,W as WORKSPACE_EXPORT_VERSION}from"./p-DhMlShij.js";export{K as KritzelThemeManager,d as darkTheme,l as lightTheme}from"./p-CjazGGq3.js";export{C as CURRENT_APP_STATE_SCHEMA_VERSION,a as CURRENT_WORKSPACE_SCHEMA_VERSION}from"./p-CW-VyJgK.js";export{KritzelActiveUsers,defineCustomElement as defineCustomElementKritzelActiveUsers}from"./kritzel-active-users.js";export{KritzelAvatar,defineCustomElement as defineCustomElementKritzelAvatar}from"./kritzel-avatar.js";export{KritzelAwarenessCursors,defineCustomElement as defineCustomElementKritzelAwarenessCursors}from"./kritzel-awareness-cursors.js";export{KritzelBackToContent,defineCustomElement as defineCustomElementKritzelBackToContent}from"./kritzel-back-to-content.js";export{KritzelBrushStyle,defineCustomElement as defineCustomElementKritzelBrushStyle}from"./kritzel-brush-style.js";export{KritzelButton,defineCustomElement as defineCustomElementKritzelButton}from"./kritzel-button.js";export{KritzelColor,defineCustomElement as defineCustomElementKritzelColor}from"./kritzel-color.js";export{KritzelColorPalette,defineCustomElement as defineCustomElementKritzelColorPalette}from"./kritzel-color-palette.js";export{KritzelContextMenu,defineCustomElement as defineCustomElementKritzelContextMenu}from"./kritzel-context-menu.js";export{KritzelControls,defineCustomElement as defineCustomElementKritzelControls}from"./kritzel-controls.js";export{KritzelCurrentUser,defineCustomElement as defineCustomElementKritzelCurrentUser}from"./kritzel-current-user.js";export{KritzelCurrentUserDialog,defineCustomElement as defineCustomElementKritzelCurrentUserDialog}from"./kritzel-current-user-dialog.js";export{KritzelCursorTrail,defineCustomElement as defineCustomElementKritzelCursorTrail}from"./kritzel-cursor-trail.js";export{KritzelDialog,defineCustomElement as defineCustomElementKritzelDialog}from"./kritzel-dialog.js";export{KritzelDropdown,defineCustomElement as defineCustomElementKritzelDropdown}from"./kritzel-dropdown.js";export{KritzelEngine,defineCustomElement as defineCustomElementKritzelEngine}from"./kritzel-engine.js";export{KritzelExport,defineCustomElement as defineCustomElementKritzelExport}from"./kritzel-export.js";export{KritzelFont,defineCustomElement as defineCustomElementKritzelFont}from"./kritzel-font.js";export{KritzelFontFamily,defineCustomElement as defineCustomElementKritzelFontFamily}from"./kritzel-font-family.js";export{KritzelFontSize,defineCustomElement as defineCustomElementKritzelFontSize}from"./kritzel-font-size.js";export{KritzelIcon,defineCustomElement as defineCustomElementKritzelIcon}from"./kritzel-icon.js";export{KritzelInput,defineCustomElement as defineCustomElementKritzelInput}from"./kritzel-input.js";export{KritzelLineEndings,defineCustomElement as defineCustomElementKritzelLineEndings}from"./kritzel-line-endings.js";export{KritzelLoginDialog,defineCustomElement as defineCustomElementKritzelLoginDialog}from"./kritzel-login-dialog.js";export{KritzelMasterDetail,defineCustomElement as defineCustomElementKritzelMasterDetail}from"./kritzel-master-detail.js";export{KritzelMenu,defineCustomElement as defineCustomElementKritzelMenu}from"./kritzel-menu.js";export{KritzelMenuItem,defineCustomElement as defineCustomElementKritzelMenuItem}from"./kritzel-menu-item.js";export{KritzelMoreMenu,defineCustomElement as defineCustomElementKritzelMoreMenu}from"./kritzel-more-menu.js";export{KritzelNumericInput,defineCustomElement as defineCustomElementKritzelNumericInput}from"./kritzel-numeric-input.js";export{KritzelOpacitySlider,defineCustomElement as defineCustomElementKritzelOpacitySlider}from"./kritzel-opacity-slider.js";export{KritzelPillTabs,defineCustomElement as defineCustomElementKritzelPillTabs}from"./kritzel-pill-tabs.js";export{KritzelPortal,defineCustomElement as defineCustomElementKritzelPortal}from"./kritzel-portal.js";export{KritzelSettings,defineCustomElement as defineCustomElementKritzelSettings}from"./kritzel-settings.js";export{KritzelShapeFill,defineCustomElement as defineCustomElementKritzelShapeFill}from"./kritzel-shape-fill.js";export{KritzelShareDialog,defineCustomElement as defineCustomElementKritzelShareDialog}from"./kritzel-share-dialog.js";export{KritzelSlideToggle,defineCustomElement as defineCustomElementKritzelSlideToggle}from"./kritzel-slide-toggle.js";export{KritzelSplitButton,defineCustomElement as defineCustomElementKritzelSplitButton}from"./kritzel-split-button.js";export{KritzelStrokeSize,defineCustomElement as defineCustomElementKritzelStrokeSize}from"./kritzel-stroke-size.js";export{KritzelToolConfig,defineCustomElement as defineCustomElementKritzelToolConfig}from"./kritzel-tool-config.js";export{KritzelTooltip,defineCustomElement as defineCustomElementKritzelTooltip}from"./kritzel-tooltip.js";export{KritzelUtilityPanel,defineCustomElement as defineCustomElementKritzelUtilityPanel}from"./kritzel-utility-panel.js";export{KritzelWorkspaceManager,defineCustomElement as defineCustomElementKritzelWorkspaceManager}from"./kritzel-workspace-manager.js";const m=Math.floor,u=127,z=Number.MAX_SAFE_INTEGER;class p{constructor(){this.cpos=0,this.cbuf=new Uint8Array(100),this.bufs=[]}}const E=()=>new p,k=e=>{const t=new Uint8Array((e=>{let t=e.cpos;for(let s=0;s<e.bufs.length;s++)t+=e.bufs[s].length;return t})(e));let s=0;for(let i=0;i<e.bufs.length;i++){const o=e.bufs[i];t.set(o,s),s+=o.length}return t.set(new Uint8Array(e.cbuf.buffer,0,e.cpos),s),t},x=(e,t)=>{const s=e.cbuf.length;e.cpos===s&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(2*s),e.cpos=0),e.cbuf[e.cpos++]=t},j=(e,t)=>{for(;t>u;)x(e,128|u&t),t=m(t/128);x(e,u&t)},T=(e,t)=>{j(e,t.byteLength),((e,t)=>{const s=e.cbuf.length,i=e.cpos,o=((e,t)=>e<t?e:t)(s-i,t.length),r=t.length-o;e.cbuf.set(t.subarray(0,o),i),e.cpos+=o,r>0&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(((e,t)=>e>t?e:t)(2*s,r)),e.cbuf.set(t.subarray(o)),e.cpos=r)})(e,t)},y=e=>Error(e),w=y("Unexpected end of array"),P=y("Integer out of Range");class M{constructor(e){this.arr=e,this.pos=0}}const U=e=>((e,t)=>{const s=new Uint8Array(e.arr.buffer,e.pos+e.arr.byteOffset,t);return e.pos+=t,s})(e,_(e)),_=e=>{let t=0,s=1;const i=e.arr.length;for(;e.pos<i;){const i=e.arr[e.pos++];if(t+=(i&u)*s,s*=128,i<128)return t;if(t>z)throw P}throw w};class v{doc;channel;_synced=!1;constructor(e,t,s){this.doc=t,this.channel=new BroadcastChannel(e),this.channel.onmessage=e=>{this.handleMessage(e.data)},this.doc.on("update",this.handleDocUpdate),this.broadcastSync(),setTimeout((()=>{this._synced=!0}),100),s?.quiet||console.info("BroadcastChannel Provider initialized: "+e)}handleDocUpdate=(e,t)=>{if(t!==this){const t=E();j(t,0),T(t,e),this.channel.postMessage(k(t))}};handleMessage(e){const s=(e=>new M(e))(new Uint8Array(e));switch(_(s)){case 0:const e=U(s);t.applyUpdate(this.doc,e,this);break;case 1:this.broadcastSync();break;case 2:const i=U(s),o=t.encodeStateAsUpdate(this.doc,i);if(o.length>0){const e=E();j(e,0),T(e,o),this.channel.postMessage(k(e))}}}broadcastSync(){const e=E();j(e,2),T(e,t.encodeStateVector(this.doc)),this.channel.postMessage(k(e))}async connect(){if(!this._synced)return new Promise((e=>{const t=()=>{this._synced?e():setTimeout(t,50)};t()}))}disconnect(){}destroy(){this.doc.off("update",this.handleDocUpdate),this.channel.close()}}class O{provider;isConnected=!1;_quiet=!1;get awareness(){return this.provider.awareness}constructor(e,t,s){const o=s?.url||"ws://localhost:1234",r=s?.roomName||e;this.provider=new i(o,r,t,{params:s?.params,protocols:s?.protocols,WebSocketPolyfill:s?.WebSocketPolyfill,awareness:s?.awareness,maxBackoffTime:s?.maxBackoffTime,disableBc:!0}),this._quiet=s?.quiet??!1,this.setupEventListeners(),this._quiet||console.info(`WebSocket Provider initialized: ${o}/${r}`)}static with(e){return{create:(t,s,i)=>{const o=i?{...e,...i}:e;return new O(t,s,o)}}}setupEventListeners(){this.provider.on("status",(({status:e})=>{"connected"===e?(this.isConnected=!0,this._quiet||console.info("WebSocket connected")):"disconnected"===e&&(this.isConnected=!1,this._quiet||console.info("WebSocket disconnected"))})),this.provider.on("sync",(e=>{e&&!this._quiet&&console.info("WebSocket synced")}))}async connect(){if(!this.isConnected)return new Promise(((e,t)=>{const s=setTimeout((()=>{t(Error("WebSocket connection timeout"))}),1e4),i=({status:t})=>{"connected"===t&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())};this.provider.on("status",i),this.provider.wsconnected&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())}))}disconnect(){this.provider&&this.provider.disconnect(),this.isConnected=!1}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class R{provider;isConnected=!1;isSynced=!1;usesSharedSocket=!1;isDestroyed=!1;connectTimeout=null;pendingConnectReject=null;get awareness(){return this.provider.awareness}static sharedWebSocketProvider=null;constructor(e,t,s){const i=s?.name||e,r=s?.url||"ws://localhost:1234",n=s?.websocketProvider||R.sharedWebSocketProvider;if(n){this.usesSharedSocket=!0;const e={websocketProvider:n,name:i,document:t,token:s?.token||null,onStatus:e=>{s?.onStatus&&s.onStatus(e)},onConnect:()=>{this.isConnected||this.isDestroyed||(this.isConnected=!0,s?.quiet||console.info("Hocuspocus connected: "+i),s?.onConnect&&s.onConnect())},onDisconnect:()=>{this.isDestroyed||!this.isConnected&&!this.isSynced||(this.isConnected=!1,this.isSynced=!1,s?.quiet||console.info("Hocuspocus disconnected: "+i),s?.onDisconnect&&s.onDisconnect())},onSynced:()=>{this.isSynced||this.isDestroyed||(this.isSynced=!0,s?.quiet||console.info("Hocuspocus synced: "+i),s?.onSynced&&s.onSynced())}};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),this.provider=new o(e),this.provider.attach(),s?.quiet||console.info("Hocuspocus Provider initialized (multiplexed): "+i)}else{this.usesSharedSocket=!1;const e={url:r,name:i,document:t,token:s?.token||null,autoConnect:!1,onStatus:e=>{s?.onStatus&&s.onStatus(e)},onConnect:()=>{this.isConnected||this.isDestroyed||(this.isConnected=!0,s?.quiet||console.info("Hocuspocus connected: "+i),s?.onConnect&&s.onConnect())},onDisconnect:()=>{this.isDestroyed||!this.isConnected&&!this.isSynced||(this.isConnected=!1,this.isSynced=!1,s?.quiet||console.info("Hocuspocus disconnected: "+i),s?.onDisconnect&&s.onDisconnect())},onSynced:()=>{this.isSynced||this.isDestroyed||(this.isSynced=!0,s?.quiet||console.info("Hocuspocus synced: "+i),s?.onSynced&&s.onSynced())}};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),s?.WebSocketPolyfill&&(e.WebSocketPolyfill=s.WebSocketPolyfill),this.provider=new o(e),s?.quiet||console.info(`Hocuspocus Provider initialized: ${r}/${i}`)}}static createSharedWebSocket(e){if(R.sharedWebSocketProvider)return console.warn("Shared WebSocket already exists. Returning existing instance."),R.sharedWebSocketProvider;const t={url:e.url};return e.WebSocketPolyfill&&(t.WebSocketPolyfill=e.WebSocketPolyfill),e.onConnect&&(t.onConnect=e.onConnect),e.onDisconnect&&(t.onDisconnect=e.onDisconnect),e.onStatus&&(t.onStatus=e.onStatus),R.sharedWebSocketProvider=new n(t),console.info("Shared Hocuspocus WebSocket created: "+e.url),R.sharedWebSocketProvider}static destroySharedWebSocket(){R.sharedWebSocketProvider&&(R.sharedWebSocketProvider.destroy(),R.sharedWebSocketProvider=null,console.info("Shared Hocuspocus WebSocket destroyed"))}static getSharedWebSocket(){return R.sharedWebSocketProvider}static with(e){return{create:(t,s,i)=>{const o=i?{...e,...i}:e;return new R(t,s,o)}}}async connect(){if(!this.isSynced&&!this.isDestroyed)return new Promise(((e,t)=>{this.pendingConnectReject=t,this.connectTimeout=setTimeout((()=>{this.pendingConnectReject=null,this.connectTimeout=null,t(Error("Hocuspocus connection timeout"))}),1e4);const s=()=>{this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),this.isDestroyed||e()};if(this.provider.on("synced",s),this.provider.isSynced)return this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),void e();this.isConnected||this.usesSharedSocket||this.provider.connect()}))}disconnect(){this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&(this.usesSharedSocket?this.provider.detach():this.provider.disconnect()),this.isConnected=!1,this.isSynced=!1}destroy(){this.isDestroyed=!0,this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&this.provider.destroy(),this.isConnected=!1,this.isSynced=!1}}export{v as BroadcastSyncProvider,R as HocuspocusSyncProvider,O as WebSocketSyncProvider}
|
|
1
|
+
export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-pebXO4LU.js";export{d as KritzelBrushTool,b as KritzelGroup,a as KritzelImage,e as KritzelLineTool,h as KritzelSelectionTool,c as KritzelShape,g as KritzelShapeTool,K as KritzelText,f as KritzelTextTool,S as ShapeType}from"./p-CJ2eHeoV.js";export{a as KritzelLine,K as KritzelPath}from"./p-DXpYcAnT.js";export{A as APP_STATE_MIGRATIONS,I as IndexedDBSyncProvider,d as KritzelAlignment,c as KritzelAnchorManager,b as KritzelCursorHelper,K as KritzelEraserTool,a as KritzelImageTool,W as WORKSPACE_MIGRATIONS,r as runMigrations}from"./p-jdYmu4SA.js";import*as t from"yjs";import{WebsocketProvider as i}from"y-websocket";import{H as n,a as o}from"./kritzel-editor.js";export{D as DEFAULT_BRUSH_CONFIG,c as DEFAULT_LINE_TOOL_CONFIG,b as DEFAULT_TEXT_CONFIG,KritzelEditor,defineCustomElement as defineCustomElementKritzelEditor}from"./kritzel-editor.js";export{K as KritzelWorkspace,W as WORKSPACE_EXPORT_VERSION}from"./p-DhMlShij.js";export{K as KritzelThemeManager,d as darkTheme,l as lightTheme}from"./p-CjazGGq3.js";export{C as CURRENT_APP_STATE_SCHEMA_VERSION,a as CURRENT_WORKSPACE_SCHEMA_VERSION}from"./p-CW-VyJgK.js";export{KritzelActiveUsers,defineCustomElement as defineCustomElementKritzelActiveUsers}from"./kritzel-active-users.js";export{KritzelAvatar,defineCustomElement as defineCustomElementKritzelAvatar}from"./kritzel-avatar.js";export{KritzelAwarenessCursors,defineCustomElement as defineCustomElementKritzelAwarenessCursors}from"./kritzel-awareness-cursors.js";export{KritzelBackToContent,defineCustomElement as defineCustomElementKritzelBackToContent}from"./kritzel-back-to-content.js";export{KritzelBrushStyle,defineCustomElement as defineCustomElementKritzelBrushStyle}from"./kritzel-brush-style.js";export{KritzelButton,defineCustomElement as defineCustomElementKritzelButton}from"./kritzel-button.js";export{KritzelColor,defineCustomElement as defineCustomElementKritzelColor}from"./kritzel-color.js";export{KritzelColorPalette,defineCustomElement as defineCustomElementKritzelColorPalette}from"./kritzel-color-palette.js";export{KritzelContextMenu,defineCustomElement as defineCustomElementKritzelContextMenu}from"./kritzel-context-menu.js";export{KritzelControls,defineCustomElement as defineCustomElementKritzelControls}from"./kritzel-controls.js";export{KritzelCurrentUser,defineCustomElement as defineCustomElementKritzelCurrentUser}from"./kritzel-current-user.js";export{KritzelCurrentUserDialog,defineCustomElement as defineCustomElementKritzelCurrentUserDialog}from"./kritzel-current-user-dialog.js";export{KritzelCursorTrail,defineCustomElement as defineCustomElementKritzelCursorTrail}from"./kritzel-cursor-trail.js";export{KritzelDialog,defineCustomElement as defineCustomElementKritzelDialog}from"./kritzel-dialog.js";export{KritzelDropdown,defineCustomElement as defineCustomElementKritzelDropdown}from"./kritzel-dropdown.js";export{KritzelEngine,defineCustomElement as defineCustomElementKritzelEngine}from"./kritzel-engine.js";export{KritzelExport,defineCustomElement as defineCustomElementKritzelExport}from"./kritzel-export.js";export{KritzelFont,defineCustomElement as defineCustomElementKritzelFont}from"./kritzel-font.js";export{KritzelFontFamily,defineCustomElement as defineCustomElementKritzelFontFamily}from"./kritzel-font-family.js";export{KritzelFontSize,defineCustomElement as defineCustomElementKritzelFontSize}from"./kritzel-font-size.js";export{KritzelIcon,defineCustomElement as defineCustomElementKritzelIcon}from"./kritzel-icon.js";export{KritzelInput,defineCustomElement as defineCustomElementKritzelInput}from"./kritzel-input.js";export{KritzelLineEndings,defineCustomElement as defineCustomElementKritzelLineEndings}from"./kritzel-line-endings.js";export{KritzelLoginDialog,defineCustomElement as defineCustomElementKritzelLoginDialog}from"./kritzel-login-dialog.js";export{KritzelMasterDetail,defineCustomElement as defineCustomElementKritzelMasterDetail}from"./kritzel-master-detail.js";export{KritzelMenu,defineCustomElement as defineCustomElementKritzelMenu}from"./kritzel-menu.js";export{KritzelMenuItem,defineCustomElement as defineCustomElementKritzelMenuItem}from"./kritzel-menu-item.js";export{KritzelMoreMenu,defineCustomElement as defineCustomElementKritzelMoreMenu}from"./kritzel-more-menu.js";export{KritzelNumericInput,defineCustomElement as defineCustomElementKritzelNumericInput}from"./kritzel-numeric-input.js";export{KritzelOpacitySlider,defineCustomElement as defineCustomElementKritzelOpacitySlider}from"./kritzel-opacity-slider.js";export{KritzelPillTabs,defineCustomElement as defineCustomElementKritzelPillTabs}from"./kritzel-pill-tabs.js";export{KritzelPortal,defineCustomElement as defineCustomElementKritzelPortal}from"./kritzel-portal.js";export{KritzelSettings,defineCustomElement as defineCustomElementKritzelSettings}from"./kritzel-settings.js";export{KritzelShapeFill,defineCustomElement as defineCustomElementKritzelShapeFill}from"./kritzel-shape-fill.js";export{KritzelShareDialog,defineCustomElement as defineCustomElementKritzelShareDialog}from"./kritzel-share-dialog.js";export{KritzelSlideToggle,defineCustomElement as defineCustomElementKritzelSlideToggle}from"./kritzel-slide-toggle.js";export{KritzelSplitButton,defineCustomElement as defineCustomElementKritzelSplitButton}from"./kritzel-split-button.js";export{KritzelStrokeSize,defineCustomElement as defineCustomElementKritzelStrokeSize}from"./kritzel-stroke-size.js";export{KritzelToolConfig,defineCustomElement as defineCustomElementKritzelToolConfig}from"./kritzel-tool-config.js";export{KritzelTooltip,defineCustomElement as defineCustomElementKritzelTooltip}from"./kritzel-tooltip.js";export{KritzelUtilityPanel,defineCustomElement as defineCustomElementKritzelUtilityPanel}from"./kritzel-utility-panel.js";export{KritzelWorkspaceManager,defineCustomElement as defineCustomElementKritzelWorkspaceManager}from"./kritzel-workspace-manager.js";const m=Math.floor,u=127,z=Number.MAX_SAFE_INTEGER;class p{constructor(){this.cpos=0,this.cbuf=new Uint8Array(100),this.bufs=[]}}const E=()=>new p,k=e=>{const t=new Uint8Array((e=>{let t=e.cpos;for(let s=0;s<e.bufs.length;s++)t+=e.bufs[s].length;return t})(e));let s=0;for(let i=0;i<e.bufs.length;i++){const n=e.bufs[i];t.set(n,s),s+=n.length}return t.set(new Uint8Array(e.cbuf.buffer,0,e.cpos),s),t},x=(e,t)=>{const s=e.cbuf.length;e.cpos===s&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(2*s),e.cpos=0),e.cbuf[e.cpos++]=t},y=(e,t)=>{for(;t>u;)x(e,128|u&t),t=m(t/128);x(e,u&t)},j=(e,t)=>{y(e,t.byteLength),((e,t)=>{const s=e.cbuf.length,i=e.cpos,n=((e,t)=>e<t?e:t)(s-i,t.length),o=t.length-n;e.cbuf.set(t.subarray(0,n),i),e.cpos+=n,o>0&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(((e,t)=>e>t?e:t)(2*s,o)),e.cbuf.set(t.subarray(n)),e.cpos=o)})(e,t)},T=e=>Error(e),w=T("Unexpected end of array"),v=T("Integer out of Range");class P{constructor(e){this.arr=e,this.pos=0}}const M=e=>((e,t)=>{const s=new Uint8Array(e.arr.buffer,e.pos+e.arr.byteOffset,t);return e.pos+=t,s})(e,U(e)),U=e=>{let t=0,s=1;const i=e.arr.length;for(;e.pos<i;){const i=e.arr[e.pos++];if(t+=(i&u)*s,s*=128,i<128)return t;if(t>z)throw v}throw w};class _{type="local";doc;channel;_synced=!1;constructor(e,t,s){this.doc=t,this.channel=new BroadcastChannel(e),this.channel.onmessage=e=>{this.handleMessage(e.data)},this.doc.on("update",this.handleDocUpdate),this.broadcastSync(),setTimeout((()=>{this._synced=!0}),100),s?.quiet||console.info("BroadcastChannel Provider initialized: "+e)}handleDocUpdate=(e,t)=>{if(t!==this){const t=E();y(t,0),j(t,e),this.channel.postMessage(k(t))}};handleMessage(e){const s=(e=>new P(e))(new Uint8Array(e));switch(U(s)){case 0:const e=M(s);t.applyUpdate(this.doc,e,this);break;case 1:this.broadcastSync();break;case 2:const i=M(s),n=t.encodeStateAsUpdate(this.doc,i);if(n.length>0){const e=E();y(e,0),j(e,n),this.channel.postMessage(k(e))}}}broadcastSync(){const e=E();y(e,2),j(e,t.encodeStateVector(this.doc)),this.channel.postMessage(k(e))}async connect(){if(!this._synced)return new Promise((e=>{const t=()=>{this._synced?e():setTimeout(t,50)};t()}))}disconnect(){}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleDocUpdate),this.channel.close()}}class O{type="network";provider;isConnected=!1;_quiet=!1;get awareness(){return this.provider.awareness}constructor(e,t,s){const n=s?.url||"ws://localhost:1234",o=s?.roomName||e;this.provider=new i(n,o,t,{params:s?.params,protocols:s?.protocols,WebSocketPolyfill:s?.WebSocketPolyfill,awareness:s?.awareness,maxBackoffTime:s?.maxBackoffTime,disableBc:!0}),this._quiet=s?.quiet??!1,this.setupEventListeners(),this._quiet||console.info(`WebSocket Provider initialized: ${n}/${o}`)}static with(e){return{create:(t,s,i)=>{const n=i?{...e,...i}:e;return new O(t,s,n)}}}setupEventListeners(){this.provider.on("status",(({status:e})=>{"connected"===e?(this.isConnected=!0,this._quiet||console.info("WebSocket connected")):"disconnected"===e&&(this.isConnected=!1,this._quiet||console.info("WebSocket disconnected"))})),this.provider.on("sync",(e=>{e&&!this._quiet&&console.info("WebSocket synced")}))}async connect(){if(!this.isConnected)return new Promise(((e,t)=>{const s=setTimeout((()=>{t(Error("WebSocket connection timeout"))}),1e4),i=({status:t})=>{"connected"===t&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())};this.provider.on("status",i),this.provider.wsconnected&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())}))}disconnect(){this.provider&&this.provider.disconnect(),this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class B{type="network";provider;isConnected=!1;isSynced=!1;usesSharedSocket=!1;isDestroyed=!1;connectTimeout=null;pendingConnectReject=null;connectionTimeoutMs;_connectionStatus="disconnected";visibilityHandler=null;onlineHandler=null;get awareness(){return this.provider.awareness}get connectionStatus(){return this._connectionStatus}static sharedWebSocketProvider=null;constructor(e,t,s){const i=s?.name||e,o=s?.url||"ws://localhost:1234";this.connectionTimeoutMs=s?.connectionTimeout??1e4;const r=s?.websocketProvider||B.sharedWebSocketProvider,l={};void 0!==s?.delay&&(l.delay=s.delay),void 0!==s?.factor&&(l.factor=s.factor),void 0!==s?.maxAttempts&&(l.maxAttempts=s.maxAttempts),void 0!==s?.minDelay&&(l.minDelay=s.minDelay),void 0!==s?.maxDelay&&(l.maxDelay=s.maxDelay);const m=()=>{this.isDestroyed||(this.isConnected=!0,this._connectionStatus="connected",s?.quiet||console.info("Hocuspocus connected: "+i),s?.onConnect&&s.onConnect())},a=()=>{this.isDestroyed||(this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected",s?.quiet||console.info("Hocuspocus disconnected: "+i),s?.onDisconnect&&s.onDisconnect())},c=()=>{this.isDestroyed||(this.isSynced=!0,this._connectionStatus="synced",s?.quiet||console.info("Hocuspocus synced: "+i),s?.onSynced&&s.onSynced())},u=e=>{this.isDestroyed||("connecting"===e.status&&(this._connectionStatus="connecting"),s?.onStatus&&s.onStatus(e))};if(r){this.usesSharedSocket=!0;const e={websocketProvider:r,name:i,document:t,token:s?.token||null,onStatus:u,onConnect:m,onDisconnect:a,onSynced:c,...l};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),this.provider=new n(e),this.provider.attach(),s?.quiet||console.info("Hocuspocus Provider initialized (multiplexed): "+i)}else{this.usesSharedSocket=!1;const e={url:o,name:i,document:t,token:s?.token||null,autoConnect:!1,onStatus:u,onConnect:m,onDisconnect:a,onSynced:c,...l};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),s?.WebSocketPolyfill&&(e.WebSocketPolyfill=s.WebSocketPolyfill),this.provider=new n(e),s?.quiet||console.info(`Hocuspocus Provider initialized: ${o}/${i}`)}this.setupBrowserEventListeners()}setupBrowserEventListeners(){"undefined"!=typeof document&&(this.visibilityHandler=()=>{"visible"!==document.visibilityState||this.isConnected||this.isDestroyed||this.provider.connect()},document.addEventListener("visibilitychange",this.visibilityHandler)),"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isConnected||this.isDestroyed||this.provider.connect()},window.addEventListener("online",this.onlineHandler))}removeBrowserEventListeners(){this.visibilityHandler&&"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.onlineHandler&&"undefined"!=typeof window&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null)}static createSharedWebSocket(e){if(B.sharedWebSocketProvider)return console.warn("Shared WebSocket already exists. Returning existing instance."),B.sharedWebSocketProvider;const t={url:e.url};return e.WebSocketPolyfill&&(t.WebSocketPolyfill=e.WebSocketPolyfill),e.onConnect&&(t.onConnect=e.onConnect),e.onDisconnect&&(t.onDisconnect=e.onDisconnect),e.onStatus&&(t.onStatus=e.onStatus),B.sharedWebSocketProvider=new o(t),console.info("Shared Hocuspocus WebSocket created: "+e.url),B.sharedWebSocketProvider}static destroySharedWebSocket(){B.sharedWebSocketProvider&&(B.sharedWebSocketProvider.destroy(),B.sharedWebSocketProvider=null,console.info("Shared Hocuspocus WebSocket destroyed"))}static getSharedWebSocket(){return B.sharedWebSocketProvider}static with(e){return{create:(t,s,i)=>{const n=i?{...e,...i}:e;return new B(t,s,n)}}}async connect(){if(!this.isSynced&&!this.isDestroyed)return this._connectionStatus="connecting",new Promise(((e,t)=>{this.pendingConnectReject=t,this.connectTimeout=setTimeout((()=>{this.pendingConnectReject=null,this.connectTimeout=null,t(Error("Hocuspocus connection timeout"))}),this.connectionTimeoutMs);const s=()=>{this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),this.isDestroyed||e()};if(this.provider.on("synced",s),this.provider.isSynced)return this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),void e();this.isConnected||this.usesSharedSocket||this.provider.connect()}))}async reconnect(){return this.disconnect(),this.connect()}disconnect(){this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&(this.usesSharedSocket?this.provider.detach():this.provider.disconnect()),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}destroy(){this.isDestroyed=!0,this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.removeBrowserEventListeners(),this.provider&&this.provider.destroy(),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}}export{_ as BroadcastSyncProvider,B as HocuspocusSyncProvider,O as WebSocketSyncProvider}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{K as o,d as
|
|
1
|
+
import{K as o,d as s}from"./p-xNwOWoiT.js";const p=o,r=s;export{p as KritzelAwarenessCursors,r as defineCustomElement}
|