kritzel-stencil 0.1.78 → 0.1.80

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 (163) hide show
  1. package/dist/cjs/{index-BRZ6e6oa.js → index-CFnj_FXt.js} +36 -9
  2. package/dist/cjs/index.cjs.js +1 -1
  3. package/dist/cjs/kritzel-active-users_42.cjs.entry.js +333 -177
  4. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  5. package/dist/cjs/loader.cjs.js +2 -2
  6. package/dist/cjs/stencil.cjs.js +3 -3
  7. package/dist/cjs/{workspace.migrations-sUPrO23c.js → workspace.migrations-DUXtSb7C.js} +244 -197
  8. package/dist/collection/classes/core/core.class.js +39 -31
  9. package/dist/collection/classes/core/store.class.js +57 -16
  10. package/dist/collection/classes/core/viewport.class.js +12 -12
  11. package/dist/collection/classes/handlers/context-menu.handler.js +1 -1
  12. package/dist/collection/classes/handlers/line-handle.handler.js +7 -4
  13. package/dist/collection/classes/handlers/move.handler.js +11 -8
  14. package/dist/collection/classes/handlers/resize.handler.js +12 -3
  15. package/dist/collection/classes/handlers/rotation.handler.js +12 -3
  16. package/dist/collection/classes/handlers/selection.handler.js +20 -15
  17. package/dist/collection/classes/managers/anchor.manager.js +4 -4
  18. package/dist/collection/classes/objects/base-object.class.js +6 -6
  19. package/dist/collection/classes/objects/custom-element.class.js +2 -2
  20. package/dist/collection/classes/objects/group.class.js +10 -10
  21. package/dist/collection/classes/objects/image.class.js +2 -2
  22. package/dist/collection/classes/objects/line.class.js +7 -7
  23. package/dist/collection/classes/objects/path.class.js +5 -5
  24. package/dist/collection/classes/objects/selection-box.class.js +1 -1
  25. package/dist/collection/classes/objects/selection-group.class.js +16 -16
  26. package/dist/collection/classes/objects/shape.class.js +37 -31
  27. package/dist/collection/classes/objects/text.class.js +40 -32
  28. package/dist/collection/classes/structures/app-state-map.structure.js +18 -16
  29. package/dist/collection/classes/structures/object-map.structure.js +30 -27
  30. package/dist/collection/classes/tools/brush-tool.class.js +14 -14
  31. package/dist/collection/classes/tools/image-tool.class.js +1 -1
  32. package/dist/collection/classes/tools/line-tool.class.js +14 -14
  33. package/dist/collection/classes/tools/selection-tool.class.js +2 -2
  34. package/dist/collection/classes/tools/shape-tool.class.js +6 -6
  35. package/dist/collection/classes/tools/text-tool.class.js +2 -2
  36. package/dist/collection/collection-manifest.json +1 -1
  37. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +6 -6
  38. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +2 -2
  39. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +70 -11
  40. package/dist/collection/components/core/kritzel-engine/kritzel-engine.css +21 -1
  41. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +159 -60
  42. package/dist/collection/configs/default-engine-config.js +2 -2
  43. package/dist/collection/constants/version.js +1 -1
  44. package/dist/collection/helpers/keyboard.helper.js +15 -11
  45. package/dist/components/index.js +1 -1
  46. package/dist/components/kritzel-active-users.js +1 -1
  47. package/dist/components/kritzel-avatar.js +1 -1
  48. package/dist/components/kritzel-awareness-cursors.js +1 -1
  49. package/dist/components/kritzel-back-to-content.js +1 -1
  50. package/dist/components/kritzel-brush-style.js +1 -1
  51. package/dist/components/kritzel-button.js +1 -1
  52. package/dist/components/kritzel-color-palette.js +1 -1
  53. package/dist/components/kritzel-color.js +1 -1
  54. package/dist/components/kritzel-context-menu.js +1 -1
  55. package/dist/components/kritzel-controls.js +1 -1
  56. package/dist/components/kritzel-current-user-dialog.js +1 -1
  57. package/dist/components/kritzel-current-user.js +1 -1
  58. package/dist/components/kritzel-cursor-trail.js +1 -1
  59. package/dist/components/kritzel-dialog.js +1 -1
  60. package/dist/components/kritzel-dropdown.js +1 -1
  61. package/dist/components/kritzel-editor.js +1 -1
  62. package/dist/components/kritzel-engine.js +1 -1
  63. package/dist/components/kritzel-export.js +1 -1
  64. package/dist/components/kritzel-font-family.js +1 -1
  65. package/dist/components/kritzel-font-size.js +1 -1
  66. package/dist/components/kritzel-font.js +1 -1
  67. package/dist/components/kritzel-icon.js +1 -1
  68. package/dist/components/kritzel-input.js +1 -1
  69. package/dist/components/kritzel-line-endings.js +1 -1
  70. package/dist/components/kritzel-login-dialog.js +1 -1
  71. package/dist/components/kritzel-master-detail.js +1 -1
  72. package/dist/components/kritzel-menu-item.js +1 -1
  73. package/dist/components/kritzel-menu.js +1 -1
  74. package/dist/components/kritzel-more-menu.js +1 -1
  75. package/dist/components/kritzel-numeric-input.js +1 -1
  76. package/dist/components/kritzel-opacity-slider.js +1 -1
  77. package/dist/components/kritzel-pill-tabs.js +1 -1
  78. package/dist/components/kritzel-portal.js +1 -1
  79. package/dist/components/kritzel-settings.js +1 -1
  80. package/dist/components/kritzel-shape-fill.js +1 -1
  81. package/dist/components/kritzel-share-dialog.js +1 -1
  82. package/dist/components/kritzel-slide-toggle.js +1 -1
  83. package/dist/components/kritzel-split-button.js +1 -1
  84. package/dist/components/kritzel-stroke-size.js +1 -1
  85. package/dist/components/kritzel-tool-config.js +1 -1
  86. package/dist/components/kritzel-tooltip.js +1 -1
  87. package/dist/components/kritzel-utility-panel.js +1 -1
  88. package/dist/components/kritzel-workspace-manager.js +1 -1
  89. package/dist/components/{p-Md9Y-b3d.js → p-1ppb4M65.js} +1 -1
  90. package/dist/components/{p-KJ4dHzrS.js → p-7yR-sKH8.js} +1 -1
  91. package/dist/components/{p-CN8IxBlU.js → p-B-Gej_Ak.js} +1 -1
  92. package/dist/components/{p-76W5pG2O.js → p-B4F19Aqj.js} +1 -1
  93. package/dist/components/{p-BuI6Gkzg.js → p-B5WII1Lk.js} +1 -1
  94. package/dist/components/p-BWj1eE2b.js +1 -0
  95. package/dist/components/{p-ZC5YELQJ.js → p-BZ8bK8qT.js} +1 -1
  96. package/dist/components/{p-DkWWzVg8.js → p-BaaFG0p-.js} +1 -1
  97. package/dist/components/{p-6NFl6EB2.js → p-Bat829Bg.js} +1 -1
  98. package/dist/components/p-BcwZ36sO.js +1 -0
  99. package/dist/components/{p-53di1Zko.js → p-BjPmEs5A.js} +1 -1
  100. package/dist/components/{p-D5IhryUR.js → p-Brwz_Dpb.js} +1 -1
  101. package/dist/components/p-BzQmBVwr.js +9 -0
  102. package/dist/components/{p-C2l9mZ1P.js → p-C-MtTi6x.js} +1 -1
  103. package/dist/components/p-C8WnYSHi.js +1 -0
  104. package/dist/components/{p-BLsH_Oi0.js → p-C9zsWWH2.js} +1 -1
  105. package/dist/components/{p-ZQ2bKafG.js → p-CJKsuQun.js} +1 -1
  106. package/dist/components/{p-m1nVDC3G.js → p-CJzg_ejc.js} +1 -1
  107. package/dist/components/{p-Dte67BWd.js → p-CM7rYf9A.js} +1 -1
  108. package/dist/components/{p-Dr3-pKVg.js → p-CMGHx71q.js} +1 -1
  109. package/dist/components/{p-CI9Nbh-x.js → p-CNMpVlot.js} +1 -1
  110. package/dist/components/{p-DDKjsXCe.js → p-CS7r-zhx.js} +1 -1
  111. package/dist/components/{p-DaGZEV0R.js → p-C_fSm7T4.js} +1 -1
  112. package/dist/components/{p-Ck1dhpUQ.js → p-CfEGaTaV.js} +1 -1
  113. package/dist/components/{p-CWMFGEe0.js → p-CvyE2Wg-.js} +1 -1
  114. package/dist/components/{p-D3pNw-SV.js → p-D5BXoK9m.js} +1 -1
  115. package/dist/components/{p-DV7Z_qfa.js → p-D7pwbRUy.js} +1 -1
  116. package/dist/components/{p-CBslLN3-.js → p-DCH4Rlqx.js} +1 -1
  117. package/dist/components/{p-CYh7yV-K.js → p-DEOUiiyI.js} +1 -1
  118. package/dist/components/{p-pCC6t6BH.js → p-DKeBfe_l.js} +1 -1
  119. package/dist/components/{p-DWsCbu01.js → p-DUQmBcTy.js} +1 -1
  120. package/dist/components/{p-D14QNK3X.js → p-DUnKjQN7.js} +1 -1
  121. package/dist/components/{p-BrZ_gL8Q.js → p-DYcsC2zO.js} +1 -1
  122. package/dist/components/{p-D7yzmu1l.js → p-DkT0KZCm.js} +1 -1
  123. package/dist/components/{p-Cns7qSKS.js → p-DrsORMoT.js} +1 -1
  124. package/dist/components/{p-BueaqfA2.js → p-Dw0Obsn5.js} +1 -1
  125. package/dist/components/{p-DxzDda_J.js → p-Gm5hSQ-e.js} +1 -1
  126. package/dist/components/{p-C4fKLlrd.js → p-HK-6khHo.js} +1 -1
  127. package/dist/components/{p-_QEHfsIk.js → p-V3VW2JKl.js} +1 -1
  128. package/dist/components/{p-Lhyh6KeB.js → p-VxgCvVox.js} +1 -1
  129. package/dist/components/{p-l_YGO7RB.js → p-cjeDomsc.js} +1 -1
  130. package/dist/components/{p-gtQlsorg.js → p-rNu5JVNH.js} +1 -1
  131. package/dist/components/p-xYCbKFih.js +1 -0
  132. package/dist/esm/{index-BbOHefEf.js → index-D9HaikfQ.js} +36 -9
  133. package/dist/esm/index.js +1 -1
  134. package/dist/esm/kritzel-active-users_42.entry.js +333 -177
  135. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  136. package/dist/esm/loader.js +3 -3
  137. package/dist/esm/stencil.js +4 -4
  138. package/dist/esm/{workspace.migrations-NhRgr2_H.js → workspace.migrations-OzSSw5kt.js} +244 -197
  139. package/dist/stencil/index.esm.js +1 -1
  140. package/dist/stencil/p-D9HaikfQ.js +2 -0
  141. package/dist/stencil/p-OzSSw5kt.js +1 -0
  142. package/dist/stencil/{p-98238bf9.entry.js → p-b0be4da1.entry.js} +1 -1
  143. package/dist/stencil/p-f7be06a8.entry.js +9 -0
  144. package/dist/stencil/stencil.esm.js +1 -1
  145. package/dist/types/classes/core/store.class.d.ts +23 -0
  146. package/dist/types/classes/objects/shape.class.d.ts +1 -0
  147. package/dist/types/classes/objects/text.class.d.ts +1 -0
  148. package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +1 -1
  149. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +7 -5
  150. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +10 -4
  151. package/dist/types/components.d.ts +35 -13
  152. package/dist/types/constants/version.d.ts +1 -1
  153. package/dist/types/helpers/tool-config.helper.d.ts +1 -1
  154. package/dist/types/interfaces/engine-state.interface.d.ts +10 -10
  155. package/package.json +1 -1
  156. package/dist/components/p-Ban3OlgZ.js +0 -9
  157. package/dist/components/p-CGGiwvWZ.js +0 -1
  158. package/dist/components/p-CHY71o5B.js +0 -1
  159. package/dist/components/p-DXpYcAnT.js +0 -1
  160. package/dist/components/p-pGzF7PUB.js +0 -1
  161. package/dist/stencil/p-4a4b38e4.entry.js +0 -9
  162. package/dist/stencil/p-BbOHefEf.js +0 -2
  163. package/dist/stencil/p-NhRgr2_H.js +0 -1
@@ -110,7 +110,7 @@ export class KritzelCore {
110
110
  setUser(user) {
111
111
  this._user = user;
112
112
  this._store.setLocalUserId(user?.id ?? null);
113
- this._store.state.objects?.setLocalUser(user);
113
+ this._store.objects?.setLocalUser(user);
114
114
  }
115
115
  /**
116
116
  * Creates a new KritzelCore instance.
@@ -229,8 +229,8 @@ export class KritzelCore {
229
229
  // Save active workspace ID to localStorage
230
230
  localStorage.setItem(this.getStorageKey('kritzel-active-workspace-id'), activeWorkspace.id);
231
231
  // Destroy old ObjectMap if switching workspaces
232
- if (this._store.state.objects && this._store.state.objects.isReady) {
233
- this._store.state.objects.destroy();
232
+ if (this._store.objects && this._store.objects.isReady) {
233
+ this._store.objects.destroy();
234
234
  }
235
235
  // Create new ObjectMap with its own Y.Doc for this workspace
236
236
  const objectsMap = new KritzelObjectMap();
@@ -300,7 +300,10 @@ export class KritzelCore {
300
300
  height: this._store.state.viewportHeight / this._store.state.scale,
301
301
  depth: 100,
302
302
  };
303
- return this._store.state.objects
303
+ if (!this._store.objects) {
304
+ return [];
305
+ }
306
+ return this._store.objects
304
307
  .query(viewportBounds)
305
308
  .filter(obj => !(obj instanceof KritzelSelectionGroup) && !(obj instanceof KritzelSelectionBox))
306
309
  .sort((a, b) => a.zIndex - b.zIndex);
@@ -355,8 +358,8 @@ export class KritzelCore {
355
358
  workspace.updatedAt = new Date();
356
359
  this.saveWorkspaceToAppState(workspace);
357
360
  // Keep metadata map in sync when the active workspace's name changes
358
- if (this._store.state.activeWorkspace?.id === workspace.id) {
359
- this._store.state.objects?.setWorkspaceName(workspace.name);
361
+ if (this._store.activeWorkspace?.id === workspace.id) {
362
+ this._store.objects?.setWorkspaceName(workspace.name);
360
363
  }
361
364
  const workspaces = this._store.state.workspaces;
362
365
  const index = workspaces.findIndex(w => w.id === workspace.id);
@@ -373,9 +376,9 @@ export class KritzelCore {
373
376
  */
374
377
  deleteWorkspace(workspace) {
375
378
  // If deleting the active workspace, need to handle ObjectMap cleanup
376
- if (this._store.state.activeWorkspace?.id === workspace.id) {
379
+ if (this._store.activeWorkspace?.id === workspace.id) {
377
380
  this.engine.viewport?.cancelPendingUpdates();
378
- this._store.state.objects?.destroy();
381
+ this._store.objects?.destroy();
379
382
  }
380
383
  this.deleteWorkspaceFromAppState(workspace.id);
381
384
  this._store.state.workspaces = this.loadWorkspacesFromAppState();
@@ -394,7 +397,7 @@ export class KritzelCore {
394
397
  * @param scale - The zoom scale of the viewport
395
398
  */
396
399
  updateWorkspaceViewport(translateX, translateY, scale) {
397
- const activeWorkspace = this._store.state.activeWorkspace;
400
+ const activeWorkspace = this._store.activeWorkspace;
398
401
  if (!activeWorkspace) {
399
402
  return;
400
403
  }
@@ -417,7 +420,7 @@ export class KritzelCore {
417
420
  * @returns True if the workspace is public, false otherwise
418
421
  */
419
422
  getIsPublic() {
420
- return this._store.state.activeWorkspace?.isPublic ?? false;
423
+ return this._store.activeWorkspace?.isPublic ?? false;
421
424
  }
422
425
  // ═══════════════════════════════════════════════════════════════════════════
423
426
  // Object Management Methods
@@ -427,7 +430,7 @@ export class KritzelCore {
427
430
  * @param object - The object to add to the canvas
428
431
  */
429
432
  addObject(object) {
430
- this._store.state.objects.insert(object);
433
+ this._store.objects.insert(object);
431
434
  }
432
435
  /**
433
436
  * Removes an object from the workspace.
@@ -455,7 +458,7 @@ export class KritzelCore {
455
458
  this._anchorManager.handleObjectDeleted(object.id);
456
459
  }
457
460
  object.isMounted = false;
458
- this._store.state.objects.remove(o => o.id === object.id);
461
+ this._store.objects.remove(o => o.id === object.id);
459
462
  }
460
463
  /**
461
464
  * Updates properties on an object and persists the changes.
@@ -468,12 +471,15 @@ export class KritzelCore {
468
471
  const changedKeys = [];
469
472
  for (const key in updatedProperties) {
470
473
  if (updatedProperties.hasOwnProperty(key)) {
471
- object[key] = updatedProperties[key];
472
- changedKeys.push(key);
474
+ const value = updatedProperties[key];
475
+ if (value !== undefined) {
476
+ object[key] = value;
477
+ changedKeys.push(key);
478
+ }
473
479
  }
474
480
  }
475
481
  object.onAfterUpdate(changedKeys);
476
- this._store.state.objects.update(object);
482
+ this._store.objects.update(object);
477
483
  }
478
484
  /**
479
485
  * Adds a selection group to the canvas.
@@ -484,7 +490,7 @@ export class KritzelCore {
484
490
  addSelectionGroup(selectionGroup) {
485
491
  this.removeSelectionGroup();
486
492
  this.removeSelectionBox();
487
- this._store.state.objects.insert(selectionGroup);
493
+ this._store.objects.insert(selectionGroup);
488
494
  this._store.setSelectionGroup(selectionGroup);
489
495
  this._kritzelEngine.triggerSelectionChange();
490
496
  }
@@ -495,7 +501,7 @@ export class KritzelCore {
495
501
  removeSelectionGroup() {
496
502
  const selectionGroup = this._store.selectionGroup;
497
503
  if (selectionGroup) {
498
- this._store.state.objects.remove(object => object.id === selectionGroup.id);
504
+ this._store.objects.remove(object => object.id === selectionGroup.id);
499
505
  this._store.setSelectionGroup(null);
500
506
  this._kritzelEngine.triggerSelectionChange();
501
507
  }
@@ -507,9 +513,9 @@ export class KritzelCore {
507
513
  removeSelectionBox() {
508
514
  const selectionBox = this._store.selectionBox;
509
515
  if (selectionBox) {
510
- this._store.state.objects.remove(object => object.id === selectionBox.id);
516
+ this._store.objects.remove(object => object.id === selectionBox.id);
511
517
  this._store.setSelectionBox(null);
512
- this._store.state.objects.clearLocalSelectionBox();
518
+ this._store.objects.clearLocalSelectionBox();
513
519
  }
514
520
  }
515
521
  /**
@@ -538,13 +544,13 @@ export class KritzelCore {
538
544
  * Undoes the last action in the history stack.
539
545
  */
540
546
  undo() {
541
- this._store.state.objects?.undo();
547
+ this._store.objects?.undo();
542
548
  }
543
549
  /**
544
550
  * Redoes the last undone action from the history stack.
545
551
  */
546
552
  redo() {
547
- this._store.state.objects?.redo();
553
+ this._store.objects?.redo();
548
554
  }
549
555
  /**
550
556
  * Deletes a specific object by its ID.
@@ -606,7 +612,7 @@ export class KritzelCore {
606
612
  if (!copiedObjects || copiedObjects.length === 0) {
607
613
  return;
608
614
  }
609
- const activeWorkspace = this._store.state.activeWorkspace;
615
+ const activeWorkspace = this._store.activeWorkspace;
610
616
  const originalIdMapping = this._store.state.copiedObjectIdMapping;
611
617
  // Check if we're pasting from a different workspace
612
618
  const isDifferentWorkspace = copiedObjects.some(obj => obj.workspaceId !== activeWorkspace.id);
@@ -651,7 +657,7 @@ export class KritzelCore {
651
657
  const baseZIndex = this._store.currentZIndex;
652
658
  // Batch all inserts and updates in a single Y.js transaction to avoid
653
659
  // N separate observer callbacks and N rerenders (fires only once at commit)
654
- this._store.state.objects.transaction(() => {
660
+ this._store.objects.transaction(() => {
655
661
  // First add all copied objects to the objectsMap with updated positions
656
662
  copiedObjects.forEach((obj, i) => {
657
663
  // Update workspace if pasting to a different workspace
@@ -703,7 +709,7 @@ export class KritzelCore {
703
709
  }
704
710
  // If anchors were updated, rebuild the anchor index and persist the change
705
711
  if (updated) {
706
- this._store.state.objects.update(obj);
712
+ this._store.objects.update(obj);
707
713
  }
708
714
  }
709
715
  });
@@ -825,7 +831,7 @@ export class KritzelCore {
825
831
  }
826
832
  const objects = selectionGroup.objects;
827
833
  const alignedPositions = AlignmentHelper.calculateAlignedPositions(objects, alignment);
828
- this._store.state.objects.transaction(() => {
834
+ this._store.objects.transaction(() => {
829
835
  for (const obj of objects) {
830
836
  const newPosition = alignedPositions.get(obj.id);
831
837
  if (newPosition) {
@@ -923,7 +929,7 @@ export class KritzelCore {
923
929
  * Switches to selection tool.
924
930
  */
925
931
  selectAllObjects() {
926
- const allObjects = this._store.state.objects
932
+ const allObjects = this._store.objects
927
933
  .allObjects()
928
934
  .filter(o => !(o instanceof KritzelSelectionGroup) && !(o instanceof KritzelSelectionBox) && !(o instanceof KritzelContextMenu));
929
935
  if (allObjects.length > 0) {
@@ -956,7 +962,7 @@ export class KritzelCore {
956
962
  height: this._store.state.viewportHeight / this._store.state.scale,
957
963
  depth: 100,
958
964
  };
959
- const objectsInViewport = this._store.state.objects
965
+ const objectsInViewport = this._store.objects
960
966
  .query(viewportBounds)
961
967
  .filter(o => o.isInViewport())
962
968
  .filter(o => !(o instanceof KritzelSelectionGroup) && !(o instanceof KritzelSelectionBox) && !(o instanceof KritzelContextMenu));
@@ -983,7 +989,7 @@ export class KritzelCore {
983
989
  clearSelection() {
984
990
  this.removeSelectionGroup();
985
991
  const localUserId = this._user?.id;
986
- this._store.state.objects.remove(o => o instanceof KritzelSelectionBox ||
992
+ this._store.objects.remove(o => o instanceof KritzelSelectionBox ||
987
993
  (o instanceof KritzelSelectionGroup && (localUserId == null || o.userId === localUserId || o.userId == null)));
988
994
  this._store.setSelectionBox(null);
989
995
  this._store.setSelectionGroup(null);
@@ -992,7 +998,7 @@ export class KritzelCore {
992
998
  this._store.state.isRotationHandleSelected = false;
993
999
  this._store.state.isLineHandleSelected = false;
994
1000
  this._store.state.isLineHandleDragging = false;
995
- this._store.state.lineHandleType = null;
1001
+ this._store.state.lineHandleType = undefined;
996
1002
  this.rerender();
997
1003
  }
998
1004
  /**
@@ -1038,7 +1044,9 @@ export class KritzelCore {
1038
1044
  const selectedObject = elementAtPoint.closest(selector);
1039
1045
  if (selectedObject) {
1040
1046
  const foundObject = this._store.allObjects.find(object => selectedObject.id === object.id);
1041
- const isHit = foundObject?.hitTest(clientX, clientY);
1047
+ if (!foundObject)
1048
+ return null;
1049
+ const isHit = foundObject.hitTest(clientX, clientY);
1042
1050
  return isHit ? foundObject : null;
1043
1051
  }
1044
1052
  return null;
@@ -1120,7 +1128,7 @@ export class KritzelCore {
1120
1128
  // saving the old workspace's viewport to the new workspace
1121
1129
  this._kritzelEngine.viewport?.cancelPendingUpdates();
1122
1130
  // Immediately save the current workspace's viewport before switching
1123
- const currentWorkspace = this._store.state.activeWorkspace;
1131
+ const currentWorkspace = this._store.activeWorkspace;
1124
1132
  if (currentWorkspace) {
1125
1133
  this.updateWorkspaceViewport(this._store.state.translateX, this._store.state.translateY, this._store.state.scale);
1126
1134
  }
@@ -28,13 +28,49 @@ export class KritzelStore {
28
28
  get state() {
29
29
  return this._state;
30
30
  }
31
+ /**
32
+ * Gets the map of all objects in the workspace.
33
+ * Throws an error if the objects map is not initialized, as it is required for all object management.
34
+ * @returns The KritzelObjectMap containing all objects
35
+ * @throws Error if the objects map is not initialized in the state
36
+ */
37
+ get objects() {
38
+ if (!this._state.objects) {
39
+ throw new Error('Objects map is not initialized.');
40
+ }
41
+ return this._state.objects;
42
+ }
43
+ /**
44
+ * Gets the currently active workspace.
45
+ * Throws an error if no active workspace is set, as it is required for workspace-bound operations.
46
+ * @returns The current active KritzelWorkspace
47
+ * @throws Error if no active workspace is set in the state
48
+ */
49
+ get activeWorkspace() {
50
+ if (!this._state.activeWorkspace) {
51
+ throw new Error('Active workspace is not set.');
52
+ }
53
+ return this._state.activeWorkspace;
54
+ }
55
+ /**
56
+ * Gets the host element for the Kritzel editor.
57
+ * Throws an error if the host is not set, as it is required for many operations.
58
+ * @returns The host HTMLElement
59
+ * @throws Error if the host element is not set in the state
60
+ */
61
+ get host() {
62
+ if (!this._state.host) {
63
+ throw new Error('Host element is not set.');
64
+ }
65
+ return this._state.host;
66
+ }
31
67
  /**
32
68
  * Gets the next available z-index for new objects.
33
69
  * Calculates the maximum z-index among all non-selection objects and adds 1.
34
70
  * @returns The next z-index value to use for new objects
35
71
  */
36
72
  get currentZIndex() {
37
- return Math.max(0, ...this._state.objects.filter(o => !(o instanceof KritzelSelectionGroup) && !(o instanceof KritzelSelectionBox)).map(o => o.zIndex)) + 1;
73
+ return Math.max(0, ...this.objects.filter(o => !(o instanceof KritzelSelectionGroup) && !(o instanceof KritzelSelectionBox)).map(o => o.zIndex)) + 1;
38
74
  }
39
75
  /**
40
76
  * Returns true if viewport boundaries are set to finite values (not Infinity).
@@ -71,7 +107,7 @@ export class KritzelStore {
71
107
  if (this.hasViewportBoundaries) {
72
108
  return this.allObjects.length;
73
109
  }
74
- return this._state.objects.totalCount;
110
+ return this.objects.totalCount;
75
111
  }
76
112
  /**
77
113
  * Gets all objects in the workspace.
@@ -80,9 +116,9 @@ export class KritzelStore {
80
116
  */
81
117
  get allObjects() {
82
118
  if (this.hasViewportBoundaries) {
83
- return this._state.objects.query(this.viewportBoundaryBounds);
119
+ return this.objects.query(this.viewportBoundaryBounds);
84
120
  }
85
- return this._state.objects.allObjects();
121
+ return this.objects.allObjects();
86
122
  }
87
123
  /**
88
124
  * Gets all objects currently visible in the viewport.
@@ -98,7 +134,7 @@ export class KritzelStore {
98
134
  height: this._state.viewportHeight / this._state.scale,
99
135
  depth: 100,
100
136
  };
101
- return this._state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
137
+ return this.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
102
138
  }
103
139
  /**
104
140
  * Gets all objects excluding selection-related objects (selection group and selection box).
@@ -123,7 +159,7 @@ export class KritzelStore {
123
159
  if (this._selectionBoxCacheValid) {
124
160
  return this._cachedSelectionBox;
125
161
  }
126
- const selectionBoxes = this._state.objects.filter(o => o instanceof KritzelSelectionBox);
162
+ const selectionBoxes = this.objects.filter(o => o instanceof KritzelSelectionBox);
127
163
  this._cachedSelectionBox = selectionBoxes.length > 0 ? selectionBoxes[0] : null;
128
164
  this._selectionBoxCacheValid = true;
129
165
  return this._cachedSelectionBox;
@@ -138,7 +174,7 @@ export class KritzelStore {
138
174
  if (this._selectionGroupCacheValid) {
139
175
  return this._cachedSelectionGroup;
140
176
  }
141
- const selectionGroups = this._state.objects.filter(o => o instanceof KritzelSelectionGroup);
177
+ const selectionGroups = this.objects.filter(o => o instanceof KritzelSelectionGroup);
142
178
  if (this._localUserId) {
143
179
  this._cachedSelectionGroup = selectionGroups.find(sg => sg.userId === this._localUserId) ?? null;
144
180
  }
@@ -187,7 +223,7 @@ export class KritzelStore {
187
223
  * @returns The text object in editing mode, or null if none
188
224
  */
189
225
  get activeText() {
190
- const activeTexts = this._state.objects.filter(o => o instanceof KritzelText && o.isEditing);
226
+ const activeTexts = this.objects.filter(o => o instanceof KritzelText && o.isEditing);
191
227
  return activeTexts.length > 0 ? activeTexts[0] : null;
192
228
  }
193
229
  /**
@@ -195,7 +231,7 @@ export class KritzelStore {
195
231
  * @returns The shape object in editing mode, or null if none
196
232
  */
197
233
  get activeShape() {
198
- const activeShapes = this._state.objects.filter(o => o instanceof KritzelShape && o.isEditing);
234
+ const activeShapes = this.objects.filter(o => o instanceof KritzelShape && o.isEditing);
199
235
  return activeShapes.length > 0 ? activeShapes[0] : null;
200
236
  }
201
237
  /**
@@ -203,7 +239,7 @@ export class KritzelStore {
203
239
  * @returns The incomplete path object, or null if no path is being drawn
204
240
  */
205
241
  get currentPath() {
206
- const drawingPaths = this._state.objects.filter(o => o instanceof KritzelPath && o.isCompleted === false);
242
+ const drawingPaths = this.objects.filter(o => o instanceof KritzelPath && o.isCompleted === false);
207
243
  return drawingPaths.length > 0 ? drawingPaths[0] : null;
208
244
  }
209
245
  /**
@@ -211,7 +247,7 @@ export class KritzelStore {
211
247
  * @returns The incomplete line object, or null if no line is being drawn
212
248
  */
213
249
  get currentLine() {
214
- const drawingLines = this._state.objects.filter(o => o instanceof KritzelLine && o.isCompleted === false);
250
+ const drawingLines = this.objects.filter(o => o instanceof KritzelLine && o.isCompleted === false);
215
251
  return drawingLines.length > 0 ? drawingLines[0] : null;
216
252
  }
217
253
  /**
@@ -219,14 +255,14 @@ export class KritzelStore {
219
255
  * @returns The left position of the host element in client coordinates
220
256
  */
221
257
  get offsetX() {
222
- return this._state.host.getBoundingClientRect().left;
258
+ return this.host.getBoundingClientRect().left;
223
259
  }
224
260
  /**
225
261
  * Gets the vertical offset of the host element from the viewport.
226
262
  * @returns The top position of the host element in client coordinates
227
263
  */
228
264
  get offsetY() {
229
- return this._state.host.getBoundingClientRect().top;
265
+ return this.host.getBoundingClientRect().top;
230
266
  }
231
267
  /**
232
268
  * Checks if the engine is currently disabled.
@@ -262,7 +298,11 @@ export class KritzelStore {
262
298
  if (!this._listeners.has(property)) {
263
299
  this._listeners.set(property, new Set());
264
300
  }
265
- this._listeners.get(property).add(listener);
301
+ const listenersForProperty = this._listeners.get(property);
302
+ if (!listenersForProperty) {
303
+ throw new Error(`Listeners set for property ${String(property)} was not initialized.`);
304
+ }
305
+ listenersForProperty.add(listener);
266
306
  }
267
307
  /**
268
308
  * Gets the current value of a state property as readonly.
@@ -284,8 +324,9 @@ export class KritzelStore {
284
324
  const oldValue = this._state[property];
285
325
  if (oldValue !== value) {
286
326
  this._state[property] = value;
287
- if (this._listeners.has(property)) {
288
- this._listeners.get(property).forEach(listener => listener(value, oldValue, String(property)));
327
+ const listenersForProperty = this._listeners.get(property);
328
+ if (listenersForProperty) {
329
+ listenersForProperty.forEach(listener => listener(value, oldValue, String(property)));
289
330
  }
290
331
  }
291
332
  }
@@ -130,8 +130,8 @@ export class KritzelViewport {
130
130
  * Updates the stored viewport dimensions and triggers a rerender.
131
131
  */
132
132
  handleResize() {
133
- this._core.store.state.viewportWidth = this._core.store.state.host.clientWidth;
134
- this._core.store.state.viewportHeight = this._core.store.state.host.clientHeight;
133
+ this._core.store.state.viewportWidth = this._core.store.host.clientWidth;
134
+ this._core.store.state.viewportHeight = this._core.store.host.clientHeight;
135
135
  this._core.store.state.hasViewportChanged = true;
136
136
  this._core.rerender();
137
137
  }
@@ -162,15 +162,15 @@ export class KritzelViewport {
162
162
  this._touchCursorBroadcastActive = false;
163
163
  }
164
164
  if (activePointers.length === 2) {
165
- this._core.store.state.objects?.clearCursorPosition();
165
+ this._core.store.objects?.clearCursorPosition();
166
166
  const currentPath = this._core.store.currentPath;
167
167
  if (currentPath) {
168
- this._core.store.state.objects.remove(obj => obj.id === currentPath.id);
168
+ this._core.store.objects.remove(obj => obj.id === currentPath.id);
169
169
  }
170
170
  // Clear selection box when scaling starts to prevent it from remaining on screen
171
171
  if (this._core.store.state.isSelecting) {
172
172
  this._core.store.state.isSelecting = false;
173
- this._core.store.state.objects.remove(obj => obj instanceof KritzelSelectionBox);
173
+ this._core.store.objects.remove(obj => obj instanceof KritzelSelectionBox);
174
174
  this._core.store.setSelectionBox(null);
175
175
  // Clear selection preview state from objects - use selectedObjects for efficiency
176
176
  this._core.store.selectedObjects.forEach(object => {
@@ -197,12 +197,12 @@ export class KritzelViewport {
197
197
  */
198
198
  handlePointerMove(event) {
199
199
  if (event.pointerType === 'mouse') {
200
- const hostRect = this._core.store.state.host.getBoundingClientRect();
200
+ const hostRect = this._core.store.host.getBoundingClientRect();
201
201
  const xRelativeToHost = event.clientX - hostRect.left;
202
202
  const yRelativeToHost = event.clientY - hostRect.top;
203
203
  this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
204
204
  this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
205
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
205
+ this._core.store.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
206
206
  if (this._core.store.state.isPanning) {
207
207
  const dx = xRelativeToHost - this._core.store.state.startX;
208
208
  const dy = yRelativeToHost - this._core.store.state.startY;
@@ -220,12 +220,12 @@ export class KritzelViewport {
220
220
  }
221
221
  }
222
222
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
223
- const hostRect = this._core.store.state.host.getBoundingClientRect();
223
+ const hostRect = this._core.store.host.getBoundingClientRect();
224
224
  const xRelativeToHost = event.clientX - hostRect.left;
225
225
  const yRelativeToHost = event.clientY - hostRect.top;
226
226
  const activePointers = Array.from(this._core.store.state.pointers.values());
227
227
  if (this._core.store.state.isScaling || activePointers.length > 1) {
228
- this._core.store.state.objects?.clearCursorPosition();
228
+ this._core.store.objects?.clearCursorPosition();
229
229
  }
230
230
  else {
231
231
  this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
@@ -238,7 +238,7 @@ export class KritzelViewport {
238
238
  }
239
239
  }
240
240
  if (this._touchCursorBroadcastActive) {
241
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
241
+ this._core.store.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
242
242
  }
243
243
  }
244
244
  if (activePointers.length === 2) {
@@ -437,12 +437,12 @@ export class KritzelViewport {
437
437
  */
438
438
  handleZoom(event) {
439
439
  this._core.store.state.isScaling = true;
440
- const rect = this._core.store.state.host.getBoundingClientRect();
440
+ const rect = this._core.store.host.getBoundingClientRect();
441
441
  const xRelativeToHost = event.clientX - rect.left;
442
442
  const yRelativeToHost = event.clientY - rect.top;
443
443
  this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
444
444
  this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
445
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
445
+ this._core.store.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
446
446
  const rawScaleFactor = 1 + (event.deltaY * -0.012);
447
447
  const scaleFactor = Math.max(0.8, Math.min(1.2, rawScaleFactor));
448
448
  const effectiveMinScale = this.getEffectiveMinScale();
@@ -53,7 +53,7 @@ export class KritzelContextMenuHandler extends KritzelBaseHandler {
53
53
  const selectionTool = this._core.store.state.activeTool;
54
54
  selectionTool?.moveHandler?.cancelPendingDrag();
55
55
  if (this._core.store.selectionBox) {
56
- this._core.store.state.objects.remove(object => object instanceof KritzelSelectionBox);
56
+ this._core.store.objects.remove(object => object instanceof KritzelSelectionBox);
57
57
  this._core.store.setSelectionBox(null);
58
58
  this._core.store.state.isSelecting = false;
59
59
  }
@@ -98,7 +98,10 @@ export class KritzelLineHandleHandler extends KritzelBaseHandler {
98
98
  if (handleType === 'start' || handleType === 'end') {
99
99
  this._core.anchorManager.removeAnchor(line.id, handleType);
100
100
  }
101
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
101
+ const timeout = this._core.store.state.longTouchTimeout;
102
+ if (timeout) {
103
+ globalThis.clearTimeout?.(timeout);
104
+ }
102
105
  }
103
106
  /**
104
107
  * Handles pointer move events during a line handle drag operation.
@@ -393,8 +396,8 @@ export class KritzelLineHandleHandler extends KritzelBaseHandler {
393
396
  const rotatedDeltaCx = deltaCx * cos - deltaCy * sin;
394
397
  const rotatedDeltaCy = deltaCx * sin + deltaCy * cos;
395
398
  // Calculate new translate
396
- const newTranslateX = this.initialTranslateX + scale * (oldWidth - newWidth) / 2 + scale * rotatedDeltaCx;
397
- const newTranslateY = this.initialTranslateY + scale * (oldHeight - newHeight) / 2 + scale * rotatedDeltaCy;
399
+ const newTranslateX = this.initialTranslateX + (scale * (oldWidth - newWidth)) / 2 + scale * rotatedDeltaCx;
400
+ const newTranslateY = this.initialTranslateY + (scale * (oldHeight - newHeight)) / 2 + scale * rotatedDeltaCy;
398
401
  // Update the line properties
399
402
  line.startX = startX;
400
403
  line.startY = startY;
@@ -409,7 +412,7 @@ export class KritzelLineHandleHandler extends KritzelBaseHandler {
409
412
  line.translateY = newTranslateY;
410
413
  // Clear cached adjusted points
411
414
  line._adjustedPoints = null;
412
- this._core.store.state.objects.update(line);
415
+ this._core.store.objects.update(line);
413
416
  }
414
417
  /**
415
418
  * Handles pointer up events to complete a line handle drag operation.
@@ -11,17 +11,17 @@ const ANCHOR_DISCONNECT_THRESHOLD = 30;
11
11
  */
12
12
  export class KritzelMoveHandler extends KritzelBaseHandler {
13
13
  /** X coordinate where the drag started (updates during drag for incremental movement) */
14
- dragStartX;
14
+ dragStartX = 0;
15
15
  /** Y coordinate where the drag started (updates during drag for incremental movement) */
16
- dragStartY;
16
+ dragStartY = 0;
17
17
  /** Initial X coordinate when starting interaction (remains constant during drag) */
18
- startX;
18
+ startX = 0;
19
19
  /** Initial Y coordinate when starting interaction (remains constant during drag) */
20
- startY;
20
+ startY = 0;
21
21
  /** Current X coordinate during move operation */
22
- endX;
22
+ endX = 0;
23
23
  /** Current Y coordinate during move operation */
24
- endY;
24
+ endY = 0;
25
25
  /** Initial position when drag started (for calculating total accumulated distance) */
26
26
  initialDragX = 0;
27
27
  initialDragY = 0;
@@ -179,7 +179,10 @@ export class KritzelMoveHandler extends KritzelBaseHandler {
179
179
  const moveDeltaY = Math.abs(y - this.startY);
180
180
  const moveThreshold = 5;
181
181
  if (this.hasMoved || moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
182
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
182
+ const timeout = this._core.store.state.longTouchTimeout;
183
+ if (timeout) {
184
+ globalThis.clearTimeout?.(timeout);
185
+ }
183
186
  // Check for anchor disconnect threshold on lines
184
187
  this.checkAndDisconnectAnchors(x, y);
185
188
  selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
@@ -278,7 +281,7 @@ export class KritzelMoveHandler extends KritzelBaseHandler {
278
281
  // Mark as disconnected so we don't try again
279
282
  this.disconnectedLineIds.add(line.id);
280
283
  // Update the line to persist the change
281
- this._core.store.state.objects.update(line);
284
+ this._core.store.objects.update(line);
282
285
  }
283
286
  }
284
287
  }
@@ -83,7 +83,10 @@ export class KritzelResizeHandler extends KritzelBaseHandler {
83
83
  this.initialSize.height = selectionGroup.height;
84
84
  this.initialSize.x = selectionGroup.translateX;
85
85
  this.initialSize.y = selectionGroup.translateY;
86
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
86
+ const timeout = this._core.store.state.longTouchTimeout;
87
+ if (timeout) {
88
+ globalThis.clearTimeout?.(timeout);
89
+ }
87
90
  }
88
91
  }
89
92
  }
@@ -178,7 +181,10 @@ export class KritzelResizeHandler extends KritzelBaseHandler {
178
181
  const resizeDeltaY = Math.abs(dy);
179
182
  const resizeThreshold = 5;
180
183
  if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
181
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
184
+ const timeout = this._core.store.state.longTouchTimeout;
185
+ if (timeout) {
186
+ globalThis.clearTimeout?.(timeout);
187
+ }
182
188
  this.hasResized = true;
183
189
  }
184
190
  if (!this.hasResized) {
@@ -287,7 +293,10 @@ export class KritzelResizeHandler extends KritzelBaseHandler {
287
293
  this._core.store.state.hasObjectsChanged = true;
288
294
  }
289
295
  this.reset();
290
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
296
+ const timeout = this._core.store.state.longTouchTimeout;
297
+ if (timeout) {
298
+ globalThis.clearTimeout?.(timeout);
299
+ }
291
300
  }
292
301
  }
293
302
  }
@@ -72,7 +72,10 @@ export class KritzelRotationHandler extends KritzelBaseHandler {
72
72
  const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
73
73
  this.initialSelectionGroupRotation = selectionGroup.rotation;
74
74
  this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
75
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
75
+ const timeout = this._core.store.state.longTouchTimeout;
76
+ if (timeout) {
77
+ globalThis.clearTimeout?.(timeout);
78
+ }
76
79
  }
77
80
  }
78
81
  }
@@ -117,7 +120,10 @@ export class KritzelRotationHandler extends KritzelBaseHandler {
117
120
  const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
118
121
  this.rotation = currentRotation - this.initialRotation;
119
122
  selectionGroup.rotate(this.rotation);
120
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
123
+ const timeout = this._core.store.state.longTouchTimeout;
124
+ if (timeout) {
125
+ globalThis.clearTimeout?.(timeout);
126
+ }
121
127
  }
122
128
  }
123
129
  }
@@ -144,7 +150,10 @@ export class KritzelRotationHandler extends KritzelBaseHandler {
144
150
  this._core.store.state.isRotating = false;
145
151
  this._core.store.state.hasObjectsChanged = true;
146
152
  this.reset();
147
- globalThis.clearTimeout?.(this._core.store.state.longTouchTimeout);
153
+ const timeout = this._core.store.state.longTouchTimeout;
154
+ if (timeout) {
155
+ globalThis.clearTimeout?.(timeout);
156
+ }
148
157
  }
149
158
  }
150
159
  }