kritzel-stencil 0.0.169 → 0.0.170

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 (100) 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 +120 -6
  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 +43 -3
  31. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  32. package/dist/collection/helpers/event.helper.js +10 -2
  33. package/dist/collection/helpers/event.helper.js.map +1 -1
  34. package/dist/collection/index.js +1 -0
  35. package/dist/collection/index.js.map +1 -1
  36. package/dist/components/index.js +3 -3
  37. package/dist/components/kritzel-brush-style.js +1 -1
  38. package/dist/components/kritzel-context-menu.js +1 -1
  39. package/dist/components/kritzel-control-brush-config.js +1 -1
  40. package/dist/components/kritzel-control-text-config.js +1 -1
  41. package/dist/components/kritzel-controls.js +1 -1
  42. package/dist/components/kritzel-editor.js +27 -12
  43. package/dist/components/kritzel-editor.js.map +1 -1
  44. package/dist/components/kritzel-engine.js +1 -1
  45. package/dist/components/kritzel-icon.js +1 -1
  46. package/dist/components/kritzel-menu-item.js +1 -1
  47. package/dist/components/kritzel-menu.js +1 -1
  48. package/dist/components/kritzel-split-button.js +1 -1
  49. package/dist/components/kritzel-utility-panel.js +1 -1
  50. package/dist/components/kritzel-workspace-manager.js +1 -1
  51. package/dist/components/{p-f_ut_1_F.js → p-9Sbn9-7-.js} +610 -19
  52. package/dist/components/p-9Sbn9-7-.js.map +1 -0
  53. package/dist/components/{p-Bwv1dxAB.js → p-BUsg2vtg.js} +12 -4
  54. package/dist/components/{p-Bwv1dxAB.js.map → p-BUsg2vtg.js.map} +1 -1
  55. package/dist/components/{p-BoazmhlG.js → p-C6-tSCMR.js} +3 -3
  56. package/dist/components/{p-BoazmhlG.js.map → p-C6-tSCMR.js.map} +1 -1
  57. package/dist/components/{p-Cv4BGNPb.js → p-CIts5Uma.js} +4 -2
  58. package/dist/components/p-CIts5Uma.js.map +1 -0
  59. package/dist/components/{p-BSBMBjhq.js → p-CQd5oYXp.js} +3 -3
  60. package/dist/components/{p-BSBMBjhq.js.map → p-CQd5oYXp.js.map} +1 -1
  61. package/dist/components/{p-D8L0t-Ro.js → p-Co5HWjr6.js} +3 -3
  62. package/dist/components/{p-D8L0t-Ro.js.map → p-Co5HWjr6.js.map} +1 -1
  63. package/dist/components/{p-5OECjGHq.js → p-DG7VXGxX.js} +3 -3
  64. package/dist/components/{p-5OECjGHq.js.map → p-DG7VXGxX.js.map} +1 -1
  65. package/dist/components/{p-D1YAsWrL.js → p-D_uh1RUI.js} +3 -3
  66. package/dist/components/{p-D1YAsWrL.js.map → p-D_uh1RUI.js.map} +1 -1
  67. package/dist/components/{p-xcQV8l_c.js → p-cv2RCdI1.js} +7 -7
  68. package/dist/components/{p-xcQV8l_c.js.map → p-cv2RCdI1.js.map} +1 -1
  69. package/dist/components/{p-BmdYFhLx.js → p-nZdy-Ii5.js} +4 -4
  70. package/dist/components/{p-BmdYFhLx.js.map → p-nZdy-Ii5.js.map} +1 -1
  71. package/dist/components/{p-CiM-IPaD.js → p-y25EBKEA.js} +5 -5
  72. package/dist/components/{p-CiM-IPaD.js.map → p-y25EBKEA.js.map} +1 -1
  73. package/dist/esm/{default-line-tool.config-PvsWHpO2.js → default-line-tool.config-CuBm2vpW.js} +514 -14
  74. package/dist/esm/default-line-tool.config-CuBm2vpW.js.map +1 -0
  75. package/dist/esm/index.js +2 -2
  76. package/dist/esm/kritzel-color_22.entry.js +120 -6
  77. package/dist/esm/loader.js +1 -1
  78. package/dist/esm/stencil.js +1 -1
  79. package/dist/stencil/index.esm.js +1 -1
  80. package/dist/stencil/p-CuBm2vpW.js +2 -0
  81. package/dist/stencil/p-CuBm2vpW.js.map +1 -0
  82. package/dist/stencil/p-c9201236.entry.js +10 -0
  83. package/dist/stencil/p-c9201236.entry.js.map +1 -0
  84. package/dist/stencil/stencil.esm.js +1 -1
  85. package/dist/types/classes/core/core.class.d.ts +11 -1
  86. package/dist/types/classes/handlers/move.handler.d.ts +11 -0
  87. package/dist/types/classes/objects/group.class.d.ts +97 -0
  88. package/dist/types/classes/objects/selection-group.class.d.ts +5 -0
  89. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -0
  90. package/dist/types/components.d.ts +4 -2
  91. package/dist/types/index.d.ts +1 -0
  92. package/package.json +1 -1
  93. package/dist/cjs/default-line-tool.config-Bs88k0jE.js.map +0 -1
  94. package/dist/components/p-Cv4BGNPb.js.map +0 -1
  95. package/dist/components/p-f_ut_1_F.js.map +0 -1
  96. package/dist/esm/default-line-tool.config-PvsWHpO2.js.map +0 -1
  97. package/dist/stencil/p-9913896b.entry.js +0 -10
  98. package/dist/stencil/p-9913896b.entry.js.map +0 -1
  99. package/dist/stencil/p-PvsWHpO2.js +0 -2
  100. package/dist/stencil/p-PvsWHpO2.js.map +0 -1
@@ -14768,7 +14768,8 @@ class KritzelLine extends KritzelBaseObject {
14768
14768
  this._core.store.state.objects.update(this);
14769
14769
  }
14770
14770
  hitTest(x, y) {
14771
- const strokeWidth = Math.max(this.strokeWidth, 10);
14771
+ const minHitThreshold = 20;
14772
+ const strokeWidth = Math.max(this.strokeWidth, minHitThreshold);
14772
14773
  const halfStroke = strokeWidth / this.scale / 2;
14773
14774
  if (this._adjustedPoints === null) {
14774
14775
  this._adjustedPoints = this.computeAdjustedPoints();
@@ -15201,6 +15202,399 @@ class KritzelLine extends KritzelBaseObject {
15201
15202
  }
15202
15203
  }
15203
15204
 
15205
+ /**
15206
+ * KritzelGroup represents a permanent grouping of objects that act as a single unit.
15207
+ *
15208
+ * Unlike KritzelSelectionGroup (which is ephemeral and used for temporary multi-selection),
15209
+ * KritzelGroup is persisted and saved with the workspace. Grouped objects move, resize,
15210
+ * rotate, copy, and delete together as one unit.
15211
+ *
15212
+ * Key features:
15213
+ * - Supports nested groups (groups can contain other groups)
15214
+ * - Clicking any child selects the entire group
15215
+ * - Can be ungrouped to restore children to top-level objects
15216
+ */
15217
+ class KritzelGroup extends KritzelBaseObject {
15218
+ __class__ = 'KritzelGroup';
15219
+ /**
15220
+ * IDs of child objects within this group.
15221
+ * Children can be any KritzelBaseObject, including other KritzelGroups for nesting.
15222
+ */
15223
+ childIds = [];
15224
+ /**
15225
+ * Snapshots of child states for transformation operations (resize, rotate).
15226
+ * Similar pattern to KritzelSelectionGroup.
15227
+ */
15228
+ unchangedChildSnapshots = new Map();
15229
+ snapshotRotation = 0;
15230
+ snapshotTranslateX = 0;
15231
+ snapshotTranslateY = 0;
15232
+ snapshotTotalWidth = 0;
15233
+ snapshotTotalHeight = 0;
15234
+ snapshotScale = 1;
15235
+ /**
15236
+ * Retrieves the actual child objects from the store by their IDs.
15237
+ */
15238
+ get children() {
15239
+ return this.childIds
15240
+ .map(id => {
15241
+ const found = this._core.store.state.objects.filter(obj => obj.id === id);
15242
+ return found.length > 0 ? found[0] : null;
15243
+ })
15244
+ .filter((obj) => obj !== null);
15245
+ }
15246
+ /**
15247
+ * Returns the number of children in this group.
15248
+ */
15249
+ get length() {
15250
+ return this.childIds.length;
15251
+ }
15252
+ /**
15253
+ * Creates a new empty KritzelGroup.
15254
+ */
15255
+ static create(core) {
15256
+ const group = new KritzelGroup();
15257
+ group._core = core;
15258
+ group.id = group.generateId();
15259
+ group.workspaceId = core.store.state.activeWorkspace.id;
15260
+ group.scale = core.store.state.scale;
15261
+ group.zIndex = core.store.currentZIndex;
15262
+ return group;
15263
+ }
15264
+ /**
15265
+ * Creates a KritzelGroup from an array of objects.
15266
+ * The objects are added as children and their bounding box is calculated.
15267
+ */
15268
+ static createFromObjects(core, objects) {
15269
+ const group = KritzelGroup.create(core);
15270
+ // Add all objects as children
15271
+ objects.forEach(obj => {
15272
+ group.childIds.push(obj.id);
15273
+ });
15274
+ // Calculate bounding box from children
15275
+ group.refreshBoundingBox();
15276
+ group.captureChildSnapshots();
15277
+ return group;
15278
+ }
15279
+ /**
15280
+ * Finds the parent KritzelGroup that contains the given object as a child.
15281
+ * Returns null if the object is not a child of any group.
15282
+ */
15283
+ static findParentGroup(core, objectId) {
15284
+ const allGroups = core.store.state.objects
15285
+ .filter(obj => obj.__class__ === 'KritzelGroup');
15286
+ for (const group of allGroups) {
15287
+ if (group.childIds.includes(objectId)) {
15288
+ return group;
15289
+ }
15290
+ }
15291
+ return null;
15292
+ }
15293
+ /**
15294
+ * Adds a child object to this group.
15295
+ */
15296
+ addChild(object) {
15297
+ if (!this.childIds.includes(object.id)) {
15298
+ this.childIds.push(object.id);
15299
+ this.refreshBoundingBox();
15300
+ this.captureChildSnapshots();
15301
+ }
15302
+ }
15303
+ /**
15304
+ * Removes a child object from this group.
15305
+ */
15306
+ removeChild(objectId) {
15307
+ const index = this.childIds.indexOf(objectId);
15308
+ if (index !== -1) {
15309
+ this.childIds.splice(index, 1);
15310
+ this.refreshBoundingBox();
15311
+ this.captureChildSnapshots();
15312
+ }
15313
+ }
15314
+ /**
15315
+ * Ungroups this group, returning all children to top-level objects.
15316
+ * The group itself should be removed after calling this.
15317
+ */
15318
+ ungroup() {
15319
+ return this.children;
15320
+ }
15321
+ /**
15322
+ * Finalizes the group after children have been positioned (e.g., after paste).
15323
+ * Refreshes the bounding box and captures child snapshots.
15324
+ */
15325
+ finalize() {
15326
+ this.refreshBoundingBox();
15327
+ this.captureChildSnapshots();
15328
+ }
15329
+ /**
15330
+ * Recalculates the group's bounding box based on its children.
15331
+ */
15332
+ refreshBoundingBox() {
15333
+ if (this.children.length === 0) {
15334
+ this.width = 0;
15335
+ this.height = 0;
15336
+ this.translateX = 0;
15337
+ this.translateY = 0;
15338
+ return;
15339
+ }
15340
+ if (this.children.length === 1) {
15341
+ // Single child: inherit its bounding box
15342
+ const child = this.children[0];
15343
+ this.translateX = child.boundingBox.x;
15344
+ this.translateY = child.boundingBox.y;
15345
+ this.width = child.boundingBox.width * this.scale;
15346
+ this.height = child.boundingBox.height * this.scale;
15347
+ return;
15348
+ }
15349
+ // Multiple children: calculate encompassing bounding box
15350
+ const rotation = this.rotation;
15351
+ const cos = Math.cos(-rotation);
15352
+ const sin = Math.sin(-rotation);
15353
+ let minX = Infinity;
15354
+ let maxX = -Infinity;
15355
+ let minY = Infinity;
15356
+ let maxY = -Infinity;
15357
+ this.children.forEach(child => {
15358
+ const polygon = child.rotatedPolygon;
15359
+ const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
15360
+ corners.forEach(corner => {
15361
+ // Rotate corner into local space (aligned with group rotation)
15362
+ const rx = corner.x * cos - corner.y * sin;
15363
+ const ry = corner.x * sin + corner.y * cos;
15364
+ if (rx < minX)
15365
+ minX = rx;
15366
+ if (rx > maxX)
15367
+ maxX = rx;
15368
+ if (ry < minY)
15369
+ minY = ry;
15370
+ if (ry > maxY)
15371
+ maxY = ry;
15372
+ });
15373
+ });
15374
+ // Dimensions in world units (unrotated)
15375
+ const worldWidth = maxX - minX;
15376
+ const worldHeight = maxY - minY;
15377
+ this.width = (worldWidth - this.padding) * this.scale;
15378
+ this.height = (worldHeight - this.padding) * this.scale;
15379
+ // Center of the box in rotated space
15380
+ const cRx = (minX + maxX) / 2;
15381
+ const cRy = (minY + maxY) / 2;
15382
+ // Rotate center back to world space
15383
+ const cosR = Math.cos(rotation);
15384
+ const sinR = Math.sin(rotation);
15385
+ const cx = cRx * cosR - cRy * sinR;
15386
+ const cy = cRx * sinR + cRy * cosR;
15387
+ this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
15388
+ this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
15389
+ }
15390
+ /**
15391
+ * Captures snapshots of child states for transformation operations.
15392
+ */
15393
+ captureChildSnapshots() {
15394
+ this.unchangedChildSnapshots.clear();
15395
+ this.snapshotRotation = this.rotation;
15396
+ this.snapshotTranslateX = this.translateX;
15397
+ this.snapshotTranslateY = this.translateY;
15398
+ this.snapshotTotalWidth = this.totalWidth;
15399
+ this.snapshotTotalHeight = this.totalHeight;
15400
+ this.snapshotScale = this.scale || 1;
15401
+ this.children.forEach(child => {
15402
+ this.unchangedChildSnapshots.set(child.id, {
15403
+ id: child.id,
15404
+ translateX: child.translateX,
15405
+ translateY: child.translateY,
15406
+ rotation: child.rotation,
15407
+ width: child.width,
15408
+ height: child.height,
15409
+ totalWidth: child.totalWidth,
15410
+ totalHeight: child.totalHeight,
15411
+ scale: child.scale,
15412
+ });
15413
+ });
15414
+ }
15415
+ // ─────────────────────────────────────────────────────────────────────────────
15416
+ // TRANSFORMATION METHODS
15417
+ // ─────────────────────────────────────────────────────────────────────────────
15418
+ move(startX, startY, endX, endY) {
15419
+ const deltaX = (startX - endX) / this._core.store.state.scale;
15420
+ const deltaY = (startY - endY) / this._core.store.state.scale;
15421
+ this.translateX += deltaX;
15422
+ this.translateY += deltaY;
15423
+ this._core.store.state.objects.transaction(() => {
15424
+ this._core.store.state.objects.update(this);
15425
+ this.children.forEach(child => {
15426
+ child.move(startX, startY, endX, endY);
15427
+ // Update any lines anchored to this child
15428
+ this._core.anchorManager.updateAnchorsForObject(child.id);
15429
+ });
15430
+ });
15431
+ // Update snapshots
15432
+ this.unchangedChildSnapshots.forEach(snapshot => {
15433
+ snapshot.translateX += deltaX;
15434
+ snapshot.translateY += deltaY;
15435
+ });
15436
+ }
15437
+ resize(x, y, width, height) {
15438
+ const widthScaleFactor = width / this.width;
15439
+ const heightScaleFactor = height / this.height;
15440
+ // Calculate old center
15441
+ const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
15442
+ const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
15443
+ // Calculate new center
15444
+ const newTotalWidth = width + this.padding * 2;
15445
+ const newTotalHeight = height + this.padding * 2;
15446
+ const newCenterX = x + newTotalWidth / 2 / this.scale;
15447
+ const newCenterY = y + newTotalHeight / 2 / this.scale;
15448
+ const rotation = this.rotation;
15449
+ const cos = Math.cos(-rotation);
15450
+ const sin = Math.sin(-rotation);
15451
+ const cosR = Math.cos(rotation);
15452
+ const sinR = Math.sin(rotation);
15453
+ this._core.store.state.objects.transaction(() => {
15454
+ this.children.forEach(child => {
15455
+ // Calculate child center
15456
+ const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
15457
+ const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
15458
+ // Vector from old group center to child center
15459
+ const dx = childCenterX - oldCenterX;
15460
+ const dy = childCenterY - oldCenterY;
15461
+ // Rotate to local space
15462
+ const localX = dx * cos - dy * sin;
15463
+ const localY = dx * sin + dy * cos;
15464
+ // Scale in local space
15465
+ const scaledLocalX = localX * widthScaleFactor;
15466
+ const scaledLocalY = localY * heightScaleFactor;
15467
+ // Rotate back to world space
15468
+ const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
15469
+ const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
15470
+ // New child center
15471
+ const newChildCenterX = newCenterX + rotatedX;
15472
+ const newChildCenterY = newCenterY + rotatedY;
15473
+ // Calculate relative rotation for scaling
15474
+ const relativeRotation = child.rotation - rotation;
15475
+ const cosRel = Math.cos(relativeRotation);
15476
+ const sinRel = Math.sin(relativeRotation);
15477
+ const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
15478
+ const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
15479
+ const updatedWidth = child.width * newChildWidthScale;
15480
+ const updatedHeight = child.height * newChildHeightScale;
15481
+ const updatedTotalWidth = updatedWidth + child.padding * 2;
15482
+ const updatedTotalHeight = updatedHeight + child.padding * 2;
15483
+ const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
15484
+ const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
15485
+ child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
15486
+ // Update anchored lines
15487
+ this._core.anchorManager.updateAnchorsForObject(child.id);
15488
+ });
15489
+ this.refreshBoundingBox();
15490
+ this.captureChildSnapshots();
15491
+ this._core.store.state.objects.update(this);
15492
+ });
15493
+ }
15494
+ rotate(value) {
15495
+ this.rotation = value;
15496
+ const centerX = this.translateX + this.totalWidth / 2 / this.scale;
15497
+ const centerY = this.translateY + this.totalHeight / 2 / this.scale;
15498
+ const angle = value - this.snapshotRotation;
15499
+ const cos = Math.cos(angle);
15500
+ const sin = Math.sin(angle);
15501
+ this._core.store.state.objects.transaction(() => {
15502
+ this._core.store.state.objects.update(this);
15503
+ this.children.forEach(child => {
15504
+ const unchangedSnapshot = this.unchangedChildSnapshots.get(child.id);
15505
+ if (!unchangedSnapshot)
15506
+ return;
15507
+ const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
15508
+ const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
15509
+ const rotatedX = cos * offsetX - sin * offsetY;
15510
+ const rotatedY = sin * offsetX + cos * offsetY;
15511
+ child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
15512
+ child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
15513
+ child.rotate(this.children.length === 1 ? value : unchangedSnapshot.rotation + angle);
15514
+ });
15515
+ });
15516
+ }
15517
+ getOffsetXToCenterFromSnapshot(snapshot) {
15518
+ const childCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
15519
+ const groupScale = this.snapshotScale || this.scale || 1;
15520
+ const groupCenterX = this.snapshotTranslateX + this.snapshotTotalWidth / groupScale / 2;
15521
+ return childCenterX - groupCenterX;
15522
+ }
15523
+ getOffsetYToCenterFromSnapshot(snapshot) {
15524
+ const childCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
15525
+ const groupScale = this.snapshotScale || this.scale || 1;
15526
+ const groupCenterY = this.snapshotTranslateY + this.snapshotTotalHeight / groupScale / 2;
15527
+ return childCenterY - groupCenterY;
15528
+ }
15529
+ // ─────────────────────────────────────────────────────────────────────────────
15530
+ // HIT TESTING
15531
+ // ─────────────────────────────────────────────────────────────────────────────
15532
+ hitTest(x, y) {
15533
+ // Check if point is inside any child
15534
+ return this.children.some(child => child.hitTest(x, y));
15535
+ }
15536
+ hitTestPolygon(polygon) {
15537
+ // Check if polygon intersects with any child
15538
+ return this.children.some(child => child.hitTestPolygon(polygon));
15539
+ }
15540
+ // ─────────────────────────────────────────────────────────────────────────────
15541
+ // COPY & SERIALIZATION
15542
+ // ─────────────────────────────────────────────────────────────────────────────
15543
+ copy() {
15544
+ const copiedGroup = KritzelGroup.create(this._core);
15545
+ // Deep copy all children but DON'T add to store yet
15546
+ // Store the copied children temporarily for paste() to handle
15547
+ const copiedChildren = [];
15548
+ this.children
15549
+ .sort((a, b) => a.zIndex - b.zIndex)
15550
+ .forEach(child => {
15551
+ const copiedChild = child.copy();
15552
+ copiedChildren.push(copiedChild);
15553
+ copiedGroup.childIds.push(copiedChild.id);
15554
+ });
15555
+ // Store copied children on the group for paste() to access
15556
+ copiedGroup._pendingChildren = copiedChildren;
15557
+ // Copy group properties
15558
+ copiedGroup.rotation = this.rotation;
15559
+ copiedGroup.translateX = this.translateX;
15560
+ copiedGroup.translateY = this.translateY;
15561
+ copiedGroup.width = this.width;
15562
+ copiedGroup.height = this.height;
15563
+ return copiedGroup;
15564
+ }
15565
+ serialize() {
15566
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedChildSnapshots, ...remainingProps } = this;
15567
+ const clonedProps = structuredClone(remainingProps);
15568
+ // Convert Map to plain object for serialization
15569
+ clonedProps.unchangedChildSnapshots = Object.fromEntries(this.unchangedChildSnapshots);
15570
+ return clonedProps;
15571
+ }
15572
+ deserialize(object) {
15573
+ super.deserialize(object);
15574
+ // Restore the Map from serialized object
15575
+ if (object.unchangedChildSnapshots) {
15576
+ this.unchangedChildSnapshots = new Map(Object.entries(object.unchangedChildSnapshots));
15577
+ }
15578
+ return this;
15579
+ }
15580
+ update() {
15581
+ this._core.store.state.objects.update(this);
15582
+ }
15583
+ /**
15584
+ * Updates the workspace ID for this group and all its children.
15585
+ */
15586
+ updateWorkspaceId(workspaceId) {
15587
+ this.workspaceId = workspaceId;
15588
+ this.children.forEach(child => {
15589
+ child.workspaceId = workspaceId;
15590
+ // Recursively update nested groups
15591
+ if (child instanceof KritzelGroup) {
15592
+ child.updateWorkspaceId(workspaceId);
15593
+ }
15594
+ });
15595
+ }
15596
+ }
15597
+
15204
15598
  exports.KritzelMouseButton = void 0;
15205
15599
  (function (KritzelMouseButton) {
15206
15600
  KritzelMouseButton[KritzelMouseButton["Left"] = 0] = "Left";
@@ -15221,11 +15615,19 @@ class KritzelEventHelper {
15221
15615
  return !!contextMenu;
15222
15616
  }
15223
15617
  static onLongPress(event, onSuccess, onCancel) {
15224
- const longPressTimeout = 400;
15225
- const moveThreshold = 10;
15618
+ if (event.pointerType !== 'touch') {
15619
+ onCancel?.();
15620
+ return () => { };
15621
+ }
15226
15622
  const startX = event.clientX;
15227
15623
  const startY = event.clientY;
15228
15624
  const target = event.target;
15625
+ if (!target) {
15626
+ onCancel?.();
15627
+ return () => { };
15628
+ }
15629
+ const longPressTimeout = 400;
15630
+ const moveThreshold = 10;
15229
15631
  const timer = setTimeout(() => {
15230
15632
  removeListeners();
15231
15633
  onSuccess(event);
@@ -15483,6 +15885,21 @@ class KritzelSelectionGroup extends KritzelBaseObject {
15483
15885
  this.captureUnchangedSnapshots();
15484
15886
  this.refreshObjectDimensions();
15485
15887
  }
15888
+ /**
15889
+ * Prepares the selection group and its children for a transform interaction (rotate/resize).
15890
+ * Ensures snapshot state is aligned with the current visual state.
15891
+ */
15892
+ beginTransform() {
15893
+ // Keep the selection group's bounding box current before snapshotting.
15894
+ this.refreshObjectDimensions();
15895
+ this.captureUnchangedSnapshots();
15896
+ // Groups need their own child snapshots aligned with the current transform.
15897
+ this.objects.forEach(obj => {
15898
+ if (obj instanceof KritzelGroup) {
15899
+ obj.finalize();
15900
+ }
15901
+ });
15902
+ }
15486
15903
  deselectAllChildren() {
15487
15904
  this.objects.forEach(obj => (obj.isSelected = false));
15488
15905
  }
@@ -15743,9 +16160,9 @@ class KritzelSelectionGroup extends KritzelBaseObject {
15743
16160
  return objCenterY - groupCenterY;
15744
16161
  }
15745
16162
  hitTest(x, y) {
15746
- const containsOnlyLines = this.objects.every(obj => obj.__class__ === 'KritzelLine');
15747
- if (containsOnlyLines) {
15748
- return this.objects.some(obj => obj.hitTest(x, y));
16163
+ const isSingleLine = this.objects.length === 1 && this.objects[0].__class__ === 'KritzelLine';
16164
+ if (isSingleLine) {
16165
+ return this.objects[0].hitTest(x, y);
15749
16166
  }
15750
16167
  const polygon = this.rotatedPolygon;
15751
16168
  const polyPoints = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
@@ -16248,6 +16665,8 @@ KritzelIconRegistry.registerIcons({
16248
16665
  'ellipsis-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis-vertical-icon lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>',
16249
16666
  'x': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
16250
16667
  'check': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>',
16668
+ 'group': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-group-icon lucide-group"><path d="M3 7V5c0-1.1.9-2 2-2h2"/><path d="M17 3h2c1.1 0 2 .9 2 2v2"/><path d="M21 17v2c0 1.1-.9 2-2 2h-2"/><path d="M7 21H5c-1.1 0-2-.9-2-2v-2"/><rect width="7" height="5" x="7" y="7" rx="1"/><rect width="7" height="5" x="10" y="12" rx="1"/></svg>',
16669
+ 'ungroup': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ungroup-icon lucide-ungroup"><rect width="8" height="6" x="5" y="4" rx="1"/><rect width="8" height="6" x="11" y="14" rx="1"/></svg>',
16251
16670
  'move-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-move-vertical-icon lucide-move-vertical" version="1.1" id="svg3"> <defs id="defs3" /> <path d="M12 2v20" id="path1" /> <path style="fill:#ffffff;stroke-width:2.5;stroke-dasharray:none;stroke-linejoin:round;paint-order:stroke fill markers" d="m 11.735575,22.661865 c -0.09259,-0.02798 -0.204674,-0.07661 -0.249076,-0.108068 -0.04441,-0.03147 -1.167275,-0.979853 -2.4952713,-2.10755 -1.8557024,-1.57581 -2.4300904,-2.079639 -2.4817336,-2.17687 -0.086514,-0.162885 -0.089504,-0.422449 -0.00664,-0.576334 0.1483053,-0.275409 0.437667,-0.436207 0.7830634,-0.435147 0.3692925,0.0011 0.3517326,-0.01122 2.168748,1.525599 L 11.12348,20.194964 V 11.999996 3.8050256 L 9.4546663,5.2164943 C 7.6376509,6.7533118 7.6552109,6.7409594 7.2859184,6.7420935 6.6681409,6.7439906 6.253658,6.1955854 6.5159903,5.723396 6.5738626,5.6192278 7.1368766,5.1267427 9.0629381,3.4955044 11.738128,1.2298067 11.640395,1.3026868 12.00355,1.3026868 c 0.363154,0 0.265421,-0.07288 2.940611,2.1928176 1.926062,1.6312383 2.489076,2.1237234 2.546948,2.2278916 0.262332,0.4721894 -0.15215,1.0205946 -0.769928,1.0186975 -0.369293,-0.00114 -0.351733,0.011218 -2.168748,-1.5255992 L 12.88362,3.8050256 v 8.1949704 8.194968 l 1.668813,-1.411469 c 1.817015,-1.536817 1.799455,-1.524464 2.168748,-1.525599 0.617772,-0.0019 1.032269,0.546521 0.769928,1.018687 -0.103474,0.18623 -4.919006,4.273935 -5.130582,4.355136 -0.20796,0.07981 -0.425829,0.09033 -0.624952,0.03014 z" id="path4" /> </svg>',
16252
16671
  'hand': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-icon lucide-hand" version="1.1" id="svg4"> <defs id="defs4" /> <path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path1" /> <path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" id="path2" /> <path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8" id="path3" /> <path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" id="path4" /> <path style="fill:#ffffff;stroke-width:0.0423032" d="M 11.478261,21.222477 C 9.6854059,21.139331 8.3341788,20.647346 7.1297169,19.639159 6.9698298,19.505327 5.949644,18.508708 4.8626374,17.42445 3.0780314,15.644357 2.8775851,15.435367 2.7968392,15.270602 2.5896561,14.847836 2.616354,14.436649 2.8771894,14.033085 c 0.136522,-0.211226 0.3837159,-0.398688 0.6367632,-0.482897 0.1529977,-0.05091 0.2326803,-0.05992 0.4470908,-0.05054 0.2250407,0.0098 0.2876577,0.02439 0.4554568,0.105827 0.1747854,0.08483 0.2933718,0.192702 1.1675186,1.062064 0.7182021,0.714271 1.0062041,0.982633 1.0998825,1.024878 0.1680197,0.07577 0.4756439,0.07817 0.6368269,0.005 0.1370772,-0.06226 0.2976691,-0.220818 0.3691296,-0.364463 0.072561,-0.145855 0.071573,-0.543545 -0.00169,-0.681911 -0.028747,-0.05429 -0.2476439,-0.296131 -0.4864385,-0.537426 l -0.4341716,-0.438718 0.00753,-3.989014 0.00753,-3.9890137 0.089246,-0.1883791 c 0.244787,-0.516692 0.7711718,-0.809716 1.3059705,-0.7269981 0.3973687,0.061462 0.7569953,0.3284904 0.9363122,0.6952277 l 0.093853,0.1919473 0.014101,2.4958872 0.014101,2.4958877 0.067385,0.149123 c 0.1186861,0.262654 0.4140438,0.457222 0.6940724,0.457222 0.2764172,0 0.5690532,-0.187563 0.6965992,-0.446482 l 0.06486,-0.131661 0.0141,-3.4970626 0.0141,-3.4970623 0.08982,-0.1896121 C 11.096301,3.0422103 11.506844,2.7755634 12,2.7755634 c 0.493156,0 0.903699,0.2666469 1.122868,0.7293016 l 0.08982,0.1896121 0.0141,3.2432432 c 0.01405,3.2315947 0.01432,3.2437077 0.07397,3.3726737 0.124721,0.269649 0.355908,0.424566 0.661411,0.443206 0.237954,0.01452 0.429018,-0.0627 0.591626,-0.239109 0.223655,-0.242637 0.208338,-0.06565 0.224113,-2.5896966 l 0.0141,-2.2561693 0.09385,-0.1919473 c 0.179317,-0.3667373 0.538944,-0.6337662 0.936313,-0.6952277 0.609359,-0.09425 1.208067,0.3054956 1.370981,0.9153772 0.03013,0.1127929 0.03773,0.6662436 0.038,2.7657391 3.74e-4,2.9328416 -0.008,2.8034316 0.197044,3.0364016 0.234927,0.266892 0.603828,0.337117 0.920407,0.175213 0.181933,-0.09304 0.329759,-0.261686 0.376309,-0.4293 0.01848,-0.06654 0.02929,-0.683932 0.0295,-1.684364 1.78e-4,-0.8783075 0.01239,-1.6530128 0.02751,-1.745346 0.08579,-0.5238478 0.505382,-0.9420803 1.039546,-1.0361716 0.607538,-0.1070155 1.25615,0.3485846 1.385876,0.973471 0.02211,0.1064847 0.02843,1.1397236 0.02169,3.5455556 -0.0093,3.324725 -0.01078,3.403075 -0.07062,3.770606 -0.126399,0.776213 -0.328814,1.41352 -0.669031,2.106456 -0.36657,0.746612 -0.72118,1.250303 -1.297841,1.843464 -1.185731,1.21966 -2.604527,1.933174 -4.300822,2.162889 -0.38234,0.05178 -2.604621,0.0785 -3.412456,0.04104 z" id="path16" /> </svg>',
16253
16672
  'hand-grab': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-grab-icon lucide-hand-grab" version="1.1" id="svg5"> <defs id="defs5" /> <path d="M18 11.5V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1.4" id="path1" /> <path d="M14 10V8a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" id="path2" /> <path d="M10 9.9V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v5" id="path3" /> <path d="M6 14a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path4" /> <path d="M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-4a8 8 0 0 1-8-8 2 2 0 1 1 4 0" id="path5" /> <path style="fill:#ffffff;stroke-width:0.0423032" d="M 9.5887192,21.221364 C 8.0526856,21.128375 6.6533474,20.587008 5.4571093,19.622953 5.3019976,19.497947 5.0101058,19.225967 4.8084606,19.018552 4.2300382,18.423579 3.8474097,17.879011 3.4960953,17.150756 3.1017715,16.333345 2.9014937,15.633198 2.8052391,14.735605 c -0.097577,-0.909922 -0.019834,-1.263323 0.3569794,-1.622742 0.753018,-0.718257 1.9463784,-0.256949 2.0619296,0.797066 0.037839,0.345154 0.089701,0.477674 0.2472582,0.631806 0.2464521,0.241095 0.5958008,0.287445 0.9036474,0.119894 0.1478567,-0.08047 0.2303871,-0.16866 0.3266699,-0.349056 0.050118,-0.0939 0.05337,-0.2326 0.066784,-2.848413 L 6.7826087,8.7144536 6.8458627,8.559342 C 6.9736603,8.2459558 7.2463773,7.9734987 7.5581669,7.8477164 c 0.1253804,-0.050581 0.2118709,-0.062705 0.4512338,-0.063254 0.275392,-6.312e-4 0.3094658,0.00564 0.4867271,0.089609 0.264676,0.1253746 0.4771417,0.334346 0.6070085,0.5970252 l 0.1048543,0.212087 0.017338,0.7488899 c 0.019395,0.8377165 0.026343,0.8713745 0.2242006,1.0860245 0.1591067,0.172611 0.3522021,0.249908 0.5883018,0.235502 0.305503,-0.01864 0.53669,-0.173557 0.661411,-0.443206 0.05734,-0.123963 0.0605,-0.181852 0.07486,-1.3703236 0.01626,-1.3456629 0.0141,-1.3267758 0.182667,-1.5938092 0.257891,-0.4085268 0.77099,-0.6442307 1.228943,-0.5645435 0.471012,0.08196 0.850379,0.4102836 0.989676,0.8565175 0.04663,0.149383 0.0514,0.2860903 0.0514,1.4721724 0,0.8344844 0.01108,1.3591794 0.03064,1.4503554 0.04109,0.191585 0.179844,0.390224 0.342505,0.490321 0.27721,0.170587 0.693425,0.126085 0.92529,-0.09893 0.228986,-0.222224 0.234357,-0.25046 0.251582,-1.3227071 l 0.0152,-0.9463484 0.104855,-0.2120434 c 0.129872,-0.2626357 0.342372,-0.471626 0.607008,-0.5969817 0.177262,-0.083967 0.211335,-0.09024 0.486727,-0.089609 0.239363,5.486e-4 0.325854,0.012673 0.451234,0.063254 0.31179,0.1257823 0.584507,0.3982394 0.712304,0.7116256 0.0621,0.1522783 0.06351,0.1824139 0.07736,1.649824 0.01408,1.492435 0.0142,1.494914 0.07947,1.627432 0.0771,0.156535 0.2216,0.291061 0.395058,0.367789 0.09695,0.04289 0.169789,0.05275 0.325757,0.04411 0.230544,-0.01277 0.363779,-0.06826 0.506411,-0.21089 0.177062,-0.177061 0.198302,-0.25307 0.219359,-0.784959 0.02002,-0.505773 0.05012,-0.6549 0.175297,-0.868492 0.498409,-0.850471 1.728484,-0.8041941 2.173478,0.08177 0.131751,0.262312 0.134447,0.313526 0.122615,2.328965 -0.0104,1.771638 -0.01517,1.923481 -0.0714,2.273746 -0.123689,0.770512 -0.327108,1.411813 -0.668163,2.106456 -0.36657,0.746612 -0.72118,1.250303 -1.297841,1.843464 -1.180268,1.214041 -2.600612,1.930754 -4.287083,2.163284 -0.28697,0.03957 -0.685446,0.04782 -2.636536,0.05459 -1.26416,0.0044 -2.4698005,-0.0024 -2.6792012,-0.01507 z" id="path6" /> </svg>',
@@ -16319,6 +16738,8 @@ class KritzelBaseHandler {
16319
16738
  }
16320
16739
  }
16321
16740
 
16741
+ /** Threshold in screen pixels for disconnecting anchored lines during drag */
16742
+ const ANCHOR_DISCONNECT_THRESHOLD = 30;
16322
16743
  class KritzelMoveHandler extends KritzelBaseHandler {
16323
16744
  dragStartX;
16324
16745
  dragStartY;
@@ -16326,6 +16747,11 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16326
16747
  startY;
16327
16748
  endX;
16328
16749
  endY;
16750
+ /** Initial position when drag started (for calculating total accumulated distance) */
16751
+ initialDragX = 0;
16752
+ initialDragY = 0;
16753
+ /** Set of line IDs that have been disconnected from anchors during this drag */
16754
+ disconnectedLineIds = new Set();
16329
16755
  hasMoved = false;
16330
16756
  trackedPointerId = null;
16331
16757
  constructor(core) {
@@ -16338,6 +16764,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16338
16764
  this.startY = 0;
16339
16765
  this.endX = 0;
16340
16766
  this.endY = 0;
16767
+ this.initialDragX = 0;
16768
+ this.initialDragY = 0;
16769
+ this.disconnectedLineIds.clear();
16341
16770
  this.hasMoved = false;
16342
16771
  this.trackedPointerId = null;
16343
16772
  }
@@ -16357,6 +16786,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16357
16786
  this.dragStartY = clientY;
16358
16787
  this.startX = this.dragStartX;
16359
16788
  this.startY = this.dragStartY;
16789
+ this.initialDragX = clientX;
16790
+ this.initialDragY = clientY;
16791
+ this.disconnectedLineIds.clear();
16360
16792
  this.trackedPointerId = event.pointerId;
16361
16793
  }
16362
16794
  else {
@@ -16382,6 +16814,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16382
16814
  this.dragStartY = y;
16383
16815
  this.startX = x;
16384
16816
  this.startY = y;
16817
+ this.initialDragX = x;
16818
+ this.initialDragY = y;
16819
+ this.disconnectedLineIds.clear();
16385
16820
  this.trackedPointerId = event.pointerId;
16386
16821
  }
16387
16822
  else {
@@ -16405,6 +16840,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16405
16840
  const moveDeltaY = Math.abs(clientY - this.startY);
16406
16841
  const moveThreshold = 5;
16407
16842
  if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
16843
+ // Check for anchor disconnect threshold on lines
16844
+ this.checkAndDisconnectAnchors(clientX, clientY);
16408
16845
  selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
16409
16846
  this.dragStartX = clientX;
16410
16847
  this.dragStartY = clientY;
@@ -16430,6 +16867,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16430
16867
  const moveThreshold = 5;
16431
16868
  if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
16432
16869
  clearTimeout(this._core.store.state.longTouchTimeout);
16870
+ // Check for anchor disconnect threshold on lines
16871
+ this.checkAndDisconnectAnchors(x, y);
16433
16872
  selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
16434
16873
  this.dragStartX = x;
16435
16874
  this.dragStartY = y;
@@ -16464,6 +16903,48 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16464
16903
  }
16465
16904
  this.reset();
16466
16905
  }
16906
+ /**
16907
+ * Checks if the total accumulated drag distance exceeds the anchor disconnect threshold.
16908
+ * If so, disconnects all anchors from any KritzelLine objects in the selection group.
16909
+ * This allows anchored lines to be freely moved after dragging beyond the threshold.
16910
+ */
16911
+ checkAndDisconnectAnchors(currentX, currentY) {
16912
+ const totalDeltaX = currentX - this.initialDragX;
16913
+ const totalDeltaY = currentY - this.initialDragY;
16914
+ const totalDistance = Math.sqrt(totalDeltaX * totalDeltaX + totalDeltaY * totalDeltaY);
16915
+ if (totalDistance < ANCHOR_DISCONNECT_THRESHOLD) {
16916
+ return;
16917
+ }
16918
+ const selectionGroup = this._core.store.selectionGroup;
16919
+ if (!selectionGroup) {
16920
+ return;
16921
+ }
16922
+ // Find and disconnect anchors from all anchored lines in the selection
16923
+ for (const obj of selectionGroup.objects) {
16924
+ // Skip if already disconnected in this drag session
16925
+ if (this.disconnectedLineIds.has(obj.id)) {
16926
+ continue;
16927
+ }
16928
+ // Check if this is a line with anchors
16929
+ if (obj instanceof KritzelLine) {
16930
+ const line = obj;
16931
+ const hasAnchors = line.startAnchor || line.endAnchor;
16932
+ if (hasAnchors) {
16933
+ // Disconnect both anchors
16934
+ if (line.startAnchor) {
16935
+ this._core.anchorManager.removeAnchor(line.id, 'start');
16936
+ }
16937
+ if (line.endAnchor) {
16938
+ this._core.anchorManager.removeAnchor(line.id, 'end');
16939
+ }
16940
+ // Mark as disconnected so we don't try again
16941
+ this.disconnectedLineIds.add(line.id);
16942
+ // Update the line to persist the change
16943
+ this._core.store.state.objects.update(line);
16944
+ }
16945
+ }
16946
+ }
16947
+ }
16467
16948
  }
16468
16949
 
16469
16950
  var KritzelHandleType;
@@ -16695,6 +17176,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
16695
17176
  if (KritzelEventHelper.isLeftClick(event)) {
16696
17177
  const selectionGroup = this._core.store.selectionGroup;
16697
17178
  if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
17179
+ selectionGroup.beginTransform();
16698
17180
  const clientX = event.clientX - this._core.store.offsetX;
16699
17181
  const clientY = event.clientY - this._core.store.offsetY;
16700
17182
  this._core.store.state.isRotating = true;
@@ -16718,6 +17200,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
16718
17200
  if (activePointers.length === 1) {
16719
17201
  const selectionGroup = this._core.store.selectionGroup;
16720
17202
  if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
17203
+ selectionGroup.beginTransform();
16721
17204
  const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
16722
17205
  const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
16723
17206
  this._core.store.state.isRotating = true;
@@ -17028,11 +17511,14 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
17028
17511
  if (!object) {
17029
17512
  return;
17030
17513
  }
17031
- object.isSelected = false;
17514
+ // Resolve child objects to their parent groups
17515
+ const parentGroup = KritzelGroup.findParentGroup(this._core, object.id);
17516
+ const objectToSelect = parentGroup || object;
17517
+ objectToSelect.isSelected = false;
17032
17518
  const selectionGroup = KritzelSelectionGroup.create(this._core);
17033
- selectionGroup.addOrRemove(object);
17519
+ selectionGroup.addOrRemove(objectToSelect);
17034
17520
  selectionGroup.isSelected = true;
17035
- selectionGroup.rotation = object.rotation;
17521
+ selectionGroup.rotation = objectToSelect.rotation;
17036
17522
  this._core.addSelectionGroup(selectionGroup);
17037
17523
  this._core.rerender();
17038
17524
  }
@@ -17041,10 +17527,24 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
17041
17527
  if (selectedObjects.length === 0) {
17042
17528
  return;
17043
17529
  }
17530
+ // Resolve child objects to their parent groups to avoid double-move
17531
+ const resolvedObjects = new Map();
17532
+ selectedObjects.forEach(obj => {
17533
+ // Check if this object is a child of a KritzelGroup
17534
+ const parentGroup = KritzelGroup.findParentGroup(this._core, obj.id);
17535
+ if (parentGroup) {
17536
+ // Add the parent group instead of the child
17537
+ resolvedObjects.set(parentGroup.id, parentGroup);
17538
+ }
17539
+ else {
17540
+ // Not a child of any group, add the object itself
17541
+ resolvedObjects.set(obj.id, obj);
17542
+ }
17543
+ obj.isSelected = false;
17544
+ });
17044
17545
  const selectionGroup = KritzelSelectionGroup.create(this._core);
17045
- selectedObjects.forEach(o => {
17546
+ resolvedObjects.forEach(o => {
17046
17547
  selectionGroup.addOrRemove(o);
17047
- o.isSelected = false;
17048
17548
  });
17049
17549
  selectionGroup.isSelected = true;
17050
17550
  if (selectionGroup.length === 1) {
@@ -33889,6 +34389,7 @@ exports.KritzelDevicesHelper = KritzelDevicesHelper;
33889
34389
  exports.KritzelEraserTool = KritzelEraserTool;
33890
34390
  exports.KritzelEventHelper = KritzelEventHelper;
33891
34391
  exports.KritzelGeometryHelper = KritzelGeometryHelper;
34392
+ exports.KritzelGroup = KritzelGroup;
33892
34393
  exports.KritzelIconRegistry = KritzelIconRegistry;
33893
34394
  exports.KritzelImage = KritzelImage;
33894
34395
  exports.KritzelImageTool = KritzelImageTool;
@@ -33941,6 +34442,6 @@ exports.varStorage = varStorage;
33941
34442
  exports.writeVarString = writeVarString$2;
33942
34443
  exports.writeVarUint = writeVarUint$2;
33943
34444
  exports.writeVarUint8Array = writeVarUint8Array$2;
33944
- //# sourceMappingURL=default-line-tool.config-Bs88k0jE.js.map
34445
+ //# sourceMappingURL=default-line-tool.config-JuTDR6PF.js.map
33945
34446
 
33946
- //# sourceMappingURL=default-line-tool.config-Bs88k0jE.js.map
34447
+ //# sourceMappingURL=default-line-tool.config-JuTDR6PF.js.map