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
@@ -14766,7 +14766,8 @@ class KritzelLine extends KritzelBaseObject {
14766
14766
  this._core.store.state.objects.update(this);
14767
14767
  }
14768
14768
  hitTest(x, y) {
14769
- const strokeWidth = Math.max(this.strokeWidth, 10);
14769
+ const minHitThreshold = 20;
14770
+ const strokeWidth = Math.max(this.strokeWidth, minHitThreshold);
14770
14771
  const halfStroke = strokeWidth / this.scale / 2;
14771
14772
  if (this._adjustedPoints === null) {
14772
14773
  this._adjustedPoints = this.computeAdjustedPoints();
@@ -15199,6 +15200,399 @@ class KritzelLine extends KritzelBaseObject {
15199
15200
  }
15200
15201
  }
15201
15202
 
15203
+ /**
15204
+ * KritzelGroup represents a permanent grouping of objects that act as a single unit.
15205
+ *
15206
+ * Unlike KritzelSelectionGroup (which is ephemeral and used for temporary multi-selection),
15207
+ * KritzelGroup is persisted and saved with the workspace. Grouped objects move, resize,
15208
+ * rotate, copy, and delete together as one unit.
15209
+ *
15210
+ * Key features:
15211
+ * - Supports nested groups (groups can contain other groups)
15212
+ * - Clicking any child selects the entire group
15213
+ * - Can be ungrouped to restore children to top-level objects
15214
+ */
15215
+ class KritzelGroup extends KritzelBaseObject {
15216
+ __class__ = 'KritzelGroup';
15217
+ /**
15218
+ * IDs of child objects within this group.
15219
+ * Children can be any KritzelBaseObject, including other KritzelGroups for nesting.
15220
+ */
15221
+ childIds = [];
15222
+ /**
15223
+ * Snapshots of child states for transformation operations (resize, rotate).
15224
+ * Similar pattern to KritzelSelectionGroup.
15225
+ */
15226
+ unchangedChildSnapshots = new Map();
15227
+ snapshotRotation = 0;
15228
+ snapshotTranslateX = 0;
15229
+ snapshotTranslateY = 0;
15230
+ snapshotTotalWidth = 0;
15231
+ snapshotTotalHeight = 0;
15232
+ snapshotScale = 1;
15233
+ /**
15234
+ * Retrieves the actual child objects from the store by their IDs.
15235
+ */
15236
+ get children() {
15237
+ return this.childIds
15238
+ .map(id => {
15239
+ const found = this._core.store.state.objects.filter(obj => obj.id === id);
15240
+ return found.length > 0 ? found[0] : null;
15241
+ })
15242
+ .filter((obj) => obj !== null);
15243
+ }
15244
+ /**
15245
+ * Returns the number of children in this group.
15246
+ */
15247
+ get length() {
15248
+ return this.childIds.length;
15249
+ }
15250
+ /**
15251
+ * Creates a new empty KritzelGroup.
15252
+ */
15253
+ static create(core) {
15254
+ const group = new KritzelGroup();
15255
+ group._core = core;
15256
+ group.id = group.generateId();
15257
+ group.workspaceId = core.store.state.activeWorkspace.id;
15258
+ group.scale = core.store.state.scale;
15259
+ group.zIndex = core.store.currentZIndex;
15260
+ return group;
15261
+ }
15262
+ /**
15263
+ * Creates a KritzelGroup from an array of objects.
15264
+ * The objects are added as children and their bounding box is calculated.
15265
+ */
15266
+ static createFromObjects(core, objects) {
15267
+ const group = KritzelGroup.create(core);
15268
+ // Add all objects as children
15269
+ objects.forEach(obj => {
15270
+ group.childIds.push(obj.id);
15271
+ });
15272
+ // Calculate bounding box from children
15273
+ group.refreshBoundingBox();
15274
+ group.captureChildSnapshots();
15275
+ return group;
15276
+ }
15277
+ /**
15278
+ * Finds the parent KritzelGroup that contains the given object as a child.
15279
+ * Returns null if the object is not a child of any group.
15280
+ */
15281
+ static findParentGroup(core, objectId) {
15282
+ const allGroups = core.store.state.objects
15283
+ .filter(obj => obj.__class__ === 'KritzelGroup');
15284
+ for (const group of allGroups) {
15285
+ if (group.childIds.includes(objectId)) {
15286
+ return group;
15287
+ }
15288
+ }
15289
+ return null;
15290
+ }
15291
+ /**
15292
+ * Adds a child object to this group.
15293
+ */
15294
+ addChild(object) {
15295
+ if (!this.childIds.includes(object.id)) {
15296
+ this.childIds.push(object.id);
15297
+ this.refreshBoundingBox();
15298
+ this.captureChildSnapshots();
15299
+ }
15300
+ }
15301
+ /**
15302
+ * Removes a child object from this group.
15303
+ */
15304
+ removeChild(objectId) {
15305
+ const index = this.childIds.indexOf(objectId);
15306
+ if (index !== -1) {
15307
+ this.childIds.splice(index, 1);
15308
+ this.refreshBoundingBox();
15309
+ this.captureChildSnapshots();
15310
+ }
15311
+ }
15312
+ /**
15313
+ * Ungroups this group, returning all children to top-level objects.
15314
+ * The group itself should be removed after calling this.
15315
+ */
15316
+ ungroup() {
15317
+ return this.children;
15318
+ }
15319
+ /**
15320
+ * Finalizes the group after children have been positioned (e.g., after paste).
15321
+ * Refreshes the bounding box and captures child snapshots.
15322
+ */
15323
+ finalize() {
15324
+ this.refreshBoundingBox();
15325
+ this.captureChildSnapshots();
15326
+ }
15327
+ /**
15328
+ * Recalculates the group's bounding box based on its children.
15329
+ */
15330
+ refreshBoundingBox() {
15331
+ if (this.children.length === 0) {
15332
+ this.width = 0;
15333
+ this.height = 0;
15334
+ this.translateX = 0;
15335
+ this.translateY = 0;
15336
+ return;
15337
+ }
15338
+ if (this.children.length === 1) {
15339
+ // Single child: inherit its bounding box
15340
+ const child = this.children[0];
15341
+ this.translateX = child.boundingBox.x;
15342
+ this.translateY = child.boundingBox.y;
15343
+ this.width = child.boundingBox.width * this.scale;
15344
+ this.height = child.boundingBox.height * this.scale;
15345
+ return;
15346
+ }
15347
+ // Multiple children: calculate encompassing bounding box
15348
+ const rotation = this.rotation;
15349
+ const cos = Math.cos(-rotation);
15350
+ const sin = Math.sin(-rotation);
15351
+ let minX = Infinity;
15352
+ let maxX = -Infinity;
15353
+ let minY = Infinity;
15354
+ let maxY = -Infinity;
15355
+ this.children.forEach(child => {
15356
+ const polygon = child.rotatedPolygon;
15357
+ const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
15358
+ corners.forEach(corner => {
15359
+ // Rotate corner into local space (aligned with group rotation)
15360
+ const rx = corner.x * cos - corner.y * sin;
15361
+ const ry = corner.x * sin + corner.y * cos;
15362
+ if (rx < minX)
15363
+ minX = rx;
15364
+ if (rx > maxX)
15365
+ maxX = rx;
15366
+ if (ry < minY)
15367
+ minY = ry;
15368
+ if (ry > maxY)
15369
+ maxY = ry;
15370
+ });
15371
+ });
15372
+ // Dimensions in world units (unrotated)
15373
+ const worldWidth = maxX - minX;
15374
+ const worldHeight = maxY - minY;
15375
+ this.width = (worldWidth - this.padding) * this.scale;
15376
+ this.height = (worldHeight - this.padding) * this.scale;
15377
+ // Center of the box in rotated space
15378
+ const cRx = (minX + maxX) / 2;
15379
+ const cRy = (minY + maxY) / 2;
15380
+ // Rotate center back to world space
15381
+ const cosR = Math.cos(rotation);
15382
+ const sinR = Math.sin(rotation);
15383
+ const cx = cRx * cosR - cRy * sinR;
15384
+ const cy = cRx * sinR + cRy * cosR;
15385
+ this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
15386
+ this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
15387
+ }
15388
+ /**
15389
+ * Captures snapshots of child states for transformation operations.
15390
+ */
15391
+ captureChildSnapshots() {
15392
+ this.unchangedChildSnapshots.clear();
15393
+ this.snapshotRotation = this.rotation;
15394
+ this.snapshotTranslateX = this.translateX;
15395
+ this.snapshotTranslateY = this.translateY;
15396
+ this.snapshotTotalWidth = this.totalWidth;
15397
+ this.snapshotTotalHeight = this.totalHeight;
15398
+ this.snapshotScale = this.scale || 1;
15399
+ this.children.forEach(child => {
15400
+ this.unchangedChildSnapshots.set(child.id, {
15401
+ id: child.id,
15402
+ translateX: child.translateX,
15403
+ translateY: child.translateY,
15404
+ rotation: child.rotation,
15405
+ width: child.width,
15406
+ height: child.height,
15407
+ totalWidth: child.totalWidth,
15408
+ totalHeight: child.totalHeight,
15409
+ scale: child.scale,
15410
+ });
15411
+ });
15412
+ }
15413
+ // ─────────────────────────────────────────────────────────────────────────────
15414
+ // TRANSFORMATION METHODS
15415
+ // ─────────────────────────────────────────────────────────────────────────────
15416
+ move(startX, startY, endX, endY) {
15417
+ const deltaX = (startX - endX) / this._core.store.state.scale;
15418
+ const deltaY = (startY - endY) / this._core.store.state.scale;
15419
+ this.translateX += deltaX;
15420
+ this.translateY += deltaY;
15421
+ this._core.store.state.objects.transaction(() => {
15422
+ this._core.store.state.objects.update(this);
15423
+ this.children.forEach(child => {
15424
+ child.move(startX, startY, endX, endY);
15425
+ // Update any lines anchored to this child
15426
+ this._core.anchorManager.updateAnchorsForObject(child.id);
15427
+ });
15428
+ });
15429
+ // Update snapshots
15430
+ this.unchangedChildSnapshots.forEach(snapshot => {
15431
+ snapshot.translateX += deltaX;
15432
+ snapshot.translateY += deltaY;
15433
+ });
15434
+ }
15435
+ resize(x, y, width, height) {
15436
+ const widthScaleFactor = width / this.width;
15437
+ const heightScaleFactor = height / this.height;
15438
+ // Calculate old center
15439
+ const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
15440
+ const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
15441
+ // Calculate new center
15442
+ const newTotalWidth = width + this.padding * 2;
15443
+ const newTotalHeight = height + this.padding * 2;
15444
+ const newCenterX = x + newTotalWidth / 2 / this.scale;
15445
+ const newCenterY = y + newTotalHeight / 2 / this.scale;
15446
+ const rotation = this.rotation;
15447
+ const cos = Math.cos(-rotation);
15448
+ const sin = Math.sin(-rotation);
15449
+ const cosR = Math.cos(rotation);
15450
+ const sinR = Math.sin(rotation);
15451
+ this._core.store.state.objects.transaction(() => {
15452
+ this.children.forEach(child => {
15453
+ // Calculate child center
15454
+ const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
15455
+ const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
15456
+ // Vector from old group center to child center
15457
+ const dx = childCenterX - oldCenterX;
15458
+ const dy = childCenterY - oldCenterY;
15459
+ // Rotate to local space
15460
+ const localX = dx * cos - dy * sin;
15461
+ const localY = dx * sin + dy * cos;
15462
+ // Scale in local space
15463
+ const scaledLocalX = localX * widthScaleFactor;
15464
+ const scaledLocalY = localY * heightScaleFactor;
15465
+ // Rotate back to world space
15466
+ const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
15467
+ const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
15468
+ // New child center
15469
+ const newChildCenterX = newCenterX + rotatedX;
15470
+ const newChildCenterY = newCenterY + rotatedY;
15471
+ // Calculate relative rotation for scaling
15472
+ const relativeRotation = child.rotation - rotation;
15473
+ const cosRel = Math.cos(relativeRotation);
15474
+ const sinRel = Math.sin(relativeRotation);
15475
+ const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
15476
+ const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
15477
+ const updatedWidth = child.width * newChildWidthScale;
15478
+ const updatedHeight = child.height * newChildHeightScale;
15479
+ const updatedTotalWidth = updatedWidth + child.padding * 2;
15480
+ const updatedTotalHeight = updatedHeight + child.padding * 2;
15481
+ const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
15482
+ const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
15483
+ child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
15484
+ // Update anchored lines
15485
+ this._core.anchorManager.updateAnchorsForObject(child.id);
15486
+ });
15487
+ this.refreshBoundingBox();
15488
+ this.captureChildSnapshots();
15489
+ this._core.store.state.objects.update(this);
15490
+ });
15491
+ }
15492
+ rotate(value) {
15493
+ this.rotation = value;
15494
+ const centerX = this.translateX + this.totalWidth / 2 / this.scale;
15495
+ const centerY = this.translateY + this.totalHeight / 2 / this.scale;
15496
+ const angle = value - this.snapshotRotation;
15497
+ const cos = Math.cos(angle);
15498
+ const sin = Math.sin(angle);
15499
+ this._core.store.state.objects.transaction(() => {
15500
+ this._core.store.state.objects.update(this);
15501
+ this.children.forEach(child => {
15502
+ const unchangedSnapshot = this.unchangedChildSnapshots.get(child.id);
15503
+ if (!unchangedSnapshot)
15504
+ return;
15505
+ const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
15506
+ const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
15507
+ const rotatedX = cos * offsetX - sin * offsetY;
15508
+ const rotatedY = sin * offsetX + cos * offsetY;
15509
+ child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
15510
+ child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
15511
+ child.rotate(this.children.length === 1 ? value : unchangedSnapshot.rotation + angle);
15512
+ });
15513
+ });
15514
+ }
15515
+ getOffsetXToCenterFromSnapshot(snapshot) {
15516
+ const childCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
15517
+ const groupScale = this.snapshotScale || this.scale || 1;
15518
+ const groupCenterX = this.snapshotTranslateX + this.snapshotTotalWidth / groupScale / 2;
15519
+ return childCenterX - groupCenterX;
15520
+ }
15521
+ getOffsetYToCenterFromSnapshot(snapshot) {
15522
+ const childCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
15523
+ const groupScale = this.snapshotScale || this.scale || 1;
15524
+ const groupCenterY = this.snapshotTranslateY + this.snapshotTotalHeight / groupScale / 2;
15525
+ return childCenterY - groupCenterY;
15526
+ }
15527
+ // ─────────────────────────────────────────────────────────────────────────────
15528
+ // HIT TESTING
15529
+ // ─────────────────────────────────────────────────────────────────────────────
15530
+ hitTest(x, y) {
15531
+ // Check if point is inside any child
15532
+ return this.children.some(child => child.hitTest(x, y));
15533
+ }
15534
+ hitTestPolygon(polygon) {
15535
+ // Check if polygon intersects with any child
15536
+ return this.children.some(child => child.hitTestPolygon(polygon));
15537
+ }
15538
+ // ─────────────────────────────────────────────────────────────────────────────
15539
+ // COPY & SERIALIZATION
15540
+ // ─────────────────────────────────────────────────────────────────────────────
15541
+ copy() {
15542
+ const copiedGroup = KritzelGroup.create(this._core);
15543
+ // Deep copy all children but DON'T add to store yet
15544
+ // Store the copied children temporarily for paste() to handle
15545
+ const copiedChildren = [];
15546
+ this.children
15547
+ .sort((a, b) => a.zIndex - b.zIndex)
15548
+ .forEach(child => {
15549
+ const copiedChild = child.copy();
15550
+ copiedChildren.push(copiedChild);
15551
+ copiedGroup.childIds.push(copiedChild.id);
15552
+ });
15553
+ // Store copied children on the group for paste() to access
15554
+ copiedGroup._pendingChildren = copiedChildren;
15555
+ // Copy group properties
15556
+ copiedGroup.rotation = this.rotation;
15557
+ copiedGroup.translateX = this.translateX;
15558
+ copiedGroup.translateY = this.translateY;
15559
+ copiedGroup.width = this.width;
15560
+ copiedGroup.height = this.height;
15561
+ return copiedGroup;
15562
+ }
15563
+ serialize() {
15564
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedChildSnapshots, ...remainingProps } = this;
15565
+ const clonedProps = structuredClone(remainingProps);
15566
+ // Convert Map to plain object for serialization
15567
+ clonedProps.unchangedChildSnapshots = Object.fromEntries(this.unchangedChildSnapshots);
15568
+ return clonedProps;
15569
+ }
15570
+ deserialize(object) {
15571
+ super.deserialize(object);
15572
+ // Restore the Map from serialized object
15573
+ if (object.unchangedChildSnapshots) {
15574
+ this.unchangedChildSnapshots = new Map(Object.entries(object.unchangedChildSnapshots));
15575
+ }
15576
+ return this;
15577
+ }
15578
+ update() {
15579
+ this._core.store.state.objects.update(this);
15580
+ }
15581
+ /**
15582
+ * Updates the workspace ID for this group and all its children.
15583
+ */
15584
+ updateWorkspaceId(workspaceId) {
15585
+ this.workspaceId = workspaceId;
15586
+ this.children.forEach(child => {
15587
+ child.workspaceId = workspaceId;
15588
+ // Recursively update nested groups
15589
+ if (child instanceof KritzelGroup) {
15590
+ child.updateWorkspaceId(workspaceId);
15591
+ }
15592
+ });
15593
+ }
15594
+ }
15595
+
15202
15596
  var KritzelMouseButton;
15203
15597
  (function (KritzelMouseButton) {
15204
15598
  KritzelMouseButton[KritzelMouseButton["Left"] = 0] = "Left";
@@ -15219,11 +15613,19 @@ class KritzelEventHelper {
15219
15613
  return !!contextMenu;
15220
15614
  }
15221
15615
  static onLongPress(event, onSuccess, onCancel) {
15222
- const longPressTimeout = 400;
15223
- const moveThreshold = 10;
15616
+ if (event.pointerType !== 'touch') {
15617
+ onCancel?.();
15618
+ return () => { };
15619
+ }
15224
15620
  const startX = event.clientX;
15225
15621
  const startY = event.clientY;
15226
15622
  const target = event.target;
15623
+ if (!target) {
15624
+ onCancel?.();
15625
+ return () => { };
15626
+ }
15627
+ const longPressTimeout = 400;
15628
+ const moveThreshold = 10;
15227
15629
  const timer = setTimeout(() => {
15228
15630
  removeListeners();
15229
15631
  onSuccess(event);
@@ -15481,6 +15883,21 @@ class KritzelSelectionGroup extends KritzelBaseObject {
15481
15883
  this.captureUnchangedSnapshots();
15482
15884
  this.refreshObjectDimensions();
15483
15885
  }
15886
+ /**
15887
+ * Prepares the selection group and its children for a transform interaction (rotate/resize).
15888
+ * Ensures snapshot state is aligned with the current visual state.
15889
+ */
15890
+ beginTransform() {
15891
+ // Keep the selection group's bounding box current before snapshotting.
15892
+ this.refreshObjectDimensions();
15893
+ this.captureUnchangedSnapshots();
15894
+ // Groups need their own child snapshots aligned with the current transform.
15895
+ this.objects.forEach(obj => {
15896
+ if (obj instanceof KritzelGroup) {
15897
+ obj.finalize();
15898
+ }
15899
+ });
15900
+ }
15484
15901
  deselectAllChildren() {
15485
15902
  this.objects.forEach(obj => (obj.isSelected = false));
15486
15903
  }
@@ -15741,9 +16158,9 @@ class KritzelSelectionGroup extends KritzelBaseObject {
15741
16158
  return objCenterY - groupCenterY;
15742
16159
  }
15743
16160
  hitTest(x, y) {
15744
- const containsOnlyLines = this.objects.every(obj => obj.__class__ === 'KritzelLine');
15745
- if (containsOnlyLines) {
15746
- return this.objects.some(obj => obj.hitTest(x, y));
16161
+ const isSingleLine = this.objects.length === 1 && this.objects[0].__class__ === 'KritzelLine';
16162
+ if (isSingleLine) {
16163
+ return this.objects[0].hitTest(x, y);
15747
16164
  }
15748
16165
  const polygon = this.rotatedPolygon;
15749
16166
  const polyPoints = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
@@ -16246,6 +16663,8 @@ KritzelIconRegistry.registerIcons({
16246
16663
  '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>',
16247
16664
  '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>',
16248
16665
  '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>',
16666
+ '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>',
16667
+ '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>',
16249
16668
  '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>',
16250
16669
  '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>',
16251
16670
  '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>',
@@ -16317,6 +16736,8 @@ class KritzelBaseHandler {
16317
16736
  }
16318
16737
  }
16319
16738
 
16739
+ /** Threshold in screen pixels for disconnecting anchored lines during drag */
16740
+ const ANCHOR_DISCONNECT_THRESHOLD = 30;
16320
16741
  class KritzelMoveHandler extends KritzelBaseHandler {
16321
16742
  dragStartX;
16322
16743
  dragStartY;
@@ -16324,6 +16745,11 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16324
16745
  startY;
16325
16746
  endX;
16326
16747
  endY;
16748
+ /** Initial position when drag started (for calculating total accumulated distance) */
16749
+ initialDragX = 0;
16750
+ initialDragY = 0;
16751
+ /** Set of line IDs that have been disconnected from anchors during this drag */
16752
+ disconnectedLineIds = new Set();
16327
16753
  hasMoved = false;
16328
16754
  trackedPointerId = null;
16329
16755
  constructor(core) {
@@ -16336,6 +16762,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16336
16762
  this.startY = 0;
16337
16763
  this.endX = 0;
16338
16764
  this.endY = 0;
16765
+ this.initialDragX = 0;
16766
+ this.initialDragY = 0;
16767
+ this.disconnectedLineIds.clear();
16339
16768
  this.hasMoved = false;
16340
16769
  this.trackedPointerId = null;
16341
16770
  }
@@ -16355,6 +16784,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16355
16784
  this.dragStartY = clientY;
16356
16785
  this.startX = this.dragStartX;
16357
16786
  this.startY = this.dragStartY;
16787
+ this.initialDragX = clientX;
16788
+ this.initialDragY = clientY;
16789
+ this.disconnectedLineIds.clear();
16358
16790
  this.trackedPointerId = event.pointerId;
16359
16791
  }
16360
16792
  else {
@@ -16380,6 +16812,9 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16380
16812
  this.dragStartY = y;
16381
16813
  this.startX = x;
16382
16814
  this.startY = y;
16815
+ this.initialDragX = x;
16816
+ this.initialDragY = y;
16817
+ this.disconnectedLineIds.clear();
16383
16818
  this.trackedPointerId = event.pointerId;
16384
16819
  }
16385
16820
  else {
@@ -16403,6 +16838,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16403
16838
  const moveDeltaY = Math.abs(clientY - this.startY);
16404
16839
  const moveThreshold = 5;
16405
16840
  if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
16841
+ // Check for anchor disconnect threshold on lines
16842
+ this.checkAndDisconnectAnchors(clientX, clientY);
16406
16843
  selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
16407
16844
  this.dragStartX = clientX;
16408
16845
  this.dragStartY = clientY;
@@ -16428,6 +16865,8 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16428
16865
  const moveThreshold = 5;
16429
16866
  if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
16430
16867
  clearTimeout(this._core.store.state.longTouchTimeout);
16868
+ // Check for anchor disconnect threshold on lines
16869
+ this.checkAndDisconnectAnchors(x, y);
16431
16870
  selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
16432
16871
  this.dragStartX = x;
16433
16872
  this.dragStartY = y;
@@ -16462,6 +16901,48 @@ class KritzelMoveHandler extends KritzelBaseHandler {
16462
16901
  }
16463
16902
  this.reset();
16464
16903
  }
16904
+ /**
16905
+ * Checks if the total accumulated drag distance exceeds the anchor disconnect threshold.
16906
+ * If so, disconnects all anchors from any KritzelLine objects in the selection group.
16907
+ * This allows anchored lines to be freely moved after dragging beyond the threshold.
16908
+ */
16909
+ checkAndDisconnectAnchors(currentX, currentY) {
16910
+ const totalDeltaX = currentX - this.initialDragX;
16911
+ const totalDeltaY = currentY - this.initialDragY;
16912
+ const totalDistance = Math.sqrt(totalDeltaX * totalDeltaX + totalDeltaY * totalDeltaY);
16913
+ if (totalDistance < ANCHOR_DISCONNECT_THRESHOLD) {
16914
+ return;
16915
+ }
16916
+ const selectionGroup = this._core.store.selectionGroup;
16917
+ if (!selectionGroup) {
16918
+ return;
16919
+ }
16920
+ // Find and disconnect anchors from all anchored lines in the selection
16921
+ for (const obj of selectionGroup.objects) {
16922
+ // Skip if already disconnected in this drag session
16923
+ if (this.disconnectedLineIds.has(obj.id)) {
16924
+ continue;
16925
+ }
16926
+ // Check if this is a line with anchors
16927
+ if (obj instanceof KritzelLine) {
16928
+ const line = obj;
16929
+ const hasAnchors = line.startAnchor || line.endAnchor;
16930
+ if (hasAnchors) {
16931
+ // Disconnect both anchors
16932
+ if (line.startAnchor) {
16933
+ this._core.anchorManager.removeAnchor(line.id, 'start');
16934
+ }
16935
+ if (line.endAnchor) {
16936
+ this._core.anchorManager.removeAnchor(line.id, 'end');
16937
+ }
16938
+ // Mark as disconnected so we don't try again
16939
+ this.disconnectedLineIds.add(line.id);
16940
+ // Update the line to persist the change
16941
+ this._core.store.state.objects.update(line);
16942
+ }
16943
+ }
16944
+ }
16945
+ }
16465
16946
  }
16466
16947
 
16467
16948
  var KritzelHandleType;
@@ -16693,6 +17174,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
16693
17174
  if (KritzelEventHelper.isLeftClick(event)) {
16694
17175
  const selectionGroup = this._core.store.selectionGroup;
16695
17176
  if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
17177
+ selectionGroup.beginTransform();
16696
17178
  const clientX = event.clientX - this._core.store.offsetX;
16697
17179
  const clientY = event.clientY - this._core.store.offsetY;
16698
17180
  this._core.store.state.isRotating = true;
@@ -16716,6 +17198,7 @@ class KritzelRotationHandler extends KritzelBaseHandler {
16716
17198
  if (activePointers.length === 1) {
16717
17199
  const selectionGroup = this._core.store.selectionGroup;
16718
17200
  if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
17201
+ selectionGroup.beginTransform();
16719
17202
  const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
16720
17203
  const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
16721
17204
  this._core.store.state.isRotating = true;
@@ -17026,11 +17509,14 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
17026
17509
  if (!object) {
17027
17510
  return;
17028
17511
  }
17029
- object.isSelected = false;
17512
+ // Resolve child objects to their parent groups
17513
+ const parentGroup = KritzelGroup.findParentGroup(this._core, object.id);
17514
+ const objectToSelect = parentGroup || object;
17515
+ objectToSelect.isSelected = false;
17030
17516
  const selectionGroup = KritzelSelectionGroup.create(this._core);
17031
- selectionGroup.addOrRemove(object);
17517
+ selectionGroup.addOrRemove(objectToSelect);
17032
17518
  selectionGroup.isSelected = true;
17033
- selectionGroup.rotation = object.rotation;
17519
+ selectionGroup.rotation = objectToSelect.rotation;
17034
17520
  this._core.addSelectionGroup(selectionGroup);
17035
17521
  this._core.rerender();
17036
17522
  }
@@ -17039,10 +17525,24 @@ class KritzelSelectionHandler extends KritzelBaseHandler {
17039
17525
  if (selectedObjects.length === 0) {
17040
17526
  return;
17041
17527
  }
17528
+ // Resolve child objects to their parent groups to avoid double-move
17529
+ const resolvedObjects = new Map();
17530
+ selectedObjects.forEach(obj => {
17531
+ // Check if this object is a child of a KritzelGroup
17532
+ const parentGroup = KritzelGroup.findParentGroup(this._core, obj.id);
17533
+ if (parentGroup) {
17534
+ // Add the parent group instead of the child
17535
+ resolvedObjects.set(parentGroup.id, parentGroup);
17536
+ }
17537
+ else {
17538
+ // Not a child of any group, add the object itself
17539
+ resolvedObjects.set(obj.id, obj);
17540
+ }
17541
+ obj.isSelected = false;
17542
+ });
17042
17543
  const selectionGroup = KritzelSelectionGroup.create(this._core);
17043
- selectedObjects.forEach(o => {
17544
+ resolvedObjects.forEach(o => {
17044
17545
  selectionGroup.addOrRemove(o);
17045
- o.isSelected = false;
17046
17546
  });
17047
17547
  selectionGroup.isSelected = true;
17048
17548
  if (selectionGroup.length === 1) {
@@ -33865,7 +34365,7 @@ const DEFAULT_LINE_TOOL_CONFIG = {
33865
34365
  },
33866
34366
  };
33867
34367
 
33868
- export { Schema as $, isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelPath as E, KritzelImage as F, KritzelLine as G, HocuspocusProvider as H, KritzelBrushTool as I, KritzelLineTool as J, KritzelText as K, KritzelEraserTool as L, KritzelImageTool as M, KritzelTextTool as N, Observable$1 as O, KritzelCursorHelper as P, KritzelSelectionTool as Q, IndexedDBSyncProvider as R, KritzelAppStateMap as S, KritzelWorkspace as T, KritzelAnchorManager as U, DEFAULT_BRUSH_CONFIG as V, DEFAULT_TEXT_CONFIG as W, DEFAULT_LINE_TOOL_CONFIG as X, KritzelDevicesHelper as Y, KritzelMouseButton as Z, KritzelBaseObject as _, writeVarUint8Array$2 as a, schema as a0, addListNodes as a1, EditorView as a2, EditorState as a3, keymap as a4, TextSelection as a5, KritzelKeyboardHelper as a6, KritzelToolRegistry as a7, KritzelGeometryHelper as a8, baseKeymap as a9, KritzelBaseTool as aa, KritzelEventHelper as ab, KritzelIconRegistry as ac, KritzelBaseHandler as ad, KritzelSelectionBox as ae, KritzelSelectionGroup as af, Doc as ag, DEFAULT_SYNC_CONFIG as ah, UndoManager as ai, ObjectHelper as aj, KritzelClassHelper as ak, 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 };
33869
- //# sourceMappingURL=default-line-tool.config-PvsWHpO2.js.map
34368
+ export { KritzelBaseObject as $, isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelPath as E, KritzelImage as F, KritzelLine as G, HocuspocusProvider as H, KritzelGroup as I, KritzelBrushTool as J, KritzelText as K, KritzelLineTool as L, KritzelEraserTool as M, KritzelImageTool as N, Observable$1 as O, KritzelTextTool as P, KritzelCursorHelper as Q, KritzelSelectionTool as R, IndexedDBSyncProvider as S, KritzelAppStateMap as T, KritzelWorkspace as U, KritzelAnchorManager as V, DEFAULT_BRUSH_CONFIG as W, DEFAULT_TEXT_CONFIG as X, DEFAULT_LINE_TOOL_CONFIG as Y, KritzelDevicesHelper as Z, KritzelMouseButton as _, writeVarUint8Array$2 as a, Schema as a0, schema as a1, addListNodes as a2, EditorView as a3, EditorState as a4, keymap as a5, TextSelection as a6, KritzelKeyboardHelper as a7, KritzelToolRegistry as a8, KritzelGeometryHelper as a9, baseKeymap as aa, KritzelBaseTool as ab, KritzelEventHelper as ac, KritzelIconRegistry as ad, KritzelBaseHandler as ae, KritzelSelectionBox as af, KritzelSelectionGroup as ag, Doc as ah, DEFAULT_SYNC_CONFIG as ai, UndoManager as aj, ObjectHelper as ak, KritzelClassHelper as al, 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 };
34369
+ //# sourceMappingURL=default-line-tool.config-CuBm2vpW.js.map
33870
34370
 
33871
- //# sourceMappingURL=default-line-tool.config-PvsWHpO2.js.map
34371
+ //# sourceMappingURL=default-line-tool.config-CuBm2vpW.js.map