kritzel-stencil 0.1.75 → 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.
@@ -176,7 +176,7 @@ const KritzelAvatar = class {
176
176
  };
177
177
  KritzelAvatar.style = kritzelAvatarCss();
178
178
 
179
- const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:9500}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0.3}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0.3}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3))}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
179
+ const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:9500}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3))}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
180
180
 
181
181
  const STALE_THRESHOLD_MS = 10_000;
182
182
  const REMOVE_THRESHOLD_MS = 30_000;
@@ -227,6 +227,10 @@ const KritzelAwarenessCursors = class {
227
227
  const cursor = state.cursor;
228
228
  const activeObjectId = state.activeObjectId || null;
229
229
  const selectionBox = state.selectionBox || null;
230
+ const existing = updated.get(clientId);
231
+ const cursorMoved = !existing ||
232
+ !existing.cursor !== !cursor ||
233
+ (cursor && existing.cursor && (cursor.x !== existing.cursor.x || cursor.y !== existing.cursor.y));
230
234
  updated.set(clientId, {
231
235
  clientId,
232
236
  user,
@@ -234,6 +238,7 @@ const KritzelAwarenessCursors = class {
234
238
  activeObjectId,
235
239
  selectionBox,
236
240
  lastUpdated: now,
241
+ lastCursorMove: cursorMoved ? now : (existing?.lastCursorMove ?? now),
237
242
  });
238
243
  });
239
244
  // Remove cursors for disconnected clients
@@ -259,7 +264,7 @@ const KritzelAwarenessCursors = class {
259
264
  }
260
265
  }
261
266
  isStale(cursor) {
262
- return Date.now() - cursor.lastUpdated > STALE_THRESHOLD_MS;
267
+ return Date.now() - cursor.lastCursorMove > STALE_THRESHOLD_MS;
263
268
  }
264
269
  hasActiveDrawingCursors() {
265
270
  for (const cursor of this.remoteCursors.values()) {
@@ -348,7 +353,7 @@ const KritzelAwarenessCursors = class {
348
353
  }
349
354
  render() {
350
355
  const cursors = Array.from(this.remoteCursors.values());
351
- return (index.h(index.Host, { key: 'b8676d9a7a3d4a79ea8ad9763355ecf4c2f90e17' }, cursors.map(remoteCursor => {
356
+ return (index.h(index.Host, { key: '4dd962322c7e955b9038c55cb10f8ffda1e0b246' }, cursors.map(remoteCursor => {
352
357
  if (!remoteCursor.cursor)
353
358
  return null;
354
359
  // When a remote user is actively drawing, derive cursor position from
@@ -19840,6 +19845,14 @@ class KritzelViewport {
19840
19845
  startX = 0;
19841
19846
  /** Starting Y position for pan/zoom gestures */
19842
19847
  startY = 0;
19848
+ /** Minimum movement distance (in screen pixels) before broadcasting touch cursor position */
19849
+ static TOUCH_CURSOR_BROADCAST_THRESHOLD = 5;
19850
+ /** Screen X position where the current touch interaction started */
19851
+ _touchStartScreenX = 0;
19852
+ /** Screen Y position where the current touch interaction started */
19853
+ _touchStartScreenY = 0;
19854
+ /** Whether the touch movement threshold has been exceeded for cursor broadcasting */
19855
+ _touchCursorBroadcastActive = false;
19843
19856
  /**
19844
19857
  * Creates a new KritzelViewport instance and initializes viewport state.
19845
19858
  * Sets up debounced handlers for viewport updates and scaling end events.
@@ -19967,6 +19980,11 @@ class KritzelViewport {
19967
19980
  }
19968
19981
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
19969
19982
  const activePointers = Array.from(this._core.store.state.pointers.values());
19983
+ if (activePointers.length === 1) {
19984
+ this._touchStartScreenX = event.clientX;
19985
+ this._touchStartScreenY = event.clientY;
19986
+ this._touchCursorBroadcastActive = false;
19987
+ }
19970
19988
  if (activePointers.length === 2) {
19971
19989
  this._core.store.state.objects?.clearCursorPosition();
19972
19990
  const currentPath = this._core.store.currentPath;
@@ -20036,7 +20054,16 @@ class KritzelViewport {
20036
20054
  else {
20037
20055
  this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
20038
20056
  this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
20039
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
20057
+ if (!this._touchCursorBroadcastActive) {
20058
+ const dx = event.clientX - this._touchStartScreenX;
20059
+ const dy = event.clientY - this._touchStartScreenY;
20060
+ if (Math.sqrt(dx * dx + dy * dy) >= KritzelViewport.TOUCH_CURSOR_BROADCAST_THRESHOLD) {
20061
+ this._touchCursorBroadcastActive = true;
20062
+ }
20063
+ }
20064
+ if (this._touchCursorBroadcastActive) {
20065
+ this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
20066
+ }
20040
20067
  }
20041
20068
  if (activePointers.length === 2) {
20042
20069
  const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
@@ -20089,6 +20116,7 @@ class KritzelViewport {
20089
20116
  }
20090
20117
  }
20091
20118
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
20119
+ this._touchCursorBroadcastActive = false;
20092
20120
  if (this._core.store.state.pointers.size === 0) {
20093
20121
  this._debounceEndScaling();
20094
20122
  }
@@ -21368,6 +21396,42 @@ class KritzelObjectMap {
21368
21396
  }
21369
21397
  this._awareness.setLocalStateField('selectionBox', null);
21370
21398
  }
21399
+ /**
21400
+ * Removes selection groups whose owner is no longer present in awareness.
21401
+ * Called when remote clients disconnect to prevent orphaned selection groups
21402
+ * from persisting in the workspace state.
21403
+ */
21404
+ removeOrphanedSelectionGroups() {
21405
+ if (!this._awareness) {
21406
+ return;
21407
+ }
21408
+ const states = this._awareness.getStates();
21409
+ const activeUserIds = new Set();
21410
+ states.forEach(state => {
21411
+ const userId = state.user?.id;
21412
+ if (userId) {
21413
+ activeUserIds.add(userId);
21414
+ }
21415
+ });
21416
+ const localUserId = this._core?.user?.id;
21417
+ const orphanedGroups = this.quadtree.filter(o => o instanceof workspace_migrations.KritzelSelectionGroup
21418
+ && o.userId != null
21419
+ && o.userId !== localUserId
21420
+ && !activeUserIds.has(o.userId));
21421
+ for (const group of orphanedGroups) {
21422
+ this.quadtree.remove(o => o.id === group.id);
21423
+ this._idMap.delete(group.id);
21424
+ if (this._objectsMap) {
21425
+ this._ydoc.transact(() => {
21426
+ this._objectsMap.delete(group.id);
21427
+ }, 'local');
21428
+ }
21429
+ }
21430
+ if (orphanedGroups.length > 0) {
21431
+ this._core?.store.invalidateSelectionCache();
21432
+ this._core?.rerender();
21433
+ }
21434
+ }
21371
21435
  /**
21372
21436
  * Registers a callback to be invoked when the awareness state changes.
21373
21437
  * The callback receives the full awareness states map.
@@ -21530,7 +21594,11 @@ class KritzelObjectMap {
21530
21594
  }
21531
21595
  // Subscribe to awareness changes
21532
21596
  if (this._awareness) {
21533
- this._awarenessChangeHandler = () => {
21597
+ this._awarenessChangeHandler = (change) => {
21598
+ // Clean up selection groups belonging to disconnected users
21599
+ if (change.removed.length > 0) {
21600
+ this.removeOrphanedSelectionGroups();
21601
+ }
21534
21602
  const now = Date.now();
21535
21603
  const timeSinceLastEmit = now - this._lastAwarenessEmitTime;
21536
21604
  // Clear any pending timeout since we have a new event
@@ -21793,11 +21861,27 @@ class KritzelObjectMap {
21793
21861
  }
21794
21862
  this.quadtree.reset();
21795
21863
  this._idMap.clear();
21796
- this._objectsMap.forEach(serialized => {
21864
+ const localUserId = this._core?.user?.id;
21865
+ const staleSelectionGroupIds = [];
21866
+ this._objectsMap.forEach((serialized, key) => {
21797
21867
  const object = this._reviver.revive(serialized);
21868
+ // Remove remote selection groups on startup — they are transient UI state
21869
+ // that should not survive an app restart. The owning user's session is gone.
21870
+ if (object instanceof workspace_migrations.KritzelSelectionGroup && object.userId != null && object.userId !== localUserId) {
21871
+ staleSelectionGroupIds.push(key);
21872
+ return;
21873
+ }
21798
21874
  this.quadtree.insert(object);
21799
21875
  this._idMap.set(object.id, object);
21800
21876
  });
21877
+ // Clean up stale remote selection groups from Yjs
21878
+ if (staleSelectionGroupIds.length > 0) {
21879
+ this._ydoc.transact(() => {
21880
+ for (const id of staleSelectionGroupIds) {
21881
+ this._objectsMap.delete(id);
21882
+ }
21883
+ }, 'local');
21884
+ }
21801
21885
  }
21802
21886
  /**
21803
21887
  * Resets the object map by clearing both the local quadtree and the Yjs objects map.
@@ -28437,7 +28521,7 @@ const KritzelPortal = class {
28437
28521
  * This file is auto-generated by the version bump scripts.
28438
28522
  * Do not modify manually.
28439
28523
  */
28440
- const KRITZEL_VERSION = '0.1.75';
28524
+ const KRITZEL_VERSION = '0.1.76';
28441
28525
 
28442
28526
  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)}`;
28443
28527
 
@@ -21,6 +21,14 @@ export class KritzelViewport {
21
21
  startX = 0;
22
22
  /** Starting Y position for pan/zoom gestures */
23
23
  startY = 0;
24
+ /** Minimum movement distance (in screen pixels) before broadcasting touch cursor position */
25
+ static TOUCH_CURSOR_BROADCAST_THRESHOLD = 5;
26
+ /** Screen X position where the current touch interaction started */
27
+ _touchStartScreenX = 0;
28
+ /** Screen Y position where the current touch interaction started */
29
+ _touchStartScreenY = 0;
30
+ /** Whether the touch movement threshold has been exceeded for cursor broadcasting */
31
+ _touchCursorBroadcastActive = false;
24
32
  /**
25
33
  * Creates a new KritzelViewport instance and initializes viewport state.
26
34
  * Sets up debounced handlers for viewport updates and scaling end events.
@@ -148,6 +156,11 @@ export class KritzelViewport {
148
156
  }
149
157
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
150
158
  const activePointers = Array.from(this._core.store.state.pointers.values());
159
+ if (activePointers.length === 1) {
160
+ this._touchStartScreenX = event.clientX;
161
+ this._touchStartScreenY = event.clientY;
162
+ this._touchCursorBroadcastActive = false;
163
+ }
151
164
  if (activePointers.length === 2) {
152
165
  this._core.store.state.objects?.clearCursorPosition();
153
166
  const currentPath = this._core.store.currentPath;
@@ -217,7 +230,16 @@ export class KritzelViewport {
217
230
  else {
218
231
  this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
219
232
  this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
220
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
233
+ if (!this._touchCursorBroadcastActive) {
234
+ const dx = event.clientX - this._touchStartScreenX;
235
+ const dy = event.clientY - this._touchStartScreenY;
236
+ if (Math.sqrt(dx * dx + dy * dy) >= KritzelViewport.TOUCH_CURSOR_BROADCAST_THRESHOLD) {
237
+ this._touchCursorBroadcastActive = true;
238
+ }
239
+ }
240
+ if (this._touchCursorBroadcastActive) {
241
+ this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
242
+ }
221
243
  }
222
244
  if (activePointers.length === 2) {
223
245
  const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
@@ -270,6 +292,7 @@ export class KritzelViewport {
270
292
  }
271
293
  }
272
294
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
295
+ this._touchCursorBroadcastActive = false;
273
296
  if (this._core.store.state.pointers.size === 0) {
274
297
  this._debounceEndScaling();
275
298
  }
@@ -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.
@@ -299,7 +335,11 @@ export class KritzelObjectMap {
299
335
  }
300
336
  // Subscribe to awareness changes
301
337
  if (this._awareness) {
302
- 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
+ }
303
343
  const now = Date.now();
304
344
  const timeSinceLastEmit = now - this._lastAwarenessEmitTime;
305
345
  // Clear any pending timeout since we have a new event
@@ -562,11 +602,27 @@ export class KritzelObjectMap {
562
602
  }
563
603
  this.quadtree.reset();
564
604
  this._idMap.clear();
565
- this._objectsMap.forEach(serialized => {
605
+ const localUserId = this._core?.user?.id;
606
+ const staleSelectionGroupIds = [];
607
+ this._objectsMap.forEach((serialized, key) => {
566
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
+ }
567
615
  this.quadtree.insert(object);
568
616
  this._idMap.set(object.id, object);
569
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
+ }
570
626
  }
571
627
  /**
572
628
  * Resets the object map by clearing both the local quadtree and the Yjs objects map.
@@ -19,7 +19,7 @@
19
19
  }
20
20
 
21
21
  .awareness-cursor.stale {
22
- opacity: 0.3;
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.3;
67
+ opacity: 0;
68
68
  }
69
69
 
70
70
  .edge-indicator.tracking-object {
@@ -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.lastUpdated > STALE_THRESHOLD_MS;
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: 'b8676d9a7a3d4a79ea8ad9763355ecf4c2f90e17' }, cursors.map(remoteCursor => {
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
@@ -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.1.75';
6
+ export const KRITZEL_VERSION = '0.1.76';
@@ -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-WmxufeOo.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
+ 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 p}from"./p-BSipRoFx.js";const s=o,r=p;export{s as KritzelAwarenessCursors,r as defineCustomElement}
1
+ import{K as o,d as s}from"./p-xNwOWoiT.js";const p=o,r=s;export{p as KritzelAwarenessCursors,r as defineCustomElement}