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
@@ -156,6 +156,7 @@ export class KritzelSelectionHandler extends KritzelBaseHandler {
156
156
  this._core.store.state.isSelecting = false;
157
157
  this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox);
158
158
  this._core.store.setSelectionBox(null);
159
+ this._core.store.state.objects.clearLocalSelectionBox();
159
160
  }
160
161
  /**
161
162
  * Initiates a mouse-based selection by creating a selection box at the click position.
@@ -174,7 +175,7 @@ export class KritzelSelectionHandler extends KritzelBaseHandler {
174
175
  selectionBox.translateX = this.startX;
175
176
  selectionBox.translateY = this.startY;
176
177
  this._core.store.state.isSelecting = true;
177
- this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup));
178
+ this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup && (o.userId == null || o.userId === this._core.user?.id)));
178
179
  this._core.store.setSelectionBox(null);
179
180
  if (!isAdditive) {
180
181
  this._core.store.setSelectionGroup(null);
@@ -206,7 +207,7 @@ export class KritzelSelectionHandler extends KritzelBaseHandler {
206
207
  selectionBox.translateX = this.startX;
207
208
  selectionBox.translateY = this.startY;
208
209
  this._core.store.state.isSelecting = true;
209
- this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup));
210
+ this._core.store.state.objects.remove(o => o instanceof KritzelSelectionBox || (!isAdditive && o instanceof KritzelSelectionGroup && (o.userId == null || o.userId === this._core.user?.id)));
210
211
  this._core.store.setSelectionBox(null);
211
212
  if (!isAdditive) {
212
213
  this._core.store.setSelectionGroup(null);
@@ -234,6 +235,12 @@ export class KritzelSelectionHandler extends KritzelBaseHandler {
234
235
  selectionBox.height = height;
235
236
  selectionBox.translateX = Math.min(currentX, this.startX);
236
237
  selectionBox.translateY = Math.min(currentY, this.startY);
238
+ this._core.store.state.objects.setLocalSelectionBox({
239
+ x: selectionBox.translateX,
240
+ y: selectionBox.translateY,
241
+ width: width / selectionBox.scale,
242
+ height: height / selectionBox.scale,
243
+ });
237
244
  if (width > 0 || height > 0) {
238
245
  this.updateSelectedObjects();
239
246
  }
@@ -267,6 +274,12 @@ export class KritzelSelectionHandler extends KritzelBaseHandler {
267
274
  selectionBox.height = height;
268
275
  selectionBox.translateX = Math.min(currentX, this.startX);
269
276
  selectionBox.translateY = Math.min(currentY, this.startY);
277
+ this._core.store.state.objects.setLocalSelectionBox({
278
+ x: selectionBox.translateX,
279
+ y: selectionBox.translateY,
280
+ width: width / selectionBox.scale,
281
+ height: height / selectionBox.scale,
282
+ });
270
283
  if (width > 0 || height > 0) {
271
284
  this.updateSelectedObjects();
272
285
  }
@@ -286,16 +286,22 @@ export class KritzelAnchorManager {
286
286
  if (!KritzelClassHelper.isInstanceOf(selectedObject, 'KritzelLine'))
287
287
  return null;
288
288
  const line = selectedObject;
289
- const startAnchorViz = this.computeAnchorVisualization(line, 'start');
290
- const endAnchorViz = this.computeAnchorVisualization(line, 'end');
289
+ const startAnchorVizRaw = this.computeAnchorVisualization(line, 'start');
290
+ const endAnchorVizRaw = this.computeAnchorVisualization(line, 'end');
291
+ const startAnchorViz = this.isFiniteAnchorVisualization(startAnchorVizRaw) ? startAnchorVizRaw : null;
292
+ const endAnchorViz = this.isFiniteAnchorVisualization(endAnchorVizRaw) ? endAnchorVizRaw : null;
291
293
  if (!startAnchorViz && !endAnchorViz)
292
294
  return null;
293
- const scale = this._core.store.state.scale;
294
- const lineStrokeWidth = line.strokeWidth / line.scale;
295
+ const scale = this.getSafeScale(this._core.store.state.scale);
296
+ const lineScale = this.getSafeScale(line.scale);
297
+ const lineStrokeWidth = line.strokeWidth / lineScale;
295
298
  const indicatorStrokeWidth = `${2 / scale}`;
296
299
  const dashLength = Math.max(lineStrokeWidth * 2, 4 / scale);
297
300
  const dashArray = `${dashLength} ${dashLength}`;
298
301
  const indicatorRadius = 8 / scale;
302
+ if (!this.areFiniteNumbers(lineStrokeWidth, dashLength, indicatorRadius)) {
303
+ return null;
304
+ }
299
305
  return {
300
306
  lineStrokeWidth,
301
307
  indicatorStrokeWidth,
@@ -317,31 +323,39 @@ export class KritzelAnchorManager {
317
323
  const snapCandidate = this.getSnapCandidate();
318
324
  if (!snapCandidate)
319
325
  return null;
320
- const scale = this._core.store.state.scale;
326
+ if (!this.areFiniteNumbers(snapCandidate.centerX, snapCandidate.centerY, snapCandidate.lineEndpointX, snapCandidate.lineEndpointY)) {
327
+ return null;
328
+ }
329
+ const scale = this.getSafeScale(this._core.store.state.scale);
321
330
  const indicatorRadius = 8 / scale;
322
331
  const indicatorStrokeWidth = `${2 / scale}`;
323
- const lineStrokeWidth = snapCandidate.lineStrokeWidth
324
- ? `${snapCandidate.lineStrokeWidth}`
325
- : `${4 / scale}`;
326
- const lineStrokeWidthNum = snapCandidate.lineStrokeWidth || (4 / scale);
332
+ const lineStrokeWidthNum = this.areFiniteNumbers(snapCandidate.lineStrokeWidth) && snapCandidate.lineStrokeWidth > 0
333
+ ? snapCandidate.lineStrokeWidth
334
+ : (4 / scale);
335
+ const lineStrokeWidth = `${lineStrokeWidthNum}`;
327
336
  const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
328
337
  const dashArray = `${dashLength} ${dashLength}`;
329
338
  const lineStroke = snapCandidate.lineStroke || '#000000';
330
- let solidLineEndX = snapCandidate.edgeX;
331
- let solidLineEndY = snapCandidate.edgeY;
339
+ let edgeX = this.areFiniteNumbers(snapCandidate.edgeX) ? snapCandidate.edgeX : undefined;
340
+ let edgeY = this.areFiniteNumbers(snapCandidate.edgeY) ? snapCandidate.edgeY : undefined;
341
+ let solidLineEndX = edgeX;
342
+ let solidLineEndY = edgeY;
332
343
  let arrowPoints;
333
- if (snapCandidate.arrowOffset && snapCandidate.edgeX !== undefined && snapCandidate.edgeY !== undefined) {
334
- const dx = snapCandidate.lineEndpointX - snapCandidate.edgeX;
335
- const dy = snapCandidate.lineEndpointY - snapCandidate.edgeY;
344
+ const arrowOffset = this.areFiniteNumbers(snapCandidate.arrowOffset) && snapCandidate.arrowOffset > 0
345
+ ? snapCandidate.arrowOffset
346
+ : undefined;
347
+ if (arrowOffset !== undefined && edgeX !== undefined && edgeY !== undefined) {
348
+ const dx = snapCandidate.lineEndpointX - edgeX;
349
+ const dy = snapCandidate.lineEndpointY - edgeY;
336
350
  const length = Math.sqrt(dx * dx + dy * dy);
337
- if (length > snapCandidate.arrowOffset) {
338
- solidLineEndX = snapCandidate.edgeX + (dx / length) * snapCandidate.arrowOffset;
339
- solidLineEndY = snapCandidate.edgeY + (dy / length) * snapCandidate.arrowOffset;
351
+ if (length > arrowOffset) {
352
+ solidLineEndX = edgeX + (dx / length) * arrowOffset;
353
+ solidLineEndY = edgeY + (dy / length) * arrowOffset;
340
354
  }
341
355
  // Calculate arrow head points
342
356
  // Direction from line endpoint to edge (arrow direction)
343
- const arrowDx = snapCandidate.edgeX - snapCandidate.lineEndpointX;
344
- const arrowDy = snapCandidate.edgeY - snapCandidate.lineEndpointY;
357
+ const arrowDx = edgeX - snapCandidate.lineEndpointX;
358
+ const arrowDy = edgeY - snapCandidate.lineEndpointY;
345
359
  const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
346
360
  if (arrowLengthTotal > 0) {
347
361
  const ux = arrowDx / arrowLengthTotal;
@@ -350,11 +364,11 @@ export class KritzelAnchorManager {
350
364
  const px = -uy;
351
365
  const py = ux;
352
366
  // Arrow dimensions
353
- const arrowLength = snapCandidate.arrowOffset;
367
+ const arrowLength = arrowOffset;
354
368
  const arrowWidth = arrowLength; // 1:1 ratio
355
369
  // Arrow tip at edge
356
- const tipX = snapCandidate.edgeX;
357
- const tipY = snapCandidate.edgeY;
370
+ const tipX = edgeX;
371
+ const tipY = edgeY;
358
372
  // Arrow base
359
373
  const baseX = tipX - ux * arrowLength;
360
374
  const baseY = tipY - uy * arrowLength;
@@ -363,9 +377,31 @@ export class KritzelAnchorManager {
363
377
  const leftY = baseY + py * arrowWidth / 2;
364
378
  const rightX = baseX - px * arrowWidth / 2;
365
379
  const rightY = baseY - py * arrowWidth / 2;
366
- arrowPoints = `${tipX},${tipY} ${leftX},${leftY} ${rightX},${rightY}`;
380
+ if (this.areFiniteNumbers(tipX, tipY, leftX, leftY, rightX, rightY)) {
381
+ arrowPoints = `${tipX},${tipY} ${leftX},${leftY} ${rightX},${rightY}`;
382
+ }
367
383
  }
368
384
  }
385
+ if (!this.areFiniteNumbers(indicatorRadius, lineStrokeWidthNum, dashLength)) {
386
+ return null;
387
+ }
388
+ const snapLinePath = (() => {
389
+ if (snapCandidate.controlX !== undefined &&
390
+ snapCandidate.controlY !== undefined &&
391
+ snapCandidate.t !== undefined &&
392
+ this.areFiniteNumbers(snapCandidate.controlX, snapCandidate.controlY, snapCandidate.t)) {
393
+ const startT = snapCandidate.endpoint === 'start' ? 1 - snapCandidate.t : snapCandidate.t;
394
+ // Ensure meaningful range
395
+ if (startT >= 1)
396
+ return undefined;
397
+ const segment = this.extractQuadraticSegment({ x: snapCandidate.lineEndpointX, y: snapCandidate.lineEndpointY }, { x: snapCandidate.controlX, y: snapCandidate.controlY }, { x: snapCandidate.centerX, y: snapCandidate.centerY }, startT, 1);
398
+ if (!this.areFiniteNumbers(segment.start.x, segment.start.y, segment.control.x, segment.control.y, segment.end.x, segment.end.y)) {
399
+ return undefined;
400
+ }
401
+ return `M ${segment.start.x} ${segment.start.y} Q ${segment.control.x} ${segment.control.y} ${segment.end.x} ${segment.end.y}`;
402
+ }
403
+ return undefined;
404
+ })();
369
405
  return {
370
406
  indicatorRadius,
371
407
  indicatorStrokeWidth,
@@ -376,27 +412,15 @@ export class KritzelAnchorManager {
376
412
  centerY: snapCandidate.centerY,
377
413
  lineEndpointX: snapCandidate.lineEndpointX,
378
414
  lineEndpointY: snapCandidate.lineEndpointY,
379
- edgeX: snapCandidate.edgeX,
380
- edgeY: snapCandidate.edgeY,
381
- arrowOffset: snapCandidate.arrowOffset,
415
+ edgeX,
416
+ edgeY,
417
+ arrowOffset,
382
418
  arrowStyle: snapCandidate.arrowStyle,
383
419
  arrowFill: snapCandidate.arrowFill,
384
420
  solidLineEndX,
385
421
  solidLineEndY,
386
422
  arrowPoints,
387
- snapLinePath: (() => {
388
- if (snapCandidate.controlX !== undefined &&
389
- snapCandidate.controlY !== undefined &&
390
- snapCandidate.t !== undefined) {
391
- const startT = snapCandidate.endpoint === 'start' ? 1 - snapCandidate.t : snapCandidate.t;
392
- // Ensure meaningful range
393
- if (startT >= 1)
394
- return undefined;
395
- const segment = this.extractQuadraticSegment({ x: snapCandidate.lineEndpointX, y: snapCandidate.lineEndpointY }, { x: snapCandidate.controlX, y: snapCandidate.controlY }, { x: snapCandidate.centerX, y: snapCandidate.centerY }, startT, 1);
396
- return `M ${segment.start.x} ${segment.start.y} Q ${segment.control.x} ${segment.control.y} ${segment.end.x} ${segment.end.y}`;
397
- }
398
- return undefined;
399
- })(),
423
+ snapLinePath,
400
424
  };
401
425
  }
402
426
  // ============================================
@@ -574,12 +598,19 @@ export class KritzelAnchorManager {
574
598
  return null;
575
599
  const centerX = targetObject.centerX;
576
600
  const centerY = targetObject.centerY;
601
+ if (!this.areFiniteNumbers(clipInfo.worldX, clipInfo.worldY, centerX, centerY)) {
602
+ return null;
603
+ }
604
+ const pathD = this.buildAnchorPath(line, endpoint, clipInfo, targetObject) ?? undefined;
605
+ if (pathD && !this.isFinitePathData(pathD)) {
606
+ return null;
607
+ }
577
608
  return {
578
609
  edgeX: clipInfo.worldX,
579
610
  edgeY: clipInfo.worldY,
580
611
  centerX,
581
612
  centerY,
582
- pathD: this.buildAnchorPath(line, endpoint, clipInfo, targetObject) ?? undefined,
613
+ pathD,
583
614
  };
584
615
  }
585
616
  /**
@@ -1292,9 +1323,10 @@ export class KritzelAnchorManager {
1292
1323
  const sin = Math.sin(line.rotation);
1293
1324
  const rotatedX = (px - cx) * cos - (py - cy) * sin + cx;
1294
1325
  const rotatedY = (px - cx) * sin + (py - cy) * cos + cy;
1326
+ const safeLineScale = this.getSafeScale(line.scale);
1295
1327
  return {
1296
- x: rotatedX / line.scale + line.translateX,
1297
- y: rotatedY / line.scale + line.translateY,
1328
+ x: rotatedX / safeLineScale + line.translateX,
1329
+ y: rotatedY / safeLineScale + line.translateY,
1298
1330
  };
1299
1331
  }
1300
1332
  /**
@@ -1308,8 +1340,9 @@ export class KritzelAnchorManager {
1308
1340
  * @returns Object with x and y coordinates in the line's local SVG space.
1309
1341
  */
1310
1342
  lineWorldToLocal(line, worldX, worldY) {
1311
- const dx = (worldX - line.translateX) * line.scale;
1312
- const dy = (worldY - line.translateY) * line.scale;
1343
+ const safeLineScale = this.getSafeScale(line.scale);
1344
+ const dx = (worldX - line.translateX) * safeLineScale;
1345
+ const dy = (worldY - line.translateY) * safeLineScale;
1313
1346
  const cx = line.totalWidth / 2;
1314
1347
  const cy = line.totalHeight / 2;
1315
1348
  const cos = Math.cos(-line.rotation);
@@ -1321,6 +1354,30 @@ export class KritzelAnchorManager {
1321
1354
  y: rotatedY + line.y,
1322
1355
  };
1323
1356
  }
1357
+ getSafeScale(scale) {
1358
+ if (Number.isFinite(scale) && Math.abs(scale) > 1e-6) {
1359
+ return scale;
1360
+ }
1361
+ return 1;
1362
+ }
1363
+ areFiniteNumbers(...values) {
1364
+ return values.every(value => value !== undefined && Number.isFinite(value));
1365
+ }
1366
+ isFiniteAnchorVisualization(visualization) {
1367
+ if (!visualization) {
1368
+ return false;
1369
+ }
1370
+ if (!this.areFiniteNumbers(visualization.edgeX, visualization.edgeY, visualization.centerX, visualization.centerY)) {
1371
+ return false;
1372
+ }
1373
+ if (visualization.pathD && !this.isFinitePathData(visualization.pathD)) {
1374
+ return false;
1375
+ }
1376
+ return true;
1377
+ }
1378
+ isFinitePathData(pathD) {
1379
+ return !pathD.includes('Infinity') && !pathD.includes('NaN');
1380
+ }
1324
1381
  /**
1325
1382
  * Checks if an object can be used as an anchor target.
1326
1383
  * Excludes selection-related objects (SelectionBox, SelectionGroup) and
@@ -28,6 +28,7 @@ export class KritzelBaseObject {
28
28
  rotation = 0;
29
29
  markedForRemoval = false;
30
30
  zIndex = 0;
31
+ userId;
31
32
  /** Whether the object is currently visible on the canvas */
32
33
  isVisible = true;
33
34
  isSelected = false;
@@ -208,6 +209,7 @@ export class KritzelBaseObject {
208
209
  object._core = core;
209
210
  object.zIndex = core.store.currentZIndex;
210
211
  object.workspaceId = core.store.state.activeWorkspace.id;
212
+ object.userId = core.user?.id;
211
213
  return object;
212
214
  }
213
215
  /**
@@ -49,6 +49,7 @@ export class KritzelCustomElement extends KritzelBaseObject {
49
49
  object._core = core;
50
50
  object.id = object.generateId();
51
51
  object.workspaceId = core.store.state.activeWorkspace.id;
52
+ object.userId = core.user?.id;
52
53
  return object;
53
54
  }
54
55
  /**
@@ -70,6 +70,7 @@ export class KritzelGroup extends KritzelBaseObject {
70
70
  group._core = core;
71
71
  group.id = group.generateId();
72
72
  group.workspaceId = core.store.state.activeWorkspace.id;
73
+ group.userId = core.user?.id;
73
74
  group.scale = core.store.state.scale;
74
75
  group.zIndex = core.store.currentZIndex;
75
76
  return group;
@@ -40,6 +40,7 @@ export class KritzelImage extends KritzelBaseObject {
40
40
  object._core = core;
41
41
  object.id = object.generateId();
42
42
  object.workspaceId = core.store.state.activeWorkspace.id;
43
+ object.userId = core.user?.id;
43
44
  object.x = 0;
44
45
  object.y = 0;
45
46
  object.translateX = 0;
@@ -80,6 +80,7 @@ export class KritzelLine extends KritzelBaseObject {
80
80
  object._core = core;
81
81
  object.id = object.generateId();
82
82
  object.workspaceId = core.store.state.activeWorkspace.id;
83
+ object.userId = core.user?.id;
83
84
  object.options = options;
84
85
  object.startX = options?.startX ?? 0;
85
86
  object.startY = options?.startY ?? 0;
@@ -62,6 +62,7 @@ export class KritzelPath extends KritzelBaseObject {
62
62
  object._core = core;
63
63
  object.id = object.generateId();
64
64
  object.workspaceId = core.store.state.activeWorkspace.id;
65
+ object.userId = core.user?.id;
65
66
  object.options = options;
66
67
  object.points = options?.points ?? [];
67
68
  object.translateX = options?.translateX ?? 0;
@@ -19,6 +19,7 @@ export class KritzelSelectionBox extends KritzelBaseObject {
19
19
  object._core = core;
20
20
  object.id = object.generateId();
21
21
  object.workspaceId = core.store.state.activeWorkspace.id;
22
+ object.userId = core.user?.id;
22
23
  object.scale = core.store.state.scale;
23
24
  object.zIndex = 99999;
24
25
  object.backgroundColor = { light: 'rgba(0, 122, 255, 0.2)', dark: 'rgba(0, 122, 255, 0.2)' };
@@ -24,6 +24,10 @@ export class KritzelSelectionGroup extends KritzelBaseObject {
24
24
  maxX;
25
25
  minY;
26
26
  maxY;
27
+ // Selection styling properties
28
+ handleColor;
29
+ handleStrokeColor;
30
+ handleSize = 6;
27
31
  /**
28
32
  * Gets the array of object IDs contained in this selection group.
29
33
  * @returns Array of string IDs for the selected objects
@@ -78,6 +82,7 @@ export class KritzelSelectionGroup extends KritzelBaseObject {
78
82
  /**
79
83
  * Factory method to create a new KritzelSelectionGroup instance.
80
84
  * Initializes the selection group with default properties and associates it with the core instance.
85
+ * Default styling uses values from the theme system (selection section).
81
86
  * @param core - The KritzelCore instance to associate with this selection group
82
87
  * @returns A new KritzelSelectionGroup instance configured with default settings
83
88
  */
@@ -86,8 +91,15 @@ export class KritzelSelectionGroup extends KritzelBaseObject {
86
91
  object._core = core;
87
92
  object.id = object.generateId();
88
93
  object.workspaceId = core.store.state.activeWorkspace.id;
94
+ object.userId = core.user?.id;
89
95
  object.scale = core.store.state.scale;
90
96
  object.zIndex = 99999;
97
+ // Initialize styling with theme-aware defaults
98
+ object.borderColor = { light: '#007AFF', dark: '#0A84FF' };
99
+ object.borderWidth = 2;
100
+ object.handleColor = { light: '#ffffff', dark: '#1a1a1a' };
101
+ object.handleStrokeColor = { light: '#007AFF', dark: '#0A84FF' };
102
+ object.handleSize = 6;
91
103
  return object;
92
104
  }
93
105
  /**
@@ -253,7 +265,7 @@ export class KritzelSelectionGroup extends KritzelBaseObject {
253
265
  * @returns A serializable object representation of the selection group
254
266
  */
255
267
  serialize() {
256
- const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, _cachedObjects, _cachedObjectIdsHash, snapshotWidth, snapshotHeight, snapshotTranslateX, snapshotTranslateY, ...remainingProps } = this;
268
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, _cachedObjects, _cachedObjectIdsHash, snapshotWidth, snapshotHeight, snapshotTranslateX, snapshotTranslateY, snapshotRotation, ...remainingProps } = this;
257
269
  const clonedProps = structuredClone(remainingProps);
258
270
  // Ensure objectIds is serialized with the correct key name (getter returns _objectIds)
259
271
  clonedProps.objectIds = this.objectIds;
@@ -100,6 +100,7 @@ export class KritzelShape extends KritzelBaseObject {
100
100
  object._core = core;
101
101
  object.id = object.generateId();
102
102
  object.workspaceId = core.store.state.activeWorkspace.id;
103
+ object.userId = core.user?.id;
103
104
  object.x = config?.x ?? 0;
104
105
  object.y = config?.y ?? 0;
105
106
  object.translateX = config?.translateX ?? 0;
@@ -116,6 +116,7 @@ export class KritzelText extends KritzelBaseObject {
116
116
  object._core = core;
117
117
  object.id = object.generateId();
118
118
  object.workspaceId = core.store.state.activeWorkspace.id;
119
+ object.userId = core.user?.id;
119
120
  object.fontSize = fontSize;
120
121
  object.fontFamily = fontFamily;
121
122
  object.translateX = 0;
@@ -6,6 +6,7 @@ import * as decoding from "lib0/decoding";
6
6
  * This is a lightweight alternative to y-webrtc for browser-tab-only sync
7
7
  */
8
8
  export class BroadcastSyncProvider {
9
+ type = 'local';
9
10
  doc;
10
11
  channel;
11
12
  _synced = false;
@@ -87,6 +88,10 @@ export class BroadcastSyncProvider {
87
88
  disconnect() {
88
89
  // BroadcastChannel doesn't have explicit disconnect
89
90
  }
91
+ async reconnect() {
92
+ this.disconnect();
93
+ return this.connect();
94
+ }
90
95
  destroy() {
91
96
  this.doc.off('update', this.handleDocUpdate);
92
97
  this.channel.close();