kritzel-stencil 0.1.73 → 0.1.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/cjs/index-Dc7LOVhs.js +2 -2
  2. package/dist/cjs/index.cjs.js +131 -86
  3. package/dist/cjs/{kritzel-active-users_41.cjs.entry.js → kritzel-active-users_42.cjs.entry.js} +593 -177
  4. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/cjs/{workspace.migrations-DcwqsqPC.js → workspace.migrations-DkmVO6dE.js} +164 -49
  8. package/dist/collection/classes/core/core.class.js +9 -1
  9. package/dist/collection/classes/core/store.class.js +20 -6
  10. package/dist/collection/classes/core/viewport.class.js +9 -3
  11. package/dist/collection/classes/handlers/selection.handler.js +15 -2
  12. package/dist/collection/classes/managers/anchor.manager.js +101 -44
  13. package/dist/collection/classes/objects/base-object.class.js +2 -0
  14. package/dist/collection/classes/objects/custom-element.class.js +1 -0
  15. package/dist/collection/classes/objects/group.class.js +1 -0
  16. package/dist/collection/classes/objects/image.class.js +1 -0
  17. package/dist/collection/classes/objects/line.class.js +1 -0
  18. package/dist/collection/classes/objects/path.class.js +1 -0
  19. package/dist/collection/classes/objects/selection-box.class.js +1 -0
  20. package/dist/collection/classes/objects/selection-group.class.js +13 -1
  21. package/dist/collection/classes/objects/shape.class.js +1 -0
  22. package/dist/collection/classes/objects/text.class.js +1 -0
  23. package/dist/collection/classes/providers/broadcast-sync-provider.class.js +5 -0
  24. package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js +120 -85
  25. package/dist/collection/classes/providers/indexeddb-sync-provider.class.js +5 -0
  26. package/dist/collection/classes/providers/websocket-sync-provider.class.js +5 -0
  27. package/dist/collection/classes/structures/app-state-map.structure.js +15 -4
  28. package/dist/collection/classes/structures/object-map.structure.js +85 -7
  29. package/dist/collection/classes/tools/brush-tool.class.js +4 -0
  30. package/dist/collection/classes/tools/line-tool.class.js +4 -0
  31. package/dist/collection/classes/tools/shape-tool.class.js +2 -0
  32. package/dist/collection/collection-manifest.json +3 -2
  33. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.css +110 -0
  34. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +347 -0
  35. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  36. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +3 -3
  37. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +150 -109
  38. package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
  39. package/dist/collection/components/shared/kritzel-brush-style/kritzel-brush-style.js +1 -1
  40. package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
  41. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  42. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  43. package/dist/collection/components/shared/kritzel-dropdown/kritzel-dropdown.js +1 -1
  44. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  45. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  46. package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
  47. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  48. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  49. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  50. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  51. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
  52. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  53. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  54. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  55. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  56. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
  57. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  58. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +1 -1
  59. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +5 -5
  60. package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +1 -1
  61. package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
  62. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
  63. package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
  64. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +1 -1
  65. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +1 -1
  66. package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
  67. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  68. package/dist/collection/constants/schema.constants.js +1 -1
  69. package/dist/collection/constants/version.js +1 -1
  70. package/dist/collection/interfaces/remote-cursor.interface.js +1 -0
  71. package/dist/collection/migrations/workspace.migrations.js +10 -1
  72. package/dist/components/index.d.ts +2 -0
  73. package/dist/components/index.js +1 -1
  74. package/dist/components/kritzel-active-users.js +1 -1
  75. package/dist/components/kritzel-avatar.js +1 -1
  76. package/dist/components/kritzel-awareness-cursors.d.ts +11 -0
  77. package/dist/components/kritzel-awareness-cursors.js +1 -0
  78. package/dist/components/kritzel-back-to-content.js +1 -1
  79. package/dist/components/kritzel-brush-style.js +1 -1
  80. package/dist/components/kritzel-button.js +1 -1
  81. package/dist/components/kritzel-color-palette.js +1 -1
  82. package/dist/components/kritzel-color.js +1 -1
  83. package/dist/components/kritzel-context-menu.js +1 -1
  84. package/dist/components/kritzel-controls.js +1 -1
  85. package/dist/components/kritzel-current-user-dialog.js +1 -1
  86. package/dist/components/kritzel-current-user.js +1 -1
  87. package/dist/components/kritzel-cursor-trail.js +1 -1
  88. package/dist/components/kritzel-dropdown.js +1 -1
  89. package/dist/components/kritzel-editor.js +1 -1
  90. package/dist/components/kritzel-engine.js +1 -1
  91. package/dist/components/kritzel-export.js +1 -1
  92. package/dist/components/kritzel-font-family.js +1 -1
  93. package/dist/components/kritzel-font-size.js +1 -1
  94. package/dist/components/kritzel-font.js +1 -1
  95. package/dist/components/kritzel-input.js +1 -1
  96. package/dist/components/kritzel-login-dialog.js +1 -1
  97. package/dist/components/kritzel-master-detail.js +1 -1
  98. package/dist/components/kritzel-menu-item.js +1 -1
  99. package/dist/components/kritzel-menu.js +1 -1
  100. package/dist/components/kritzel-more-menu.js +1 -1
  101. package/dist/components/kritzel-numeric-input.js +1 -1
  102. package/dist/components/kritzel-opacity-slider.js +1 -1
  103. package/dist/components/kritzel-portal.js +1 -1
  104. package/dist/components/kritzel-settings.js +1 -1
  105. package/dist/components/kritzel-share-dialog.js +1 -1
  106. package/dist/components/kritzel-slide-toggle.js +1 -1
  107. package/dist/components/kritzel-split-button.js +1 -1
  108. package/dist/components/kritzel-stroke-size.js +1 -1
  109. package/dist/components/kritzel-tool-config.js +1 -1
  110. package/dist/components/kritzel-tooltip.js +1 -1
  111. package/dist/components/kritzel-utility-panel.js +1 -1
  112. package/dist/components/kritzel-workspace-manager.js +1 -1
  113. package/dist/components/{p-Dp8idtVD.js → p-0kShCfeb.js} +1 -1
  114. package/dist/components/{p-B47JuZiD.js → p-2OYw6GJ7.js} +1 -1
  115. package/dist/components/p-7o2FWtFx.js +1 -0
  116. package/dist/components/{p-C5KuV1pK.js → p-BA0ayKqO.js} +1 -1
  117. package/dist/components/{p-NbNVTRk6.js → p-BEJQ2kP7.js} +1 -1
  118. package/dist/components/p-BSipRoFx.js +1 -0
  119. package/dist/components/{p-CDadAOMw.js → p-BeFUNGEI.js} +1 -1
  120. package/dist/components/{p-35nrk8s0.js → p-BiByyU2C.js} +1 -1
  121. package/dist/components/{p-CCAWSyDD.js → p-BiouZo1q.js} +1 -1
  122. package/dist/components/{p-CSExtYKI.js → p-ByR0VXeU.js} +1 -1
  123. package/dist/components/{p-1MGcXTLv.js → p-C1uR_ZNW.js} +1 -1
  124. package/dist/components/{p-x8PzaMuD.js → p-C69Stayh.js} +1 -1
  125. package/dist/components/{p-Ch0UlFwq.js → p-C7SBI_0T.js} +1 -1
  126. package/dist/components/{p-DEzfXrGX.js → p-CAIGuV2J.js} +1 -1
  127. package/dist/components/p-CJ2eHeoV.js +1 -0
  128. package/dist/components/p-CW-VyJgK.js +1 -0
  129. package/dist/components/{p-DW4ADV9w.js → p-CZhyKp-f.js} +1 -1
  130. package/dist/components/p-CsR4owzk.js +1 -0
  131. package/dist/components/{p-BG1IxseV.js → p-CsoDfhD5.js} +1 -1
  132. package/dist/components/{p-BgDhcDNw.js → p-D0MQFmqi.js} +1 -1
  133. package/dist/components/{p-DpFu5yAt.js → p-D1O7DxL4.js} +1 -1
  134. package/dist/components/{p-B5ouV8EQ.js → p-DRbG92F9.js} +1 -1
  135. package/dist/components/{p-C3eaM9TB.js → p-DS0xx1eT.js} +1 -1
  136. package/dist/components/{p-jx8VOz7S.js → p-DSzQ6H2j.js} +1 -1
  137. package/dist/components/{p-DsIlDGDO.js → p-DXjuuVq9.js} +1 -1
  138. package/dist/components/p-DXpYcAnT.js +1 -0
  139. package/dist/components/{p-DiFVw6IQ.js → p-Da46jw3N.js} +1 -1
  140. package/dist/components/{p-C6kZf91d.js → p-Dj_Qjga5.js} +1 -1
  141. package/dist/components/{p-Do0Q5-iC.js → p-DvIEvoZu.js} +1 -1
  142. package/dist/components/{p-CnVzLD5e.js → p-GYI7sDxr.js} +1 -1
  143. package/dist/components/{p-CcBM_ClD.js → p-HLbqRJGs.js} +1 -1
  144. package/dist/components/{p-VHyNcODZ.js → p-KQzWumjB.js} +1 -1
  145. package/dist/components/{p-VAkeZOZL.js → p-TyR-YTXm.js} +1 -1
  146. package/dist/components/p-WmxufeOo.js +9 -0
  147. package/dist/components/{p-CHtn5xr6.js → p-b4gyXoju.js} +1 -1
  148. package/dist/components/p-iRL0wQHQ.js +1 -0
  149. package/dist/components/{p-CqLaHE27.js → p-kj9wbLY8.js} +1 -1
  150. package/dist/components/{p-DaHq4iG1.js → p-xM-_OeRO.js} +1 -1
  151. package/dist/esm/index-MV-81ybv.js +2 -2
  152. package/dist/esm/index.js +132 -87
  153. package/dist/esm/{kritzel-active-users_41.entry.js → kritzel-active-users_42.entry.js} +593 -178
  154. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  155. package/dist/esm/loader.js +1 -1
  156. package/dist/esm/stencil.js +1 -1
  157. package/dist/esm/{workspace.migrations-BGixvB76.js → workspace.migrations-D48_Bqvh.js} +164 -49
  158. package/dist/stencil/index.esm.js +1 -1
  159. package/dist/stencil/p-0dbd9a2f.entry.js +9 -0
  160. package/dist/stencil/p-D48_Bqvh.js +1 -0
  161. package/dist/stencil/{p-016ad76a.entry.js → p-fc21e29c.entry.js} +1 -1
  162. package/dist/stencil/stencil.esm.js +1 -1
  163. package/dist/types/classes/core/store.class.d.ts +10 -2
  164. package/dist/types/classes/managers/anchor.manager.d.ts +4 -0
  165. package/dist/types/classes/objects/base-object.class.d.ts +1 -0
  166. package/dist/types/classes/objects/selection-group.class.d.ts +5 -0
  167. package/dist/types/classes/providers/broadcast-sync-provider.class.d.ts +2 -0
  168. package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +37 -1
  169. package/dist/types/classes/providers/indexeddb-sync-provider.class.d.ts +2 -0
  170. package/dist/types/classes/providers/websocket-sync-provider.class.d.ts +2 -0
  171. package/dist/types/classes/structures/object-map.structure.d.ts +38 -0
  172. package/dist/types/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +26 -0
  173. package/dist/types/components.d.ts +39 -4
  174. package/dist/types/constants/schema.constants.d.ts +1 -1
  175. package/dist/types/constants/version.d.ts +1 -1
  176. package/dist/types/interfaces/object.interface.d.ts +1 -0
  177. package/dist/types/interfaces/remote-cursor.interface.d.ts +17 -0
  178. package/dist/types/interfaces/sync-provider.interface.d.ts +16 -0
  179. package/dist/types/interfaces/theme.interface.d.ts +7 -0
  180. package/package.json +1 -1
  181. package/dist/components/p-BvToKcu1.js +0 -1
  182. package/dist/components/p-By3NOY-k.js +0 -9
  183. package/dist/components/p-CNro30tB.js +0 -1
  184. package/dist/components/p-Duv3EM3w.js +0 -1
  185. package/dist/components/p-KFsLHwYm.js +0 -1
  186. package/dist/components/p-hCORwbZh.js +0 -1
  187. package/dist/stencil/p-67c79d75.entry.js +0 -9
  188. package/dist/stencil/p-BGixvB76.js +0 -1
@@ -266,6 +266,7 @@ class KritzelBaseObject {
266
266
  rotation = 0;
267
267
  markedForRemoval = false;
268
268
  zIndex = 0;
269
+ userId;
269
270
  /** Whether the object is currently visible on the canvas */
270
271
  isVisible = true;
271
272
  isSelected = false;
@@ -446,6 +447,7 @@ class KritzelBaseObject {
446
447
  object._core = core;
447
448
  object.zIndex = core.store.currentZIndex;
448
449
  object.workspaceId = core.store.state.activeWorkspace.id;
450
+ object.userId = core.user?.id;
449
451
  return object;
450
452
  }
451
453
  /**
@@ -15296,6 +15298,7 @@ class KritzelText extends KritzelBaseObject {
15296
15298
  object._core = core;
15297
15299
  object.id = object.generateId();
15298
15300
  object.workspaceId = core.store.state.activeWorkspace.id;
15301
+ object.userId = core.user?.id;
15299
15302
  object.fontSize = fontSize;
15300
15303
  object.fontFamily = fontFamily;
15301
15304
  object.translateX = 0;
@@ -15685,6 +15688,7 @@ class KritzelPath extends KritzelBaseObject {
15685
15688
  object._core = core;
15686
15689
  object.id = object.generateId();
15687
15690
  object.workspaceId = core.store.state.activeWorkspace.id;
15691
+ object.userId = core.user?.id;
15688
15692
  object.options = options;
15689
15693
  object.points = options?.points ?? [];
15690
15694
  object.translateX = options?.translateX ?? 0;
@@ -16186,6 +16190,7 @@ class KritzelImage extends KritzelBaseObject {
16186
16190
  object._core = core;
16187
16191
  object.id = object.generateId();
16188
16192
  object.workspaceId = core.store.state.activeWorkspace.id;
16193
+ object.userId = core.user?.id;
16189
16194
  object.x = 0;
16190
16195
  object.y = 0;
16191
16196
  object.translateX = 0;
@@ -16336,6 +16341,7 @@ class KritzelLine extends KritzelBaseObject {
16336
16341
  object._core = core;
16337
16342
  object.id = object.generateId();
16338
16343
  object.workspaceId = core.store.state.activeWorkspace.id;
16344
+ object.userId = core.user?.id;
16339
16345
  object.options = options;
16340
16346
  object.startX = options?.startX ?? 0;
16341
16347
  object.startY = options?.startY ?? 0;
@@ -17095,6 +17101,7 @@ class KritzelGroup extends KritzelBaseObject {
17095
17101
  group._core = core;
17096
17102
  group.id = group.generateId();
17097
17103
  group.workspaceId = core.store.state.activeWorkspace.id;
17104
+ group.userId = core.user?.id;
17098
17105
  group.scale = core.store.state.scale;
17099
17106
  group.zIndex = core.store.currentZIndex;
17100
17107
  return group;
@@ -17646,6 +17653,7 @@ class KritzelShape extends KritzelBaseObject {
17646
17653
  object._core = core;
17647
17654
  object.id = object.generateId();
17648
17655
  object.workspaceId = core.store.state.activeWorkspace.id;
17656
+ object.userId = core.user?.id;
17649
17657
  object.x = config?.x ?? 0;
17650
17658
  object.y = config?.y ?? 0;
17651
17659
  object.translateX = config?.translateX ?? 0;
@@ -18326,6 +18334,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18326
18334
  path.isCompleted = false;
18327
18335
  this._currentPathId = path.id;
18328
18336
  this._core.store.state.objects.insert(path);
18337
+ this._core.store.state.objects?.setActiveDrawingObject(path.id);
18329
18338
  }
18330
18339
  }
18331
18340
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
@@ -18349,6 +18358,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18349
18358
  path.isCompleted = false;
18350
18359
  this._currentPathId = path.id;
18351
18360
  this._core.store.state.objects.insert(path);
18361
+ this._core.store.state.objects?.setActiveDrawingObject(path.id);
18352
18362
  }
18353
18363
  }
18354
18364
  }
@@ -18439,6 +18449,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18439
18449
  this._core.engine.emitObjectsChange();
18440
18450
  this._core.engine.emitObjectsAdded([currentPath]);
18441
18451
  }
18452
+ this._core.store.state.objects?.setActiveDrawingObject(null);
18442
18453
  this._currentPathId = null;
18443
18454
  }
18444
18455
  }
@@ -18454,6 +18465,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18454
18465
  this._core.engine.emitObjectsChange();
18455
18466
  this._core.engine.emitObjectsAdded([currentPath]);
18456
18467
  }
18468
+ this._core.store.state.objects?.setActiveDrawingObject(null);
18457
18469
  this._currentPathId = null;
18458
18470
  }
18459
18471
  }
@@ -18484,6 +18496,10 @@ class KritzelSelectionGroup extends KritzelBaseObject {
18484
18496
  maxX;
18485
18497
  minY;
18486
18498
  maxY;
18499
+ // Selection styling properties
18500
+ handleColor;
18501
+ handleStrokeColor;
18502
+ handleSize = 6;
18487
18503
  /**
18488
18504
  * Gets the array of object IDs contained in this selection group.
18489
18505
  * @returns Array of string IDs for the selected objects
@@ -18538,6 +18554,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
18538
18554
  /**
18539
18555
  * Factory method to create a new KritzelSelectionGroup instance.
18540
18556
  * Initializes the selection group with default properties and associates it with the core instance.
18557
+ * Default styling uses values from the theme system (selection section).
18541
18558
  * @param core - The KritzelCore instance to associate with this selection group
18542
18559
  * @returns A new KritzelSelectionGroup instance configured with default settings
18543
18560
  */
@@ -18546,8 +18563,15 @@ class KritzelSelectionGroup extends KritzelBaseObject {
18546
18563
  object._core = core;
18547
18564
  object.id = object.generateId();
18548
18565
  object.workspaceId = core.store.state.activeWorkspace.id;
18566
+ object.userId = core.user?.id;
18549
18567
  object.scale = core.store.state.scale;
18550
18568
  object.zIndex = 99999;
18569
+ // Initialize styling with theme-aware defaults
18570
+ object.borderColor = { light: '#007AFF', dark: '#0A84FF' };
18571
+ object.borderWidth = 2;
18572
+ object.handleColor = { light: '#ffffff', dark: '#1a1a1a' };
18573
+ object.handleStrokeColor = { light: '#007AFF', dark: '#0A84FF' };
18574
+ object.handleSize = 6;
18551
18575
  return object;
18552
18576
  }
18553
18577
  /**
@@ -18713,7 +18737,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
18713
18737
  * @returns A serializable object representation of the selection group
18714
18738
  */
18715
18739
  serialize() {
18716
- const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, _cachedObjects, _cachedObjectIdsHash, snapshotWidth, snapshotHeight, snapshotTranslateX, snapshotTranslateY, ...remainingProps } = this;
18740
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, _cachedObjects, _cachedObjectIdsHash, snapshotWidth, snapshotHeight, snapshotTranslateX, snapshotTranslateY, snapshotRotation, ...remainingProps } = this;
18717
18741
  const clonedProps = structuredClone(remainingProps);
18718
18742
  // Ensure objectIds is serialized with the correct key name (getter returns _objectIds)
18719
18743
  clonedProps.objectIds = this.objectIds;
@@ -19103,6 +19127,7 @@ class KritzelLineTool extends KritzelBaseTool {
19103
19127
  line.isCompleted = false;
19104
19128
  this._currentLineId = line.id;
19105
19129
  this._core.store.state.objects.insert(line);
19130
+ this._core.store.state.objects?.setActiveDrawingObject(line.id);
19106
19131
  }
19107
19132
  }
19108
19133
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
@@ -19132,6 +19157,7 @@ class KritzelLineTool extends KritzelBaseTool {
19132
19157
  line.isCompleted = false;
19133
19158
  this._currentLineId = line.id;
19134
19159
  this._core.store.state.objects.insert(line);
19160
+ this._core.store.state.objects?.setActiveDrawingObject(line.id);
19135
19161
  }
19136
19162
  }
19137
19163
  }
@@ -19230,6 +19256,7 @@ class KritzelLineTool extends KritzelBaseTool {
19230
19256
  // Switch to selection tool and select the drawn line
19231
19257
  this.selectLineAndSwitchTool(currentLine);
19232
19258
  }
19259
+ this._core.store.state.objects?.setActiveDrawingObject(null);
19233
19260
  this._currentLineId = null;
19234
19261
  }
19235
19262
  }
@@ -19247,6 +19274,7 @@ class KritzelLineTool extends KritzelBaseTool {
19247
19274
  // Switch to selection tool and select the drawn line
19248
19275
  this.selectLineAndSwitchTool(currentLine);
19249
19276
  }
19277
+ this._core.store.state.objects?.setActiveDrawingObject(null);
19250
19278
  this._currentLineId = null;
19251
19279
  }
19252
19280
  }
@@ -19828,6 +19856,7 @@ class KritzelShapeTool extends KritzelBaseTool {
19828
19856
  scale: lockScale ? 1 : viewportScale,
19829
19857
  });
19830
19858
  this._core.store.state.objects.insert(this.currentShape);
19859
+ this._core.store.state.objects?.setActiveDrawingObject(this.currentShape.id);
19831
19860
  this._core.rerender();
19832
19861
  }
19833
19862
  /**
@@ -19888,6 +19917,7 @@ class KritzelShapeTool extends KritzelBaseTool {
19888
19917
  this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
19889
19918
  }
19890
19919
  this.isDrawing = false;
19920
+ this._core.store.state.objects?.setActiveDrawingObject(null);
19891
19921
  this.currentShape = null;
19892
19922
  this._core.rerender();
19893
19923
  }
@@ -20838,6 +20868,7 @@ class KritzelSelectionBox extends KritzelBaseObject {
20838
20868
  object._core = core;
20839
20869
  object.id = object.generateId();
20840
20870
  object.workspaceId = core.store.state.activeWorkspace.id;
20871
+ object.userId = core.user?.id;
20841
20872
  object.scale = core.store.state.scale;
20842
20873
  object.zIndex = 99999;
20843
20874
  object.backgroundColor = { light: 'rgba(0, 122, 255, 0.2)', dark: 'rgba(0, 122, 255, 0.2)' };
@@ -21002,6 +21033,7 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
21002
21033
  this._core.store.state.isSelecting = false;
21003
21034
  this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox);
21004
21035
  this._core.store.setSelectionBox(null);
21036
+ this._core.store.state.objects.clearLocalSelectionBox();
21005
21037
  }
21006
21038
  /**
21007
21039
  * Initiates a mouse-based selection by creating a selection box at the click position.
@@ -21020,7 +21052,7 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
21020
21052
  selectionBox.translateX = this.startX;
21021
21053
  selectionBox.translateY = this.startY;
21022
21054
  this._core.store.state.isSelecting = true;
21023
- this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup));
21055
+ this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup && (o.userId == null || o.userId === this._core.user?.id)));
21024
21056
  this._core.store.setSelectionBox(null);
21025
21057
  if (!isAdditive) {
21026
21058
  this._core.store.setSelectionGroup(null);
@@ -21052,7 +21084,7 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
21052
21084
  selectionBox.translateX = this.startX;
21053
21085
  selectionBox.translateY = this.startY;
21054
21086
  this._core.store.state.isSelecting = true;
21055
- this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup));
21087
+ this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup && (o.userId == null || o.userId === this._core.user?.id)));
21056
21088
  this._core.store.setSelectionBox(null);
21057
21089
  if (!isAdditive) {
21058
21090
  this._core.store.setSelectionGroup(null);
@@ -21080,6 +21112,12 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
21080
21112
  selectionBox.height = height;
21081
21113
  selectionBox.translateX = Math.min(currentX, this.startX);
21082
21114
  selectionBox.translateY = Math.min(currentY, this.startY);
21115
+ this._core.store.state.objects.setLocalSelectionBox({
21116
+ x: selectionBox.translateX,
21117
+ y: selectionBox.translateY,
21118
+ width: width / selectionBox.scale,
21119
+ height: height / selectionBox.scale,
21120
+ });
21083
21121
  if (width > 0 || height > 0) {
21084
21122
  this.updateSelectedObjects();
21085
21123
  }
@@ -21113,6 +21151,12 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
21113
21151
  selectionBox.height = height;
21114
21152
  selectionBox.translateX = Math.min(currentX, this.startX);
21115
21153
  selectionBox.translateY = Math.min(currentY, this.startY);
21154
+ this._core.store.state.objects.setLocalSelectionBox({
21155
+ x: selectionBox.translateX,
21156
+ y: selectionBox.translateY,
21157
+ width: width / selectionBox.scale,
21158
+ height: height / selectionBox.scale,
21159
+ });
21116
21160
  if (width > 0 || height > 0) {
21117
21161
  this.updateSelectedObjects();
21118
21162
  }
@@ -22342,6 +22386,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
22342
22386
  * IndexedDB sync provider for local persistence
22343
22387
  */
22344
22388
  class IndexedDBSyncProvider {
22389
+ type = 'local';
22345
22390
  provider;
22346
22391
  isConnected = false;
22347
22392
  constructor(docName, doc, options) {
@@ -22363,6 +22408,10 @@ class IndexedDBSyncProvider {
22363
22408
  // IndexedDB doesn't need explicit disconnect
22364
22409
  this.isConnected = false;
22365
22410
  }
22411
+ async reconnect() {
22412
+ this.disconnect();
22413
+ return this.connect();
22414
+ }
22366
22415
  destroy() {
22367
22416
  if (this.provider) {
22368
22417
  this.provider.destroy();
@@ -25558,16 +25607,22 @@ class KritzelAnchorManager {
25558
25607
  if (!KritzelClassHelper.isInstanceOf(selectedObject, 'KritzelLine'))
25559
25608
  return null;
25560
25609
  const line = selectedObject;
25561
- const startAnchorViz = this.computeAnchorVisualization(line, 'start');
25562
- const endAnchorViz = this.computeAnchorVisualization(line, 'end');
25610
+ const startAnchorVizRaw = this.computeAnchorVisualization(line, 'start');
25611
+ const endAnchorVizRaw = this.computeAnchorVisualization(line, 'end');
25612
+ const startAnchorViz = this.isFiniteAnchorVisualization(startAnchorVizRaw) ? startAnchorVizRaw : null;
25613
+ const endAnchorViz = this.isFiniteAnchorVisualization(endAnchorVizRaw) ? endAnchorVizRaw : null;
25563
25614
  if (!startAnchorViz && !endAnchorViz)
25564
25615
  return null;
25565
- const scale = this._core.store.state.scale;
25566
- const lineStrokeWidth = line.strokeWidth / line.scale;
25616
+ const scale = this.getSafeScale(this._core.store.state.scale);
25617
+ const lineScale = this.getSafeScale(line.scale);
25618
+ const lineStrokeWidth = line.strokeWidth / lineScale;
25567
25619
  const indicatorStrokeWidth = `${2 / scale}`;
25568
25620
  const dashLength = Math.max(lineStrokeWidth * 2, 4 / scale);
25569
25621
  const dashArray = `${dashLength} ${dashLength}`;
25570
25622
  const indicatorRadius = 8 / scale;
25623
+ if (!this.areFiniteNumbers(lineStrokeWidth, dashLength, indicatorRadius)) {
25624
+ return null;
25625
+ }
25571
25626
  return {
25572
25627
  lineStrokeWidth,
25573
25628
  indicatorStrokeWidth,
@@ -25589,31 +25644,39 @@ class KritzelAnchorManager {
25589
25644
  const snapCandidate = this.getSnapCandidate();
25590
25645
  if (!snapCandidate)
25591
25646
  return null;
25592
- const scale = this._core.store.state.scale;
25647
+ if (!this.areFiniteNumbers(snapCandidate.centerX, snapCandidate.centerY, snapCandidate.lineEndpointX, snapCandidate.lineEndpointY)) {
25648
+ return null;
25649
+ }
25650
+ const scale = this.getSafeScale(this._core.store.state.scale);
25593
25651
  const indicatorRadius = 8 / scale;
25594
25652
  const indicatorStrokeWidth = `${2 / scale}`;
25595
- const lineStrokeWidth = snapCandidate.lineStrokeWidth
25596
- ? `${snapCandidate.lineStrokeWidth}`
25597
- : `${4 / scale}`;
25598
- const lineStrokeWidthNum = snapCandidate.lineStrokeWidth || (4 / scale);
25653
+ const lineStrokeWidthNum = this.areFiniteNumbers(snapCandidate.lineStrokeWidth) && snapCandidate.lineStrokeWidth > 0
25654
+ ? snapCandidate.lineStrokeWidth
25655
+ : (4 / scale);
25656
+ const lineStrokeWidth = `${lineStrokeWidthNum}`;
25599
25657
  const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
25600
25658
  const dashArray = `${dashLength} ${dashLength}`;
25601
25659
  const lineStroke = snapCandidate.lineStroke || '#000000';
25602
- let solidLineEndX = snapCandidate.edgeX;
25603
- let solidLineEndY = snapCandidate.edgeY;
25660
+ let edgeX = this.areFiniteNumbers(snapCandidate.edgeX) ? snapCandidate.edgeX : undefined;
25661
+ let edgeY = this.areFiniteNumbers(snapCandidate.edgeY) ? snapCandidate.edgeY : undefined;
25662
+ let solidLineEndX = edgeX;
25663
+ let solidLineEndY = edgeY;
25604
25664
  let arrowPoints;
25605
- if (snapCandidate.arrowOffset && snapCandidate.edgeX !== undefined && snapCandidate.edgeY !== undefined) {
25606
- const dx = snapCandidate.lineEndpointX - snapCandidate.edgeX;
25607
- const dy = snapCandidate.lineEndpointY - snapCandidate.edgeY;
25665
+ const arrowOffset = this.areFiniteNumbers(snapCandidate.arrowOffset) && snapCandidate.arrowOffset > 0
25666
+ ? snapCandidate.arrowOffset
25667
+ : undefined;
25668
+ if (arrowOffset !== undefined && edgeX !== undefined && edgeY !== undefined) {
25669
+ const dx = snapCandidate.lineEndpointX - edgeX;
25670
+ const dy = snapCandidate.lineEndpointY - edgeY;
25608
25671
  const length = Math.sqrt(dx * dx + dy * dy);
25609
- if (length > snapCandidate.arrowOffset) {
25610
- solidLineEndX = snapCandidate.edgeX + (dx / length) * snapCandidate.arrowOffset;
25611
- solidLineEndY = snapCandidate.edgeY + (dy / length) * snapCandidate.arrowOffset;
25672
+ if (length > arrowOffset) {
25673
+ solidLineEndX = edgeX + (dx / length) * arrowOffset;
25674
+ solidLineEndY = edgeY + (dy / length) * arrowOffset;
25612
25675
  }
25613
25676
  // Calculate arrow head points
25614
25677
  // Direction from line endpoint to edge (arrow direction)
25615
- const arrowDx = snapCandidate.edgeX - snapCandidate.lineEndpointX;
25616
- const arrowDy = snapCandidate.edgeY - snapCandidate.lineEndpointY;
25678
+ const arrowDx = edgeX - snapCandidate.lineEndpointX;
25679
+ const arrowDy = edgeY - snapCandidate.lineEndpointY;
25617
25680
  const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
25618
25681
  if (arrowLengthTotal > 0) {
25619
25682
  const ux = arrowDx / arrowLengthTotal;
@@ -25622,11 +25685,11 @@ class KritzelAnchorManager {
25622
25685
  const px = -uy;
25623
25686
  const py = ux;
25624
25687
  // Arrow dimensions
25625
- const arrowLength = snapCandidate.arrowOffset;
25688
+ const arrowLength = arrowOffset;
25626
25689
  const arrowWidth = arrowLength; // 1:1 ratio
25627
25690
  // Arrow tip at edge
25628
- const tipX = snapCandidate.edgeX;
25629
- const tipY = snapCandidate.edgeY;
25691
+ const tipX = edgeX;
25692
+ const tipY = edgeY;
25630
25693
  // Arrow base
25631
25694
  const baseX = tipX - ux * arrowLength;
25632
25695
  const baseY = tipY - uy * arrowLength;
@@ -25635,9 +25698,31 @@ class KritzelAnchorManager {
25635
25698
  const leftY = baseY + py * arrowWidth / 2;
25636
25699
  const rightX = baseX - px * arrowWidth / 2;
25637
25700
  const rightY = baseY - py * arrowWidth / 2;
25638
- arrowPoints = `${tipX},${tipY} ${leftX},${leftY} ${rightX},${rightY}`;
25701
+ if (this.areFiniteNumbers(tipX, tipY, leftX, leftY, rightX, rightY)) {
25702
+ arrowPoints = `${tipX},${tipY} ${leftX},${leftY} ${rightX},${rightY}`;
25703
+ }
25639
25704
  }
25640
25705
  }
25706
+ if (!this.areFiniteNumbers(indicatorRadius, lineStrokeWidthNum, dashLength)) {
25707
+ return null;
25708
+ }
25709
+ const snapLinePath = (() => {
25710
+ if (snapCandidate.controlX !== undefined &&
25711
+ snapCandidate.controlY !== undefined &&
25712
+ snapCandidate.t !== undefined &&
25713
+ this.areFiniteNumbers(snapCandidate.controlX, snapCandidate.controlY, snapCandidate.t)) {
25714
+ const startT = snapCandidate.endpoint === 'start' ? 1 - snapCandidate.t : snapCandidate.t;
25715
+ // Ensure meaningful range
25716
+ if (startT >= 1)
25717
+ return undefined;
25718
+ const segment = this.extractQuadraticSegment({ x: snapCandidate.lineEndpointX, y: snapCandidate.lineEndpointY }, { x: snapCandidate.controlX, y: snapCandidate.controlY }, { x: snapCandidate.centerX, y: snapCandidate.centerY }, startT, 1);
25719
+ if (!this.areFiniteNumbers(segment.start.x, segment.start.y, segment.control.x, segment.control.y, segment.end.x, segment.end.y)) {
25720
+ return undefined;
25721
+ }
25722
+ return `M ${segment.start.x} ${segment.start.y} Q ${segment.control.x} ${segment.control.y} ${segment.end.x} ${segment.end.y}`;
25723
+ }
25724
+ return undefined;
25725
+ })();
25641
25726
  return {
25642
25727
  indicatorRadius,
25643
25728
  indicatorStrokeWidth,
@@ -25648,27 +25733,15 @@ class KritzelAnchorManager {
25648
25733
  centerY: snapCandidate.centerY,
25649
25734
  lineEndpointX: snapCandidate.lineEndpointX,
25650
25735
  lineEndpointY: snapCandidate.lineEndpointY,
25651
- edgeX: snapCandidate.edgeX,
25652
- edgeY: snapCandidate.edgeY,
25653
- arrowOffset: snapCandidate.arrowOffset,
25736
+ edgeX,
25737
+ edgeY,
25738
+ arrowOffset,
25654
25739
  arrowStyle: snapCandidate.arrowStyle,
25655
25740
  arrowFill: snapCandidate.arrowFill,
25656
25741
  solidLineEndX,
25657
25742
  solidLineEndY,
25658
25743
  arrowPoints,
25659
- snapLinePath: (() => {
25660
- if (snapCandidate.controlX !== undefined &&
25661
- snapCandidate.controlY !== undefined &&
25662
- snapCandidate.t !== undefined) {
25663
- const startT = snapCandidate.endpoint === 'start' ? 1 - snapCandidate.t : snapCandidate.t;
25664
- // Ensure meaningful range
25665
- if (startT >= 1)
25666
- return undefined;
25667
- const segment = this.extractQuadraticSegment({ x: snapCandidate.lineEndpointX, y: snapCandidate.lineEndpointY }, { x: snapCandidate.controlX, y: snapCandidate.controlY }, { x: snapCandidate.centerX, y: snapCandidate.centerY }, startT, 1);
25668
- return `M ${segment.start.x} ${segment.start.y} Q ${segment.control.x} ${segment.control.y} ${segment.end.x} ${segment.end.y}`;
25669
- }
25670
- return undefined;
25671
- })(),
25744
+ snapLinePath,
25672
25745
  };
25673
25746
  }
25674
25747
  // ============================================
@@ -25846,12 +25919,19 @@ class KritzelAnchorManager {
25846
25919
  return null;
25847
25920
  const centerX = targetObject.centerX;
25848
25921
  const centerY = targetObject.centerY;
25922
+ if (!this.areFiniteNumbers(clipInfo.worldX, clipInfo.worldY, centerX, centerY)) {
25923
+ return null;
25924
+ }
25925
+ const pathD = this.buildAnchorPath(line, endpoint, clipInfo, targetObject) ?? undefined;
25926
+ if (pathD && !this.isFinitePathData(pathD)) {
25927
+ return null;
25928
+ }
25849
25929
  return {
25850
25930
  edgeX: clipInfo.worldX,
25851
25931
  edgeY: clipInfo.worldY,
25852
25932
  centerX,
25853
25933
  centerY,
25854
- pathD: this.buildAnchorPath(line, endpoint, clipInfo, targetObject) ?? undefined,
25934
+ pathD,
25855
25935
  };
25856
25936
  }
25857
25937
  /**
@@ -26564,9 +26644,10 @@ class KritzelAnchorManager {
26564
26644
  const sin = Math.sin(line.rotation);
26565
26645
  const rotatedX = (px - cx) * cos - (py - cy) * sin + cx;
26566
26646
  const rotatedY = (px - cx) * sin + (py - cy) * cos + cy;
26647
+ const safeLineScale = this.getSafeScale(line.scale);
26567
26648
  return {
26568
- x: rotatedX / line.scale + line.translateX,
26569
- y: rotatedY / line.scale + line.translateY,
26649
+ x: rotatedX / safeLineScale + line.translateX,
26650
+ y: rotatedY / safeLineScale + line.translateY,
26570
26651
  };
26571
26652
  }
26572
26653
  /**
@@ -26580,8 +26661,9 @@ class KritzelAnchorManager {
26580
26661
  * @returns Object with x and y coordinates in the line's local SVG space.
26581
26662
  */
26582
26663
  lineWorldToLocal(line, worldX, worldY) {
26583
- const dx = (worldX - line.translateX) * line.scale;
26584
- const dy = (worldY - line.translateY) * line.scale;
26664
+ const safeLineScale = this.getSafeScale(line.scale);
26665
+ const dx = (worldX - line.translateX) * safeLineScale;
26666
+ const dy = (worldY - line.translateY) * safeLineScale;
26585
26667
  const cx = line.totalWidth / 2;
26586
26668
  const cy = line.totalHeight / 2;
26587
26669
  const cos = Math.cos(-line.rotation);
@@ -26593,6 +26675,30 @@ class KritzelAnchorManager {
26593
26675
  y: rotatedY + line.y,
26594
26676
  };
26595
26677
  }
26678
+ getSafeScale(scale) {
26679
+ if (Number.isFinite(scale) && Math.abs(scale) > 1e-6) {
26680
+ return scale;
26681
+ }
26682
+ return 1;
26683
+ }
26684
+ areFiniteNumbers(...values) {
26685
+ return values.every(value => value !== undefined && Number.isFinite(value));
26686
+ }
26687
+ isFiniteAnchorVisualization(visualization) {
26688
+ if (!visualization) {
26689
+ return false;
26690
+ }
26691
+ if (!this.areFiniteNumbers(visualization.edgeX, visualization.edgeY, visualization.centerX, visualization.centerY)) {
26692
+ return false;
26693
+ }
26694
+ if (visualization.pathD && !this.isFinitePathData(visualization.pathD)) {
26695
+ return false;
26696
+ }
26697
+ return true;
26698
+ }
26699
+ isFinitePathData(pathD) {
26700
+ return !pathD.includes('Infinity') && !pathD.includes('NaN');
26701
+ }
26596
26702
  /**
26597
26703
  * Checks if an object can be used as an anchor target.
26598
26704
  * Excludes selection-related objects (SelectionBox, SelectionGroup) and
@@ -26653,7 +26759,7 @@ exports.KritzelAlignment = void 0;
26653
26759
  /** Current schema version for the app-state Y.Doc (workspace list & settings). */
26654
26760
  const CURRENT_APP_STATE_SCHEMA_VERSION = 2;
26655
26761
  /** Current schema version for workspace Y.Docs (drawable objects). */
26656
- const CURRENT_WORKSPACE_SCHEMA_VERSION = 1;
26762
+ const CURRENT_WORKSPACE_SCHEMA_VERSION = 2;
26657
26763
 
26658
26764
  /**
26659
26765
  * Runs pending schema migrations on a Y.Doc.
@@ -26758,7 +26864,16 @@ const APP_STATE_MIGRATIONS = [
26758
26864
  * 2. Append a new entry with fromVersion = old version, toVersion = new version
26759
26865
  * 3. Implement the migrate() function — it must be idempotent
26760
26866
  */
26761
- const WORKSPACE_MIGRATIONS = [];
26867
+ const WORKSPACE_MIGRATIONS = [
26868
+ {
26869
+ fromVersion: 1,
26870
+ toVersion: 2,
26871
+ migrate(_ydoc, _maps) {
26872
+ // No-op: adds optional userId property to objects.
26873
+ // Existing objects without userId are valid (field is optional).
26874
+ },
26875
+ },
26876
+ ];
26762
26877
 
26763
26878
  exports.APP_STATE_MIGRATIONS = APP_STATE_MIGRATIONS;
26764
26879
  exports.CURRENT_APP_STATE_SCHEMA_VERSION = CURRENT_APP_STATE_SCHEMA_VERSION;
@@ -109,6 +109,7 @@ export class KritzelCore {
109
109
  */
110
110
  setUser(user) {
111
111
  this._user = user;
112
+ this._store.setLocalUserId(user?.id ?? null);
112
113
  this._store.state.objects?.setLocalUser(user);
113
114
  }
114
115
  /**
@@ -508,6 +509,7 @@ export class KritzelCore {
508
509
  if (selectionBox) {
509
510
  this._store.state.objects.remove(object => object.id === selectionBox.id);
510
511
  this._store.setSelectionBox(null);
512
+ this._store.state.objects.clearLocalSelectionBox();
511
513
  }
512
514
  }
513
515
  /**
@@ -980,7 +982,9 @@ export class KritzelCore {
980
982
  */
981
983
  clearSelection() {
982
984
  this.removeSelectionGroup();
983
- this._store.state.objects.remove(o => o instanceof KritzelSelectionBox || o instanceof KritzelSelectionGroup);
985
+ const localUserId = this._user?.id;
986
+ this._store.state.objects.remove(o => o instanceof KritzelSelectionBox ||
987
+ (o instanceof KritzelSelectionGroup && (localUserId == null || o.userId === localUserId || o.userId == null)));
984
988
  this._store.setSelectionBox(null);
985
989
  this._store.setSelectionGroup(null);
986
990
  this._store.state.isSelecting = false;
@@ -1134,6 +1138,10 @@ export class KritzelCore {
1134
1138
  if (!object.isSelected) {
1135
1139
  return false;
1136
1140
  }
1141
+ // Remote selection groups always show group UI (border only, no handles)
1142
+ if (object instanceof KritzelSelectionGroup && this._user?.id != null && object.userId != null && object.userId !== this._user.id) {
1143
+ return true;
1144
+ }
1137
1145
  const selectionGroup = this._store.selectionGroup;
1138
1146
  if (!selectionGroup) {
1139
1147
  // During selection phase (no group yet), hide UI for KritzelLine objects
@@ -20,6 +20,7 @@ export class KritzelStore {
20
20
  _cachedSelectionGroup = null;
21
21
  _selectionBoxCacheValid = false;
22
22
  _selectionGroupCacheValid = false;
23
+ _localUserId = null;
23
24
  /**
24
25
  * Gets the current engine state.
25
26
  * @returns The mutable engine state object
@@ -97,9 +98,7 @@ export class KritzelStore {
97
98
  height: this._state.viewportHeight / this._state.scale,
98
99
  depth: 100,
99
100
  };
100
- return this._state.objects
101
- .query(viewportBounds)
102
- .sort((a, b) => a.zIndex - b.zIndex);
101
+ return this._state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
103
102
  }
104
103
  /**
105
104
  * Gets all objects excluding selection-related objects (selection group and selection box).
@@ -130,16 +129,22 @@ export class KritzelStore {
130
129
  return this._cachedSelectionBox;
131
130
  }
132
131
  /**
133
- * Gets the current selection group if one exists.
132
+ * Gets the current local user's selection group if one exists.
133
+ * When a local user ID is set, returns only the selection group belonging to that user.
134
134
  * Uses caching for O(1) access after initial lookup.
135
- * @returns The selection group or null if none exists
135
+ * @returns The local user's selection group or null if none exists
136
136
  */
137
137
  get selectionGroup() {
138
138
  if (this._selectionGroupCacheValid) {
139
139
  return this._cachedSelectionGroup;
140
140
  }
141
141
  const selectionGroups = this._state.objects.filter(o => o instanceof KritzelSelectionGroup);
142
- this._cachedSelectionGroup = selectionGroups.length > 0 ? selectionGroups[0] : null;
142
+ if (this._localUserId) {
143
+ this._cachedSelectionGroup = selectionGroups.find(sg => sg.userId === this._localUserId) ?? null;
144
+ }
145
+ else {
146
+ this._cachedSelectionGroup = selectionGroups.length > 0 ? selectionGroups[0] : null;
147
+ }
143
148
  this._selectionGroupCacheValid = true;
144
149
  return this._cachedSelectionGroup;
145
150
  }
@@ -168,6 +173,15 @@ export class KritzelStore {
168
173
  this._cachedSelectionGroup = group;
169
174
  this._selectionGroupCacheValid = true;
170
175
  }
176
+ /**
177
+ * Sets the local user ID for scoping selection group lookups.
178
+ * When set, the selectionGroup getter returns only the group owned by this user.
179
+ * @param userId - The local user's ID, or null for non-collaborative mode
180
+ */
181
+ setLocalUserId(userId) {
182
+ this._localUserId = userId;
183
+ this.invalidateSelectionCache();
184
+ }
171
185
  /**
172
186
  * Gets the currently active text object being edited.
173
187
  * @returns The text object in editing mode, or null if none
@@ -149,6 +149,7 @@ export class KritzelViewport {
149
149
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
150
150
  const activePointers = Array.from(this._core.store.state.pointers.values());
151
151
  if (activePointers.length === 2) {
152
+ this._core.store.state.objects?.clearCursorPosition();
152
153
  const currentPath = this._core.store.currentPath;
153
154
  if (currentPath) {
154
155
  this._core.store.state.objects.remove(obj => obj.id === currentPath.id);
@@ -209,10 +210,15 @@ export class KritzelViewport {
209
210
  const hostRect = this._core.store.state.host.getBoundingClientRect();
210
211
  const xRelativeToHost = event.clientX - hostRect.left;
211
212
  const yRelativeToHost = event.clientY - hostRect.top;
212
- this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
213
- this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
214
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
215
213
  const activePointers = Array.from(this._core.store.state.pointers.values());
214
+ if (this._core.store.state.isScaling || activePointers.length > 1) {
215
+ this._core.store.state.objects?.clearCursorPosition();
216
+ }
217
+ else {
218
+ this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
219
+ 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);
221
+ }
216
222
  if (activePointers.length === 2) {
217
223
  const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
218
224
  const firstTouchY = activePointers[0].clientY - this._core.store.offsetY;