@inditextech/weave-sdk 5.4.1 → 5.6.0

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.
package/dist/sdk.node.js CHANGED
@@ -5940,6 +5940,7 @@ const MOVE_TOOL_STATE = {
5940
5940
  };
5941
5941
  //#endregion
5942
5942
  //#region src/nodes/node.ts
5943
+ const nodeInstanceListeners = /* @__PURE__ */ new WeakMap();
5943
5944
  const augmentKonvaNodeClass = (config) => {
5944
5945
  const { transform } = config ?? {};
5945
5946
  Konva.Node.prototype.getTransformerProperties = function() {
@@ -5955,6 +5956,8 @@ const augmentKonvaNodeClass = (config) => {
5955
5956
  Konva.Node.prototype.triggerCrop = function() {};
5956
5957
  Konva.Node.prototype.closeCrop = function() {};
5957
5958
  Konva.Node.prototype.resetCrop = function() {};
5959
+ Konva.Node.prototype.flipHorizontal = function() {};
5960
+ Konva.Node.prototype.flipVertical = function() {};
5958
5961
  Konva.Node.prototype.dblClick = function() {};
5959
5962
  Konva.Node.prototype.click = function() {};
5960
5963
  Konva.Node.prototype.allowedAnchors = function() {
@@ -6040,6 +6043,8 @@ var WeaveNode = class {
6040
6043
  };
6041
6044
  node.updatePosition = function() {};
6042
6045
  node.resetCrop = function() {};
6046
+ node.flipHorizontal = function() {};
6047
+ node.flipVertical = function() {};
6043
6048
  node.handleMouseover = function() {};
6044
6049
  node.handleMouseout = function() {};
6045
6050
  node.handleSelectNode = function() {};
@@ -6210,7 +6215,7 @@ var WeaveNode = class {
6210
6215
  node.draggable(false);
6211
6216
  };
6212
6217
  if (node.getAttrs().locked ?? false) {
6213
- this.instance.removeEventListener("onNodesChange", handleNodesChange);
6218
+ this.removeNodeInstanceListeners(node, false);
6214
6219
  node.off("transformstart");
6215
6220
  node.off("transform");
6216
6221
  node.off("transformend");
@@ -6220,7 +6225,7 @@ var WeaveNode = class {
6220
6225
  node.off("pointerover");
6221
6226
  node.off("pointerleave");
6222
6227
  } else {
6223
- this.instance.addEventListener("onNodesChange", handleNodesChange);
6228
+ this.setNodeInstanceListener(node, "onNodesChange", handleNodesChange);
6224
6229
  node.off("transformstart");
6225
6230
  node.on("transformstart", (e) => {
6226
6231
  if (e.target.getAttrs().strokeScaleEnabled !== false) {
@@ -6279,7 +6284,7 @@ var WeaveNode = class {
6279
6284
  });
6280
6285
  const stage = this.instance.getStage();
6281
6286
  let originalPosition = null;
6282
- this.instance.addEventListener("onSelectionState", (state) => {
6287
+ this.setNodeInstanceListener(node, "onSelectionState", (state) => {
6283
6288
  const selectedNodes = this.getSelectionPlugin()?.getSelectedNodes() ?? [];
6284
6289
  if (!state && selectedNodes?.some((n) => n.getAttrs().id === node.getAttrs().id)) originalPosition = node.getAbsolutePosition();
6285
6290
  });
@@ -6595,8 +6600,39 @@ var WeaveNode = class {
6595
6600
  async onRegister() {}
6596
6601
  onAdd(nodeInstance) {}
6597
6602
  onDestroy(nodeInstance) {
6603
+ this.removeNodeInstanceListeners(nodeInstance);
6598
6604
  nodeInstance.destroy();
6599
6605
  }
6606
+ /**
6607
+ * Registers a Weave instance listener on behalf of a node. Only one listener
6608
+ * is kept per node and event: registering the same event again for the same
6609
+ * node replaces the previous listener, including the ones registered by
6610
+ * setupDefaultNodeEvents (onNodesChange, onSelectionState).
6611
+ */
6612
+ setNodeInstanceListener(node, event, callback) {
6613
+ let listeners = nodeInstanceListeners.get(node);
6614
+ if (!listeners) {
6615
+ listeners = /* @__PURE__ */ new Map();
6616
+ nodeInstanceListeners.set(node, listeners);
6617
+ }
6618
+ const previous = listeners.get(event);
6619
+ if (previous) this.instance.removeEventListener(event, previous);
6620
+ listeners.set(event, callback);
6621
+ this.instance.addEventListener(event, callback);
6622
+ }
6623
+ /**
6624
+ * Removes the listeners registered with setNodeInstanceListener for the node
6625
+ * and, unless disabled, for its descendants.
6626
+ */
6627
+ removeNodeInstanceListeners(node, includeDescendants = true) {
6628
+ const nodes = includeDescendants && node instanceof Konva.Container ? [node, ...node.find(() => true)] : [node];
6629
+ for (const actNode of nodes) {
6630
+ const listeners = nodeInstanceListeners.get(actNode);
6631
+ if (!listeners) continue;
6632
+ for (const [event, callback] of listeners) this.instance.removeEventListener(event, callback);
6633
+ nodeInstanceListeners.delete(actNode);
6634
+ }
6635
+ }
6600
6636
  onDestroyInstance() {}
6601
6637
  serialize(instance) {
6602
6638
  const attrs = instance.getAttrs();
@@ -9469,7 +9505,7 @@ var WeaveRegisterManager = class {
9469
9505
  };
9470
9506
  //#endregion
9471
9507
  //#region package.json
9472
- var version = "5.4.1";
9508
+ var version = "5.6.0";
9473
9509
  //#endregion
9474
9510
  //#region src/managers/setup.ts
9475
9511
  var WeaveSetupManager = class {
@@ -11190,6 +11226,9 @@ var Weave = class {
11190
11226
  getBoundingBox(nodes, config) {
11191
11227
  return getBoundingBox(nodes, config);
11192
11228
  }
11229
+ flipImage(node, options) {
11230
+ this.getNodeHandler("image")?.flipImage(node, options);
11231
+ }
11193
11232
  stateTransactional(callback, origin) {
11194
11233
  this.stateManager.stateTransactional(callback, origin);
11195
11234
  }
@@ -14447,24 +14486,64 @@ var WeaveImageCrop = class WeaveImageCrop {
14447
14486
  this.onClose = () => {};
14448
14487
  this.handleHide = this.hide.bind(this);
14449
14488
  }
14450
- setupCropImage(imageAttrs, visible) {
14451
- this.cropGroup.destroyChildren();
14489
+ /**
14490
+ * Mirrors the start of a segment inside a container along one axis:
14491
+ * the segment [start, start + length] inside [0, size] becomes
14492
+ * [size - start - length, size - start]. Used to convert between visual
14493
+ * space (what the user sees on a flipped image) and source space (where
14494
+ * the crop information is stored). It is its own inverse.
14495
+ */
14496
+ static mirrorOffset(start, length, size) {
14497
+ return size - start - length;
14498
+ }
14499
+ /**
14500
+ * Returns the offset (in uncropped display units) of the visible crop
14501
+ * window inside the visually rendered uncropped image, along with the real
14502
+ * scale. When the image is flipped the stored crop (source space) is
14503
+ * mirrored to match what the user sees.
14504
+ */
14505
+ getVisualCropOffset(imageAttrs) {
14452
14506
  const actualScale = imageAttrs.uncroppedImage.width / imageAttrs.imageInfo.width;
14453
14507
  const realScale = actualScale / (imageAttrs.cropInfo ? imageAttrs.cropInfo.scaleX : actualScale);
14508
+ let x = 0;
14509
+ let y = 0;
14510
+ if (imageAttrs.cropInfo) {
14511
+ x = imageAttrs.cropInfo.x * realScale;
14512
+ y = imageAttrs.cropInfo.y * realScale;
14513
+ if (imageAttrs.flipX === true) x = WeaveImageCrop.mirrorOffset(x, imageAttrs.cropInfo.width * realScale, imageAttrs.uncroppedImage.width);
14514
+ if (imageAttrs.flipY === true) y = WeaveImageCrop.mirrorOffset(y, imageAttrs.cropInfo.height * realScale, imageAttrs.uncroppedImage.height);
14515
+ }
14516
+ return {
14517
+ x,
14518
+ y,
14519
+ realScale
14520
+ };
14521
+ }
14522
+ setupCropImage(imageAttrs, visible) {
14523
+ this.cropGroup.destroyChildren();
14524
+ const { x, y, realScale } = this.getVisualCropOffset(imageAttrs);
14525
+ const flipX = imageAttrs.flipX === true;
14526
+ const flipY = imageAttrs.flipY === true;
14454
14527
  this.cropImage = new Konva.Image({
14455
- x: imageAttrs.cropInfo ? -imageAttrs.cropInfo.x * realScale : 0,
14456
- y: imageAttrs.cropInfo ? -imageAttrs.cropInfo.y * realScale : 0,
14528
+ x: -x,
14529
+ y: -y,
14457
14530
  width: imageAttrs.uncroppedImage.width,
14458
14531
  height: imageAttrs.uncroppedImage.height,
14459
- scaleX: 1,
14460
- scaleY: 1,
14532
+ scaleX: flipX ? -1 : 1,
14533
+ scaleY: flipY ? -1 : 1,
14534
+ offsetX: flipX ? imageAttrs.uncroppedImage.width : 0,
14535
+ offsetY: flipY ? imageAttrs.uncroppedImage.height : 0,
14461
14536
  image: this.internalImage.image(),
14462
14537
  crop: void 0,
14463
14538
  visible,
14464
14539
  listening: false,
14465
14540
  draggable: false
14466
14541
  });
14467
- return { realScale };
14542
+ return {
14543
+ realScale,
14544
+ offsetX: x,
14545
+ offsetY: y
14546
+ };
14468
14547
  }
14469
14548
  show(onClose, options) {
14470
14549
  this.onClose = onClose;
@@ -14475,9 +14554,9 @@ var WeaveImageCrop = class WeaveImageCrop {
14475
14554
  this.image.listening(false);
14476
14555
  const imageAttrs = this.image.getAttrs();
14477
14556
  this.internalImage.hide();
14478
- const { realScale } = this.setupCropImage(imageAttrs, true);
14479
- this.imageOffsetX = imageAttrs.cropInfo ? imageAttrs.cropInfo.x * realScale : 0;
14480
- this.imageOffsetY = imageAttrs.cropInfo ? imageAttrs.cropInfo.y * realScale : 0;
14557
+ const { realScale, offsetX, offsetY } = this.setupCropImage(imageAttrs, true);
14558
+ this.imageOffsetX = offsetX;
14559
+ this.imageOffsetY = offsetY;
14481
14560
  const cropModeConfiguration = this.node.getConfiguration().cropMode;
14482
14561
  this.cropRect = new Konva.Rect({
14483
14562
  x: 0,
@@ -14525,7 +14604,10 @@ var WeaveImageCrop = class WeaveImageCrop {
14525
14604
  const center = this.cropImage.getAbsolutePosition();
14526
14605
  const width = this.cropImage.width() * stage.scaleX();
14527
14606
  const height = this.cropImage.height() * stage.scaleY();
14528
- const offset = this.cropImage.offset();
14607
+ const offset = {
14608
+ x: this.cropImage.scaleX() < 0 ? 0 : this.cropImage.offsetX(),
14609
+ y: this.cropImage.scaleY() < 0 ? 0 : this.cropImage.offsetY()
14610
+ };
14529
14611
  const corners = [
14530
14612
  {
14531
14613
  x: -offset.x,
@@ -14869,11 +14951,15 @@ var WeaveImageCrop = class WeaveImageCrop {
14869
14951
  if (!intersectionRect) return;
14870
14952
  const imageAttrs = this.image.getAttrs();
14871
14953
  const actualScale = imageAttrs.uncroppedImage.width / imageAttrs.imageInfo.width;
14954
+ let sourceX = clipRectGroup.x + this.imageOffsetX;
14955
+ let sourceY = clipRectGroup.y + this.imageOffsetY;
14956
+ if (imageAttrs.flipX === true) sourceX = WeaveImageCrop.mirrorOffset(sourceX, clipRectGroup.width, imageAttrs.uncroppedImage.width);
14957
+ if (imageAttrs.flipY === true) sourceY = WeaveImageCrop.mirrorOffset(sourceY, clipRectGroup.height, imageAttrs.uncroppedImage.height);
14872
14958
  const realClipRect = {
14873
14959
  scaleX: actualScale,
14874
14960
  scaleY: actualScale,
14875
- x: WeaveImageCrop.roundTo6Decimals(clipRectGroup.x + this.imageOffsetX),
14876
- y: WeaveImageCrop.roundTo6Decimals(clipRectGroup.y + this.imageOffsetY),
14961
+ x: WeaveImageCrop.roundTo6Decimals(sourceX),
14962
+ y: WeaveImageCrop.roundTo6Decimals(sourceY),
14877
14963
  width: WeaveImageCrop.roundTo6Decimals(clipRectGroup.width),
14878
14964
  height: WeaveImageCrop.roundTo6Decimals(clipRectGroup.height)
14879
14965
  };
@@ -14915,16 +15001,18 @@ var WeaveImageCrop = class WeaveImageCrop {
14915
15001
  if (!intersectionRect || !intersectionRectAbs) return;
14916
15002
  const imageAttrs = this.image.getAttrs();
14917
15003
  const actualScale = imageAttrs.uncroppedImage.width / imageAttrs.imageInfo.width;
14918
- const realScale = actualScale / (imageAttrs.cropInfo ? imageAttrs.cropInfo.scaleX : actualScale);
14919
- const imageOffsetX = imageAttrs.cropInfo ? imageAttrs.cropInfo.x * realScale : 0;
14920
- const imageOffsetY = imageAttrs.cropInfo ? imageAttrs.cropInfo.y * realScale : 0;
15004
+ const { x: imageOffsetX, y: imageOffsetY } = this.getVisualCropOffset(imageAttrs);
14921
15005
  const diffX = intersectionRect.x - imageRect.x;
14922
15006
  const diffY = intersectionRect.y - imageRect.y;
15007
+ let sourceX = diffX + imageOffsetX;
15008
+ let sourceY = diffY + imageOffsetY;
15009
+ if (imageAttrs.flipX === true) sourceX = WeaveImageCrop.mirrorOffset(sourceX, intersectionRect.width, imageAttrs.uncroppedImage.width);
15010
+ if (imageAttrs.flipY === true) sourceY = WeaveImageCrop.mirrorOffset(sourceY, intersectionRect.height, imageAttrs.uncroppedImage.height);
14923
15011
  const realClipRect = {
14924
15012
  scaleX: actualScale,
14925
15013
  scaleY: actualScale,
14926
- x: WeaveImageCrop.roundTo6Decimals(diffX + imageOffsetX),
14927
- y: WeaveImageCrop.roundTo6Decimals(diffY + imageOffsetY),
15014
+ x: WeaveImageCrop.roundTo6Decimals(sourceX),
15015
+ y: WeaveImageCrop.roundTo6Decimals(sourceY),
14928
15016
  width: WeaveImageCrop.roundTo6Decimals(intersectionRect.width),
14929
15017
  height: WeaveImageCrop.roundTo6Decimals(intersectionRect.height)
14930
15018
  };
@@ -15230,6 +15318,27 @@ var WeaveImageNode = class extends WeaveNode {
15230
15318
  if (!internalImage || !cropGroup) return;
15231
15319
  new WeaveImageCrop(this.instance, this, imageNode, internalImage, cropGroup).unCrop();
15232
15320
  };
15321
+ flipImage = (imageNode, options) => {
15322
+ if (imageNode.getAttrs().cropping) return;
15323
+ const prop = options.axis === "horizontal" ? "flipX" : "flipY";
15324
+ this.instance.stateTransactional(() => {
15325
+ imageNode.setAttrs({ [prop]: !(imageNode.getAttrs()[prop] ?? false) });
15326
+ this.applyFlip(imageNode);
15327
+ this.cacheNode(imageNode);
15328
+ this.instance.updateNode(this.serialize(imageNode));
15329
+ });
15330
+ };
15331
+ applyFlip(image) {
15332
+ const attrs = image.getAttrs();
15333
+ const internalImage = image.findOne(`#${attrs.id}-image`);
15334
+ if (!internalImage) return;
15335
+ const flipX = attrs.flipX === true;
15336
+ const flipY = attrs.flipY === true;
15337
+ internalImage.scaleX(flipX ? -1 : 1);
15338
+ internalImage.scaleY(flipY ? -1 : 1);
15339
+ internalImage.offsetX(flipX ? internalImage.width() : 0);
15340
+ internalImage.offsetY(flipY ? internalImage.height() : 0);
15341
+ }
15233
15342
  loadAsyncElement(nodeId) {
15234
15343
  this.instance.loadAsyncElement(nodeId, WEAVE_IMAGE_NODE_TYPE);
15235
15344
  }
@@ -15277,6 +15386,12 @@ var WeaveImageNode = class extends WeaveNode {
15277
15386
  new WeaveImageCrop(this.instance, this, image, internalImage, cropGroup).unCrop();
15278
15387
  };
15279
15388
  }
15389
+ image.flipHorizontal = () => {
15390
+ this.flipImage(image, { axis: "horizontal" });
15391
+ };
15392
+ image.flipVertical = () => {
15393
+ this.flipImage(image, { axis: "vertical" });
15394
+ };
15280
15395
  const defaultTransformerProperties = this.defaultGetTransformerProperties(this.config.transform);
15281
15396
  image.getTransformerProperties = function() {
15282
15397
  return defaultTransformerProperties;
@@ -15892,6 +16007,10 @@ var WeaveImageNode = class extends WeaveNode {
15892
16007
  if (!this.imageState[imageAttrs.id ?? ""]?.loaded) return;
15893
16008
  }
15894
16009
  updateImageCrop(image) {
16010
+ this.updateImageCropGeometry(image);
16011
+ this.applyFlip(image);
16012
+ }
16013
+ updateImageCropGeometry(image) {
15895
16014
  const imageAttrs = image.getAttrs();
15896
16015
  const internalImage = image?.findOne(`#${imageAttrs.id}-image`);
15897
16016
  if (!this.imageState[imageAttrs.id ?? ""]?.loaded) return;
@@ -15969,8 +16088,12 @@ var WeaveImageNode = class extends WeaveNode {
15969
16088
  });
15970
16089
  }
15971
16090
  if (internalImage) {
15972
- internalImage.width(Math.max(5, internalImage.width() * internalImage.scaleX()));
15973
- internalImage.height(Math.max(5, internalImage.height() * internalImage.scaleY()));
16091
+ internalImage.offset({
16092
+ x: 0,
16093
+ y: 0
16094
+ });
16095
+ internalImage.width(Math.max(5, internalImage.width() * Math.abs(internalImage.scaleX())));
16096
+ internalImage.height(Math.max(5, internalImage.height() * Math.abs(internalImage.scaleY())));
15974
16097
  internalImage.scale({
15975
16098
  x: 1,
15976
16099
  y: 1
@@ -16025,7 +16148,7 @@ var WeaveImageNode = class extends WeaveNode {
16025
16148
  clearTimeout(this.imageTryoutIds[nodeId]);
16026
16149
  delete this.imageTryoutIds[nodeId];
16027
16150
  }
16028
- nodeInstance.destroy();
16151
+ super.onDestroy(nodeInstance);
16029
16152
  }
16030
16153
  loadImageTryout(imageId) {
16031
16154
  this.imageTryoutIds[imageId] = setTimeout(() => {
@@ -16112,7 +16235,9 @@ var WeaveImageNode = class extends WeaveNode {
16112
16235
  width: props.uncroppedImage.width,
16113
16236
  height: props.uncroppedImage.height
16114
16237
  },
16115
- cropping: props.cropping
16238
+ cropping: props.cropping,
16239
+ ...props.flipX !== void 0 && { flipX: props.flipX },
16240
+ ...props.flipY !== void 0 && { flipY: props.flipY }
16116
16241
  } });
16117
16242
  }
16118
16243
  static updateNodeState(prevNodeState, nextProps) {
@@ -16135,7 +16260,9 @@ var WeaveImageNode = class extends WeaveNode {
16135
16260
  width: nextProps.uncroppedImage?.width,
16136
16261
  height: nextProps.uncroppedImage?.height
16137
16262
  } },
16138
- cropping: nextProps.cropping
16263
+ cropping: nextProps.cropping,
16264
+ ...nextProps.flipX !== void 0 && { flipX: nextProps.flipX },
16265
+ ...nextProps.flipY !== void 0 && { flipY: nextProps.flipY }
16139
16266
  } });
16140
16267
  }
16141
16268
  static getSchema() {
@@ -16159,7 +16286,9 @@ var WeaveImageNode = class extends WeaveNode {
16159
16286
  width: z.number().describe("The width of the image before cropping, used for cropping calculations"),
16160
16287
  height: z.number().describe("The height of the image before cropping, used for cropping calculations")
16161
16288
  }),
16162
- cropping: z.boolean().default(false).describe("Whether the image is currently being cropped")
16289
+ cropping: z.boolean().default(false).describe("Whether the image is currently being cropped"),
16290
+ flipX: z.boolean().optional().describe("Whether the image is flipped horizontally"),
16291
+ flipY: z.boolean().optional().describe("Whether the image is flipped vertically")
16163
16292
  })
16164
16293
  });
16165
16294
  }
@@ -19588,7 +19717,7 @@ var WeaveCommentNode = class extends WeaveNode {
19588
19717
  this.normalizeNodeSize(commentNode);
19589
19718
  }
19590
19719
  onDestroy(nodeInstance) {
19591
- nodeInstance.destroy();
19720
+ super.onDestroy(nodeInstance);
19592
19721
  }
19593
19722
  expandNode(commentNode) {
19594
19723
  const widthExpanded = this.config.style.expanded.width;
@@ -23242,6 +23371,70 @@ const RECTANGLE_TOOL_STATE = {
23242
23371
  ["ADDED"]: "added"
23243
23372
  };
23244
23373
  //#endregion
23374
+ //#region src/actions/shared/shape-geometry.ts
23375
+ /** Returns the normalized bounds described by a drag, including its modifiers. */
23376
+ function getShapeDragBounds(anchor, pointer, options = {}) {
23377
+ const { centered = false, constrained = false, aspectRatio = 1 } = options;
23378
+ const deltaX = Math.abs(pointer.x - anchor.x);
23379
+ const deltaY = Math.abs(pointer.y - anchor.y);
23380
+ let width = deltaX;
23381
+ let height = deltaY;
23382
+ if (constrained && aspectRatio > 0) {
23383
+ if (width / (height || Number.EPSILON) > aspectRatio) height = width / aspectRatio;
23384
+ else width = height * aspectRatio;
23385
+ }
23386
+ if (constrained && aspectRatio > 0 && width > 0 && height > 0) {
23387
+ const minimumSizeScale = Math.max(1, 1 / width, 1 / height);
23388
+ width *= minimumSizeScale;
23389
+ height *= minimumSizeScale;
23390
+ } else {
23391
+ width = Math.max(1, width);
23392
+ height = Math.max(1, height);
23393
+ }
23394
+ if (centered) {
23395
+ width *= 2;
23396
+ height *= 2;
23397
+ return {
23398
+ x: anchor.x - width / 2,
23399
+ y: anchor.y - height / 2,
23400
+ width,
23401
+ height
23402
+ };
23403
+ }
23404
+ const signedWidth = Math.sign(pointer.x - anchor.x || 1) * width;
23405
+ const signedHeight = Math.sign(pointer.y - anchor.y || 1) * height;
23406
+ return {
23407
+ x: Math.min(anchor.x, anchor.x + signedWidth),
23408
+ y: Math.min(anchor.y, anchor.y + signedHeight),
23409
+ width,
23410
+ height
23411
+ };
23412
+ }
23413
+ //#endregion
23414
+ //#region src/actions/shared/shape-keyboard.ts
23415
+ /** Binds the common keyboard behavior for tools that create shapes by dragging. */
23416
+ function bindShapeDragKeyboardHandlers(signal, isActionActive, isDragInProgress, canUpdateModifiers, onCancel, onModifiersChange) {
23417
+ const handleModifierKey = (event) => {
23418
+ if (event.key !== "Shift" && event.key !== "Alt") return;
23419
+ const actionActive = isActionActive();
23420
+ const dragInProgress = isDragInProgress();
23421
+ if (event.type === "keydown" && event.key === "Alt" && actionActive && dragInProgress) event.preventDefault();
23422
+ if (!actionActive || !canUpdateModifiers()) return;
23423
+ onModifiersChange({
23424
+ shiftKey: event.shiftKey,
23425
+ altKey: event.altKey
23426
+ });
23427
+ };
23428
+ window.addEventListener("keydown", (event) => {
23429
+ if (isActionActive() && (event.code === "Enter" || event.code === "Escape")) {
23430
+ onCancel();
23431
+ return;
23432
+ }
23433
+ handleModifierKey(event);
23434
+ }, { signal });
23435
+ window.addEventListener("keyup", handleModifierKey, { signal });
23436
+ }
23437
+ //#endregion
23245
23438
  //#region src/actions/rectangle-tool/rectangle-tool.ts
23246
23439
  var WeaveRectangleToolAction = class extends WeaveAction {
23247
23440
  initialized = false;
@@ -23287,16 +23480,7 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23287
23480
  }
23288
23481
  setupEvents() {
23289
23482
  const stage = this.instance.getStage();
23290
- window.addEventListener("keydown", (e) => {
23291
- if (e.code === "Enter" && this.instance.getActiveAction() === "rectangleTool") {
23292
- this.cancelAction();
23293
- return;
23294
- }
23295
- if (e.code === "Escape" && this.instance.getActiveAction() === "rectangleTool") {
23296
- this.cancelAction();
23297
- return;
23298
- }
23299
- }, { signal: this.instance.getEventsController().signal });
23483
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === RECTANGLE_TOOL_ACTION_NAME, () => this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE, () => this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
23300
23484
  stage.on("pointermove", () => {
23301
23485
  if (this.state === RECTANGLE_TOOL_STATE.IDLE) return;
23302
23486
  this.setCursor();
@@ -23322,12 +23506,12 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23322
23506
  this.state = RECTANGLE_TOOL_STATE.ADDING;
23323
23507
  return;
23324
23508
  }
23325
- if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleMovement();
23509
+ if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleMovement(e.evt);
23326
23510
  });
23327
23511
  stage.on("pointerup", (e) => {
23328
23512
  this.pointers.delete(e.evt.pointerId);
23329
23513
  if (this.isTap(e)) this.moved = false;
23330
- if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleSettingSize();
23514
+ if (this.state === RECTANGLE_TOOL_STATE.DEFINING_SIZE) this.handleSettingSize(e.evt);
23331
23515
  });
23332
23516
  this.initialized = true;
23333
23517
  }
@@ -23361,7 +23545,7 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23361
23545
  }
23362
23546
  this.setState(RECTANGLE_TOOL_STATE.DEFINING_SIZE);
23363
23547
  }
23364
- handleSettingSize() {
23548
+ handleSettingSize(modifiers = {}) {
23365
23549
  if (this.rectId && this.tempRectNode && this.clickPoint && this.container) {
23366
23550
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
23367
23551
  const rectPos = {
@@ -23371,10 +23555,14 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23371
23555
  let rectWidth = this.props.width;
23372
23556
  let rectHeight = this.props.height;
23373
23557
  if (this.moved) {
23374
- rectPos.x = Math.min(this.clickPoint.x, mousePoint.x);
23375
- rectPos.y = Math.min(this.clickPoint.y, mousePoint.y);
23376
- rectWidth = Math.abs(this.clickPoint.x - mousePoint.x);
23377
- rectHeight = Math.abs(this.clickPoint.y - mousePoint.y);
23558
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23559
+ centered: modifiers.altKey,
23560
+ constrained: modifiers.shiftKey
23561
+ });
23562
+ rectPos.x = bounds.x;
23563
+ rectPos.y = bounds.y;
23564
+ rectWidth = bounds.width;
23565
+ rectHeight = bounds.height;
23378
23566
  }
23379
23567
  this.tempRectNode.setAttrs({
23380
23568
  ...this.props,
@@ -23406,16 +23594,20 @@ var WeaveRectangleToolAction = class extends WeaveAction {
23406
23594
  }
23407
23595
  this.cancelAction();
23408
23596
  }
23409
- handleMovement() {
23597
+ handleMovement(modifiers = {}) {
23410
23598
  if (this.state !== RECTANGLE_TOOL_STATE.DEFINING_SIZE) return;
23411
23599
  if (this.rectId && this.tempRectNode && this.measureContainer && this.clickPoint) {
23412
23600
  this.moved = true;
23413
23601
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.measureContainer);
23414
- const deltaX = mousePoint.x - this.clickPoint?.x;
23415
- const deltaY = mousePoint.y - this.clickPoint?.y;
23602
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23603
+ centered: modifiers.altKey,
23604
+ constrained: modifiers.shiftKey
23605
+ });
23416
23606
  this.tempRectNode.setAttrs({
23417
- width: deltaX,
23418
- height: deltaY
23607
+ x: bounds.x,
23608
+ y: bounds.y,
23609
+ width: bounds.width,
23610
+ height: bounds.height
23419
23611
  });
23420
23612
  }
23421
23613
  }
@@ -23514,16 +23706,7 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23514
23706
  }
23515
23707
  setupEvents() {
23516
23708
  const stage = this.instance.getStage();
23517
- window.addEventListener("keydown", (e) => {
23518
- if (e.code === "Enter" && this.instance.getActiveAction() === "ellipseTool") {
23519
- this.cancelAction();
23520
- return;
23521
- }
23522
- if (e.code === "Escape" && this.instance.getActiveAction() === "ellipseTool") {
23523
- this.cancelAction();
23524
- return;
23525
- }
23526
- }, { signal: this.instance.getEventsController().signal });
23709
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === ELLIPSE_TOOL_ACTION_NAME, () => this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE, () => this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
23527
23710
  stage.on("pointerdown", (e) => {
23528
23711
  this.setTapStart(e);
23529
23712
  this.pointers.set(e.evt.pointerId, {
@@ -23550,7 +23733,7 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23550
23733
  }
23551
23734
  if (this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE) {
23552
23735
  this.moved = true;
23553
- this.handleMovement();
23736
+ this.handleMovement(e.evt);
23554
23737
  }
23555
23738
  });
23556
23739
  stage.on("pointerup", (e) => {
@@ -23558,7 +23741,7 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23558
23741
  if (this.isTap(e)) this.moved = false;
23559
23742
  if (this.state === ELLIPSE_TOOL_STATE.DEFINING_SIZE) {
23560
23743
  this.creating = false;
23561
- this.handleSettingSize();
23744
+ this.handleSettingSize(e.evt);
23562
23745
  }
23563
23746
  });
23564
23747
  this.initialized = true;
@@ -23582,8 +23765,8 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23582
23765
  if (nodeHandler) {
23583
23766
  const node = nodeHandler.create(this.ellipseId, {
23584
23767
  ...this.props,
23585
- x: this.clickPoint?.x ?? 0 + this.props.radiusX,
23586
- y: this.clickPoint?.y ?? 0 + this.props.radiusY,
23768
+ x: this.clickPoint?.x ?? 0,
23769
+ y: this.clickPoint?.y ?? 0,
23587
23770
  radiusX: 1,
23588
23771
  radiusY: 1
23589
23772
  });
@@ -23591,22 +23774,28 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23591
23774
  }
23592
23775
  this.setState(ELLIPSE_TOOL_STATE.DEFINING_SIZE);
23593
23776
  }
23594
- handleSettingSize() {
23777
+ handleSettingSize(modifiers = {}) {
23595
23778
  const ellipse = this.instance.getStage().findOne(`#${this.ellipseId}`);
23596
23779
  if (this.ellipseId && this.clickPoint && this.container && ellipse) {
23597
23780
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
23598
23781
  const nodeHandler = this.instance.getNodeHandler("ellipse");
23599
- const ellipsePos = {
23782
+ let ellipsePos = {
23600
23783
  x: this.clickPoint.x,
23601
23784
  y: this.clickPoint.y
23602
23785
  };
23603
- let ellipseRadiusX = this.props.radiusY;
23786
+ let ellipseRadiusX = this.props.radiusX;
23604
23787
  let ellipseRadiusY = this.props.radiusY;
23605
23788
  if (this.moved) {
23606
- ellipsePos.x = Math.min(this.clickPoint.x, mousePoint.x);
23607
- ellipsePos.y = Math.min(this.clickPoint.y, mousePoint.y);
23608
- ellipseRadiusX = Math.abs(this.clickPoint.x - mousePoint.x);
23609
- ellipseRadiusY = Math.abs(this.clickPoint.y - mousePoint.y);
23789
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23790
+ centered: modifiers.altKey,
23791
+ constrained: modifiers.shiftKey
23792
+ });
23793
+ ellipsePos = {
23794
+ x: bounds.x,
23795
+ y: bounds.y
23796
+ };
23797
+ ellipseRadiusX = bounds.width;
23798
+ ellipseRadiusY = bounds.height;
23610
23799
  }
23611
23800
  ellipse.setAttrs({
23612
23801
  ...this.props,
@@ -23620,27 +23809,23 @@ var WeaveEllipseToolAction = class extends WeaveAction {
23620
23809
  }
23621
23810
  this.cancelAction();
23622
23811
  }
23623
- handleMovement() {
23812
+ handleMovement(modifiers = {}) {
23624
23813
  if (this.state !== ELLIPSE_TOOL_STATE.DEFINING_SIZE) return;
23625
23814
  const ellipse = this.instance.getStage().findOne(`#${this.ellipseId}`);
23626
23815
  if (this.ellipseId && this.container && this.clickPoint && ellipse) {
23627
23816
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
23628
- const deltaX = Math.abs(mousePoint.x - this.clickPoint?.x);
23629
- const deltaY = Math.abs(mousePoint.y - this.clickPoint?.y);
23630
- const ellipsePos = {
23631
- x: this.clickPoint.x,
23632
- y: this.clickPoint.y
23633
- };
23634
- if (this.moved) {
23635
- ellipsePos.x = Math.min(this.clickPoint.x, mousePoint.x);
23636
- ellipsePos.y = Math.min(this.clickPoint.y, mousePoint.y);
23637
- }
23817
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
23818
+ centered: modifiers.altKey,
23819
+ constrained: modifiers.shiftKey
23820
+ });
23638
23821
  const nodeHandler = this.instance.getNodeHandler("ellipse");
23639
23822
  if (nodeHandler) nodeHandler.onUpdate(ellipse, {
23640
23823
  ...this.props,
23641
23824
  id: this.ellipseId,
23642
- radiusX: deltaX / 2,
23643
- radiusY: deltaY / 2
23825
+ x: bounds.x,
23826
+ y: bounds.y,
23827
+ radiusX: bounds.width / 2,
23828
+ radiusY: bounds.height / 2
23644
23829
  });
23645
23830
  }
23646
23831
  }
@@ -26611,16 +26796,7 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26611
26796
  }
26612
26797
  setupEvents() {
26613
26798
  const stage = this.instance.getStage();
26614
- window.addEventListener("keydown", (e) => {
26615
- if (e.code === "Enter" && this.instance.getActiveAction() === "regularPolygonTool") {
26616
- this.cancelAction();
26617
- return;
26618
- }
26619
- if (e.code === "Escape" && this.instance.getActiveAction() === "regularPolygonTool") {
26620
- this.cancelAction();
26621
- return;
26622
- }
26623
- }, { signal: this.instance.getEventsController().signal });
26799
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === REGULAR_POLYGON_TOOL_ACTION_NAME, () => this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE, () => this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
26624
26800
  stage.on("pointerdown", (e) => {
26625
26801
  this.setTapStart(e);
26626
26802
  this.pointers.set(e.evt.pointerId, {
@@ -26647,7 +26823,7 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26647
26823
  }
26648
26824
  if (this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE) {
26649
26825
  this.moved = true;
26650
- this.handleMovement();
26826
+ this.handleMovement(e.evt);
26651
26827
  }
26652
26828
  });
26653
26829
  stage.on("pointerup", (e) => {
@@ -26655,7 +26831,7 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26655
26831
  if (this.isTap(e)) this.moved = false;
26656
26832
  if (this.state === REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE) {
26657
26833
  this.creating = false;
26658
- this.handleSettingSize();
26834
+ this.handleSettingSize(e.evt);
26659
26835
  }
26660
26836
  });
26661
26837
  this.initialized = true;
@@ -26687,20 +26863,26 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26687
26863
  }
26688
26864
  this.setState(REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE);
26689
26865
  }
26690
- handleSettingSize() {
26866
+ handleSettingSize(modifiers = {}) {
26691
26867
  const regularPolygon = this.instance.getStage().findOne(`#${this.regularPolygonId}`);
26692
26868
  if (this.regularPolygonId && this.clickPoint && this.container && regularPolygon) {
26693
26869
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
26694
26870
  const nodeHandler = this.instance.getNodeHandler("regular-polygon");
26695
- const starPos = {
26871
+ let starPos = {
26696
26872
  x: this.clickPoint.x,
26697
26873
  y: this.clickPoint.y
26698
26874
  };
26699
26875
  let newRadius = this.props.radius;
26700
26876
  if (this.moved) {
26701
- starPos.x = Math.min(this.clickPoint.x, mousePoint.x);
26702
- starPos.y = Math.min(this.clickPoint.y, mousePoint.y);
26703
- newRadius = Math.abs(this.clickPoint.x - mousePoint.x);
26877
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
26878
+ centered: modifiers.altKey,
26879
+ constrained: true
26880
+ });
26881
+ starPos = {
26882
+ x: bounds.x,
26883
+ y: bounds.y
26884
+ };
26885
+ newRadius = bounds.width;
26704
26886
  }
26705
26887
  regularPolygon.setAttrs({
26706
26888
  ...this.props,
@@ -26713,25 +26895,22 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
26713
26895
  }
26714
26896
  this.cancelAction();
26715
26897
  }
26716
- handleMovement() {
26898
+ handleMovement(modifiers = {}) {
26717
26899
  if (this.state !== REGULAR_POLYGON_TOOL_STATE.DEFINING_SIZE) return;
26718
26900
  const regularPolygon = this.instance.getStage().findOne(`#${this.regularPolygonId}`);
26719
26901
  if (this.regularPolygonId && this.container && this.clickPoint && regularPolygon) {
26720
26902
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
26721
- const deltaX = Math.abs(mousePoint.x - this.clickPoint?.x);
26722
- const starPos = {
26723
- x: this.clickPoint.x,
26724
- y: this.clickPoint.y
26725
- };
26726
- if (this.moved) {
26727
- starPos.x = Math.min(this.clickPoint.x, mousePoint.x);
26728
- starPos.y = Math.min(this.clickPoint.y, mousePoint.y);
26729
- }
26903
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
26904
+ centered: modifiers.altKey,
26905
+ constrained: true
26906
+ });
26730
26907
  const nodeHandler = this.instance.getNodeHandler("regular-polygon");
26731
26908
  if (nodeHandler) nodeHandler.onUpdate(regularPolygon, {
26732
26909
  ...this.props,
26733
26910
  id: this.regularPolygonId,
26734
- radius: deltaX / 2
26911
+ x: bounds.x,
26912
+ y: bounds.y,
26913
+ radius: bounds.width / 2
26735
26914
  });
26736
26915
  }
26737
26916
  }
@@ -26780,14 +26959,22 @@ const POLYGON_TOOL_ACTION_NAME = "polygonTool";
26780
26959
  const POLYGON_TOOL_STATE = {
26781
26960
  ["IDLE"]: "idle",
26782
26961
  ["ADDING"]: "adding",
26962
+ ["DEFINING_SIZE"]: "defining-size",
26783
26963
  ["ADDED"]: "added"
26784
26964
  };
26785
26965
  //#endregion
26786
26966
  //#region src/actions/polygon-tool/polygon-tool.ts
26967
+ const MIN_POLYGON_DRAG_DISTANCE = 3;
26787
26968
  var WeavePolygonToolAction = class extends WeaveAction {
26788
26969
  initialized = false;
26789
26970
  state;
26790
26971
  polygonId;
26972
+ pointers;
26973
+ clickPoint;
26974
+ container;
26975
+ moved;
26976
+ placementCommitted;
26977
+ polygonNode;
26791
26978
  cancelAction;
26792
26979
  preset;
26793
26980
  onPropsChange = void 0;
@@ -26801,6 +26988,12 @@ var WeavePolygonToolAction = class extends WeaveAction {
26801
26988
  this.initialized = false;
26802
26989
  this.state = POLYGON_TOOL_STATE.IDLE;
26803
26990
  this.polygonId = null;
26991
+ this.pointers = /* @__PURE__ */ new Map();
26992
+ this.clickPoint = null;
26993
+ this.container = void 0;
26994
+ this.moved = false;
26995
+ this.placementCommitted = false;
26996
+ this.polygonNode = null;
26804
26997
  this.props = this.initProps();
26805
26998
  }
26806
26999
  getName() {
@@ -26825,18 +27018,41 @@ var WeavePolygonToolAction = class extends WeaveAction {
26825
27018
  }
26826
27019
  setupEvents() {
26827
27020
  const stage = this.instance.getStage();
26828
- window.addEventListener("keydown", (e) => {
26829
- if ((e.code === "Enter" || e.code === "Escape") && this.instance.getActiveAction() === "polygonTool") this.cancelAction();
26830
- }, { signal: this.instance.getEventsController().signal });
27021
+ bindShapeDragKeyboardHandlers(this.instance.getEventsController().signal, () => this.instance.getActiveAction() === POLYGON_TOOL_ACTION_NAME, () => this.state === POLYGON_TOOL_STATE.DEFINING_SIZE, () => this.state === POLYGON_TOOL_STATE.DEFINING_SIZE && this.moved, () => this.cancelAction(), (modifiers) => this.handleMovement(modifiers));
26831
27022
  stage.on("pointermove", () => {
26832
27023
  if (this.state === POLYGON_TOOL_STATE.IDLE) return;
26833
27024
  this.setCursor();
26834
27025
  });
26835
27026
  stage.on("pointerdown", (e) => {
26836
27027
  this.setTapStart(e);
27028
+ this.pointers.set(e.evt.pointerId, {
27029
+ x: e.evt.clientX,
27030
+ y: e.evt.clientY
27031
+ });
26837
27032
  if (this.state !== POLYGON_TOOL_STATE.ADDING) return;
26838
27033
  this.handleAdding();
26839
27034
  });
27035
+ stage.on("pointermove", (e) => {
27036
+ if (this.state === POLYGON_TOOL_STATE.IDLE) return;
27037
+ this.setCursor();
27038
+ if (!this.isPressed(e) || !this.pointers.has(e.evt.pointerId)) return;
27039
+ if (this.state === POLYGON_TOOL_STATE.DEFINING_SIZE) {
27040
+ const pointerStart = this.pointers.get(e.evt.pointerId);
27041
+ if (!pointerStart || Math.hypot(e.evt.clientX - pointerStart.x, e.evt.clientY - pointerStart.y) < MIN_POLYGON_DRAG_DISTANCE) return;
27042
+ this.moved = true;
27043
+ this.handleMovement(e.evt);
27044
+ }
27045
+ });
27046
+ stage.on("pointerup", (e) => {
27047
+ this.pointers.delete(e.evt.pointerId);
27048
+ if (this.isTap(e)) this.moved = false;
27049
+ if (this.state === POLYGON_TOOL_STATE.DEFINING_SIZE) this.handleSettingSize(e.evt);
27050
+ });
27051
+ stage.on("pointercancel", (e) => {
27052
+ if (!this.pointers.has(e.evt.pointerId)) return;
27053
+ this.pointers.delete(e.evt.pointerId);
27054
+ if (this.state === POLYGON_TOOL_STATE.DEFINING_SIZE) this.cancelAction();
27055
+ });
26840
27056
  this.initialized = true;
26841
27057
  }
26842
27058
  setState(state) {
@@ -26850,7 +27066,10 @@ var WeavePolygonToolAction = class extends WeaveAction {
26850
27066
  }
26851
27067
  handleAdding() {
26852
27068
  const { mousePoint, container } = this.instance.getMousePointer();
27069
+ this.clickPoint = mousePoint;
27070
+ this.container = container;
26853
27071
  this.polygonId = v4();
27072
+ this.polygonNode = null;
26854
27073
  const presetDef = WEAVE_POLYGON_PRESETS[this.preset];
26855
27074
  const scaleFactor = this.props.scaleFactor ?? 1;
26856
27075
  const { points, innerRect, width, height } = instantiatePreset(presetDef, presetDef.defaultWidth * scaleFactor, presetDef.defaultHeight * scaleFactor);
@@ -26866,15 +27085,49 @@ var WeavePolygonToolAction = class extends WeaveAction {
26866
27085
  points,
26867
27086
  innerRect
26868
27087
  });
27088
+ this.polygonNode = node;
26869
27089
  this.instance.addNode(node, container?.getAttrs().id);
26870
27090
  }
26871
- this.instance.emitEvent("onAddedPolygon");
27091
+ this.setState(POLYGON_TOOL_STATE.DEFINING_SIZE);
27092
+ }
27093
+ handleMovement(modifiers = {}) {
27094
+ if (this.state !== POLYGON_TOOL_STATE.DEFINING_SIZE || !this.polygonId || !this.clickPoint || !this.container) return;
27095
+ const polygon = this.instance.getStage().findOne(`#${this.polygonId}`);
27096
+ const nodeHandler = this.instance.getNodeHandler(WEAVE_POLYGON_NODE_TYPE);
27097
+ const presetDef = WEAVE_POLYGON_PRESETS[this.preset];
27098
+ if (!polygon || !nodeHandler) return;
27099
+ const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.container);
27100
+ const defaultGeometry = instantiatePreset(presetDef, presetDef.defaultWidth, presetDef.defaultHeight);
27101
+ const bounds = getShapeDragBounds(this.clickPoint, mousePoint, {
27102
+ centered: modifiers.altKey,
27103
+ constrained: modifiers.shiftKey,
27104
+ aspectRatio: defaultGeometry.width / defaultGeometry.height
27105
+ });
27106
+ const geometry = instantiatePreset(presetDef, bounds.width * presetDef.defaultWidth / defaultGeometry.width, bounds.height * presetDef.defaultHeight / defaultGeometry.height);
27107
+ nodeHandler.onUpdate(polygon, {
27108
+ ...this.props,
27109
+ id: this.polygonId,
27110
+ x: bounds.x,
27111
+ y: bounds.y,
27112
+ ...geometry
27113
+ });
27114
+ }
27115
+ handleSettingSize(modifiers = {}) {
27116
+ if (this.moved) this.handleMovement(modifiers);
27117
+ const polygon = this.polygonId && this.instance.getStage().findOne(`#${this.polygonId}`);
27118
+ const nodeHandler = this.instance.getNodeHandler(WEAVE_POLYGON_NODE_TYPE);
27119
+ if (polygon && nodeHandler) {
27120
+ this.instance.updateNode(nodeHandler.serialize(polygon));
27121
+ this.placementCommitted = true;
27122
+ this.instance.emitEvent("onAddedPolygon");
27123
+ }
26872
27124
  this.cancelAction();
26873
27125
  }
26874
27126
  trigger(cancelAction, params) {
26875
27127
  if (!this.instance) throw new Error("Instance not defined");
26876
27128
  if (!this.initialized) this.setupEvents();
26877
27129
  this.preset = params?.presetId ?? "pentagon";
27130
+ this.placementCommitted = false;
26878
27131
  const stage = this.instance.getStage();
26879
27132
  stage.container().tabIndex = 1;
26880
27133
  stage.container().focus();
@@ -26887,13 +27140,21 @@ var WeavePolygonToolAction = class extends WeaveAction {
26887
27140
  cleanup() {
26888
27141
  const stage = this.instance.getStage();
26889
27142
  stage.container().style.cursor = "default";
27143
+ const isCancelledPlacement = this.state === POLYGON_TOOL_STATE.DEFINING_SIZE && this.polygonId !== null && !this.placementCommitted;
27144
+ if (isCancelledPlacement && this.polygonNode) this.instance.removeNode(this.polygonNode);
26890
27145
  const selectionPlugin = this.instance.getPlugin("nodesSelection");
26891
27146
  if (selectionPlugin) {
26892
- const node = stage.findOne(`#${this.polygonId}`);
27147
+ const node = isCancelledPlacement ? void 0 : stage.findOne(`#${this.polygonId}`);
26893
27148
  if (node) selectionPlugin.setSelectedNodes([node]);
26894
27149
  this.instance.triggerAction(SELECTION_TOOL_ACTION_NAME);
26895
27150
  }
26896
27151
  this.polygonId = null;
27152
+ this.pointers.clear();
27153
+ this.clickPoint = null;
27154
+ this.container = void 0;
27155
+ this.moved = false;
27156
+ this.placementCommitted = false;
27157
+ this.polygonNode = null;
26897
27158
  this.setState(POLYGON_TOOL_STATE.IDLE);
26898
27159
  }
26899
27160
  setCursor() {
@@ -33284,14 +33545,26 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
33284
33545
  }
33285
33546
  };
33286
33547
  //#endregion
33548
+ //#region src/utils/native-backend.ts
33549
+ const MODULE_NOT_FOUND_CODES = ["ERR_MODULE_NOT_FOUND", "MODULE_NOT_FOUND"];
33550
+ const isMissingPackageError = (error, packageName) => error instanceof Error && MODULE_NOT_FOUND_CODES.includes(error.code ?? "") && error.message.includes(`'${packageName}'`);
33551
+ const loadNativeBackend = async (packageName, setupFunctionName, load) => {
33552
+ try {
33553
+ await load();
33554
+ } catch (error) {
33555
+ if (isMissingPackageError(error, packageName)) throw new Error(`${setupFunctionName}() requires the "${packageName}" package. Install it with \`npm install ${packageName}\`, using the version required by @inditextech/weave-sdk.`, { cause: error });
33556
+ throw error;
33557
+ }
33558
+ };
33559
+ //#endregion
33287
33560
  //#region src/backend.ts
33288
33561
  const setupSkiaBackend = async () => {
33289
33562
  globalThis._weave_serverSideBackend = WEAVE_KONVA_BACKEND.SKIA;
33290
- await import("konva/skia-backend");
33563
+ await loadNativeBackend("skia-canvas", "setupSkiaBackend", () => import("konva/skia-backend"));
33291
33564
  };
33292
33565
  const setupCanvasBackend = async () => {
33293
33566
  globalThis._weave_serverSideBackend = WEAVE_KONVA_BACKEND.CANVAS;
33294
- await import("konva/canvas-backend");
33567
+ await loadNativeBackend("canvas", "setupCanvasBackend", () => import("konva/canvas-backend"));
33295
33568
  };
33296
33569
  //#endregion
33297
33570
  export { ALIGN_NODES_ALIGN_TO, ALIGN_NODES_TOOL_ACTION_NAME, ALIGN_NODES_TOOL_STATE, BRUSH_TOOL_ACTION_NAME, BRUSH_TOOL_DEFAULT_CONFIG, BRUSH_TOOL_STATE, CONNECTOR_TOOL_ACTION_NAME, CONNECTOR_TOOL_DEFAULT_CONFIG, CONNECTOR_TOOL_STATE, COPY_PASTE_NODES_PLUGIN_STATE, DEFAULT_GUIDE_TOOL_ACTION_CONFIG, DEFAULT_SNAPPING_MANAGER_CONFIG, ELLIPSE_TOOL_ACTION_NAME, ELLIPSE_TOOL_STATE, ERASER_TOOL_ACTION_NAME, ERASER_TOOL_STATE, FRAME_TOOL_ACTION_NAME, FRAME_TOOL_STATE, GUIDE_DISTANCE_NAME, GUIDE_DISTANCE_ORIGIN, GUIDE_KIND, GUIDE_NAME, GUIDE_ORIENTATION, GUIDE_STATE, GUIDE_TOOL_ACTION_NAME, GUIDE_TOOL_STATE, LINE_TOOL_ACTION_NAME, LINE_TOOL_DEFAULT_CONFIG, LINE_TOOL_STATE, MEASURE_TOOL_ACTION_NAME, MEASURE_TOOL_STATE, MOVE_ORIENTATION, MOVE_TOOL_ACTION_NAME, MOVE_TOOL_STATE, PEN_TOOL_ACTION_NAME, PEN_TOOL_STATE, POLYGON_TOOL_ACTION_NAME, POLYGON_TOOL_STATE, PREVIEW_HANDOVER_MAX_FRAMES, PREVIEW_ID_SUFFIX, RECTANGLE_TOOL_ACTION_NAME, RECTANGLE_TOOL_STATE, REGULAR_POLYGON_TOOL_ACTION_NAME, REGULAR_POLYGON_TOOL_STATE, SELECTION_TOOL_ACTION_NAME, SELECTION_TOOL_STATE, STAGE_MINIMAP_DEFAULT_CONFIG, STAR_TOOL_ACTION_NAME, STAR_TOOL_STATE, TEXT_LAYOUT, TEXT_TOOL_ACTION_NAME, TEXT_TOOL_STATE, VIDEO_TOOL_ACTION_NAME, VIDEO_TOOL_STATE, WEAVE_ARROW_NODE_TYPE, WEAVE_ARROW_TOOL_ACTION_NAME, WEAVE_ARROW_TOOL_STATE, WEAVE_COMMENTS_RENDERER_KEY, WEAVE_COMMENTS_TOOL_LAYER_ID, WEAVE_COMMENT_CREATE_ACTION, WEAVE_COMMENT_NODE_ACTION, WEAVE_COMMENT_NODE_DEFAULTS, WEAVE_COMMENT_NODE_TYPE, WEAVE_COMMENT_STATUS, WEAVE_COMMENT_TOOL_ACTION_NAME, WEAVE_COMMENT_TOOL_DEFAULT_CONFIG, WEAVE_COMMENT_TOOL_STATE, WEAVE_COMMENT_VIEW_ACTION, WEAVE_CONNECTOR_NODE_ANCHOR_ORIGIN, WEAVE_CONNECTOR_NODE_DECORATOR_TYPE, WEAVE_CONNECTOR_NODE_DEFAULT_CONFIG, WEAVE_CONNECTOR_NODE_LINE_ORIGIN, WEAVE_CONNECTOR_NODE_LINE_TYPE, WEAVE_CONNECTOR_NODE_TYPE, WEAVE_COPY_PASTE_CONFIG_DEFAULT, WEAVE_COPY_PASTE_NODES_KEY, WEAVE_COPY_PASTE_PASTE_CATCHER_ID, WEAVE_COPY_PASTE_PASTE_MODES, WEAVE_DEFAULT_USER_INFO_FUNCTION, WEAVE_ELLIPSE_NODE_TYPE, WEAVE_FRAME_DEFAULT_BACKGROUND_COLOR, WEAVE_FRAME_NODE_DEFAULT_CONFIG, WEAVE_FRAME_NODE_DEFAULT_PROPS, WEAVE_FRAME_NODE_TYPE, WEAVE_GRID_DEFAULT_CONFIG, WEAVE_GRID_DOT_TYPES, WEAVE_GRID_LAYER_ID, WEAVE_GRID_TYPES, WEAVE_GROUP_NODE_TYPE, WEAVE_IMAGES_TOOL_ACTION_NAME, WEAVE_IMAGES_TOOL_DEFAULT_CONFIG, WEAVE_IMAGES_TOOL_STATE, WEAVE_IMAGES_TOOL_UPLOAD_TYPE, WEAVE_IMAGE_CROP_ANCHOR_POSITION, WEAVE_IMAGE_CROP_END_TYPE, WEAVE_IMAGE_DEFAULT_CONFIG, WEAVE_IMAGE_NODE_TYPE, WEAVE_IMAGE_TOOL_ACTION_NAME, WEAVE_IMAGE_TOOL_CONFIG_DEFAULT, WEAVE_IMAGE_TOOL_STATE, WEAVE_IMAGE_TOOL_UPLOAD_TYPE, WEAVE_LAYER_NODE_TYPE, WEAVE_LINE_NODE_DEFAULT_CONFIG, WEAVE_LINE_NODE_TYPE, WEAVE_MEASURE_NODE_DEFAULT_CONFIG, WEAVE_MEASURE_NODE_TYPE, WEAVE_MEASURE_TOOL_DEFAULT_CONFIG, WEAVE_NODES_MULTI_SELECTION_FEEDBACK_PLUGIN_DEFAULT_CONFIG, WEAVE_NODES_MULTI_SELECTION_FEEDBACK_PLUGIN_KEY, WEAVE_NODES_MULTI_SELECTION_FEEDBACK_PLUGIN_LAYER_ID, WEAVE_NODES_SELECTION_DEFAULT_CONFIG, WEAVE_NODES_SELECTION_KEY, WEAVE_NODES_SELECTION_LAYER_ID, WEAVE_NODES_SNAPPING_PLUGIN_KEY, WEAVE_POLYGON_NODE_TYPE, WEAVE_POLYGON_PRESETS, WEAVE_RECTANGLE_NODE_TYPE, WEAVE_REGULAR_POLYGON_NODE_TYPE, WEAVE_SHAPE_LABEL_DEFAULTS, WEAVE_STAGE_DEFAULT_MODE, WEAVE_STAGE_DROP_AREA_KEY, WEAVE_STAGE_GRID_PLUGIN_KEY, WEAVE_STAGE_IMAGE_CROPPING_MODE, WEAVE_STAGE_KEYBOARD_MOVE_DEFAULT_CONFIG, WEAVE_STAGE_KEYBOARD_MOVE_KEY, WEAVE_STAGE_KEYBOARD_MOVE_ORIENTATION, WEAVE_STAGE_MINIMAP_KEY, WEAVE_STAGE_NODE_TYPE, WEAVE_STAGE_PANNING_DEFAULT_CONFIG, WEAVE_STAGE_PANNING_KEY, WEAVE_STAGE_PANNING_THROTTLE_MS, WEAVE_STAGE_SHAPE_LABEL_EDITION_MODE, WEAVE_STAGE_TEXT_EDITION_MODE, WEAVE_STAGE_ZOOM_DEFAULT_CONFIG, WEAVE_STAGE_ZOOM_KEY, WEAVE_STAGE_ZOOM_TYPE, WEAVE_STAR_NODE_TYPE, WEAVE_STROKE_NODE_DEFAULT_CONFIG, WEAVE_STROKE_NODE_TYPE, WEAVE_STROKE_SINGLE_NODE_DEFAULT_CONFIG, WEAVE_STROKE_SINGLE_NODE_TIP_SIDE, WEAVE_STROKE_SINGLE_NODE_TIP_TYPE, WEAVE_STROKE_SINGLE_NODE_TYPE, WEAVE_STROKE_TOOL_ACTION_NAME, WEAVE_STROKE_TOOL_ACTION_NAME_ALIASES, WEAVE_STROKE_TOOL_DEFAULT_CONFIG, WEAVE_STROKE_TOOL_STATE, WEAVE_TEXT_LINK_ALLOWED_PROTOCOLS, WEAVE_TEXT_NODE_DEFAULT_CONFIG, WEAVE_TEXT_NODE_TYPE, WEAVE_USERS_POINTERS_CONFIG_DEFAULT_PROPS, WEAVE_USERS_POINTERS_KEY, WEAVE_USERS_PRESENCE_CONFIG_DEFAULT_PROPS, WEAVE_USERS_PRESENCE_PLUGIN_KEY, WEAVE_USERS_SELECTION_KEY, WEAVE_USER_POINTER_KEY, WEAVE_USER_PRESENCE_KEY, WEAVE_USER_SELECTION_KEY, WEAVE_VIDEO_DEFAULT_CONFIG, WEAVE_VIDEO_NODE_TYPE, Weave, WeaveAction, WeaveAlignNodesToolAction, WeaveArrowNode, WeaveArrowToolAction, WeaveBrushToolAction, WeaveCommentNode, WeaveCommentToolAction, WeaveCommentsRendererPlugin, WeaveConnectedUsersPlugin, WeaveConnectorNode, WeaveConnectorToolAction, WeaveContextMenuPlugin, WeaveCopyPasteNodesPlugin, WeaveEllipseNode, WeaveEllipseToolAction, WeaveEraserToolAction, WeaveExportNodesToolAction, WeaveExportStageToolAction, WeaveFitToScreenToolAction, WeaveFitToSelectionToolAction, WeaveFrameNode, WeaveFrameToolAction, WeaveGroupNode, WeaveGuideToolAction, WeaveImageNode, WeaveImageToolAction, WeaveImagesToolAction, WeaveLayerNode, WeaveLineNode, WeaveLineToolAction, WeaveMeasureNode, WeaveMeasureToolAction, WeaveMoveToolAction, WeaveNode, WeaveNodesMultiSelectionFeedbackPlugin, WeaveNodesSelectionPlugin, WeaveNodesSnappingPlugin, WeavePenToolAction, WeavePlugin, WeavePolygonNode, WeavePolygonToolAction, WeaveRectangleNode, WeaveRectangleToolAction, WeaveRegularPolygonNode, WeaveRegularPolygonToolAction, WeaveRenderer, WeaveSelectionToolAction, WeaveStageDropAreaPlugin, WeaveStageGridPlugin, WeaveStageKeyboardMovePlugin, WeaveStageMinimapPlugin, WeaveStageNode, WeaveStagePanningPlugin, WeaveStageResizePlugin, WeaveStageZoomPlugin, WeaveStarNode, WeaveStarToolAction, WeaveStateManipulation, WeaveStore, WeaveStrokeNode, WeaveStrokeSingleNode, WeaveStrokeToolAction, WeaveTextNode, WeaveTextToolAction, WeaveUsersPointersPlugin, WeaveUsersPresencePlugin, WeaveUsersSelectionPlugin, WeaveVideoNode, WeaveVideoToolAction, WeaveZoomInToolAction, WeaveZoomOutToolAction, buildAncestorGroupIds, canComposite, clearContainerTargets, computeEllipseLabelMinSize, computePolygonLabelMinSize, computeRectangleLabelMinSize, containerOverCursor, containsNodeDeep, defaultInitialState, downscaleImageFile, downscaleImageFromURL, getBoundingBox, getDownscaleRatio, getExportBoundingBox, getImageSizeFromFile, getSelectedNodesMetadata, getShapeLabelSchemaFields, getStageClickPoint, getTargetAndSkipNodes, getTargetedNode, getTopmostShadowHost, getVisibleNodes, getVisibleNodesInViewport, hasFrames, hasImages, instantiatePreset, intersectArrays, isIOS, isInShadowDOM, isNodeInSelection, isNumber, isServer, labelId, loadImageSource, memoize, mergeExceptArrays, moveNodeToContainer, moveNodeToContainerNT, resetScale, setupCanvasBackend, setupSkiaBackend, sleep, spreadLabelProps };