kritzel-stencil 0.0.169 → 0.0.171

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 (117) hide show
  1. package/dist/cjs/{default-line-tool.config-Bs88k0jE.js → default-line-tool.config-JuTDR6PF.js} +514 -13
  2. package/dist/cjs/default-line-tool.config-JuTDR6PF.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +2 -1
  4. package/dist/cjs/index.cjs.js.map +1 -1
  5. package/dist/cjs/kritzel-color_22.cjs.entry.js +170 -41
  6. package/dist/cjs/loader.cjs.js +1 -1
  7. package/dist/cjs/stencil.cjs.js +1 -1
  8. package/dist/collection/classes/core/core.class.js +89 -1
  9. package/dist/collection/classes/core/core.class.js.map +1 -1
  10. package/dist/collection/classes/core/reviver.class.js +4 -0
  11. package/dist/collection/classes/core/reviver.class.js.map +1 -1
  12. package/dist/collection/classes/handlers/key.handler.js +3 -0
  13. package/dist/collection/classes/handlers/key.handler.js.map +1 -1
  14. package/dist/collection/classes/handlers/move.handler.js +63 -0
  15. package/dist/collection/classes/handlers/move.handler.js.map +1 -1
  16. package/dist/collection/classes/handlers/rotation.handler.js +2 -0
  17. package/dist/collection/classes/handlers/rotation.handler.js.map +1 -1
  18. package/dist/collection/classes/handlers/selection.handler.js +23 -5
  19. package/dist/collection/classes/handlers/selection.handler.js.map +1 -1
  20. package/dist/collection/classes/objects/group.class.js +394 -0
  21. package/dist/collection/classes/objects/group.class.js.map +1 -0
  22. package/dist/collection/classes/objects/line.class.js +2 -1
  23. package/dist/collection/classes/objects/line.class.js.map +1 -1
  24. package/dist/collection/classes/objects/selection-group.class.js +19 -3
  25. package/dist/collection/classes/objects/selection-group.class.js.map +1 -1
  26. package/dist/collection/classes/registries/icon-registry.class.js +2 -0
  27. package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
  28. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +17 -2
  29. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
  30. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +44 -4
  31. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  32. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +3 -66
  33. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js.map +1 -1
  34. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +38 -1
  35. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js.map +1 -1
  36. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +2 -2
  37. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +2 -2
  38. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +4 -4
  39. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js.map +1 -1
  40. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  41. package/dist/collection/helpers/event.helper.js +10 -2
  42. package/dist/collection/helpers/event.helper.js.map +1 -1
  43. package/dist/collection/index.js +1 -0
  44. package/dist/collection/index.js.map +1 -1
  45. package/dist/components/index.js +3 -3
  46. package/dist/components/kritzel-brush-style.js +1 -1
  47. package/dist/components/kritzel-context-menu.js +1 -1
  48. package/dist/components/kritzel-control-brush-config.js +1 -1
  49. package/dist/components/kritzel-control-text-config.js +1 -1
  50. package/dist/components/kritzel-controls.js +1 -1
  51. package/dist/components/kritzel-editor.js +28 -13
  52. package/dist/components/kritzel-editor.js.map +1 -1
  53. package/dist/components/kritzel-engine.js +1 -1
  54. package/dist/components/kritzel-icon.js +1 -1
  55. package/dist/components/kritzel-menu-item.js +1 -1
  56. package/dist/components/kritzel-menu.js +1 -1
  57. package/dist/components/kritzel-split-button.js +1 -1
  58. package/dist/components/kritzel-tooltip.js +1 -1
  59. package/dist/components/kritzel-utility-panel.js +1 -1
  60. package/dist/components/kritzel-workspace-manager.js +1 -1
  61. package/dist/components/{p-Bwv1dxAB.js → p-BUsg2vtg.js} +12 -4
  62. package/dist/components/{p-Bwv1dxAB.js.map → p-BUsg2vtg.js.map} +1 -1
  63. package/dist/components/{p-xcQV8l_c.js → p-BhLtP5Cg.js} +12 -12
  64. package/dist/components/p-BhLtP5Cg.js.map +1 -0
  65. package/dist/components/{p-5OECjGHq.js → p-C29Efgmc.js} +5 -5
  66. package/dist/components/{p-5OECjGHq.js.map → p-C29Efgmc.js.map} +1 -1
  67. package/dist/components/{p-BoazmhlG.js → p-C6-tSCMR.js} +3 -3
  68. package/dist/components/{p-BoazmhlG.js.map → p-C6-tSCMR.js.map} +1 -1
  69. package/dist/components/{p-Cv4BGNPb.js → p-CIts5Uma.js} +4 -2
  70. package/dist/components/p-CIts5Uma.js.map +1 -0
  71. package/dist/components/{p-D8L0t-Ro.js → p-CO8bwl_3.js} +5 -5
  72. package/dist/components/{p-D8L0t-Ro.js.map → p-CO8bwl_3.js.map} +1 -1
  73. package/dist/components/{p-f_ut_1_F.js → p-D0UgEnEL.js} +611 -20
  74. package/dist/components/p-D0UgEnEL.js.map +1 -0
  75. package/dist/components/{p-BSBMBjhq.js → p-DGnDUmrk.js} +4 -4
  76. package/dist/components/{p-BSBMBjhq.js.map → p-DGnDUmrk.js.map} +1 -1
  77. package/dist/components/{p-C1S1zPH-.js → p-DO4auCYf.js} +4 -29
  78. package/dist/components/p-DO4auCYf.js.map +1 -0
  79. package/dist/components/{p-D1YAsWrL.js → p-nIBAWFcK.js} +41 -4
  80. package/dist/components/p-nIBAWFcK.js.map +1 -0
  81. package/dist/components/{p-BmdYFhLx.js → p-nZdy-Ii5.js} +4 -4
  82. package/dist/components/{p-BmdYFhLx.js.map → p-nZdy-Ii5.js.map} +1 -1
  83. package/dist/components/{p-CiM-IPaD.js → p-y25EBKEA.js} +5 -5
  84. package/dist/components/{p-CiM-IPaD.js.map → p-y25EBKEA.js.map} +1 -1
  85. package/dist/esm/{default-line-tool.config-PvsWHpO2.js → default-line-tool.config-CuBm2vpW.js} +514 -14
  86. package/dist/esm/default-line-tool.config-CuBm2vpW.js.map +1 -0
  87. package/dist/esm/index.js +2 -2
  88. package/dist/esm/kritzel-color_22.entry.js +170 -41
  89. package/dist/esm/loader.js +1 -1
  90. package/dist/esm/stencil.js +1 -1
  91. package/dist/stencil/index.esm.js +1 -1
  92. package/dist/stencil/p-CuBm2vpW.js +2 -0
  93. package/dist/stencil/p-CuBm2vpW.js.map +1 -0
  94. package/dist/stencil/p-d3f7214e.entry.js +10 -0
  95. package/dist/stencil/p-d3f7214e.entry.js.map +1 -0
  96. package/dist/stencil/stencil.esm.js +1 -1
  97. package/dist/types/classes/core/core.class.d.ts +11 -1
  98. package/dist/types/classes/handlers/move.handler.d.ts +11 -0
  99. package/dist/types/classes/objects/group.class.d.ts +97 -0
  100. package/dist/types/classes/objects/selection-group.class.d.ts +5 -0
  101. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -0
  102. package/dist/types/components/shared/kritzel-tooltip/kritzel-tooltip.d.ts +0 -3
  103. package/dist/types/components/ui/kritzel-context-menu/kritzel-context-menu.d.ts +3 -0
  104. package/dist/types/components.d.ts +4 -18
  105. package/dist/types/index.d.ts +1 -0
  106. package/package.json +1 -1
  107. package/dist/cjs/default-line-tool.config-Bs88k0jE.js.map +0 -1
  108. package/dist/components/p-C1S1zPH-.js.map +0 -1
  109. package/dist/components/p-Cv4BGNPb.js.map +0 -1
  110. package/dist/components/p-D1YAsWrL.js.map +0 -1
  111. package/dist/components/p-f_ut_1_F.js.map +0 -1
  112. package/dist/components/p-xcQV8l_c.js.map +0 -1
  113. package/dist/esm/default-line-tool.config-PvsWHpO2.js.map +0 -1
  114. package/dist/stencil/p-9913896b.entry.js +0 -10
  115. package/dist/stencil/p-9913896b.entry.js.map +0 -1
  116. package/dist/stencil/p-PvsWHpO2.js +0 -2
  117. package/dist/stencil/p-PvsWHpO2.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { p as proxyCustomElement, H, c as createEvent, h, d as Host } from './p-CwkUrTy1.js';
2
2
  import { K as KritzelMouseButton } from './p-D8W6LE-c.js';
3
- import { e as KritzelBaseObject, f as KritzelGeometryHelper, g as KritzelBaseTool, h as KritzelEventHelper, i as KritzelToolRegistry, S as Schema, s as schema, j as addListNodes, E as EditorView, k as EditorState, l as keymap, T as TextSelection, d as KritzelKeyboardHelper, m as baseKeymap, c as KritzelTextTool, b as KritzelBrushTool, K as KritzelText, a as KritzelPath } from './p-Bwv1dxAB.js';
4
- import { K as KritzelContextMenu, d as defineCustomElement$3 } from './p-D1YAsWrL.js';
3
+ import { e as KritzelBaseObject, f as KritzelGeometryHelper, g as KritzelBaseTool, h as KritzelEventHelper, i as KritzelToolRegistry, S as Schema, s as schema, j as addListNodes, E as EditorView, k as EditorState, l as keymap, T as TextSelection, d as KritzelKeyboardHelper, m as baseKeymap, c as KritzelTextTool, b as KritzelBrushTool, K as KritzelText, a as KritzelPath } from './p-BUsg2vtg.js';
4
+ import { K as KritzelContextMenu, d as defineCustomElement$3 } from './p-nIBAWFcK.js';
5
5
  import { O as ObjectHelper } from './p-B0kd2rUI.js';
6
6
  import { K as KritzelDevicesHelper } from './p-l10It7Nm.js';
7
- import { K as KritzelIconRegistry, d as defineCustomElement$1 } from './p-Cv4BGNPb.js';
7
+ import { K as KritzelIconRegistry, d as defineCustomElement$1 } from './p-CIts5Uma.js';
8
8
  import { K as KritzelWorkspace } from './p-n789Y3S-.js';
9
9
  import { d as defineCustomElement$2 } from './p-B_3OZeom.js';
10
10
 
@@ -195,7 +195,8 @@ class KritzelLine extends KritzelBaseObject {
195
195
  this._core.store.state.objects.update(this);
196
196
  }
197
197
  hitTest(x, y) {
198
- const strokeWidth = Math.max(this.strokeWidth, 10);
198
+ const minHitThreshold = 20;
199
+ const strokeWidth = Math.max(this.strokeWidth, minHitThreshold);
199
200
  const halfStroke = strokeWidth / this.scale / 2;
200
201
  if (this._adjustedPoints === null) {
201
202
  this._adjustedPoints = this.computeAdjustedPoints();
@@ -628,6 +629,399 @@ class KritzelLine extends KritzelBaseObject {
628
629
  }
629
630
  }
630
631
 
632
+ /**
633
+ * KritzelGroup represents a permanent grouping of objects that act as a single unit.
634
+ *
635
+ * Unlike KritzelSelectionGroup (which is ephemeral and used for temporary multi-selection),
636
+ * KritzelGroup is persisted and saved with the workspace. Grouped objects move, resize,
637
+ * rotate, copy, and delete together as one unit.
638
+ *
639
+ * Key features:
640
+ * - Supports nested groups (groups can contain other groups)
641
+ * - Clicking any child selects the entire group
642
+ * - Can be ungrouped to restore children to top-level objects
643
+ */
644
+ class KritzelGroup extends KritzelBaseObject {
645
+ __class__ = 'KritzelGroup';
646
+ /**
647
+ * IDs of child objects within this group.
648
+ * Children can be any KritzelBaseObject, including other KritzelGroups for nesting.
649
+ */
650
+ childIds = [];
651
+ /**
652
+ * Snapshots of child states for transformation operations (resize, rotate).
653
+ * Similar pattern to KritzelSelectionGroup.
654
+ */
655
+ unchangedChildSnapshots = new Map();
656
+ snapshotRotation = 0;
657
+ snapshotTranslateX = 0;
658
+ snapshotTranslateY = 0;
659
+ snapshotTotalWidth = 0;
660
+ snapshotTotalHeight = 0;
661
+ snapshotScale = 1;
662
+ /**
663
+ * Retrieves the actual child objects from the store by their IDs.
664
+ */
665
+ get children() {
666
+ return this.childIds
667
+ .map(id => {
668
+ const found = this._core.store.state.objects.filter(obj => obj.id === id);
669
+ return found.length > 0 ? found[0] : null;
670
+ })
671
+ .filter((obj) => obj !== null);
672
+ }
673
+ /**
674
+ * Returns the number of children in this group.
675
+ */
676
+ get length() {
677
+ return this.childIds.length;
678
+ }
679
+ /**
680
+ * Creates a new empty KritzelGroup.
681
+ */
682
+ static create(core) {
683
+ const group = new KritzelGroup();
684
+ group._core = core;
685
+ group.id = group.generateId();
686
+ group.workspaceId = core.store.state.activeWorkspace.id;
687
+ group.scale = core.store.state.scale;
688
+ group.zIndex = core.store.currentZIndex;
689
+ return group;
690
+ }
691
+ /**
692
+ * Creates a KritzelGroup from an array of objects.
693
+ * The objects are added as children and their bounding box is calculated.
694
+ */
695
+ static createFromObjects(core, objects) {
696
+ const group = KritzelGroup.create(core);
697
+ // Add all objects as children
698
+ objects.forEach(obj => {
699
+ group.childIds.push(obj.id);
700
+ });
701
+ // Calculate bounding box from children
702
+ group.refreshBoundingBox();
703
+ group.captureChildSnapshots();
704
+ return group;
705
+ }
706
+ /**
707
+ * Finds the parent KritzelGroup that contains the given object as a child.
708
+ * Returns null if the object is not a child of any group.
709
+ */
710
+ static findParentGroup(core, objectId) {
711
+ const allGroups = core.store.state.objects
712
+ .filter(obj => obj.__class__ === 'KritzelGroup');
713
+ for (const group of allGroups) {
714
+ if (group.childIds.includes(objectId)) {
715
+ return group;
716
+ }
717
+ }
718
+ return null;
719
+ }
720
+ /**
721
+ * Adds a child object to this group.
722
+ */
723
+ addChild(object) {
724
+ if (!this.childIds.includes(object.id)) {
725
+ this.childIds.push(object.id);
726
+ this.refreshBoundingBox();
727
+ this.captureChildSnapshots();
728
+ }
729
+ }
730
+ /**
731
+ * Removes a child object from this group.
732
+ */
733
+ removeChild(objectId) {
734
+ const index = this.childIds.indexOf(objectId);
735
+ if (index !== -1) {
736
+ this.childIds.splice(index, 1);
737
+ this.refreshBoundingBox();
738
+ this.captureChildSnapshots();
739
+ }
740
+ }
741
+ /**
742
+ * Ungroups this group, returning all children to top-level objects.
743
+ * The group itself should be removed after calling this.
744
+ */
745
+ ungroup() {
746
+ return this.children;
747
+ }
748
+ /**
749
+ * Finalizes the group after children have been positioned (e.g., after paste).
750
+ * Refreshes the bounding box and captures child snapshots.
751
+ */
752
+ finalize() {
753
+ this.refreshBoundingBox();
754
+ this.captureChildSnapshots();
755
+ }
756
+ /**
757
+ * Recalculates the group's bounding box based on its children.
758
+ */
759
+ refreshBoundingBox() {
760
+ if (this.children.length === 0) {
761
+ this.width = 0;
762
+ this.height = 0;
763
+ this.translateX = 0;
764
+ this.translateY = 0;
765
+ return;
766
+ }
767
+ if (this.children.length === 1) {
768
+ // Single child: inherit its bounding box
769
+ const child = this.children[0];
770
+ this.translateX = child.boundingBox.x;
771
+ this.translateY = child.boundingBox.y;
772
+ this.width = child.boundingBox.width * this.scale;
773
+ this.height = child.boundingBox.height * this.scale;
774
+ return;
775
+ }
776
+ // Multiple children: calculate encompassing bounding box
777
+ const rotation = this.rotation;
778
+ const cos = Math.cos(-rotation);
779
+ const sin = Math.sin(-rotation);
780
+ let minX = Infinity;
781
+ let maxX = -Infinity;
782
+ let minY = Infinity;
783
+ let maxY = -Infinity;
784
+ this.children.forEach(child => {
785
+ const polygon = child.rotatedPolygon;
786
+ const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
787
+ corners.forEach(corner => {
788
+ // Rotate corner into local space (aligned with group rotation)
789
+ const rx = corner.x * cos - corner.y * sin;
790
+ const ry = corner.x * sin + corner.y * cos;
791
+ if (rx < minX)
792
+ minX = rx;
793
+ if (rx > maxX)
794
+ maxX = rx;
795
+ if (ry < minY)
796
+ minY = ry;
797
+ if (ry > maxY)
798
+ maxY = ry;
799
+ });
800
+ });
801
+ // Dimensions in world units (unrotated)
802
+ const worldWidth = maxX - minX;
803
+ const worldHeight = maxY - minY;
804
+ this.width = (worldWidth - this.padding) * this.scale;
805
+ this.height = (worldHeight - this.padding) * this.scale;
806
+ // Center of the box in rotated space
807
+ const cRx = (minX + maxX) / 2;
808
+ const cRy = (minY + maxY) / 2;
809
+ // Rotate center back to world space
810
+ const cosR = Math.cos(rotation);
811
+ const sinR = Math.sin(rotation);
812
+ const cx = cRx * cosR - cRy * sinR;
813
+ const cy = cRx * sinR + cRy * cosR;
814
+ this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
815
+ this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
816
+ }
817
+ /**
818
+ * Captures snapshots of child states for transformation operations.
819
+ */
820
+ captureChildSnapshots() {
821
+ this.unchangedChildSnapshots.clear();
822
+ this.snapshotRotation = this.rotation;
823
+ this.snapshotTranslateX = this.translateX;
824
+ this.snapshotTranslateY = this.translateY;
825
+ this.snapshotTotalWidth = this.totalWidth;
826
+ this.snapshotTotalHeight = this.totalHeight;
827
+ this.snapshotScale = this.scale || 1;
828
+ this.children.forEach(child => {
829
+ this.unchangedChildSnapshots.set(child.id, {
830
+ id: child.id,
831
+ translateX: child.translateX,
832
+ translateY: child.translateY,
833
+ rotation: child.rotation,
834
+ width: child.width,
835
+ height: child.height,
836
+ totalWidth: child.totalWidth,
837
+ totalHeight: child.totalHeight,
838
+ scale: child.scale,
839
+ });
840
+ });
841
+ }
842
+ // ─────────────────────────────────────────────────────────────────────────────
843
+ // TRANSFORMATION METHODS
844
+ // ─────────────────────────────────────────────────────────────────────────────
845
+ move(startX, startY, endX, endY) {
846
+ const deltaX = (startX - endX) / this._core.store.state.scale;
847
+ const deltaY = (startY - endY) / this._core.store.state.scale;
848
+ this.translateX += deltaX;
849
+ this.translateY += deltaY;
850
+ this._core.store.state.objects.transaction(() => {
851
+ this._core.store.state.objects.update(this);
852
+ this.children.forEach(child => {
853
+ child.move(startX, startY, endX, endY);
854
+ // Update any lines anchored to this child
855
+ this._core.anchorManager.updateAnchorsForObject(child.id);
856
+ });
857
+ });
858
+ // Update snapshots
859
+ this.unchangedChildSnapshots.forEach(snapshot => {
860
+ snapshot.translateX += deltaX;
861
+ snapshot.translateY += deltaY;
862
+ });
863
+ }
864
+ resize(x, y, width, height) {
865
+ const widthScaleFactor = width / this.width;
866
+ const heightScaleFactor = height / this.height;
867
+ // Calculate old center
868
+ const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
869
+ const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
870
+ // Calculate new center
871
+ const newTotalWidth = width + this.padding * 2;
872
+ const newTotalHeight = height + this.padding * 2;
873
+ const newCenterX = x + newTotalWidth / 2 / this.scale;
874
+ const newCenterY = y + newTotalHeight / 2 / this.scale;
875
+ const rotation = this.rotation;
876
+ const cos = Math.cos(-rotation);
877
+ const sin = Math.sin(-rotation);
878
+ const cosR = Math.cos(rotation);
879
+ const sinR = Math.sin(rotation);
880
+ this._core.store.state.objects.transaction(() => {
881
+ this.children.forEach(child => {
882
+ // Calculate child center
883
+ const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
884
+ const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
885
+ // Vector from old group center to child center
886
+ const dx = childCenterX - oldCenterX;
887
+ const dy = childCenterY - oldCenterY;
888
+ // Rotate to local space
889
+ const localX = dx * cos - dy * sin;
890
+ const localY = dx * sin + dy * cos;
891
+ // Scale in local space
892
+ const scaledLocalX = localX * widthScaleFactor;
893
+ const scaledLocalY = localY * heightScaleFactor;
894
+ // Rotate back to world space
895
+ const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
896
+ const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
897
+ // New child center
898
+ const newChildCenterX = newCenterX + rotatedX;
899
+ const newChildCenterY = newCenterY + rotatedY;
900
+ // Calculate relative rotation for scaling
901
+ const relativeRotation = child.rotation - rotation;
902
+ const cosRel = Math.cos(relativeRotation);
903
+ const sinRel = Math.sin(relativeRotation);
904
+ const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
905
+ const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
906
+ const updatedWidth = child.width * newChildWidthScale;
907
+ const updatedHeight = child.height * newChildHeightScale;
908
+ const updatedTotalWidth = updatedWidth + child.padding * 2;
909
+ const updatedTotalHeight = updatedHeight + child.padding * 2;
910
+ const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
911
+ const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
912
+ child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
913
+ // Update anchored lines
914
+ this._core.anchorManager.updateAnchorsForObject(child.id);
915
+ });
916
+ this.refreshBoundingBox();
917
+ this.captureChildSnapshots();
918
+ this._core.store.state.objects.update(this);
919
+ });
920
+ }
921
+ rotate(value) {
922
+ this.rotation = value;
923
+ const centerX = this.translateX + this.totalWidth / 2 / this.scale;
924
+ const centerY = this.translateY + this.totalHeight / 2 / this.scale;
925
+ const angle = value - this.snapshotRotation;
926
+ const cos = Math.cos(angle);
927
+ const sin = Math.sin(angle);
928
+ this._core.store.state.objects.transaction(() => {
929
+ this._core.store.state.objects.update(this);
930
+ this.children.forEach(child => {
931
+ const unchangedSnapshot = this.unchangedChildSnapshots.get(child.id);
932
+ if (!unchangedSnapshot)
933
+ return;
934
+ const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
935
+ const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
936
+ const rotatedX = cos * offsetX - sin * offsetY;
937
+ const rotatedY = sin * offsetX + cos * offsetY;
938
+ child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
939
+ child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
940
+ child.rotate(this.children.length === 1 ? value : unchangedSnapshot.rotation + angle);
941
+ });
942
+ });
943
+ }
944
+ getOffsetXToCenterFromSnapshot(snapshot) {
945
+ const childCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
946
+ const groupScale = this.snapshotScale || this.scale || 1;
947
+ const groupCenterX = this.snapshotTranslateX + this.snapshotTotalWidth / groupScale / 2;
948
+ return childCenterX - groupCenterX;
949
+ }
950
+ getOffsetYToCenterFromSnapshot(snapshot) {
951
+ const childCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
952
+ const groupScale = this.snapshotScale || this.scale || 1;
953
+ const groupCenterY = this.snapshotTranslateY + this.snapshotTotalHeight / groupScale / 2;
954
+ return childCenterY - groupCenterY;
955
+ }
956
+ // ─────────────────────────────────────────────────────────────────────────────
957
+ // HIT TESTING
958
+ // ─────────────────────────────────────────────────────────────────────────────
959
+ hitTest(x, y) {
960
+ // Check if point is inside any child
961
+ return this.children.some(child => child.hitTest(x, y));
962
+ }
963
+ hitTestPolygon(polygon) {
964
+ // Check if polygon intersects with any child
965
+ return this.children.some(child => child.hitTestPolygon(polygon));
966
+ }
967
+ // ─────────────────────────────────────────────────────────────────────────────
968
+ // COPY & SERIALIZATION
969
+ // ─────────────────────────────────────────────────────────────────────────────
970
+ copy() {
971
+ const copiedGroup = KritzelGroup.create(this._core);
972
+ // Deep copy all children but DON'T add to store yet
973
+ // Store the copied children temporarily for paste() to handle
974
+ const copiedChildren = [];
975
+ this.children
976
+ .sort((a, b) => a.zIndex - b.zIndex)
977
+ .forEach(child => {
978
+ const copiedChild = child.copy();
979
+ copiedChildren.push(copiedChild);
980
+ copiedGroup.childIds.push(copiedChild.id);
981
+ });
982
+ // Store copied children on the group for paste() to access
983
+ copiedGroup._pendingChildren = copiedChildren;
984
+ // Copy group properties
985
+ copiedGroup.rotation = this.rotation;
986
+ copiedGroup.translateX = this.translateX;
987
+ copiedGroup.translateY = this.translateY;
988
+ copiedGroup.width = this.width;
989
+ copiedGroup.height = this.height;
990
+ return copiedGroup;
991
+ }
992
+ serialize() {
993
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedChildSnapshots, ...remainingProps } = this;
994
+ const clonedProps = structuredClone(remainingProps);
995
+ // Convert Map to plain object for serialization
996
+ clonedProps.unchangedChildSnapshots = Object.fromEntries(this.unchangedChildSnapshots);
997
+ return clonedProps;
998
+ }
999
+ deserialize(object) {
1000
+ super.deserialize(object);
1001
+ // Restore the Map from serialized object
1002
+ if (object.unchangedChildSnapshots) {
1003
+ this.unchangedChildSnapshots = new Map(Object.entries(object.unchangedChildSnapshots));
1004
+ }
1005
+ return this;
1006
+ }
1007
+ update() {
1008
+ this._core.store.state.objects.update(this);
1009
+ }
1010
+ /**
1011
+ * Updates the workspace ID for this group and all its children.
1012
+ */
1013
+ updateWorkspaceId(workspaceId) {
1014
+ this.workspaceId = workspaceId;
1015
+ this.children.forEach(child => {
1016
+ child.workspaceId = workspaceId;
1017
+ // Recursively update nested groups
1018
+ if (child instanceof KritzelGroup) {
1019
+ child.updateWorkspaceId(workspaceId);
1020
+ }
1021
+ });
1022
+ }
1023
+ }
1024
+
631
1025
  class KritzelSelectionGroup extends KritzelBaseObject {
632
1026
  __class__ = 'KritzelSelectionGroup';
633
1027
  // Store only object IDs instead of full objects
@@ -671,6 +1065,21 @@ class KritzelSelectionGroup extends KritzelBaseObject {
671
1065
  this.captureUnchangedSnapshots();
672
1066
  this.refreshObjectDimensions();
673
1067
  }
1068
+ /**
1069
+ * Prepares the selection group and its children for a transform interaction (rotate/resize).
1070
+ * Ensures snapshot state is aligned with the current visual state.
1071
+ */
1072
+ beginTransform() {
1073
+ // Keep the selection group's bounding box current before snapshotting.
1074
+ this.refreshObjectDimensions();
1075
+ this.captureUnchangedSnapshots();
1076
+ // Groups need their own child snapshots aligned with the current transform.
1077
+ this.objects.forEach(obj => {
1078
+ if (obj instanceof KritzelGroup) {
1079
+ obj.finalize();
1080
+ }
1081
+ });
1082
+ }
674
1083
  deselectAllChildren() {
675
1084
  this.objects.forEach(obj => (obj.isSelected = false));
676
1085
  }
@@ -931,9 +1340,9 @@ class KritzelSelectionGroup extends KritzelBaseObject {
931
1340
  return objCenterY - groupCenterY;
932
1341
  }
933
1342
  hitTest(x, y) {
934
- const containsOnlyLines = this.objects.every(obj => obj.__class__ === 'KritzelLine');
935
- if (containsOnlyLines) {
936
- return this.objects.some(obj => obj.hitTest(x, y));
1343
+ const isSingleLine = this.objects.length === 1 && this.objects[0].__class__ === 'KritzelLine';
1344
+ if (isSingleLine) {
1345
+ return this.objects[0].hitTest(x, y);
937
1346
  }
938
1347
  const polygon = this.rotatedPolygon;
939
1348
  const polyPoints = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
@@ -1346,6 +1755,8 @@ class KritzelBaseHandler {
1346
1755
  }
1347
1756
  }
1348
1757
 
1758
+ /** Threshold in screen pixels for disconnecting anchored lines during drag */
1759
+ const ANCHOR_DISCONNECT_THRESHOLD = 30;
1349
1760
  class KritzelMoveHandler extends KritzelBaseHandler {
1350
1761
  dragStartX;
1351
1762
  dragStartY;
@@ -1353,6 +1764,11 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1353
1764
  startY;
1354
1765
  endX;
1355
1766
  endY;
1767
+ /** Initial position when drag started (for calculating total accumulated distance) */
1768
+ initialDragX = 0;
1769
+ initialDragY = 0;
1770
+ /** Set of line IDs that have been disconnected from anchors during this drag */
1771
+ disconnectedLineIds = new Set();
1356
1772
  hasMoved = false;
1357
1773
  trackedPointerId = null;
1358
1774
  constructor(core) {
@@ -1365,6 +1781,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1365
1781
  this.startY = 0;
1366
1782
  this.endX = 0;
1367
1783
  this.endY = 0;
1784
+ this.initialDragX = 0;
1785
+ this.initialDragY = 0;
1786
+ this.disconnectedLineIds.clear();
1368
1787
  this.hasMoved = false;
1369
1788
  this.trackedPointerId = null;
1370
1789
  }
@@ -1384,6 +1803,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1384
1803
  this.dragStartY = clientY;
1385
1804
  this.startX = this.dragStartX;
1386
1805
  this.startY = this.dragStartY;
1806
+ this.initialDragX = clientX;
1807
+ this.initialDragY = clientY;
1808
+ this.disconnectedLineIds.clear();
1387
1809
  this.trackedPointerId = event.pointerId;
1388
1810
  }
1389
1811
  else {
@@ -1409,6 +1831,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1409
1831
  this.dragStartY = y;
1410
1832
  this.startX = x;
1411
1833
  this.startY = y;
1834
+ this.initialDragX = x;
1835
+ this.initialDragY = y;
1836
+ this.disconnectedLineIds.clear();
1412
1837
  this.trackedPointerId = event.pointerId;
1413
1838
  }
1414
1839
  else {
@@ -1432,6 +1857,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1432
1857
  const moveDeltaY = Math.abs(clientY - this.startY);
1433
1858
  const moveThreshold = 5;
1434
1859
  if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
1860
+ // Check for anchor disconnect threshold on lines
1861
+ this.checkAndDisconnectAnchors(clientX, clientY);
1435
1862
  selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
1436
1863
  this.dragStartX = clientX;
1437
1864
  this.dragStartY = clientY;
@@ -1457,6 +1884,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1457
1884
  const moveThreshold = 5;
1458
1885
  if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
1459
1886
  clearTimeout(this._core.store.state.longTouchTimeout);
1887
+ // Check for anchor disconnect threshold on lines
1888
+ this.checkAndDisconnectAnchors(x, y);
1460
1889
  selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
1461
1890
  this.dragStartX = x;
1462
1891
  this.dragStartY = y;
@@ -1491,6 +1920,48 @@ class KritzelMoveHandler extends KritzelBaseHandler {
1491
1920
  }
1492
1921
  this.reset();
1493
1922
  }
1923
+ /**
1924
+ * Checks if the total accumulated drag distance exceeds the anchor disconnect threshold.
1925
+ * If so, disconnects all anchors from any KritzelLine objects in the selection group.
1926
+ * This allows anchored lines to be freely moved after dragging beyond the threshold.
1927
+ */
1928
+ checkAndDisconnectAnchors(currentX, currentY) {
1929
+ const totalDeltaX = currentX - this.initialDragX;
1930
+ const totalDeltaY = currentY - this.initialDragY;
1931
+ const totalDistance = Math.sqrt(totalDeltaX * totalDeltaX + totalDeltaY * totalDeltaY);
1932
+ if (totalDistance < ANCHOR_DISCONNECT_THRESHOLD) {
1933
+ return;
1934
+ }
1935
+ const selectionGroup = this._core.store.selectionGroup;
1936
+ if (!selectionGroup) {
1937
+ return;
1938
+ }
1939
+ // Find and disconnect anchors from all anchored lines in the selection
1940
+ for (const obj of selectionGroup.objects) {
1941
+ // Skip if already disconnected in this drag session
1942
+ if (this.disconnectedLineIds.has(obj.id)) {
1943
+ continue;
1944
+ }
1945
+ // Check if this is a line with anchors
1946
+ if (obj instanceof KritzelLine) {
1947
+ const line = obj;
1948
+ const hasAnchors = line.startAnchor || line.endAnchor;
1949
+ if (hasAnchors) {
1950
+ // Disconnect both anchors
1951
+ if (line.startAnchor) {
1952
+ this._core.anchorManager.removeAnchor(line.id, 'start');
1953
+ }
1954
+ if (line.endAnchor) {
1955
+ this._core.anchorManager.removeAnchor(line.id, 'end');
1956
+ }
1957
+ // Mark as disconnected so we don't try again
1958
+ this.disconnectedLineIds.add(line.id);
1959
+ // Update the line to persist the change
1960
+ this._core.store.state.objects.update(line);
1961
+ }
1962
+ }
1963
+ }
1964
+ }
1494
1965
  }
1495
1966
 
1496
1967
  var KritzelHandleType;
@@ -1722,6 +2193,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
1722
2193
  if (KritzelEventHelper.isLeftClick(event)) {
1723
2194
  const selectionGroup = this._core.store.selectionGroup;
1724
2195
  if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
2196
+ selectionGroup.beginTransform();
1725
2197
  const clientX = event.clientX - this._core.store.offsetX;
1726
2198
  const clientY = event.clientY - this._core.store.offsetY;
1727
2199
  this._core.store.state.isRotating = true;
@@ -1745,6 +2217,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
1745
2217
  if (activePointers.length === 1) {
1746
2218
  const selectionGroup = this._core.store.selectionGroup;
1747
2219
  if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
2220
+ selectionGroup.beginTransform();
1748
2221
  const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
1749
2222
  const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
1750
2223
  this._core.store.state.isRotating = true;
@@ -2055,11 +2528,14 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
2055
2528
  if (!object) {
2056
2529
  return;
2057
2530
  }
2058
- object.isSelected = false;
2531
+ // Resolve child objects to their parent groups
2532
+ const parentGroup = KritzelGroup.findParentGroup(this._core, object.id);
2533
+ const objectToSelect = parentGroup || object;
2534
+ objectToSelect.isSelected = false;
2059
2535
  const selectionGroup = KritzelSelectionGroup.create(this._core);
2060
- selectionGroup.addOrRemove(object);
2536
+ selectionGroup.addOrRemove(objectToSelect);
2061
2537
  selectionGroup.isSelected = true;
2062
- selectionGroup.rotation = object.rotation;
2538
+ selectionGroup.rotation = objectToSelect.rotation;
2063
2539
  this._core.addSelectionGroup(selectionGroup);
2064
2540
  this._core.rerender();
2065
2541
  }
@@ -2068,10 +2544,24 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
2068
2544
  if (selectedObjects.length === 0) {
2069
2545
  return;
2070
2546
  }
2547
+ // Resolve child objects to their parent groups to avoid double-move
2548
+ const resolvedObjects = new Map();
2549
+ selectedObjects.forEach(obj => {
2550
+ // Check if this object is a child of a KritzelGroup
2551
+ const parentGroup = KritzelGroup.findParentGroup(this._core, obj.id);
2552
+ if (parentGroup) {
2553
+ // Add the parent group instead of the child
2554
+ resolvedObjects.set(parentGroup.id, parentGroup);
2555
+ }
2556
+ else {
2557
+ // Not a child of any group, add the object itself
2558
+ resolvedObjects.set(obj.id, obj);
2559
+ }
2560
+ obj.isSelected = false;
2561
+ });
2071
2562
  const selectionGroup = KritzelSelectionGroup.create(this._core);
2072
- selectedObjects.forEach(o => {
2563
+ resolvedObjects.forEach(o => {
2073
2564
  selectionGroup.addOrRemove(o);
2074
- o.isSelected = false;
2075
2565
  });
2076
2566
  selectionGroup.isSelected = true;
2077
2567
  if (selectionGroup.length === 1) {
@@ -36735,6 +37225,9 @@ class KritzelKeyHandler extends KritzelBaseHandler {
36735
37225
  { key: '-', ctrl: true, condition: c => !!c.store.selectionGroup, action: c => c.sendBackward() },
36736
37226
  { key: '*', shift: true, condition: c => !!c.store.selectionGroup, action: c => c.bringToFront() },
36737
37227
  { key: '_', shift: true, condition: c => !!c.store.selectionGroup, action: c => c.sendToBack() },
37228
+ // Grouping
37229
+ { key: 'g', ctrl: true, condition: c => !!c.store.selectionGroup && c.store.selectionGroup.objects.length >= 2, action: c => c.group() },
37230
+ { key: 'G', ctrl: true, shift: true, condition: c => !!c.store.selectionGroup, action: c => c.ungroup() },
36738
37231
  ];
36739
37232
  constructor(core) {
36740
37233
  super(core);
@@ -36979,6 +37472,9 @@ class KritzelReviver {
36979
37472
  case 'KritzelSelectionGroup':
36980
37473
  revivedObj = KritzelSelectionGroup.create(this._core).deserialize(obj);
36981
37474
  break;
37475
+ case 'KritzelGroup':
37476
+ revivedObj = KritzelGroup.create(this._core).deserialize(obj);
37477
+ break;
36982
37478
  case 'KritzelWorkspace':
36983
37479
  revivedObj = KritzelWorkspace.create(this._core, obj).deserialize(obj);
36984
37480
  break;
@@ -37814,7 +38310,15 @@ class KritzelCore {
37814
38310
  addObject(object) {
37815
38311
  this._store.state.objects.insert(object);
37816
38312
  }
37817
- removeObject(object) {
38313
+ removeObject(object, preserveChildren = false) {
38314
+ // Handle KritzelGroup: recursively delete all children first (unless preserveChildren is true)
38315
+ if (object instanceof KritzelGroup && !preserveChildren) {
38316
+ // Get children before removing the group
38317
+ const children = [...object.children];
38318
+ children.forEach(child => {
38319
+ this.removeObject(child);
38320
+ });
38321
+ }
37818
38322
  // Handle anchor cleanup
37819
38323
  if (object instanceof KritzelLine) {
37820
38324
  // If removing a line, clean up its anchor index entries
@@ -37951,6 +38455,28 @@ class KritzelCore {
37951
38455
  obj.updatePosition(obj.translateX + offsetX, obj.translateY + offsetY);
37952
38456
  // Update z-index
37953
38457
  obj.zIndex = this._store.currentZIndex + i;
38458
+ // Handle KritzelGroup: also add pending children with offset
38459
+ if (obj instanceof KritzelGroup) {
38460
+ const pendingChildren = obj._pendingChildren;
38461
+ if (pendingChildren) {
38462
+ pendingChildren.forEach((child, childIndex) => {
38463
+ // Update workspace
38464
+ if (child.workspaceId !== activeWorkspace.id) {
38465
+ child.workspaceId = activeWorkspace.id;
38466
+ }
38467
+ // Update position with offset
38468
+ child.updatePosition(child.translateX + offsetX, child.translateY + offsetY);
38469
+ // Update z-index
38470
+ child.zIndex = this._store.currentZIndex + i + childIndex;
38471
+ // Add child to store
38472
+ this.addObject(child);
38473
+ });
38474
+ // Clear pending children
38475
+ delete obj._pendingChildren;
38476
+ // Finalize the group's bounding box and snapshots
38477
+ obj.finalize();
38478
+ }
38479
+ }
37954
38480
  // Add to objectsMap
37955
38481
  this.addObject(obj);
37956
38482
  // Add to selection group
@@ -38062,6 +38588,63 @@ class KritzelCore {
38062
38588
  });
38063
38589
  this.rerender();
38064
38590
  }
38591
+ /**
38592
+ * Groups the currently selected objects into a KritzelGroup.
38593
+ * Requires at least 2 objects to be selected.
38594
+ */
38595
+ group() {
38596
+ const selectionGroup = this._store.selectionGroup;
38597
+ if (!selectionGroup || selectionGroup.objects.length < 2) {
38598
+ return;
38599
+ }
38600
+ // Create a group from the selected objects
38601
+ const group = KritzelGroup.createFromObjects(this, selectionGroup.objects);
38602
+ // Add the group to the object store
38603
+ this.addObject(group);
38604
+ // Remove the selection group and create a new one with just the group
38605
+ this.removeSelectionGroup();
38606
+ const newSelectionGroup = KritzelSelectionGroup.create(this);
38607
+ newSelectionGroup.addOrRemove(group);
38608
+ newSelectionGroup.isSelected = true;
38609
+ newSelectionGroup.rotation = group.rotation;
38610
+ this.addSelectionGroup(newSelectionGroup);
38611
+ this.engine.emitObjectsChange();
38612
+ this.rerender();
38613
+ }
38614
+ /**
38615
+ * Ungroups any KritzelGroup objects in the current selection.
38616
+ * Children are extracted and become top-level objects.
38617
+ */
38618
+ ungroup() {
38619
+ const selectionGroup = this._store.selectionGroup;
38620
+ if (!selectionGroup) {
38621
+ return;
38622
+ }
38623
+ const groups = selectionGroup.objects.filter(obj => obj instanceof KritzelGroup);
38624
+ if (groups.length === 0) {
38625
+ return;
38626
+ }
38627
+ // Collect all children from all groups
38628
+ const allChildren = [];
38629
+ const nonGroupObjects = selectionGroup.objects.filter(obj => !(obj instanceof KritzelGroup));
38630
+ groups.forEach(group => {
38631
+ // Get children before removing the group
38632
+ const children = group.ungroup();
38633
+ allChildren.push(...children);
38634
+ // Remove the group from the store (but keep children)
38635
+ this.removeObject(group, true);
38636
+ });
38637
+ // Create a new selection group with all the ungrouped children and non-group objects
38638
+ this.removeSelectionGroup();
38639
+ const newSelectionGroup = KritzelSelectionGroup.create(this);
38640
+ [...allChildren, ...nonGroupObjects].forEach(child => {
38641
+ newSelectionGroup.addOrRemove(child);
38642
+ });
38643
+ newSelectionGroup.isSelected = true;
38644
+ this.addSelectionGroup(newSelectionGroup);
38645
+ this.engine.emitObjectsChange();
38646
+ this.rerender();
38647
+ }
38065
38648
  selectObjects(objects) {
38066
38649
  if (objects.length === 0) {
38067
38650
  return;
@@ -38460,6 +39043,12 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
38460
39043
  async sendToBack(object) {
38461
39044
  this.core.sendToBack(object);
38462
39045
  }
39046
+ async group() {
39047
+ this.core.group();
39048
+ }
39049
+ async ungroup() {
39050
+ this.core.ungroup();
39051
+ }
38463
39052
  async undo() {
38464
39053
  this.core.undo();
38465
39054
  }
@@ -38646,7 +39235,7 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
38646
39235
  };
38647
39236
  const visibleObjects = this.core.store.state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
38648
39237
  this.core.cursorManager.applyCursor();
38649
- return (h(Host, { key: '209ccf8414c635c9e04ca4807c95a29c629071ec' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: 'df6a03d1341887960bb91cbaa0a5dca6880a9293', class: "debug-panel" }, h("div", { key: '65157757ec6549ad2af311085a9618ab661f5ac0' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '20137b6c651a9aa8df15bce66d5bfe870a7af139' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '49dd30286641236ae428c731bb181cb06269852e' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: 'd8ee431a10335e99aec40a596817765d7ee3f67b' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: '34b1f31146080780433d6c1718d13bbfe397cb9b' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: '4c279a106101cf808c91a2be4987569b21ae52b3' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: '6ebd7ad3c803ef45d58a74180c52e8be8af8bca3' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: '345970c8b2b54f3a0d8fe1d9bb1b7b33023db386' }, "Scale: ", this.core.store.state?.scale), h("div", { key: 'fc265ae0e71cf22311cab3006dfc29b9d24397d2' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: '9f921032a58b55e60f82d11a76f269051357d3e1' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: '6d197e06cc362a9ecb8bfe00d272aecfec2b49e1' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: '5313589ba2a509c95a5730dcf29c4003d9884cbf' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '8af9f9d6e33a2897c20962048bbf2a058f9d4a45' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: '81aec0050d3e2122643b61fd725e71b25d97bc27' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: 'c9956f05c01b74ee1666d9395a2715c3463fe401' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: 'ae0a01c8f8aafc08326b136720c6a75b6aec6299' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: '7816d40d73248a55120440e6d721325e1894178e' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: 'c22383bacca0ada584f2e8a8577523c905d74ae2' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: '8185a7318c6cdfde558cc372b79582d58b706f9e' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '2d56b05377693ef032ab2316fa753ecedf264b9c' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: '2e2bf9fd321ef04dc0ac6dc21539eb0117c9fb23' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '3118ab970d5d37e01405c42308fc051d675d4bfa' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: 'd8a31065843f89552f2d9c03dcbca960a716fa6c' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: 'cb8d28b6ea63cbb6572f761011fccb847b748227' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: 'd112a7e833c265085246817423557bfdc5b30b74' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: 'df4f302192a8fc9644b7f883618b0a0f8a600dc8', id: "origin", class: "origin", style: {
39238
+ return (h(Host, { key: 'd0f13eb1d34a6edcba439a501edbaab1875112f1' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: 'f722474cfd43ab13e306cea908871af77015d015', class: "debug-panel" }, h("div", { key: 'efd483bc92223d78f6a0f661a1911820de83682a' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '37c213c1e3d57cb79ccb99ff65404fa3e03f07e0' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '68bcdc9e10d89a6a4284dbdc11d679f6c8bed6df' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: 'c653966e412a801d536bfdcf8ab79176376693a5' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: 'db83b7d58514296daa1465926867d099df6988f9' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: '1f15e3edec3165920207ba8f1b2886aa72049d08' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: 'c4e468e3c602f1fd62d4b13de1ec39dd1302768f' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: '8eae67aac27495a611ba125e029473119665bb7e' }, "Scale: ", this.core.store.state?.scale), h("div", { key: '8b22934652db52d915cb21f48f8340ceb2fe1327' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: 'd4d1872b8e1c33f0925a2f3cfcf4bc652cc5637e' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: '76fc159e2f38324248db21cab17fe76dd7add144' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: 'aa5a5a7c5faac899492f9c87cb9bf5cf960329cb' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '21d4d43e7fc91a187055906c9f38529407f4ec6f' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: 'b43ba7bafd3ac52f5622e7d980456e7cbf5ff0fb' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: 'ecf9749bf4eece2970a62230d323fc205ce62fed' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: '6f9c7b7fa357f86c05aa76c02428ab0584856f3d' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: '7b61ed971fd7b4a029204861b19677421c049a33' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: '875ed8cdf18b46c362b6b0c331a8f9fcba6bc1e2' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: 'f56f27eff2c7e715528a32b4fe874da5220bc1ef' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '7831c3571031b029766b0fa16fe2b0e29e8c19b5' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: 'd4167e465da2f690b9d1f6d3cb8778f4a618843f' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '00dfeba01da344a0e3c40f2f92ca539ef59d5da5' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: '30948e66636501df9863caa67da468d45517a44f' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: '9ee2c0bb69e9a150b7ab7c0ca9881897b5ec2a72' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: '9d470ca319b0c32de7565c46b28c6483d4b9c1cb' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: '4eaaf1e928e62c872b354523e55f33f01c25c5e7', id: "origin", class: "origin", style: {
38650
39239
  transform: `matrix(${this.core.store.state?.scale}, 0, 0, ${this.core.store.state?.scale}, ${this.core.store.state?.translateX}, ${this.core.store.state?.translateY})`,
38651
39240
  } }, visibleObjects?.map(object => {
38652
39241
  return (h("div", { key: object.id, style: {
@@ -38895,18 +39484,18 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
38895
39484
  stroke: 'var(--kritzel-snap-indicator-stroke, #3b82f6)',
38896
39485
  strokeWidth: data.indicatorStrokeWidth,
38897
39486
  } }))));
38898
- })()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '70aefa0f3cf33036edb455dbe75448d2521e6dc9', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
39487
+ })()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: 'f726cabc77f66c24618dc0b54cca8bd681608dde', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
38899
39488
  position: 'fixed',
38900
39489
  left: `${this.core.store.state.contextMenuX}px`,
38901
39490
  top: `${this.core.store.state.contextMenuY}px`,
38902
- zIndex: '10000',
39491
+ zIndex: '10002',
38903
39492
  }, onActionSelected: event => {
38904
39493
  event.detail.action({
38905
39494
  x: (-this.core.store.state.translateX + this.core.store.state.contextMenuX) / this.core.store.state.scale,
38906
39495
  y: (-this.core.store.state.translateY + this.core.store.state.contextMenuY) / this.core.store.state.scale,
38907
39496
  }, this.core.store.selectionGroup?.objects);
38908
39497
  this.hideContextMenu();
38909
- }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: '68106a187ca81a38a819f1d52895b2858a8a28cb', core: this.core })));
39498
+ }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: 'bf42f27e52c03f9d00a5459883ac45ed39bb1dcd', core: this.core })));
38910
39499
  }
38911
39500
  static get watchers() { return {
38912
39501
  "workspace": ["onWorkspaceChange"],
@@ -38936,6 +39525,8 @@ const KritzelEngine = /*@__PURE__*/ proxyCustomElement(class KritzelEngine exten
38936
39525
  "sendBackward": [64],
38937
39526
  "bringToFront": [64],
38938
39527
  "sendToBack": [64],
39528
+ "group": [64],
39529
+ "ungroup": [64],
38939
39530
  "undo": [64],
38940
39531
  "redo": [64],
38941
39532
  "hideContextMenu": [64],
@@ -38989,7 +39580,7 @@ function defineCustomElement() {
38989
39580
  } });
38990
39581
  }
38991
39582
 
38992
- export { isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelLine as E, KritzelLineTool as F, KritzelEraserTool as G, HocuspocusProvider as H, KritzelImageTool as I, KritzelCursorHelper as J, KritzelImage as K, KritzelSelectionTool as L, IndexedDBSyncProvider as M, KritzelAppStateMap as N, Observable$1 as O, KritzelAnchorManager as P, ABSOLUTE_SCALE_MAX as Q, ABSOLUTE_SCALE_MIN as R, ShapeType as S, KritzelShapeTool as T, defineCustomElement as U, KritzelEngine as V, writeVarUint8Array$2 as a, readVarUint8Array$2 as b, applyUpdate as c, encodeStateVector as d, encodeStateAsUpdate as e, createEncoder$1 as f, createDecoder$1 as g, create$8 as h, fromBase64 as i, toBase64 as j, createUint8ArrayFromArrayBuffer as k, offChange as l, readVarString$2 as m, floor$2 as n, onChange as o, getUnixTime$1 as p, equalityDeep$1 as q, readVarUint$2 as r, setIfUndefined$1 as s, toUint8Array$1 as t, writeVarString$2 as u, varStorage as v, writeVarUint$2 as w, map as x, ObservableV2 as y, length$3 as z };
38993
- //# sourceMappingURL=p-f_ut_1_F.js.map
39583
+ export { isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelLine as E, KritzelGroup as F, KritzelLineTool as G, HocuspocusProvider as H, KritzelEraserTool as I, KritzelImageTool as J, KritzelImage as K, KritzelCursorHelper as L, KritzelSelectionTool as M, IndexedDBSyncProvider as N, Observable$1 as O, KritzelAppStateMap as P, KritzelAnchorManager as Q, ABSOLUTE_SCALE_MAX as R, ShapeType as S, ABSOLUTE_SCALE_MIN as T, KritzelShapeTool as U, defineCustomElement as V, KritzelEngine as W, writeVarUint8Array$2 as a, readVarUint8Array$2 as b, applyUpdate as c, encodeStateVector as d, encodeStateAsUpdate as e, createEncoder$1 as f, createDecoder$1 as g, create$8 as h, fromBase64 as i, toBase64 as j, createUint8ArrayFromArrayBuffer as k, offChange as l, readVarString$2 as m, floor$2 as n, onChange as o, getUnixTime$1 as p, equalityDeep$1 as q, readVarUint$2 as r, setIfUndefined$1 as s, toUint8Array$1 as t, writeVarString$2 as u, varStorage as v, writeVarUint$2 as w, map as x, ObservableV2 as y, length$3 as z };
39584
+ //# sourceMappingURL=p-D0UgEnEL.js.map
38994
39585
 
38995
- //# sourceMappingURL=p-f_ut_1_F.js.map
39586
+ //# sourceMappingURL=p-D0UgEnEL.js.map