@inditextech/weave-sdk 5.0.0-SNAPSHOT.366.1 → 5.0.0-SNAPSHOT.397.1

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
@@ -4355,6 +4355,7 @@ var TransformerController = class {
4355
4355
  listening: true,
4356
4356
  shouldOverdrawWholeArea: true
4357
4357
  });
4358
+ this.tr.boundBoxFunc(this.getBoundBoxFunc());
4358
4359
  layer.add(this.tr);
4359
4360
  this.trHover = new Konva.Transformer({
4360
4361
  id: "hoverTransformer",
@@ -4370,6 +4371,20 @@ var TransformerController = class {
4370
4371
  this.registerTransformerEvents();
4371
4372
  this.registerInstanceEvents();
4372
4373
  }
4374
+ getBoundBoxFunc() {
4375
+ return (oldBox, newBox) => {
4376
+ const sx = newBox.width / oldBox.width;
4377
+ const sy = newBox.height / oldBox.height;
4378
+ const violatesConstraint = this.tr.nodes().some((node) => {
4379
+ const rect = node.getClientRect({ skipStroke: true });
4380
+ const { width: minWidth, height: minHeight } = node.getNodeMinSize();
4381
+ if (["middle-right", "middle-left"].includes(this.tr.getActiveAnchor() ?? "")) return rect.width * sx < minWidth;
4382
+ if (["top-center", "bottom-center"].includes(this.tr.getActiveAnchor() ?? "")) return rect.height * sy < minHeight;
4383
+ return rect.width * sx < minWidth || rect.height * sy < minHeight;
4384
+ });
4385
+ return violatesConstraint ? oldBox : newBox;
4386
+ };
4387
+ }
4373
4388
  getTransformer() {
4374
4389
  return this.tr;
4375
4390
  }
@@ -4834,9 +4849,10 @@ function handleClickOrTap(ctx, e) {
4834
4849
  const isMainLayer = parent === mainLayer;
4835
4850
  const isContainerEmptyArea = e.target.getAttrs().isContainerPrincipal !== void 0 && !e.target.getAttrs().isContainerPrincipal;
4836
4851
  if (isStage || isMainLayer || isContainerEmptyArea) ctx.setSelectedNodes([]);
4852
+ ctx.triggerSelectedNodesEvent();
4837
4853
  return;
4838
4854
  }
4839
- if (nodeTargeted.getAttrs().nodeId) {
4855
+ if (!nodeTargeted.getAttrs().name?.includes("node") && nodeTargeted.getAttrs().nodeId) {
4840
4856
  const realNode = stage.findOne(`#${nodeTargeted.getAttrs().nodeId}`);
4841
4857
  if (realNode) nodeTargeted = realNode;
4842
4858
  }
@@ -4949,6 +4965,7 @@ function handlePointerDown(ctx, e) {
4949
4965
  for (const node of nodesSelected) node.fire("onSelectionCleared", { bubbles: true });
4950
4966
  }
4951
4967
  ctx.selectNone();
4968
+ ctx.triggerSelectedNodesEvent();
4952
4969
  ctx.getWeaveInstance().emitEvent("onSelectionState", true);
4953
4970
  ctx.getEdgePanning().start();
4954
4971
  }
@@ -5431,6 +5448,9 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
5431
5448
  isDragging() {
5432
5449
  return this.transformerCtrl.isDragging();
5433
5450
  }
5451
+ getBoundBoxFunc() {
5452
+ return this.transformerCtrl.getBoundBoxFunc();
5453
+ }
5434
5454
  getSelectorConfig() {
5435
5455
  return this.config.selection;
5436
5456
  }
@@ -6005,6 +6025,12 @@ const augmentKonvaNodeClass = (config) => {
6005
6025
  };
6006
6026
  Konva.Node.prototype.lockMutex = function() {};
6007
6027
  Konva.Node.prototype.releaseMutex = function() {};
6028
+ Konva.Node.prototype.getNodeMinSize = function() {
6029
+ return {
6030
+ width: 0,
6031
+ height: 0
6032
+ };
6033
+ };
6008
6034
  };
6009
6035
  var WeaveNode = class {
6010
6036
  async register(instance) {
@@ -6168,8 +6194,8 @@ var WeaveNode = class {
6168
6194
  if (selectionPlugin?.getSelectedNodes().map((node) => node.getAttrs().id).includes(ele.getAttrs().id)) return true;
6169
6195
  return false;
6170
6196
  }
6171
- scaleReset(node) {
6172
- const scale = node.scale();
6197
+ scaleReset(node, scaleCustom) {
6198
+ const scale = scaleCustom ?? node.scale();
6173
6199
  node.width(Math.max(5, node.width() * scale.x));
6174
6200
  node.height(Math.max(5, node.height() * scale.y));
6175
6201
  node.scale({
@@ -6259,8 +6285,6 @@ var WeaveNode = class {
6259
6285
  if (e.target.getAttrs()._revertStrokeScaleEnabled === true) e.target.setAttr("strokeScaleEnabled", true);
6260
6286
  e.target.setAttr("_revertStrokeScaleEnabled", void 0);
6261
6287
  this.instance.emitEvent("onTransform", null);
6262
- const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
6263
- if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
6264
6288
  if (performScaleReset) this.scaleReset(node$1);
6265
6289
  if (this.getSelectionPlugin()?.getSelectedNodes().length === 1) {
6266
6290
  this.getNodesSelectionFeedbackPlugin()?.showSelectionHalo(node$1);
@@ -6269,8 +6293,13 @@ var WeaveNode = class {
6269
6293
  const nodeHandler = this.instance.getNodeHandler(node$1.getAttrs().nodeType);
6270
6294
  if (nodeHandler) {
6271
6295
  const shouldUpdateOnTransform = node$1.getAttrs().shouldUpdateOnTransform ?? true;
6272
- if (shouldUpdateOnTransform) this.instance.updateNode(nodeHandler.serialize(node$1));
6296
+ if (shouldUpdateOnTransform) {
6297
+ const serializedNode = nodeHandler.serialize(node$1);
6298
+ this.instance.updateNode(serializedNode);
6299
+ }
6273
6300
  }
6301
+ const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
6302
+ if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
6274
6303
  this.getNodesSelectionPlugin()?.getHoverTransformer().forceUpdate();
6275
6304
  });
6276
6305
  const stage = this.instance.getStage();
@@ -8415,7 +8444,11 @@ var WeaveGroupsManager = class {
8415
8444
  this.instance.removeNodes(sortedNodesByZIndex);
8416
8445
  groupInstance.destroy();
8417
8446
  const groupNode = stage.findOne(`#${groupId}`);
8418
- if (groupHandler && groupNode) this.instance.updateNodeNT(groupHandler.serialize(groupNode));
8447
+ if (groupHandler && groupNode) {
8448
+ groupNode.x(0);
8449
+ groupNode.y(0);
8450
+ this.instance.updateNodeNT(groupHandler.serialize(groupNode));
8451
+ }
8419
8452
  setTimeout(() => {
8420
8453
  this.getNodesMultiSelectionFeedbackPlugin()?.cleanupSelectedHalos();
8421
8454
  const groupNode$1 = stage.findOne(`#${groupId}`);
@@ -8477,6 +8510,8 @@ var WeaveGroupsManager = class {
8477
8510
  y: absScale.y / stage.scaleY()
8478
8511
  });
8479
8512
  child.rotation(absRotation);
8513
+ const nodeHandler = this.instance.getNodeHandler(child.getAttrs().nodeType);
8514
+ if (nodeHandler) nodeHandler.scaleReset(child);
8480
8515
  child.zIndex(newLayerChildrenAmount - 1 + child.zIndex());
8481
8516
  child.setAttr("draggable", true);
8482
8517
  newChildId = child.getAttrs().id;
@@ -9287,6 +9322,7 @@ var WeaveStateManager = class {
9287
9322
  }
9288
9323
  const yjsProps = yjsNode.get("props");
9289
9324
  this.updateYjsMapFromObject(yjsProps, node.props);
9325
+ if (Array.isArray(node.props.children) && node.props.children.length > 0) for (const child of node.props.children) this.updateNode(child);
9290
9326
  this.instance.emitEvent("onNodeUpdated", node);
9291
9327
  }
9292
9328
  updateNodes(nodes) {
@@ -9481,7 +9517,7 @@ var WeaveRegisterManager = class {
9481
9517
 
9482
9518
  //#endregion
9483
9519
  //#region package.json
9484
- var version = "5.0.0-SNAPSHOT.366.1";
9520
+ var version = "5.0.0-SNAPSHOT.397.1";
9485
9521
 
9486
9522
  //#endregion
9487
9523
  //#region src/managers/setup.ts
@@ -11870,10 +11906,36 @@ var WeaveGroupNode = class extends WeaveNode {
11870
11906
  return intersectArrays(anchorsArrays);
11871
11907
  };
11872
11908
  this.setupDefaultNodeEvents(group);
11909
+ group.on("transform", () => {
11910
+ const sx = group.scaleX();
11911
+ const sy = group.scaleY();
11912
+ group.getChildren().forEach((child) => {
11913
+ child.scaleX(child.scaleX() * sx);
11914
+ child.scaleY(child.scaleY() * sy);
11915
+ child.x(child.x() * sx);
11916
+ child.y(child.y() * sy);
11917
+ const nodeHandler = this.instance.getNodeHandler(child.getAttrs().nodeType);
11918
+ if (nodeHandler) {
11919
+ nodeHandler.scaleReset(child);
11920
+ nodeHandler.onUpdate(child, child.getAttrs());
11921
+ }
11922
+ });
11923
+ group.scale({
11924
+ x: 1,
11925
+ y: 1
11926
+ });
11927
+ });
11873
11928
  return group;
11874
11929
  }
11875
11930
  onUpdate(nodeInstance, nextProps) {
11876
- nodeInstance.setAttrs({ ...nextProps });
11931
+ nodeInstance.setAttrs({
11932
+ ...nextProps,
11933
+ x: nextProps.x ?? 0,
11934
+ y: nextProps.y ?? 0,
11935
+ scaleX: nextProps.scaleX ?? 1,
11936
+ scaleY: nextProps.scaleY ?? 1,
11937
+ rotation: nextProps.rotation ?? 0
11938
+ });
11877
11939
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
11878
11940
  if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
11879
11941
  }
@@ -11910,18 +11972,571 @@ var WeaveGroupNode = class extends WeaveNode {
11910
11972
  }
11911
11973
  };
11912
11974
  }
11913
- scaleReset() {}
11975
+ scaleReset(node) {
11976
+ const sx = node.scaleX();
11977
+ const sy = node.scaleY();
11978
+ node.getChildren().forEach((child) => {
11979
+ child.scaleX(child.scaleX() * sx);
11980
+ child.scaleY(child.scaleY() * sy);
11981
+ child.x(child.x() * sx);
11982
+ child.y(child.y() * sy);
11983
+ const nodeHandler = this.instance.getNodeHandler(child.getAttrs().nodeType);
11984
+ if (nodeHandler) {
11985
+ nodeHandler.scaleReset(child);
11986
+ nodeHandler.onUpdate(child, child.getAttrs());
11987
+ }
11988
+ });
11989
+ node.scale({
11990
+ x: 1,
11991
+ y: 1
11992
+ });
11993
+ }
11914
11994
  };
11915
11995
 
11916
11996
  //#endregion
11917
11997
  //#region src/nodes/rectangle/constants.ts
11918
11998
  const WEAVE_RECTANGLE_NODE_TYPE = "rectangle";
11919
11999
 
12000
+ //#endregion
12001
+ //#region src/nodes/shared/shape-label.constants.ts
12002
+ const WEAVE_STAGE_SHAPE_LABEL_EDITION_MODE = "shape-label-edition";
12003
+ const WEAVE_SHAPE_LABEL_DEFAULTS = {
12004
+ labelText: "",
12005
+ labelFontFamily: "Arial, sans-serif",
12006
+ labelFontSize: 14,
12007
+ labelFontStyle: "normal",
12008
+ labelFontVariant: "normal",
12009
+ labelTextDecoration: "",
12010
+ labelFill: "#000000",
12011
+ labelAlign: "center",
12012
+ labelVerticalAlign: "middle",
12013
+ labelLetterSpacing: 0,
12014
+ labelLineHeight: 1,
12015
+ labelPaddingX: 8,
12016
+ labelPaddingY: 8
12017
+ };
12018
+ const labelId = (id) => `${id}-label`;
12019
+
12020
+ //#endregion
12021
+ //#region src/nodes/shared/shape-label-editor.ts
12022
+ var WeaveShapeLabelEditor = class {
12023
+ editing = false;
12024
+ editingGroup = null;
12025
+ editingTextBounds = null;
12026
+ textArea = null;
12027
+ onLiveResize = null;
12028
+ constructor(instance) {
12029
+ this.instance = instance;
12030
+ }
12031
+ isEditing() {
12032
+ return this.editing;
12033
+ }
12034
+ renderLabel(group, props, textBounds) {
12035
+ const labelText = props.labelText ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelText;
12036
+ const labelNode = new Konva.Text({
12037
+ id: labelId(props.id),
12038
+ x: textBounds.x,
12039
+ y: textBounds.y,
12040
+ width: textBounds.width,
12041
+ height: textBounds.height,
12042
+ text: labelText,
12043
+ fontFamily: props.labelFontFamily ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontFamily,
12044
+ fontSize: props.labelFontSize ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontSize,
12045
+ fontStyle: props.labelFontStyle ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontStyle,
12046
+ fontVariant: props.labelFontVariant ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontVariant,
12047
+ textDecoration: props.labelTextDecoration ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelTextDecoration,
12048
+ fill: props.labelFill ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFill,
12049
+ align: props.labelAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelAlign,
12050
+ verticalAlign: props.labelVerticalAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelVerticalAlign,
12051
+ letterSpacing: props.labelLetterSpacing ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelLetterSpacing,
12052
+ lineHeight: props.labelLineHeight ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelLineHeight,
12053
+ wrap: "word",
12054
+ listening: false,
12055
+ visible: labelText !== ""
12056
+ });
12057
+ group.add(labelNode);
12058
+ return labelNode;
12059
+ }
12060
+ updateLabel(group, nextProps, textBounds, growCallback) {
12061
+ const labelNode = group.findOne(`#${labelId(nextProps.id)}`);
12062
+ if (!labelNode) return;
12063
+ const labelText = nextProps.labelText ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelText;
12064
+ labelNode.setAttrs({
12065
+ x: textBounds.x,
12066
+ y: textBounds.y,
12067
+ width: textBounds.width,
12068
+ height: textBounds.height,
12069
+ text: labelText,
12070
+ fontFamily: nextProps.labelFontFamily ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontFamily,
12071
+ fontSize: nextProps.labelFontSize ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontSize,
12072
+ fontStyle: nextProps.labelFontStyle ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontStyle,
12073
+ fontVariant: nextProps.labelFontVariant ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontVariant,
12074
+ textDecoration: nextProps.labelTextDecoration ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelTextDecoration,
12075
+ fill: nextProps.labelFill ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFill,
12076
+ align: nextProps.labelAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelAlign,
12077
+ verticalAlign: nextProps.labelVerticalAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelVerticalAlign,
12078
+ letterSpacing: nextProps.labelLetterSpacing ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelLetterSpacing,
12079
+ lineHeight: nextProps.labelLineHeight ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelLineHeight,
12080
+ wrap: "word",
12081
+ visible: !this.editing && labelText !== ""
12082
+ });
12083
+ if (labelText !== "") {
12084
+ labelNode.setAttr("height", void 0);
12085
+ const measuredHeight = labelNode.height();
12086
+ labelNode.height(Math.max(textBounds.height, measuredHeight));
12087
+ if (growCallback && measuredHeight > textBounds.height) {
12088
+ const paddingY = nextProps.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12089
+ growCallback(measuredHeight + paddingY * 2);
12090
+ }
12091
+ }
12092
+ }
12093
+ computeVerticalOffset(verticalAlign, boundsHeightPx, contentHeightPx) {
12094
+ if (verticalAlign === "top") return 0;
12095
+ if (verticalAlign === "bottom") return Math.max(0, boundsHeightPx - contentHeightPx);
12096
+ return Math.max(0, (boundsHeightPx - contentHeightPx) / 2);
12097
+ }
12098
+ triggerEditMode(group, textBounds, onCommit, onLiveResize) {
12099
+ if (this.editing) return;
12100
+ const lockAcquired = this.instance.setMutexLock({
12101
+ nodeIds: [group.id()],
12102
+ operation: "label-edit"
12103
+ });
12104
+ if (!lockAcquired) return;
12105
+ this.editing = true;
12106
+ this.editingGroup = group;
12107
+ this.editingTextBounds = textBounds;
12108
+ this.onLiveResize = onLiveResize ?? null;
12109
+ const labelNode = group.findOne(`#${labelId(group.id())}`);
12110
+ if (labelNode) labelNode.visible(false);
12111
+ const selectionPlugin = this.instance.getPlugin("nodesSelection");
12112
+ if (selectionPlugin) {
12113
+ const tr = selectionPlugin.getTransformer();
12114
+ this.instance.disablePlugin("nodesSelection");
12115
+ tr.hide();
12116
+ }
12117
+ const stage = this.instance.getStage();
12118
+ const upscaleScale = stage.getAttr("upscaleScale") ?? 1;
12119
+ const absoluteTransform = group.getAbsoluteTransform();
12120
+ const topLeft = absoluteTransform.point({
12121
+ x: textBounds.x,
12122
+ y: textBounds.y
12123
+ });
12124
+ this.createTextAreaDOM(group, textBounds, topLeft, upscaleScale, onCommit, onLiveResize);
12125
+ this.instance.getStage().mode(WEAVE_STAGE_SHAPE_LABEL_EDITION_MODE);
12126
+ }
12127
+ exitEditMode() {
12128
+ if (!this.editing) return;
12129
+ this.instance.releaseMutexLock();
12130
+ this.instance.getStage().mode(WEAVE_STAGE_DEFAULT_MODE);
12131
+ this.editing = false;
12132
+ if (this.editingGroup) {
12133
+ const liveGroup = this.instance.getStage().findOne(`#${this.editingGroup.id()}`);
12134
+ const labelNode = liveGroup?.findOne(`#${labelId(this.editingGroup.id())}`);
12135
+ if (labelNode) {
12136
+ labelNode.visible(true);
12137
+ labelNode.getLayer()?.batchDraw();
12138
+ }
12139
+ this.editingGroup = null;
12140
+ }
12141
+ if (this.textArea) this.textArea.remove();
12142
+ this.textArea = null;
12143
+ this.onLiveResize = null;
12144
+ this.editingTextBounds = null;
12145
+ this.instance.getStage().off(".weaveLabelEdit");
12146
+ const selectionPlugin = this.instance.getPlugin("nodesSelection");
12147
+ if (selectionPlugin) this.instance.enablePlugin("nodesSelection");
12148
+ }
12149
+ updateTextAreaPosition(group, textBounds) {
12150
+ if (!this.editing || !this.textArea) return;
12151
+ const stage = this.instance.getStage();
12152
+ const upscaleScale = stage.getAttr("upscaleScale") ?? 1;
12153
+ const absoluteTransform = group.getAbsoluteTransform();
12154
+ const topLeft = absoluteTransform.point({
12155
+ x: textBounds.x,
12156
+ y: textBounds.y
12157
+ });
12158
+ this.textArea.style.left = `${topLeft.x * upscaleScale}px`;
12159
+ const absScale = group.getAbsoluteScale();
12160
+ const props = group.getAttrs();
12161
+ const fontSize = (props.labelFontSize ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontSize) * absScale.x;
12162
+ this.textArea.style.fontSize = `${fontSize * upscaleScale}px`;
12163
+ const textWidth = textBounds.width * absScale.x;
12164
+ this.textArea.style.width = `${textWidth * upscaleScale}px`;
12165
+ const originalBoundsHeightPx = textBounds.height * absScale.y * upscaleScale;
12166
+ const paddingY = props.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12167
+ const verticalAlign = props.labelVerticalAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelVerticalAlign;
12168
+ this.textArea.style.height = "auto";
12169
+ const contentHeightPx = this.textArea.scrollHeight;
12170
+ if (contentHeightPx <= originalBoundsHeightPx) {
12171
+ this.textArea.style.height = `${contentHeightPx}px`;
12172
+ const offsetY = this.computeVerticalOffset(verticalAlign, originalBoundsHeightPx, contentHeightPx);
12173
+ this.textArea.style.top = `${topLeft.y * upscaleScale + offsetY}px`;
12174
+ } else {
12175
+ this.textArea.style.height = `${contentHeightPx}px`;
12176
+ this.textArea.style.top = `${topLeft.y * upscaleScale}px`;
12177
+ }
12178
+ if (this.onLiveResize) {
12179
+ const contentHeightInCanvas = contentHeightPx / (absScale.y * upscaleScale);
12180
+ this.onLiveResize(contentHeightInCanvas + paddingY * 2);
12181
+ }
12182
+ }
12183
+ /**
12184
+ * Updates the textarea `left`, `width`, and `top` to match new text bounds.
12185
+ * Call this from an `onLiveResize` callback when the shape grows symmetrically
12186
+ * (e.g. regular polygon) so the textarea tracks the new position on all axes.
12187
+ * Does NOT call `onLiveResize` — there is no re-entrancy risk, but also no need.
12188
+ */
12189
+ repositionTextArea(group, textBounds) {
12190
+ if (!this.editing || !this.textArea) return;
12191
+ this.editingTextBounds = textBounds;
12192
+ const stage = this.instance.getStage();
12193
+ const upscaleScale = stage.getAttr("upscaleScale") ?? 1;
12194
+ const absoluteTransform = group.getAbsoluteTransform();
12195
+ const topLeft = absoluteTransform.point({
12196
+ x: textBounds.x,
12197
+ y: textBounds.y
12198
+ });
12199
+ const absScale = group.getAbsoluteScale();
12200
+ const textWidth = textBounds.width * absScale.x;
12201
+ this.textArea.style.left = `${topLeft.x * upscaleScale}px`;
12202
+ this.textArea.style.width = `${textWidth * upscaleScale}px`;
12203
+ const newBoundsHeightPx = textBounds.height * absScale.y * upscaleScale;
12204
+ const savedHeight = this.textArea.style.height;
12205
+ this.textArea.style.height = "auto";
12206
+ const actualContentHeightPx = this.textArea.scrollHeight;
12207
+ this.textArea.style.height = savedHeight;
12208
+ const groupProps = group.getAttrs();
12209
+ const verticalAlign = groupProps.labelVerticalAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelVerticalAlign;
12210
+ const offsetY = this.computeVerticalOffset(verticalAlign, newBoundsHeightPx, actualContentHeightPx);
12211
+ this.textArea.style.top = `${topLeft.y * upscaleScale + offsetY}px`;
12212
+ }
12213
+ /**
12214
+ * Convergence loop: onLiveResize may change the textarea width (e.g. a
12215
+ * growing polygon becomes wider), which changes line-wrapping, which may
12216
+ * require a different shape height. Iterates until scrollHeight is stable
12217
+ * or a safety limit is reached (5 passes cover any practical input).
12218
+ *
12219
+ * Oscillation prevention: if the sequence alternates (narrow→grow→wide→
12220
+ * restore→narrow→…) the loop exits with the polygon under-sized.
12221
+ * We track `lastUsedPx` — the height last passed to onLiveResize — and
12222
+ * fire a final corrective grow whenever `prevHeightPx > lastUsedPx`
12223
+ * (content at the current width still overflows what was last asked for).
12224
+ */
12225
+ runLiveResizeLoop(onLiveResize, contentHeightPx, effectiveScale, upscaleScale, paddingY) {
12226
+ const MAX_PASSES = 5;
12227
+ let maxNeededPx = contentHeightPx;
12228
+ let prevHeightPx = contentHeightPx;
12229
+ let lastUsedPx = 0;
12230
+ for (let pass = 0; pass < MAX_PASSES; pass++) {
12231
+ lastUsedPx = prevHeightPx;
12232
+ const neededInCanvas = prevHeightPx / (effectiveScale * upscaleScale);
12233
+ onLiveResize(neededInCanvas + paddingY * 2);
12234
+ if (!this.textArea) break;
12235
+ this.textArea.style.height = "auto";
12236
+ const measuredPx = this.textArea.scrollHeight;
12237
+ this.textArea.style.height = `${measuredPx}px`;
12238
+ if (measuredPx > maxNeededPx) maxNeededPx = measuredPx;
12239
+ if (measuredPx === prevHeightPx) break;
12240
+ prevHeightPx = measuredPx;
12241
+ }
12242
+ if (this.textArea && prevHeightPx > lastUsedPx) {
12243
+ const finalInCanvas = maxNeededPx / (effectiveScale * upscaleScale);
12244
+ onLiveResize(finalInCanvas + paddingY * 2);
12245
+ this.textArea.style.height = "auto";
12246
+ const finalPx = this.textArea.scrollHeight;
12247
+ this.textArea.style.height = `${finalPx}px`;
12248
+ }
12249
+ }
12250
+ createTextAreaDOM(group, textBounds, position, upscaleScale, onCommit, onLiveResize) {
12251
+ const stage = this.instance.getStage();
12252
+ const props = group.getAttrs();
12253
+ const absScale = group.getAbsoluteScale();
12254
+ const effectiveScale = absScale.x;
12255
+ this.textArea = document.createElement("textarea");
12256
+ this.textArea.id = `${group.id()}_label_textarea`;
12257
+ this.textArea.rows = 1;
12258
+ stage.container().appendChild(this.textArea);
12259
+ stage.on("dragmove.weaveLabelEdit xChange.weaveLabelEdit yChange.weaveLabelEdit", () => {
12260
+ if (this.editingGroup && this.editingTextBounds) this.repositionTextArea(this.editingGroup, this.editingTextBounds);
12261
+ });
12262
+ const textWidth = textBounds.width * effectiveScale;
12263
+ const fontSize = (props.labelFontSize ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontSize) * effectiveScale;
12264
+ const originalBoundsHeightPx = textBounds.height * effectiveScale * upscaleScale;
12265
+ const paddingY = props.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12266
+ this.textArea.style.position = "absolute";
12267
+ this.textArea.style.visibility = "hidden";
12268
+ this.textArea.style.left = `${position.x * upscaleScale}px`;
12269
+ this.textArea.style.width = `${textWidth * upscaleScale}px`;
12270
+ this.textArea.style.border = "solid 0px #1e40af";
12271
+ this.textArea.style.background = "transparent";
12272
+ this.textArea.style.backgroundColor = "transparent";
12273
+ this.textArea.style.boxSizing = "border-box";
12274
+ this.textArea.style.overflow = "hidden";
12275
+ const rotation = group.getAbsoluteRotation();
12276
+ if (rotation) {
12277
+ this.textArea.style.transformOrigin = "left top";
12278
+ this.textArea.style.transform = `rotate(${rotation}deg)`;
12279
+ }
12280
+ this.textArea.value = props.labelText ?? "";
12281
+ this.textArea.style.fontSize = `${fontSize * upscaleScale}px`;
12282
+ this.textArea.style.fontFamily = props.labelFontFamily ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontFamily;
12283
+ this.textArea.style.letterSpacing = `${props.labelLetterSpacing ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelLetterSpacing}px`;
12284
+ this.textArea.style.lineHeight = `${props.labelLineHeight ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelLineHeight}em`;
12285
+ const fontStyle = props.labelFontStyle ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontStyle;
12286
+ this.textArea.style.fontStyle = fontStyle.includes("italic") ? "italic" : "normal";
12287
+ this.textArea.style.textDecoration = props.labelTextDecoration ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelTextDecoration;
12288
+ let fontWeight = "normal";
12289
+ const matchNumber = fontStyle.match(/\d+/);
12290
+ if (fontStyle.includes("bold")) fontWeight = "bold";
12291
+ if (matchNumber) fontWeight = matchNumber[0];
12292
+ this.textArea.style.fontWeight = fontWeight;
12293
+ this.textArea.style.fontVariant = props.labelFontVariant ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontVariant;
12294
+ this.textArea.style.color = props.labelFill ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFill;
12295
+ this.textArea.style.textAlign = props.labelAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelAlign;
12296
+ this.textArea.style.outline = "none";
12297
+ this.textArea.style.resize = "none";
12298
+ this.textArea.style.margin = "0";
12299
+ this.textArea.style.padding = "0";
12300
+ this.textArea.style.caretColor = "black";
12301
+ this.textArea.style.overscrollBehavior = "contains";
12302
+ const resizeTextarea = () => {
12303
+ if (!this.textArea) return;
12304
+ this.textArea.style.height = "auto";
12305
+ const contentHeightPx = this.textArea.scrollHeight;
12306
+ const fonts = this.instance.getFonts();
12307
+ const font = fonts.find((f) => f.name === (props.labelFontFamily ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontFamily));
12308
+ const currentBounds = this.editingTextBounds ?? textBounds;
12309
+ const liveTL = group.getAbsoluteTransform().point({
12310
+ x: currentBounds.x,
12311
+ y: currentBounds.y + (font?.offsetY ?? 0)
12312
+ });
12313
+ this.textArea.style.left = `${liveTL.x * upscaleScale}px`;
12314
+ if (contentHeightPx <= originalBoundsHeightPx) {
12315
+ this.textArea.style.height = `${contentHeightPx}px`;
12316
+ const verticalAlign = props.labelVerticalAlign ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelVerticalAlign;
12317
+ const offsetY = this.computeVerticalOffset(verticalAlign, originalBoundsHeightPx, contentHeightPx);
12318
+ this.textArea.style.top = `${liveTL.y * upscaleScale + offsetY}px`;
12319
+ } else {
12320
+ this.textArea.style.height = `${contentHeightPx}px`;
12321
+ this.textArea.style.top = `${liveTL.y * upscaleScale}px`;
12322
+ }
12323
+ if (onLiveResize) this.runLiveResizeLoop(onLiveResize, contentHeightPx, effectiveScale, upscaleScale, paddingY);
12324
+ if (this.textArea.style.visibility === "hidden") this.textArea.style.visibility = "";
12325
+ };
12326
+ const commit = (text) => {
12327
+ window.removeEventListener("pointerup", handleOutsideClick);
12328
+ this.exitEditMode();
12329
+ const liveGroup = this.instance.getStage().findOne(`#${group.id()}`);
12330
+ const labelNode = liveGroup?.findOne(`#${labelId(group.id())}`);
12331
+ if (labelNode) labelNode.visible(text !== "");
12332
+ onCommit(text);
12333
+ };
12334
+ this.textArea.addEventListener("keydown", (e) => {
12335
+ e.stopPropagation();
12336
+ if (e.code === "Escape") {
12337
+ e.preventDefault();
12338
+ commit(this.textArea?.value ?? "");
12339
+ return;
12340
+ }
12341
+ resizeTextarea();
12342
+ }, { signal: this.instance.getEventsController().signal });
12343
+ this.textArea.addEventListener("keyup", () => resizeTextarea(), { signal: this.instance.getEventsController().signal });
12344
+ this.textArea.addEventListener("input", () => resizeTextarea(), { signal: this.instance.getEventsController().signal });
12345
+ this.textArea.addEventListener("scroll", () => {
12346
+ if (this.textArea) {
12347
+ this.textArea.scrollTop = 0;
12348
+ this.textArea.scrollLeft = 0;
12349
+ }
12350
+ }, { signal: this.instance.getEventsController().signal });
12351
+ const handleOutsideClick = (e) => {
12352
+ e.stopPropagation();
12353
+ if (!this.textArea) return;
12354
+ const mouseX = e.clientX;
12355
+ const mouseY = e.clientY;
12356
+ let elementUnderMouse = document.elementFromPoint(mouseX, mouseY);
12357
+ if (isInShadowDOM(stage.container())) {
12358
+ const shadowHost = getTopmostShadowHost(stage.container());
12359
+ if (shadowHost) elementUnderMouse = shadowHost.elementFromPoint(mouseX, mouseY);
12360
+ }
12361
+ const clickedOutside = elementUnderMouse?.id !== `${group.id()}_label_textarea`;
12362
+ if (clickedOutside) commit(this.textArea.value);
12363
+ };
12364
+ setTimeout(() => {
12365
+ window.addEventListener("pointerup", handleOutsideClick, { signal: this.instance.getEventsController().signal });
12366
+ }, 0);
12367
+ this.textArea.tabIndex = 1;
12368
+ requestAnimationFrame(() => {
12369
+ resizeTextarea();
12370
+ this.textArea?.focus({ preventScroll: true });
12371
+ if (this.textArea?.value) this.textArea.select();
12372
+ });
12373
+ }
12374
+ };
12375
+
12376
+ //#endregion
12377
+ //#region src/nodes/shared/shape-label.utils.ts
12378
+ /**
12379
+ * Returns a partial props object containing only the label-related fields that
12380
+ * are explicitly set on `props`. Spread this into the `props` section of
12381
+ * `addNodeState` / `updateNodeState` to avoid duplicating the 12-field pattern
12382
+ * across every shape node that supports inline text labels.
12383
+ */
12384
+ function spreadLabelProps(props) {
12385
+ return {
12386
+ ...props.labelText !== void 0 && { labelText: props.labelText },
12387
+ ...props.labelFontFamily !== void 0 && { labelFontFamily: props.labelFontFamily },
12388
+ ...props.labelFontSize !== void 0 && { labelFontSize: props.labelFontSize },
12389
+ ...props.labelFontStyle !== void 0 && { labelFontStyle: props.labelFontStyle },
12390
+ ...props.labelFontVariant !== void 0 && { labelFontVariant: props.labelFontVariant },
12391
+ ...props.labelFill !== void 0 && { labelFill: props.labelFill },
12392
+ ...props.labelAlign !== void 0 && { labelAlign: props.labelAlign },
12393
+ ...props.labelVerticalAlign !== void 0 && { labelVerticalAlign: props.labelVerticalAlign },
12394
+ ...props.labelLetterSpacing !== void 0 && { labelLetterSpacing: props.labelLetterSpacing },
12395
+ ...props.labelLineHeight !== void 0 && { labelLineHeight: props.labelLineHeight },
12396
+ ...props.labelPaddingX !== void 0 && { labelPaddingX: props.labelPaddingX },
12397
+ ...props.labelPaddingY !== void 0 && { labelPaddingY: props.labelPaddingY }
12398
+ };
12399
+ }
12400
+ /**
12401
+ * Returns the shared Zod schema fields for inline text label properties.
12402
+ * Spread the result of this function into a shape node's `props` schema
12403
+ * extension to avoid duplicating the 10-field label schema across rectangle,
12404
+ * ellipse, and any future shape that supports text labels.
12405
+ */
12406
+ function getShapeLabelSchemaFields() {
12407
+ return {
12408
+ labelText: z.string().optional().describe("Text label displayed inside the shape"),
12409
+ labelFontFamily: z.string().optional().describe("Font family for the label text"),
12410
+ labelFontSize: z.number().optional().describe("Font size for the label text in pixels"),
12411
+ labelFontStyle: z.string().optional().describe("Font style for the label text (e.g. \"normal\", \"bold\", \"italic\", \"bold italic\")"),
12412
+ labelFontVariant: z.string().optional().describe("Font variant for the label text (e.g. \"normal\", \"small-caps\")"),
12413
+ labelFill: z.string().optional().describe("Color of the label text in hex format (e.g. #RRGGBBAA)"),
12414
+ labelAlign: z.string().optional().describe("Horizontal alignment of the label text (\"left\", \"center\", \"right\")"),
12415
+ labelVerticalAlign: z.string().optional().describe("Vertical alignment of the label text (\"top\", \"middle\", \"bottom\")"),
12416
+ labelLetterSpacing: z.number().optional().describe("Letter spacing for the label text in pixels"),
12417
+ labelLineHeight: z.number().optional().describe("Line height multiplier for the label text"),
12418
+ labelPaddingX: z.number().optional().describe("Horizontal inset (padding) in pixels applied on each side of the label"),
12419
+ labelPaddingY: z.number().optional().describe("Vertical inset (padding) in pixels applied on top and bottom of the label")
12420
+ };
12421
+ }
12422
+ /**
12423
+ * Extracts the label node and typography settings from a Konva group.
12424
+ * Returns `null` when the group has no label text or no label Konva.Text child.
12425
+ * Used internally by `computeRectangleLabelMinSize`, `computeEllipseLabelMinSize`,
12426
+ * and `computePolygonLabelMinSize` to avoid duplicating the setup logic.
12427
+ */
12428
+ function extractLabelNodeContext(group, skipTransformInClientRect = true) {
12429
+ const attrs = group.getAttrs();
12430
+ const labelText = attrs.labelText ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelText;
12431
+ if (!labelText) return null;
12432
+ const labelNode = group.findOne(`#${labelId(group.id())}`);
12433
+ if (!labelNode) return null;
12434
+ const paddingX = attrs.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12435
+ const paddingY = attrs.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12436
+ const fontSize = attrs.labelFontSize ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelFontSize;
12437
+ const cloneLabel = labelNode.clone({ visible: false });
12438
+ cloneLabel.height(void 0);
12439
+ const naturalSize = cloneLabel.getClientRect({
12440
+ skipTransform: skipTransformInClientRect,
12441
+ skipShadow: true
12442
+ });
12443
+ return {
12444
+ paddingX,
12445
+ paddingY,
12446
+ fontSize,
12447
+ labelNode,
12448
+ naturalSize
12449
+ };
12450
+ }
12451
+ /**
12452
+ * Returns the minimum bounding size `{ width, height }` (in Konva canvas
12453
+ * units) that the rectangle must have so its label text is fully visible —
12454
+ * no vertical truncation and no horizontal clipping of the widest word.
12455
+ *
12456
+ * Returns `{ width: 0, height: 0 }` when the label is empty.
12457
+ *
12458
+ * @param group - The rectangle `Konva.Group` returned by `onRender`.
12459
+ */
12460
+ function computeRectangleLabelMinSize(stage, group) {
12461
+ const ctx = extractLabelNodeContext(group);
12462
+ if (!ctx) return {
12463
+ width: 0,
12464
+ height: 0
12465
+ };
12466
+ const { paddingX, paddingY, fontSize, naturalSize } = ctx;
12467
+ return {
12468
+ width: (paddingX * 2 + fontSize) * stage.scaleX(),
12469
+ height: (naturalSize.height + paddingY * 2) * stage.scaleX()
12470
+ };
12471
+ }
12472
+ /**
12473
+ * Returns the minimum bounding box size `{ minWidth, minHeight }` (in Konva
12474
+ * canvas units, i.e. `radiusX * 2` × `radiusY * 2`) that the ellipse must have
12475
+ * so its inscribed label text is fully visible.
12476
+ *
12477
+ * The ellipse label sits inside the largest axis-aligned rectangle inscribed in
12478
+ * the ellipse: `inscribedW = radiusX * √2`, `inscribedH = radiusY * √2`. The
12479
+ * minimum radiusY is back-computed from the text's natural height:
12480
+ * `minRadiusY = ceil(naturalTextH / √2)`
12481
+ *
12482
+ * Returns `{ minWidth: 0, minHeight: 0 }` when the label is empty.
12483
+ *
12484
+ * @param group - The ellipse `Konva.Group` returned by `onRender`.
12485
+ */
12486
+ function computeEllipseLabelMinSize(stage, group) {
12487
+ const ctx = extractLabelNodeContext(group);
12488
+ if (!ctx) return {
12489
+ width: 0,
12490
+ height: 0
12491
+ };
12492
+ const { paddingX, paddingY, fontSize, naturalSize } = ctx;
12493
+ const minRadiusY = Math.ceil((naturalSize.height + paddingY * 2) / Math.SQRT2);
12494
+ const minRadiusX = Math.ceil((fontSize + paddingX * 2) / Math.SQRT2);
12495
+ return {
12496
+ width: minRadiusX * 2 * stage.scaleX(),
12497
+ height: minRadiusY * 2 * stage.scaleY()
12498
+ };
12499
+ }
12500
+ /**
12501
+ * Returns the minimum bounding-box size `{ width, height }` (in Konva canvas
12502
+ * units) that the polygon node must have so its label text is fully visible.
12503
+ *
12504
+ * The polygon label sits inside the stored `innerRect` attribute. The minimum
12505
+ * size is back-computed from the label text's natural wrapped height and the
12506
+ * current ratio of `innerRect` to the overall bounding box.
12507
+ *
12508
+ * Returns `{ width: 0, height: 0 }` when the label is empty.
12509
+ *
12510
+ * @param group - The polygon `Konva.Group` returned by `onRender`.
12511
+ */
12512
+ function computePolygonLabelMinSize(stage, group) {
12513
+ const ctx = extractLabelNodeContext(group);
12514
+ if (!ctx) return {
12515
+ width: 0,
12516
+ height: 0
12517
+ };
12518
+ const { paddingX, paddingY, fontSize, naturalSize } = ctx;
12519
+ const attrs = group.getAttrs();
12520
+ const innerRect = attrs.innerRect;
12521
+ if (!innerRect) return {
12522
+ width: 0,
12523
+ height: 0
12524
+ };
12525
+ return {
12526
+ width: (paddingX * 2 + fontSize) * stage.scaleX(),
12527
+ height: (naturalSize.height + paddingY * 2) * stage.scaleX()
12528
+ };
12529
+ }
12530
+
11920
12531
  //#endregion
11921
12532
  //#region src/nodes/rectangle/rectangle.ts
11922
12533
  var WeaveRectangleNode = class extends WeaveNode {
11923
12534
  nodeType = WEAVE_RECTANGLE_NODE_TYPE;
11924
12535
  initialize = void 0;
12536
+ _transforming = false;
12537
+ get shapeLabelEditor() {
12538
+ return this._shapeLabelEditor ??= new WeaveShapeLabelEditor(this.instance);
12539
+ }
11925
12540
  constructor(params) {
11926
12541
  super();
11927
12542
  const { config } = params ?? {};
@@ -11930,13 +12545,15 @@ var WeaveRectangleNode = class extends WeaveNode {
11930
12545
  onRender(props) {
11931
12546
  const rectangle = new Konva.Group({
11932
12547
  ...props,
12548
+ id: `${props.id}`,
11933
12549
  name: "node"
11934
12550
  });
11935
12551
  const internalRectBg = new Konva.Rect({
11936
12552
  ...props,
11937
12553
  name: void 0,
11938
- id: `${props.id}-bg`,
12554
+ nodeType: void 0,
11939
12555
  nodeId: props.id,
12556
+ id: `${props.id}-bg`,
11940
12557
  x: 0,
11941
12558
  y: 0,
11942
12559
  width: props.width,
@@ -11951,6 +12568,7 @@ var WeaveRectangleNode = class extends WeaveNode {
11951
12568
  const internalRectBorder = new Konva.Rect({
11952
12569
  ...props,
11953
12570
  name: void 0,
12571
+ nodeType: void 0,
11954
12572
  id: `${props.id}-border`,
11955
12573
  x: props.strokeWidth / 2,
11956
12574
  y: props.strokeWidth / 2,
@@ -11960,9 +12578,19 @@ var WeaveRectangleNode = class extends WeaveNode {
11960
12578
  strokeWidth: props.strokeWidth || 0,
11961
12579
  strokeScaleEnabled: true,
11962
12580
  rotation: 0,
11963
- listening: false
12581
+ listening: false,
12582
+ draggable: false
11964
12583
  });
11965
12584
  rectangle.add(internalRectBorder);
12585
+ const paddingX = props.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12586
+ const paddingY = props.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12587
+ const labelTextBounds = {
12588
+ x: paddingX,
12589
+ y: paddingY,
12590
+ width: Math.max(1, props.width - paddingX * 2),
12591
+ height: Math.max(1, props.height - paddingY * 2)
12592
+ };
12593
+ this.shapeLabelEditor.renderLabel(rectangle, props, labelTextBounds);
11966
12594
  internalRectBorder.moveToTop();
11967
12595
  internalRectBg.moveToBottom();
11968
12596
  this.setupDefaultNodeAugmentation(rectangle);
@@ -11971,6 +12599,51 @@ var WeaveRectangleNode = class extends WeaveNode {
11971
12599
  return defaultTransformerProperties;
11972
12600
  };
11973
12601
  this.setupDefaultNodeEvents(rectangle);
12602
+ rectangle.on("transformstart", () => {
12603
+ this._transforming = true;
12604
+ });
12605
+ rectangle.on("transform", () => {
12606
+ this.scaleReset(rectangle);
12607
+ this.onUpdate(rectangle, rectangle.getAttrs());
12608
+ });
12609
+ rectangle.on("transformend", () => {
12610
+ this._transforming = false;
12611
+ });
12612
+ rectangle.dblClick = () => {
12613
+ if (this.shapeLabelEditor.isEditing()) return;
12614
+ if (!(this.isSelecting() && this.isNodeSelected(rectangle))) return;
12615
+ const onCommit = (labelText) => {
12616
+ const updatedGroup = this.instance.getStage().findOne(`#${props.id}`);
12617
+ if (!updatedGroup) return;
12618
+ const serialized = this.serialize(updatedGroup);
12619
+ serialized.props.labelText = labelText;
12620
+ this.instance.updateNode(serialized);
12621
+ };
12622
+ const currentAttrs = rectangle.getAttrs();
12623
+ const curPaddingX = currentAttrs.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12624
+ const curPaddingY = currentAttrs.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12625
+ const currentLabelTextBounds = {
12626
+ x: curPaddingX,
12627
+ y: curPaddingY,
12628
+ width: Math.max(1, (currentAttrs.width ?? 0) - curPaddingX * 2),
12629
+ height: Math.max(1, (currentAttrs.height ?? 0) - curPaddingY * 2)
12630
+ };
12631
+ const originalHeight = currentAttrs.height ?? 0;
12632
+ this.shapeLabelEditor.triggerEditMode(rectangle, currentLabelTextBounds, onCommit, (neededShapeHeight) => {
12633
+ const finalHeight = Math.max(neededShapeHeight, originalHeight);
12634
+ const liveAttrs = rectangle.getAttrs();
12635
+ const strokeW = liveAttrs.strokeWidth || 0;
12636
+ const bg = rectangle.findOne(`#${liveAttrs.id}-bg`);
12637
+ const border = rectangle.findOne(`#${liveAttrs.id}-border`);
12638
+ rectangle.setAttrs({ height: finalHeight });
12639
+ bg?.setAttrs({ height: finalHeight });
12640
+ border?.setAttrs({ height: Math.max(0, finalHeight - strokeW) });
12641
+ rectangle.getLayer()?.batchDraw();
12642
+ });
12643
+ };
12644
+ rectangle.getNodeMinSize = () => {
12645
+ return computeRectangleLabelMinSize(this.instance.getStage(), rectangle);
12646
+ };
11974
12647
  return rectangle;
11975
12648
  }
11976
12649
  onUpdate(nodeInstance, nextProps) {
@@ -12000,6 +12673,7 @@ var WeaveRectangleNode = class extends WeaveNode {
12000
12673
  internalRectBorder.setAttrs({
12001
12674
  ...nextProps,
12002
12675
  name: void 0,
12676
+ fill: "transparent",
12003
12677
  id: `${nextProps.id}-border`,
12004
12678
  x: nextProps.strokeWidth / 2,
12005
12679
  y: nextProps.strokeWidth / 2,
@@ -12013,6 +12687,26 @@ var WeaveRectangleNode = class extends WeaveNode {
12013
12687
  });
12014
12688
  internalRectBorder.moveToTop();
12015
12689
  }
12690
+ const paddingX = nextProps.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12691
+ const paddingY = nextProps.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12692
+ const labelTextBounds = {
12693
+ x: paddingX,
12694
+ y: paddingY,
12695
+ width: Math.max(1, nextProps.width - paddingX * 2),
12696
+ height: Math.max(1, nextProps.height - paddingY * 2)
12697
+ };
12698
+ this.shapeLabelEditor.updateLabel(rectangle, nextProps, labelTextBounds, (neededShapeHeight) => {
12699
+ nodeInstance.setAttrs({ height: neededShapeHeight });
12700
+ internalRectBg?.setAttrs({ height: neededShapeHeight });
12701
+ internalRectBorder?.setAttrs({ height: neededShapeHeight - nextProps.strokeWidth });
12702
+ if (!this._transforming) this.instance.updateNode(this.serialize(nodeInstance));
12703
+ });
12704
+ const labelNode = rectangle.findOne(`#${labelId(nextProps.id ?? "")}`);
12705
+ if (labelNode) {
12706
+ labelNode.moveToTop();
12707
+ internalRectBg?.moveToBottom();
12708
+ internalRectBorder?.moveToTop();
12709
+ }
12016
12710
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
12017
12711
  if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
12018
12712
  }
@@ -12033,7 +12727,8 @@ var WeaveRectangleNode = class extends WeaveNode {
12033
12727
  strokeScaleEnabled: true,
12034
12728
  rotation: 0,
12035
12729
  zIndex: 1,
12036
- children: []
12730
+ children: [],
12731
+ ...WEAVE_SHAPE_LABEL_DEFAULTS
12037
12732
  }
12038
12733
  };
12039
12734
  }
@@ -12046,7 +12741,8 @@ var WeaveRectangleNode = class extends WeaveNode {
12046
12741
  rotation: props.rotation,
12047
12742
  fill: props.fill,
12048
12743
  ...props.stroke && { stroke: props.stroke },
12049
- ...props.strokeWidth && { strokeWidth: props.strokeWidth }
12744
+ ...props.strokeWidth && { strokeWidth: props.strokeWidth },
12745
+ ...spreadLabelProps(props)
12050
12746
  } });
12051
12747
  }
12052
12748
  static updateNodeState(prevNodeState, nextProps) {
@@ -12058,7 +12754,8 @@ var WeaveRectangleNode = class extends WeaveNode {
12058
12754
  rotation: nextProps.rotation,
12059
12755
  fill: nextProps.fill,
12060
12756
  ...nextProps.stroke && { stroke: nextProps.stroke },
12061
- ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth }
12757
+ ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth },
12758
+ ...spreadLabelProps(nextProps)
12062
12759
  } });
12063
12760
  }
12064
12761
  static getSchema() {
@@ -12072,7 +12769,8 @@ var WeaveRectangleNode = class extends WeaveNode {
12072
12769
  fill: z.string().describe("Fill color of the rectangle in hex format with alpha channel (e.g. #RRGGBBAA)"),
12073
12770
  stroke: z.string().describe("Stroke color of the rectangle in hex format with alpha channel (e.g. #RRGGBBAA)"),
12074
12771
  strokeWidth: z.number().describe("Stroke width of the rectangle in pixels"),
12075
- strokeScaleEnabled: z.boolean().describe("Whether the rectangle stroke width should scale when the node is scaled. Defaults to true.")
12772
+ strokeScaleEnabled: z.boolean().describe("Whether the rectangle stroke width should scale when the node is scaled. Defaults to true."),
12773
+ ...getShapeLabelSchemaFields()
12076
12774
  })
12077
12775
  });
12078
12776
  return nodeSchema;
@@ -12088,11 +12786,25 @@ const WEAVE_ELLIPSE_NODE_TYPE = "ellipse";
12088
12786
  var WeaveEllipseNode = class extends WeaveNode {
12089
12787
  nodeType = WEAVE_ELLIPSE_NODE_TYPE;
12090
12788
  initialize = void 0;
12789
+ _transforming = false;
12790
+ get shapeLabelEditor() {
12791
+ return this._shapeLabelEditor ??= new WeaveShapeLabelEditor(this.instance);
12792
+ }
12091
12793
  constructor(params) {
12092
12794
  super();
12093
12795
  const { config } = params ?? {};
12094
12796
  this.config = { transform: { ...config?.transform } };
12095
12797
  }
12798
+ getLabelTextBounds(radiusX, radiusY, paddingX, paddingY) {
12799
+ const inscribedW = radiusX * Math.SQRT2;
12800
+ const inscribedH = radiusY * Math.SQRT2;
12801
+ return {
12802
+ x: radiusX - inscribedW / 2 + paddingX,
12803
+ y: radiusY - inscribedH / 2 + paddingY,
12804
+ width: Math.max(1, inscribedW - paddingX * 2),
12805
+ height: Math.max(1, inscribedH - paddingY * 2)
12806
+ };
12807
+ }
12096
12808
  onRender(props) {
12097
12809
  const ellipse = new Konva.Group({
12098
12810
  ...props,
@@ -12130,6 +12842,10 @@ var WeaveEllipseNode = class extends WeaveNode {
12130
12842
  listening: false
12131
12843
  });
12132
12844
  ellipse.add(internalEllipseBorder);
12845
+ const paddingX = props.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12846
+ const paddingY = props.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12847
+ const labelTextBounds = this.getLabelTextBounds(Math.max(1, baseRadiusX), Math.max(1, baseRadiusY), paddingX, paddingY);
12848
+ this.shapeLabelEditor.renderLabel(ellipse, props, labelTextBounds);
12133
12849
  internalEllipseBorder.moveToTop();
12134
12850
  internalEllipseBg.moveToBottom();
12135
12851
  this.setupDefaultNodeAugmentation(ellipse);
@@ -12168,6 +12884,56 @@ var WeaveEllipseNode = class extends WeaveNode {
12168
12884
  ];
12169
12885
  };
12170
12886
  this.setupDefaultNodeEvents(ellipse);
12887
+ ellipse.on("transformstart", () => {
12888
+ this._transforming = true;
12889
+ });
12890
+ ellipse.on("transform", () => {
12891
+ this.scaleReset(ellipse);
12892
+ this.onUpdate(ellipse, ellipse.getAttrs());
12893
+ });
12894
+ ellipse.on("transformend", () => {
12895
+ this._transforming = false;
12896
+ });
12897
+ ellipse.dblClick = () => {
12898
+ if (this.shapeLabelEditor.isEditing()) return;
12899
+ if (!(this.isSelecting() && this.isNodeSelected(ellipse))) return;
12900
+ const onCommit = (labelText) => {
12901
+ const updatedGroup = this.instance.getStage().findOne(`#${props.id}`);
12902
+ if (!updatedGroup) return;
12903
+ const serialized = this.serialize(updatedGroup);
12904
+ serialized.props.labelText = labelText;
12905
+ this.instance.updateNode(serialized);
12906
+ };
12907
+ const currentAttrs = ellipse.getAttrs();
12908
+ const curPaddingX = currentAttrs.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12909
+ const curPaddingY = currentAttrs.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12910
+ const currentRadiusX = Math.max(1, currentAttrs.radiusX);
12911
+ const currentRadiusY = Math.max(1, currentAttrs.radiusY);
12912
+ const currentLabelTextBounds = this.getLabelTextBounds(currentRadiusX, currentRadiusY, curPaddingX, curPaddingY);
12913
+ const originalRadiusY = currentRadiusY;
12914
+ const originalNeededHeight = currentLabelTextBounds.height + curPaddingY * 2;
12915
+ this.shapeLabelEditor.triggerEditMode(ellipse, currentLabelTextBounds, onCommit, (neededShapeHeight) => {
12916
+ const newRadiusY = neededShapeHeight <= originalNeededHeight ? originalRadiusY : Math.ceil(neededShapeHeight / Math.SQRT2);
12917
+ const liveAttrs = ellipse.getAttrs();
12918
+ const strokeW = liveAttrs.strokeWidth || 0;
12919
+ const bg = ellipse.findOne(`#${liveAttrs.id}-bg`);
12920
+ const border = ellipse.findOne(`#${liveAttrs.id}-border`);
12921
+ ellipse.setAttrs({ radiusY: newRadiusY });
12922
+ bg?.setAttrs({
12923
+ radiusY: Math.max(1, newRadiusY),
12924
+ y: Math.max(1, newRadiusY)
12925
+ });
12926
+ border?.setAttrs({
12927
+ radiusY: Math.max(1, newRadiusY) - strokeW / 2,
12928
+ y: Math.max(1, newRadiusY)
12929
+ });
12930
+ const newLabelTextBounds = this.getLabelTextBounds(currentRadiusX, newRadiusY, curPaddingX, curPaddingY);
12931
+ this.shapeLabelEditor.repositionTextArea(ellipse, newLabelTextBounds);
12932
+ });
12933
+ };
12934
+ ellipse.getNodeMinSize = () => {
12935
+ return computeEllipseLabelMinSize(this.instance.getStage(), ellipse);
12936
+ };
12171
12937
  return ellipse;
12172
12938
  }
12173
12939
  onUpdate(nodeInstance, nextProps) {
@@ -12205,12 +12971,34 @@ var WeaveEllipseNode = class extends WeaveNode {
12205
12971
  radiusY: Math.max(1, baseRadiusY) - (nextProps.strokeWidth || 0) / 2,
12206
12972
  stroke: nextProps.stroke || "transparent",
12207
12973
  strokeWidth: nextProps.strokeWidth || 0,
12974
+ fill: "transparent",
12208
12975
  strokeScaleEnabled: true,
12209
12976
  listening: false,
12210
12977
  rotation: 0
12211
12978
  });
12212
12979
  internalEllipseBorder.moveToTop();
12213
12980
  }
12981
+ const paddingX = nextProps.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
12982
+ const paddingY = nextProps.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
12983
+ const labelTextBounds = this.getLabelTextBounds(Math.max(1, baseRadiusX), Math.max(1, baseRadiusY), paddingX, paddingY);
12984
+ this.shapeLabelEditor.updateLabel(ellipse, nextProps, labelTextBounds, (neededHeight) => {
12985
+ const newRadiusY = Math.ceil(neededHeight / Math.SQRT2);
12986
+ nodeInstance.setAttrs({ radiusY: newRadiusY });
12987
+ internalEllipseBg?.setAttrs({
12988
+ radiusY: Math.max(1, newRadiusY),
12989
+ y: Math.max(1, newRadiusY)
12990
+ });
12991
+ internalEllipseBorder?.setAttrs({
12992
+ radiusY: Math.max(1, newRadiusY) - (nextProps.strokeWidth || 0) / 2,
12993
+ y: Math.max(1, newRadiusY)
12994
+ });
12995
+ if (!this._transforming) this.instance.updateNode(this.serialize(nodeInstance));
12996
+ });
12997
+ const labelNode = ellipse.findOne(`#${labelId(nextProps.id)}`);
12998
+ if (labelNode) {
12999
+ labelNode.moveToTop();
13000
+ internalEllipseBorder?.moveToTop();
13001
+ }
12214
13002
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
12215
13003
  if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
12216
13004
  }
@@ -12253,7 +13041,8 @@ var WeaveEllipseNode = class extends WeaveNode {
12253
13041
  strokeScaleEnabled: true,
12254
13042
  rotation: 0,
12255
13043
  zIndex: 1,
12256
- children: []
13044
+ children: [],
13045
+ ...WEAVE_SHAPE_LABEL_DEFAULTS
12257
13046
  }
12258
13047
  };
12259
13048
  }
@@ -12266,7 +13055,8 @@ var WeaveEllipseNode = class extends WeaveNode {
12266
13055
  rotation: props.rotation,
12267
13056
  fill: props.fill,
12268
13057
  ...props.stroke && { stroke: props.stroke },
12269
- ...props.strokeWidth && { strokeWidth: props.strokeWidth }
13058
+ ...props.strokeWidth && { strokeWidth: props.strokeWidth },
13059
+ ...spreadLabelProps(props)
12270
13060
  } });
12271
13061
  }
12272
13062
  static updateNodeState(prevNodeState, nextProps) {
@@ -12278,7 +13068,8 @@ var WeaveEllipseNode = class extends WeaveNode {
12278
13068
  rotation: nextProps.rotation,
12279
13069
  fill: nextProps.fill,
12280
13070
  ...nextProps.stroke && { stroke: nextProps.stroke },
12281
- ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth }
13071
+ ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth },
13072
+ ...spreadLabelProps(nextProps)
12282
13073
  } });
12283
13074
  }
12284
13075
  static getSchema() {
@@ -12292,7 +13083,8 @@ var WeaveEllipseNode = class extends WeaveNode {
12292
13083
  fill: z.string().describe("Fill color of the ellipse in hex format with alpha channel (e.g. #RRGGBBAA)"),
12293
13084
  stroke: z.string().describe("Stroke color of the ellipse in hex format with alpha channel (e.g. #RRGGBBAA)"),
12294
13085
  strokeWidth: z.number().describe("Stroke width of the ellipse in pixels"),
12295
- strokeScaleEnabled: z.boolean().describe("Whether the ellipse stroke width should scale when the node is scaled. Defaults to true.")
13086
+ strokeScaleEnabled: z.boolean().describe("Whether the ellipse stroke width should scale when the node is scaled. Defaults to true."),
13087
+ ...getShapeLabelSchemaFields()
12296
13088
  })
12297
13089
  });
12298
13090
  return nodeSchema;
@@ -12670,7 +13462,8 @@ const WEAVE_STAGE_TEXT_EDITION_MODE = "text-edition";
12670
13462
  const WEAVE_TEXT_NODE_DEFAULT_CONFIG = {
12671
13463
  transform: { ...WEAVE_NODES_SELECTION_DEFAULT_CONFIG.selection },
12672
13464
  outline: { enabled: false },
12673
- cursor: { color: "#000000" }
13465
+ cursor: { color: "#000000" },
13466
+ edition: { borderSize: 2 }
12674
13467
  };
12675
13468
  const TEXT_LAYOUT = {
12676
13469
  ["SMART"]: "smart",
@@ -12966,7 +13759,8 @@ var WeaveTextNode = class extends WeaveNode {
12966
13759
  if (!this.textArea || !this.textAreaContainer) return;
12967
13760
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART && !textNode.getAttrs().smartFixedWidth) {
12968
13761
  const { width: textAreaWidth } = this.textRenderedSize(this.textArea.value, textNode);
12969
- const width = (textAreaWidth + 2) * textNode.getAbsoluteScale().x / this.instance.getStage().scaleX();
13762
+ const borderSize = this.config.edition.borderSize;
13763
+ const width = (textAreaWidth + borderSize * 2) * textNode.getAbsoluteScale().x / this.instance.getStage().scaleX();
12970
13764
  this.textAreaContainer.style.width = width + "px";
12971
13765
  }
12972
13766
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) {
@@ -13065,13 +13859,15 @@ var WeaveTextNode = class extends WeaveNode {
13065
13859
  this.textAreaContainer.style.top = position.y * upscaleScale + "px";
13066
13860
  this.textAreaContainer.style.left = position.x * upscaleScale + "px";
13067
13861
  if (textNode.getAttrs().layout === TEXT_LAYOUT.SMART && !textNode.getAttrs().smartFixedWidth) {
13862
+ const borderSize$1 = this.config.edition.borderSize;
13068
13863
  const rect = textNode.getClientRect({ relativeTo: stage });
13069
- this.textAreaContainer.style.width = (rect.width + 2) * textNode.getAbsoluteScale().x + "px";
13864
+ this.textAreaContainer.style.width = (rect.width + borderSize$1 * 2) * textNode.getAbsoluteScale().x + "px";
13070
13865
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13071
13866
  }
13072
13867
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL) {
13868
+ const borderSize$1 = this.config.edition.borderSize;
13073
13869
  const rect = textNode.getClientRect({ relativeTo: stage });
13074
- this.textAreaContainer.style.width = (rect.width + 2) * textNode.getAbsoluteScale().x + "px";
13870
+ this.textAreaContainer.style.width = (rect.width + borderSize$1 * 2) * textNode.getAbsoluteScale().x + "px";
13075
13871
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13076
13872
  }
13077
13873
  if (textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.SMART && textNode.getAttrs().smartFixedWidth) {
@@ -13084,7 +13880,9 @@ var WeaveTextNode = class extends WeaveNode {
13084
13880
  this.textAreaContainer.style.width = (textNode.width() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13085
13881
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13086
13882
  }
13087
- this.textAreaContainer.style.border = "solid 1px #1e40af";
13883
+ const size = this.textRenderedSize(textNode.text(), textNode);
13884
+ const borderSize = this.config.edition.borderSize;
13885
+ this.textAreaContainer.style.border = `solid ${borderSize}px #1e40af`;
13088
13886
  this.textArea.style.position = "absolute";
13089
13887
  this.textArea.style.top = "0px";
13090
13888
  this.textArea.style.left = "0px";
@@ -13093,11 +13891,12 @@ var WeaveTextNode = class extends WeaveNode {
13093
13891
  this.textArea.style.scrollBehavior = "auto";
13094
13892
  this.textArea.style.caretColor = "black";
13095
13893
  this.textArea.style.width = "100%";
13894
+ this.textArea.style.height = `${size.height}px`;
13096
13895
  this.textArea.style.minHeight = "auto";
13097
13896
  this.textArea.style.margin = "0px";
13098
13897
  this.textArea.style.padding = "0px";
13099
13898
  this.textArea.style.paddingTop = "0px";
13100
- this.textArea.style.boxSizing = "border-box";
13899
+ this.textArea.style.boxSizing = "content-box";
13101
13900
  this.textArea.style.overflow = "hidden";
13102
13901
  this.textArea.style.background = "transparent";
13103
13902
  this.textArea.style.border = "none";
@@ -13111,8 +13910,8 @@ var WeaveTextNode = class extends WeaveNode {
13111
13910
  this.textArea.style.backgroundColor = "transparent";
13112
13911
  this.textAreaContainer.style.transformOrigin = "left top";
13113
13912
  this.mimicTextNode(textNode);
13114
- this.textArea.style.left = `-1px`;
13115
- this.textArea.style.top = `-1px`;
13913
+ this.textArea.style.left = `${-borderSize}px`;
13914
+ this.textArea.style.top = `${-borderSize + (size.height - this.textArea.offsetHeight)}px`;
13116
13915
  this.textArea.onfocus = () => {
13117
13916
  this.textAreaDomResize(textNode);
13118
13917
  };
@@ -13158,7 +13957,10 @@ var WeaveTextNode = class extends WeaveNode {
13158
13957
  const width = textAreaWidth / this.instance.getStage().scaleX();
13159
13958
  textNode.width(width);
13160
13959
  }
13161
- if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) textNode.height(this.textArea.scrollHeight * (1 / textNode.getAbsoluteScale().x));
13960
+ if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) {
13961
+ const size$1 = this.textRenderedSize(this.textArea.value, textNode);
13962
+ textNode.height(size$1.height * (1 / textNode.getAbsoluteScale().x));
13963
+ }
13162
13964
  };
13163
13965
  const handleKeyDown = (e) => {
13164
13966
  if (this.textArea && textNode && e.code === "Escape") {
@@ -15049,6 +15851,7 @@ var WeaveImageNode = class extends WeaveNode {
15049
15851
  x: 1,
15050
15852
  y: 1
15051
15853
  });
15854
+ this.updateImageCrop(node);
15052
15855
  }
15053
15856
  getIsAsync() {
15054
15857
  return true;
@@ -15560,127 +16363,975 @@ var WeaveRegularPolygonNode = class extends WeaveNode {
15560
16363
  rotation: 0,
15561
16364
  listening: false
15562
16365
  });
15563
- const internalRPBorderBox = internalRPBorder.getClientRect({ relativeTo: regularPolygon });
15564
- internalRPBorder.x(internalRPBorder.x() - internalRPBorderBox.x);
15565
- internalRPBorder.y(internalRPBorder.y() - internalRPBorderBox.y);
15566
- regularPolygon.add(internalRPBorder);
15567
- internalRPBorder.moveToTop();
15568
- internalRPBg.moveToBottom();
15569
- this.setupDefaultNodeAugmentation(regularPolygon);
16366
+ const internalRPBorderBox = internalRPBorder.getClientRect({ relativeTo: regularPolygon });
16367
+ internalRPBorder.x(internalRPBorder.x() - internalRPBorderBox.x);
16368
+ internalRPBorder.y(internalRPBorder.y() - internalRPBorderBox.y);
16369
+ regularPolygon.add(internalRPBorder);
16370
+ internalRPBorder.moveToTop();
16371
+ internalRPBg.moveToBottom();
16372
+ this.setupDefaultNodeAugmentation(regularPolygon);
16373
+ const defaultTransformerProperties = this.defaultGetTransformerProperties(this.config.transform);
16374
+ regularPolygon.getTransformerProperties = function() {
16375
+ return {
16376
+ ...defaultTransformerProperties,
16377
+ enabledAnchors: [
16378
+ "top-left",
16379
+ "top-right",
16380
+ "bottom-left",
16381
+ "bottom-right"
16382
+ ],
16383
+ keepRatio: true
16384
+ };
16385
+ };
16386
+ regularPolygon.allowedAnchors = function() {
16387
+ return [
16388
+ "top-left",
16389
+ "top-right",
16390
+ "bottom-left",
16391
+ "bottom-right"
16392
+ ];
16393
+ };
16394
+ this.setupDefaultNodeEvents(regularPolygon);
16395
+ return regularPolygon;
16396
+ }
16397
+ onUpdate(nodeInstance, nextProps) {
16398
+ nodeInstance.setAttrs({ ...nextProps });
16399
+ const sides = nodeInstance.getAttr("sides");
16400
+ const radius = nodeInstance.getAttr("radius");
16401
+ const regularPolygon = nodeInstance;
16402
+ const internalRPBg = regularPolygon.findOne(`#${nextProps.id}-bg`);
16403
+ const internalRPBorder = regularPolygon.findOne(`#${nextProps.id}-border`);
16404
+ if (internalRPBg) {
16405
+ internalRPBg.setAttrs({
16406
+ ...nextProps,
16407
+ name: void 0,
16408
+ id: `${nextProps.id}-bg`,
16409
+ nodeId: nextProps.id,
16410
+ x: radius,
16411
+ y: radius,
16412
+ sides,
16413
+ radius,
16414
+ fill: nextProps.fill || "transparent",
16415
+ strokeWidth: 0,
16416
+ strokeScaleEnabled: true,
16417
+ rotation: 0
16418
+ });
16419
+ const internalRPBgBox = internalRPBg.getClientRect({ relativeTo: regularPolygon });
16420
+ internalRPBg.x(internalRPBg.x() - internalRPBgBox.x);
16421
+ internalRPBg.y(internalRPBg.y() - internalRPBgBox.y);
16422
+ internalRPBg.moveToBottom();
16423
+ }
16424
+ if (internalRPBorder) {
16425
+ internalRPBorder.setAttrs({
16426
+ ...nextProps,
16427
+ name: void 0,
16428
+ id: `${nextProps.id}-border`,
16429
+ x: radius,
16430
+ y: radius,
16431
+ sides,
16432
+ radius: radius - (nextProps.strokeWidth || 0) / 2,
16433
+ stroke: nextProps.stroke || "transparent",
16434
+ strokeWidth: nextProps.strokeWidth || 0,
16435
+ strokeScaleEnabled: true,
16436
+ fill: "transparent",
16437
+ listening: false,
16438
+ rotation: 0
16439
+ });
16440
+ const internalRPBorderBox = internalRPBorder.getClientRect({ relativeTo: regularPolygon });
16441
+ internalRPBorder.x(internalRPBorder.x() - internalRPBorderBox.x);
16442
+ internalRPBorder.y(internalRPBorder.y() - internalRPBorderBox.y);
16443
+ internalRPBorder.moveToTop();
16444
+ }
16445
+ const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
16446
+ if (nodesSelectionPlugin) {
16447
+ const actualSelectedNodes = nodesSelectionPlugin.getSelectedNodes();
16448
+ nodesSelectionPlugin.setSelectedNodes(actualSelectedNodes);
16449
+ nodesSelectionPlugin.getTransformer().forceUpdate();
16450
+ }
16451
+ }
16452
+ scaleReset(node) {
16453
+ const absTransform = node.getAbsoluteTransform().copy();
16454
+ const radius = node.getAttr("radius");
16455
+ node.setAttrs({ radius: radius * node.scaleX() });
16456
+ node.scaleX(1);
16457
+ node.scaleY(1);
16458
+ const newTransform = node.getAbsoluteTransform();
16459
+ const dx = absTransform.m[4] - newTransform.m[4];
16460
+ const dy = absTransform.m[5] - newTransform.m[5];
16461
+ node.x(node.x() + dx);
16462
+ node.y(node.y() + dy);
16463
+ }
16464
+ realOffset(element) {
16465
+ return {
16466
+ x: element.props.radius,
16467
+ y: element.props.radius
16468
+ };
16469
+ }
16470
+ static defaultState(nodeId) {
16471
+ return {
16472
+ ...super.defaultState(nodeId),
16473
+ type: WEAVE_REGULAR_POLYGON_NODE_TYPE,
16474
+ props: {
16475
+ ...super.defaultState(nodeId).props,
16476
+ nodeType: WEAVE_REGULAR_POLYGON_NODE_TYPE,
16477
+ x: 0,
16478
+ y: 0,
16479
+ sides: 5,
16480
+ radius: 100,
16481
+ stroke: "#000000",
16482
+ fill: "#FFFFFF",
16483
+ strokeWidth: 1,
16484
+ strokeScaleEnabled: true,
16485
+ rotation: 0,
16486
+ zIndex: 1,
16487
+ children: []
16488
+ }
16489
+ };
16490
+ }
16491
+ static addNodeState(defaultNodeState, props) {
16492
+ return mergeExceptArrays(defaultNodeState, { props: {
16493
+ x: props.x,
16494
+ y: props.y,
16495
+ sides: props.sides,
16496
+ radius: props.radius,
16497
+ rotation: props.rotation,
16498
+ fill: props.fill,
16499
+ ...props.stroke && { stroke: props.stroke },
16500
+ ...props.strokeWidth && { strokeWidth: props.strokeWidth }
16501
+ } });
16502
+ }
16503
+ static updateNodeState(prevNodeState, nextProps) {
16504
+ return mergeExceptArrays(prevNodeState, { props: {
16505
+ x: nextProps.x,
16506
+ y: nextProps.y,
16507
+ sides: nextProps.sides,
16508
+ radius: nextProps.radius,
16509
+ rotation: nextProps.rotation,
16510
+ fill: nextProps.fill,
16511
+ ...nextProps.stroke && { stroke: nextProps.stroke },
16512
+ ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth }
16513
+ } });
16514
+ }
16515
+ static getSchema() {
16516
+ const baseSchema = super.getSchema();
16517
+ const nodeSchema = baseSchema.extend({
16518
+ type: z.literal(WEAVE_REGULAR_POLYGON_NODE_TYPE).describe(`Type of the node, for a regular polygon node it will always be "${WEAVE_REGULAR_POLYGON_NODE_TYPE}"`),
16519
+ props: baseSchema.shape.props.extend({
16520
+ nodeType: z.literal(WEAVE_REGULAR_POLYGON_NODE_TYPE).describe(`Type of the node, for a regular polygon node it will always be "${WEAVE_REGULAR_POLYGON_NODE_TYPE}"`),
16521
+ sides: z.number().describe("Number of sides of the regular polygon, must be 3 or more"),
16522
+ radius: z.number().describe("Radius of the regular polygon in pixels, distance from the center to any vertex"),
16523
+ fill: z.string().describe("Fill color of the regular polygon in hex format with alpha channel (e.g. #RRGGBBAA)"),
16524
+ stroke: z.string().describe("Stroke color of the regular polygon in hex format with alpha channel (e.g. #RRGGBBAA)"),
16525
+ strokeWidth: z.number().describe("Stroke width of the regular polygon in pixels"),
16526
+ strokeScaleEnabled: z.boolean().describe("Whether the regular polygon stroke width should scale when the node is scaled. Defaults to true.")
16527
+ })
16528
+ });
16529
+ return nodeSchema;
16530
+ }
16531
+ };
16532
+
16533
+ //#endregion
16534
+ //#region src/nodes/polygon/constants.ts
16535
+ const WEAVE_POLYGON_NODE_TYPE = "polygon";
16536
+
16537
+ //#endregion
16538
+ //#region src/nodes/polygon/presets.ts
16539
+ const WEAVE_POLYGON_PRESETS = {
16540
+ triangle: {
16541
+ label: "Triangle",
16542
+ sides: 3,
16543
+ defaultWidth: 100,
16544
+ defaultHeight: 100,
16545
+ normalizedPoints: [
16546
+ {
16547
+ x: .5,
16548
+ y: 0
16549
+ },
16550
+ {
16551
+ x: .933013,
16552
+ y: .75
16553
+ },
16554
+ {
16555
+ x: .066987,
16556
+ y: .75
16557
+ }
16558
+ ],
16559
+ normalizedInnerRect: {
16560
+ tl: {
16561
+ x: .283494,
16562
+ y: .375
16563
+ },
16564
+ tr: {
16565
+ x: .716506,
16566
+ y: .375
16567
+ },
16568
+ bl: {
16569
+ x: .283494,
16570
+ y: .75
16571
+ },
16572
+ br: {
16573
+ x: .716506,
16574
+ y: .75
16575
+ }
16576
+ }
16577
+ },
16578
+ diamond: {
16579
+ label: "Diamond",
16580
+ sides: 4,
16581
+ defaultWidth: 100,
16582
+ defaultHeight: 100,
16583
+ normalizedPoints: [
16584
+ {
16585
+ x: .5,
16586
+ y: 0
16587
+ },
16588
+ {
16589
+ x: 1,
16590
+ y: .5
16591
+ },
16592
+ {
16593
+ x: .5,
16594
+ y: 1
16595
+ },
16596
+ {
16597
+ x: 0,
16598
+ y: .5
16599
+ }
16600
+ ],
16601
+ normalizedInnerRect: {
16602
+ tl: {
16603
+ x: .25,
16604
+ y: .25
16605
+ },
16606
+ tr: {
16607
+ x: .75,
16608
+ y: .25
16609
+ },
16610
+ bl: {
16611
+ x: .25,
16612
+ y: .75
16613
+ },
16614
+ br: {
16615
+ x: .75,
16616
+ y: .75
16617
+ }
16618
+ }
16619
+ },
16620
+ pentagon: {
16621
+ label: "Pentagon",
16622
+ sides: 5,
16623
+ defaultWidth: 100,
16624
+ defaultHeight: 100,
16625
+ normalizedPoints: [
16626
+ {
16627
+ x: .5,
16628
+ y: 0
16629
+ },
16630
+ {
16631
+ x: .975528,
16632
+ y: .345492
16633
+ },
16634
+ {
16635
+ x: .793893,
16636
+ y: .904508
16637
+ },
16638
+ {
16639
+ x: .206107,
16640
+ y: .904508
16641
+ },
16642
+ {
16643
+ x: .024472,
16644
+ y: .345492
16645
+ }
16646
+ ],
16647
+ normalizedInnerRect: {
16648
+ tl: {
16649
+ x: .132634,
16650
+ y: .316578
16651
+ },
16652
+ tr: {
16653
+ x: .867366,
16654
+ y: .316578
16655
+ },
16656
+ bl: {
16657
+ x: .132634,
16658
+ y: .678381
16659
+ },
16660
+ br: {
16661
+ x: .867366,
16662
+ y: .678381
16663
+ }
16664
+ }
16665
+ },
16666
+ hexagon: {
16667
+ label: "Hexagon",
16668
+ sides: 6,
16669
+ defaultWidth: 100,
16670
+ defaultHeight: 100,
16671
+ normalizedPoints: [
16672
+ {
16673
+ x: .5,
16674
+ y: 0
16675
+ },
16676
+ {
16677
+ x: .933013,
16678
+ y: .25
16679
+ },
16680
+ {
16681
+ x: .933013,
16682
+ y: .75
16683
+ },
16684
+ {
16685
+ x: .5,
16686
+ y: 1
16687
+ },
16688
+ {
16689
+ x: .066987,
16690
+ y: .75
16691
+ },
16692
+ {
16693
+ x: .066987,
16694
+ y: .25
16695
+ }
16696
+ ],
16697
+ normalizedInnerRect: {
16698
+ tl: {
16699
+ x: .066987,
16700
+ y: .25
16701
+ },
16702
+ tr: {
16703
+ x: .933013,
16704
+ y: .25
16705
+ },
16706
+ bl: {
16707
+ x: .066987,
16708
+ y: .75
16709
+ },
16710
+ br: {
16711
+ x: .933013,
16712
+ y: .75
16713
+ }
16714
+ }
16715
+ },
16716
+ octagon: {
16717
+ label: "Octagon",
16718
+ sides: 8,
16719
+ defaultWidth: 100,
16720
+ defaultHeight: 100,
16721
+ normalizedPoints: [
16722
+ {
16723
+ x: .5,
16724
+ y: 0
16725
+ },
16726
+ {
16727
+ x: .853553,
16728
+ y: .146447
16729
+ },
16730
+ {
16731
+ x: 1,
16732
+ y: .5
16733
+ },
16734
+ {
16735
+ x: .853553,
16736
+ y: .853553
16737
+ },
16738
+ {
16739
+ x: .5,
16740
+ y: 1
16741
+ },
16742
+ {
16743
+ x: .146447,
16744
+ y: .853553
16745
+ },
16746
+ {
16747
+ x: 0,
16748
+ y: .5
16749
+ },
16750
+ {
16751
+ x: .146447,
16752
+ y: .146447
16753
+ }
16754
+ ],
16755
+ normalizedInnerRect: {
16756
+ tl: {
16757
+ x: .25,
16758
+ y: .25
16759
+ },
16760
+ tr: {
16761
+ x: .75,
16762
+ y: .25
16763
+ },
16764
+ bl: {
16765
+ x: .25,
16766
+ y: .75
16767
+ },
16768
+ br: {
16769
+ x: .75,
16770
+ y: .75
16771
+ }
16772
+ }
16773
+ },
16774
+ decagon: {
16775
+ label: "Decagon",
16776
+ sides: 10,
16777
+ defaultWidth: 100,
16778
+ defaultHeight: 100,
16779
+ normalizedPoints: [
16780
+ {
16781
+ x: .5,
16782
+ y: 0
16783
+ },
16784
+ {
16785
+ x: .793893,
16786
+ y: .095492
16787
+ },
16788
+ {
16789
+ x: .975528,
16790
+ y: .345492
16791
+ },
16792
+ {
16793
+ x: .975528,
16794
+ y: .654508
16795
+ },
16796
+ {
16797
+ x: .793893,
16798
+ y: .904508
16799
+ },
16800
+ {
16801
+ x: .5,
16802
+ y: 1
16803
+ },
16804
+ {
16805
+ x: .206107,
16806
+ y: .904508
16807
+ },
16808
+ {
16809
+ x: .024472,
16810
+ y: .654508
16811
+ },
16812
+ {
16813
+ x: .024472,
16814
+ y: .345492
16815
+ },
16816
+ {
16817
+ x: .206107,
16818
+ y: .095492
16819
+ }
16820
+ ],
16821
+ normalizedInnerRect: {
16822
+ tl: {
16823
+ x: .093851,
16824
+ y: .35
16825
+ },
16826
+ tr: {
16827
+ x: .906149,
16828
+ y: .35
16829
+ },
16830
+ bl: {
16831
+ x: .093851,
16832
+ y: .75
16833
+ },
16834
+ br: {
16835
+ x: .906149,
16836
+ y: .75
16837
+ }
16838
+ }
16839
+ }
16840
+ };
16841
+ /**
16842
+ * Scales a preset's normalized points and inner rect to actual pixel dimensions.
16843
+ *
16844
+ * Points are normalized so that minX = 0 and minY = 0 in the resulting pixel
16845
+ * space. This ensures the polygon group's position corresponds exactly to the
16846
+ * visual top-left of the polygon, which is required for the snapping system to
16847
+ * work correctly (it assumes nodeBox.x === node.x()).
16848
+ */
16849
+ function instantiatePreset(def, width, height) {
16850
+ const rawPoints = def.normalizedPoints.map((p) => ({
16851
+ x: p.x * width,
16852
+ y: p.y * height
16853
+ }));
16854
+ const minX = Math.min(...rawPoints.map((p) => p.x));
16855
+ const minY = Math.min(...rawPoints.map((p) => p.y));
16856
+ const points = rawPoints.map((p) => ({
16857
+ x: p.x - minX,
16858
+ y: p.y - minY
16859
+ }));
16860
+ const ir = def.normalizedInnerRect;
16861
+ const innerRect = {
16862
+ tl: {
16863
+ x: ir.tl.x * width - minX,
16864
+ y: ir.tl.y * height - minY
16865
+ },
16866
+ tr: {
16867
+ x: ir.tr.x * width - minX,
16868
+ y: ir.tr.y * height - minY
16869
+ },
16870
+ bl: {
16871
+ x: ir.bl.x * width - minX,
16872
+ y: ir.bl.y * height - minY
16873
+ },
16874
+ br: {
16875
+ x: ir.br.x * width - minX,
16876
+ y: ir.br.y * height - minY
16877
+ }
16878
+ };
16879
+ const visualWidth = Math.max(...points.map((p) => p.x));
16880
+ const visualHeight = Math.max(...points.map((p) => p.y));
16881
+ return {
16882
+ points,
16883
+ innerRect,
16884
+ width: visualWidth,
16885
+ height: visualHeight
16886
+ };
16887
+ }
16888
+
16889
+ //#endregion
16890
+ //#region src/nodes/polygon/polygon.ts
16891
+ function computePolygonBounds(points) {
16892
+ if (!points.length) return {
16893
+ width: 0,
16894
+ height: 0
16895
+ };
16896
+ const maxX = Math.max(...points.map((p) => p.x));
16897
+ const maxY = Math.max(...points.map((p) => p.y));
16898
+ return {
16899
+ width: Math.max(1, maxX),
16900
+ height: Math.max(1, maxY)
16901
+ };
16902
+ }
16903
+ function polygonSelfRect() {
16904
+ const pts = this.getAttr("points");
16905
+ if (!pts?.length) return {
16906
+ x: 0,
16907
+ y: 0,
16908
+ width: 0,
16909
+ height: 0
16910
+ };
16911
+ const minX = Math.min(...pts.map((p) => p.x));
16912
+ const minY = Math.min(...pts.map((p) => p.y));
16913
+ const maxX = Math.max(...pts.map((p) => p.x));
16914
+ const maxY = Math.max(...pts.map((p) => p.y));
16915
+ return {
16916
+ x: minX,
16917
+ y: minY,
16918
+ width: maxX - minX,
16919
+ height: maxY - minY
16920
+ };
16921
+ }
16922
+ function getPolygonLabelTextBounds(innerRect, paddingX, paddingY) {
16923
+ return {
16924
+ x: innerRect.tl.x + paddingX,
16925
+ y: innerRect.tl.y + paddingY,
16926
+ width: Math.max(1, innerRect.tr.x - innerRect.tl.x - paddingX * 2),
16927
+ height: Math.max(1, innerRect.bl.y - innerRect.tl.y - paddingY * 2)
16928
+ };
16929
+ }
16930
+ function sceneFunc(context, shape) {
16931
+ const pts = shape.getAttr("points");
16932
+ if (!pts || pts.length < 3) return;
16933
+ context.beginPath();
16934
+ context.moveTo(pts[0].x, pts[0].y);
16935
+ for (let i = 1; i < pts.length; i++) context.lineTo(pts[i].x, pts[i].y);
16936
+ context.closePath();
16937
+ context.fillStrokeShape(shape);
16938
+ }
16939
+ /**
16940
+ * Draws an "inside" stroke for the border shape.
16941
+ * Clips to the polygon interior then draws 2× the stroke width so the outer
16942
+ * half is clipped away — resulting in a stroke of correct visual width that
16943
+ * stays entirely inside the polygon boundary.
16944
+ * strokeWidth is intentionally 0 on the shape (so getClientRect doesn't
16945
+ * expand the bounding box); the real width is stored in `innerStrokeWidth`.
16946
+ */
16947
+ function borderSceneFunc(context, shape) {
16948
+ const pts = shape.getAttr("points");
16949
+ if (!pts || pts.length < 3) return;
16950
+ const sw = shape.getAttr("innerStrokeWidth");
16951
+ if (!sw) return;
16952
+ const stroke = shape.stroke();
16953
+ if (!stroke || stroke === "transparent") return;
16954
+ const ctx = context._context;
16955
+ const drawPath = () => {
16956
+ ctx.beginPath();
16957
+ ctx.moveTo(pts[0].x, pts[0].y);
16958
+ for (let i = 1; i < pts.length; i++) ctx.lineTo(pts[i].x, pts[i].y);
16959
+ ctx.closePath();
16960
+ };
16961
+ ctx.save();
16962
+ drawPath();
16963
+ ctx.clip();
16964
+ drawPath();
16965
+ ctx.lineWidth = sw * 2;
16966
+ ctx.strokeStyle = stroke;
16967
+ ctx.stroke();
16968
+ ctx.restore();
16969
+ }
16970
+ var WeavePolygonNode = class extends WeaveNode {
16971
+ nodeType = WEAVE_POLYGON_NODE_TYPE;
16972
+ initialize = void 0;
16973
+ _transforming = false;
16974
+ get shapeLabelEditor() {
16975
+ this._shapeLabelEditor ??= new WeaveShapeLabelEditor(this.instance);
16976
+ return this._shapeLabelEditor;
16977
+ }
16978
+ constructor(params) {
16979
+ super();
16980
+ const { config } = params ?? {};
16981
+ this.config = { transform: { ...config?.transform } };
16982
+ }
16983
+ getLabelTextBounds(group) {
16984
+ const attrs = group.getAttrs();
16985
+ const innerRect = attrs.innerRect;
16986
+ const paddingX = attrs.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
16987
+ const paddingY = attrs.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
16988
+ if (!innerRect) return {
16989
+ x: 0,
16990
+ y: 0,
16991
+ width: 1,
16992
+ height: 1
16993
+ };
16994
+ return getPolygonLabelTextBounds(innerRect, paddingX, paddingY);
16995
+ }
16996
+ scalePolygonByDimensions(polygon, nextProps, nodeInstance) {
16997
+ let points = polygon.getAttr("points");
16998
+ const propsMaxX = points.length ? Math.max(...points.map((p) => p.x)) : 0;
16999
+ const propsMaxY = points.length ? Math.max(...points.map((p) => p.y)) : 0;
17000
+ const wantWidth = nextProps.width;
17001
+ const wantHeight = nextProps.height;
17002
+ if (wantWidth === void 0 || wantHeight === void 0) return points;
17003
+ const sX = propsMaxX > 0 ? wantWidth / propsMaxX : 1;
17004
+ const sY = propsMaxY > 0 ? wantHeight / propsMaxY : 1;
17005
+ if (Math.abs(sX - 1) <= .001 && Math.abs(sY - 1) <= .001) return points;
17006
+ const scaledPoints = points.map((p) => ({
17007
+ x: p.x * sX,
17008
+ y: p.y * sY
17009
+ }));
17010
+ const prevInnerRect = polygon.getAttr("innerRect");
17011
+ if (prevInnerRect) {
17012
+ const scaledInnerRect = {
17013
+ tl: {
17014
+ x: prevInnerRect.tl.x * sX,
17015
+ y: prevInnerRect.tl.y * sY
17016
+ },
17017
+ tr: {
17018
+ x: prevInnerRect.tr.x * sX,
17019
+ y: prevInnerRect.tr.y * sY
17020
+ },
17021
+ bl: {
17022
+ x: prevInnerRect.bl.x * sX,
17023
+ y: prevInnerRect.bl.y * sY
17024
+ },
17025
+ br: {
17026
+ x: prevInnerRect.br.x * sX,
17027
+ y: prevInnerRect.br.y * sY
17028
+ }
17029
+ };
17030
+ polygon.setAttr("innerRect", scaledInnerRect);
17031
+ }
17032
+ polygon.setAttr("points", scaledPoints);
17033
+ points = scaledPoints;
17034
+ if (!this._transforming) this.instance.updateNode(this.serialize(nodeInstance));
17035
+ return points;
17036
+ }
17037
+ onLabelGrow(polygon, bgShape, borderShape, nodeInstance, neededHeight) {
17038
+ const livePoints = polygon.getAttr("points");
17039
+ const liveInnerRect = polygon.getAttr("innerRect");
17040
+ if (!liveInnerRect) return;
17041
+ const currentBoundsHeight = liveInnerRect.bl.y - liveInnerRect.tl.y;
17042
+ if (neededHeight <= currentBoundsHeight) return;
17043
+ const oldHeight = Math.max(...livePoints.map((p) => p.y));
17044
+ const scale = currentBoundsHeight > 0 ? neededHeight / currentBoundsHeight : 1;
17045
+ const newHeight = oldHeight * scale;
17046
+ const newPoints = livePoints.map((p) => ({
17047
+ ...p,
17048
+ y: p.y * scale
17049
+ }));
17050
+ const newInnerRect = {
17051
+ tl: {
17052
+ ...liveInnerRect.tl,
17053
+ y: liveInnerRect.tl.y * scale
17054
+ },
17055
+ tr: {
17056
+ ...liveInnerRect.tr,
17057
+ y: liveInnerRect.tr.y * scale
17058
+ },
17059
+ bl: {
17060
+ ...liveInnerRect.bl,
17061
+ y: liveInnerRect.bl.y * scale
17062
+ },
17063
+ br: {
17064
+ ...liveInnerRect.br,
17065
+ y: liveInnerRect.br.y * scale
17066
+ }
17067
+ };
17068
+ polygon.setAttr("points", newPoints);
17069
+ polygon.setAttr("innerRect", newInnerRect);
17070
+ polygon.setAttr("height", newHeight);
17071
+ bgShape?.setAttr("points", newPoints);
17072
+ borderShape?.setAttr("points", newPoints);
17073
+ if (!this._transforming) this.instance.updateNode(this.serialize(nodeInstance));
17074
+ }
17075
+ triggerPolygonLabelEdit(polygon, props) {
17076
+ const onCommit = (labelText) => {
17077
+ const updatedGroup = this.instance.getStage().findOne(`#${props.id}`);
17078
+ if (!updatedGroup) return;
17079
+ const serialized = this.serialize(updatedGroup);
17080
+ serialized.props.labelText = labelText;
17081
+ this.instance.updateNode(serialized);
17082
+ };
17083
+ const currentLabelTextBounds = this.getLabelTextBounds(polygon);
17084
+ this.shapeLabelEditor.triggerEditMode(polygon, currentLabelTextBounds, onCommit, (neededShapeHeight) => {
17085
+ const liveAttrs = polygon.getAttrs();
17086
+ const livePoints = liveAttrs.points;
17087
+ const liveInnerRect = liveAttrs.innerRect;
17088
+ const liveInnerRectHeight = liveInnerRect.bl.y - liveInnerRect.tl.y;
17089
+ if (neededShapeHeight <= liveInnerRectHeight) return;
17090
+ const oldHeight = Math.max(...livePoints.map((p) => p.y));
17091
+ const scale = liveInnerRectHeight > 0 ? neededShapeHeight / liveInnerRectHeight : 1;
17092
+ const newHeight = oldHeight * scale;
17093
+ const newPoints = livePoints.map((p) => ({
17094
+ ...p,
17095
+ y: p.y * scale
17096
+ }));
17097
+ const newInnerRect = {
17098
+ tl: {
17099
+ ...liveInnerRect.tl,
17100
+ y: liveInnerRect.tl.y * scale
17101
+ },
17102
+ tr: {
17103
+ ...liveInnerRect.tr,
17104
+ y: liveInnerRect.tr.y * scale
17105
+ },
17106
+ bl: {
17107
+ ...liveInnerRect.bl,
17108
+ y: liveInnerRect.bl.y * scale
17109
+ },
17110
+ br: {
17111
+ ...liveInnerRect.br,
17112
+ y: liveInnerRect.br.y * scale
17113
+ }
17114
+ };
17115
+ polygon.setAttrs({
17116
+ points: newPoints,
17117
+ innerRect: newInnerRect,
17118
+ height: newHeight
17119
+ });
17120
+ this.onUpdate(polygon, polygon.getAttrs());
17121
+ const newLabelTextBounds = this.getLabelTextBounds(polygon);
17122
+ this.shapeLabelEditor.repositionTextArea(polygon, newLabelTextBounds);
17123
+ });
17124
+ }
17125
+ scaleReset(group) {
17126
+ const scaleX = group.scaleX();
17127
+ const scaleY = group.scaleY();
17128
+ if (scaleX === 1 && scaleY === 1) return;
17129
+ const points = group.getAttr("points");
17130
+ const innerRect = group.getAttr("innerRect");
17131
+ const newPoints = points.map((p) => ({
17132
+ x: p.x * scaleX,
17133
+ y: p.y * scaleY
17134
+ }));
17135
+ const newInnerRect = innerRect ? {
17136
+ tl: {
17137
+ x: innerRect.tl.x * scaleX,
17138
+ y: innerRect.tl.y * scaleY
17139
+ },
17140
+ tr: {
17141
+ x: innerRect.tr.x * scaleX,
17142
+ y: innerRect.tr.y * scaleY
17143
+ },
17144
+ bl: {
17145
+ x: innerRect.bl.x * scaleX,
17146
+ y: innerRect.bl.y * scaleY
17147
+ },
17148
+ br: {
17149
+ x: innerRect.br.x * scaleX,
17150
+ y: innerRect.br.y * scaleY
17151
+ }
17152
+ } : void 0;
17153
+ const absTransform = group.getAbsoluteTransform().copy();
17154
+ group.setAttr("points", newPoints);
17155
+ if (newInnerRect) group.setAttr("innerRect", newInnerRect);
17156
+ group.scaleX(1);
17157
+ group.scaleY(1);
17158
+ group.setAttr("width", Math.max(...newPoints.map((p) => p.x)));
17159
+ group.setAttr("height", Math.max(...newPoints.map((p) => p.y)));
17160
+ const newTransform = group.getAbsoluteTransform();
17161
+ const dx = absTransform.m[4] - newTransform.m[4];
17162
+ const dy = absTransform.m[5] - newTransform.m[5];
17163
+ group.x(group.x() + dx);
17164
+ group.y(group.y() + dy);
17165
+ }
17166
+ onRender(props) {
17167
+ const polygon = new Konva.Group({
17168
+ ...props,
17169
+ name: "node"
17170
+ });
17171
+ const points = polygon.getAttr("points");
17172
+ const strokeWidth = props.strokeWidth || 0;
17173
+ const bgShape = new Konva.Shape({
17174
+ id: `${props.id}-bg`,
17175
+ nodeId: props.id,
17176
+ points,
17177
+ ...computePolygonBounds(points),
17178
+ fill: props.fill || "transparent",
17179
+ strokeWidth: 0,
17180
+ strokeScaleEnabled: false,
17181
+ sceneFunc
17182
+ });
17183
+ bgShape.getSelfRect = polygonSelfRect.bind(bgShape);
17184
+ polygon.add(bgShape);
17185
+ const borderShape = new Konva.Shape({
17186
+ id: `${props.id}-border`,
17187
+ points,
17188
+ fill: "transparent",
17189
+ stroke: props.stroke || "transparent",
17190
+ strokeWidth: 0,
17191
+ innerStrokeWidth: strokeWidth,
17192
+ strokeScaleEnabled: false,
17193
+ listening: false,
17194
+ sceneFunc: borderSceneFunc
17195
+ });
17196
+ borderShape.getSelfRect = polygonSelfRect.bind(borderShape);
17197
+ polygon.add(borderShape);
17198
+ const innerRect = polygon.getAttr("innerRect");
17199
+ const paddingX = props.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
17200
+ const paddingY = props.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
17201
+ const labelTextBounds = innerRect ? getPolygonLabelTextBounds(innerRect, paddingX, paddingY) : {
17202
+ x: 0,
17203
+ y: 0,
17204
+ width: 1,
17205
+ height: 1
17206
+ };
17207
+ this.shapeLabelEditor.renderLabel(polygon, props, labelTextBounds);
17208
+ borderShape.moveToTop();
17209
+ bgShape.moveToBottom();
17210
+ this.setupDefaultNodeAugmentation(polygon);
15570
17211
  const defaultTransformerProperties = this.defaultGetTransformerProperties(this.config.transform);
15571
- regularPolygon.getTransformerProperties = function() {
17212
+ polygon.getTransformerProperties = function() {
15572
17213
  return {
15573
17214
  ...defaultTransformerProperties,
15574
17215
  enabledAnchors: [
15575
17216
  "top-left",
17217
+ "top-center",
15576
17218
  "top-right",
17219
+ "middle-right",
17220
+ "middle-left",
15577
17221
  "bottom-left",
17222
+ "bottom-center",
15578
17223
  "bottom-right"
15579
17224
  ],
15580
- keepRatio: true
17225
+ keepRatio: false
15581
17226
  };
15582
17227
  };
15583
- regularPolygon.allowedAnchors = function() {
17228
+ polygon.allowedAnchors = function() {
15584
17229
  return [
15585
17230
  "top-left",
17231
+ "top-center",
15586
17232
  "top-right",
17233
+ "middle-right",
17234
+ "middle-left",
15587
17235
  "bottom-left",
17236
+ "bottom-center",
15588
17237
  "bottom-right"
15589
17238
  ];
15590
17239
  };
15591
- this.setupDefaultNodeEvents(regularPolygon);
15592
- return regularPolygon;
17240
+ this.setupDefaultNodeEvents(polygon);
17241
+ polygon.on("transformstart", () => {
17242
+ this._transforming = true;
17243
+ });
17244
+ polygon.on("transform", () => {
17245
+ this.scaleReset(polygon);
17246
+ this.onUpdate(polygon, polygon.getAttrs());
17247
+ });
17248
+ polygon.on("transformend", () => {
17249
+ this._transforming = false;
17250
+ });
17251
+ polygon.dblClick = () => {
17252
+ if (this.shapeLabelEditor.isEditing()) return;
17253
+ if (!(this.isSelecting() && this.isNodeSelected(polygon))) return;
17254
+ this.triggerPolygonLabelEdit(polygon, props);
17255
+ };
17256
+ polygon.getNodeMinSize = () => {
17257
+ return computePolygonLabelMinSize(this.instance.getStage(), polygon);
17258
+ };
17259
+ return polygon;
15593
17260
  }
15594
17261
  onUpdate(nodeInstance, nextProps) {
15595
17262
  nodeInstance.setAttrs({ ...nextProps });
15596
- const sides = nodeInstance.getAttr("sides");
15597
- const radius = nodeInstance.getAttr("radius");
15598
- const regularPolygon = nodeInstance;
15599
- const internalRPBg = regularPolygon.findOne(`#${nextProps.id}-bg`);
15600
- const internalRPBorder = regularPolygon.findOne(`#${nextProps.id}-border`);
15601
- if (internalRPBg) {
15602
- internalRPBg.setAttrs({
15603
- ...nextProps,
15604
- name: void 0,
15605
- id: `${nextProps.id}-bg`,
15606
- nodeId: nextProps.id,
15607
- x: radius,
15608
- y: radius,
15609
- sides,
15610
- radius,
17263
+ const polygon = nodeInstance;
17264
+ const strokeWidth = nextProps.strokeWidth || 0;
17265
+ const points = this.scalePolygonByDimensions(polygon, nextProps, nodeInstance);
17266
+ const bgShape = polygon.findOne(`#${nextProps.id}-bg`);
17267
+ if (bgShape) {
17268
+ bgShape.setAttrs({
17269
+ points,
17270
+ ...computePolygonBounds(points),
15611
17271
  fill: nextProps.fill || "transparent",
15612
17272
  strokeWidth: 0,
15613
- strokeScaleEnabled: true,
15614
- rotation: 0
17273
+ strokeScaleEnabled: false
15615
17274
  });
15616
- const internalRPBgBox = internalRPBg.getClientRect({ relativeTo: regularPolygon });
15617
- internalRPBg.x(internalRPBg.x() - internalRPBgBox.x);
15618
- internalRPBg.y(internalRPBg.y() - internalRPBgBox.y);
15619
- internalRPBg.moveToBottom();
17275
+ bgShape.moveToBottom();
15620
17276
  }
15621
- if (internalRPBorder) {
15622
- internalRPBorder.setAttrs({
15623
- ...nextProps,
15624
- name: void 0,
15625
- id: `${nextProps.id}-border`,
15626
- x: radius,
15627
- y: radius,
15628
- sides,
15629
- radius: radius - (nextProps.strokeWidth || 0) / 2,
15630
- stroke: nextProps.stroke || "transparent",
15631
- strokeWidth: nextProps.strokeWidth || 0,
15632
- strokeScaleEnabled: true,
15633
- listening: false,
15634
- rotation: 0
15635
- });
15636
- const internalRPBorderBox = internalRPBorder.getClientRect({ relativeTo: regularPolygon });
15637
- internalRPBorder.x(internalRPBorder.x() - internalRPBorderBox.x);
15638
- internalRPBorder.y(internalRPBorder.y() - internalRPBorderBox.y);
15639
- internalRPBorder.moveToTop();
17277
+ const borderShape = polygon.findOne(`#${nextProps.id}-border`);
17278
+ if (borderShape) borderShape.setAttrs({
17279
+ points,
17280
+ fill: "transparent",
17281
+ stroke: nextProps.stroke || "transparent",
17282
+ strokeWidth: 0,
17283
+ innerStrokeWidth: strokeWidth,
17284
+ strokeScaleEnabled: false,
17285
+ listening: false
17286
+ });
17287
+ const paddingX = nextProps.labelPaddingX ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingX;
17288
+ const paddingY = nextProps.labelPaddingY ?? WEAVE_SHAPE_LABEL_DEFAULTS.labelPaddingY;
17289
+ const innerRect = polygon.getAttr("innerRect");
17290
+ const labelTextBounds = innerRect ? getPolygonLabelTextBounds(innerRect, paddingX, paddingY) : {
17291
+ x: 0,
17292
+ y: 0,
17293
+ width: 1,
17294
+ height: 1
17295
+ };
17296
+ this.shapeLabelEditor.updateLabel(polygon, nextProps, labelTextBounds, (neededHeight) => this.onLabelGrow(polygon, bgShape, borderShape, nodeInstance, neededHeight));
17297
+ const labelNode = polygon.findOne(`#${labelId(nextProps.id)}`);
17298
+ if (labelNode) {
17299
+ labelNode.moveToTop();
17300
+ borderShape?.moveToTop();
15640
17301
  }
15641
17302
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
15642
- if (nodesSelectionPlugin) {
15643
- const actualSelectedNodes = nodesSelectionPlugin.getSelectedNodes();
15644
- nodesSelectionPlugin.setSelectedNodes(actualSelectedNodes);
15645
- nodesSelectionPlugin.getTransformer().forceUpdate();
15646
- }
15647
- }
15648
- scaleReset(node) {
15649
- const absTransform = node.getAbsoluteTransform().copy();
15650
- const radius = node.getAttr("radius");
15651
- node.setAttrs({ radius: radius * node.scaleX() });
15652
- node.scaleX(1);
15653
- node.scaleY(1);
15654
- const newTransform = node.getAbsoluteTransform();
15655
- const dx = absTransform.m[4] - newTransform.m[4];
15656
- const dy = absTransform.m[5] - newTransform.m[5];
15657
- node.x(node.x() + dx);
15658
- node.y(node.y() + dy);
17303
+ if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
15659
17304
  }
15660
- realOffset(element) {
17305
+ realOffset(_element) {
15661
17306
  return {
15662
- x: element.props.radius,
15663
- y: element.props.radius
17307
+ x: 0,
17308
+ y: 0
15664
17309
  };
15665
17310
  }
15666
17311
  static defaultState(nodeId) {
17312
+ const preset = WEAVE_POLYGON_PRESETS.pentagon;
17313
+ const { points, innerRect, width, height } = instantiatePreset(preset, preset.defaultWidth, preset.defaultHeight);
15667
17314
  return {
15668
17315
  ...super.defaultState(nodeId),
15669
- type: WEAVE_REGULAR_POLYGON_NODE_TYPE,
17316
+ type: WEAVE_POLYGON_NODE_TYPE,
15670
17317
  props: {
15671
17318
  ...super.defaultState(nodeId).props,
15672
- nodeType: WEAVE_REGULAR_POLYGON_NODE_TYPE,
17319
+ nodeType: WEAVE_POLYGON_NODE_TYPE,
15673
17320
  x: 0,
15674
17321
  y: 0,
15675
- sides: 5,
15676
- radius: 100,
17322
+ width,
17323
+ height,
17324
+ sides: preset.sides,
17325
+ points,
17326
+ innerRect,
15677
17327
  stroke: "#000000",
15678
17328
  fill: "#FFFFFF",
15679
17329
  strokeWidth: 1,
15680
- strokeScaleEnabled: true,
17330
+ strokeScaleEnabled: false,
15681
17331
  rotation: 0,
15682
17332
  zIndex: 1,
15683
- children: []
17333
+ children: [],
17334
+ ...WEAVE_SHAPE_LABEL_DEFAULTS
15684
17335
  }
15685
17336
  };
15686
17337
  }
@@ -15688,38 +17339,103 @@ var WeaveRegularPolygonNode = class extends WeaveNode {
15688
17339
  return mergeExceptArrays(defaultNodeState, { props: {
15689
17340
  x: props.x,
15690
17341
  y: props.y,
17342
+ width: props.width,
17343
+ height: props.height,
15691
17344
  sides: props.sides,
15692
- radius: props.radius,
17345
+ points: props.points,
17346
+ innerRect: props.innerRect,
15693
17347
  rotation: props.rotation,
15694
17348
  fill: props.fill,
15695
17349
  ...props.stroke && { stroke: props.stroke },
15696
- ...props.strokeWidth && { strokeWidth: props.strokeWidth }
17350
+ ...props.strokeWidth !== void 0 && { strokeWidth: props.strokeWidth },
17351
+ ...props.labelText !== void 0 && { labelText: props.labelText },
17352
+ ...props.labelFontFamily !== void 0 && { labelFontFamily: props.labelFontFamily },
17353
+ ...props.labelFontSize !== void 0 && { labelFontSize: props.labelFontSize },
17354
+ ...props.labelFontStyle !== void 0 && { labelFontStyle: props.labelFontStyle },
17355
+ ...props.labelFontVariant !== void 0 && { labelFontVariant: props.labelFontVariant },
17356
+ ...props.labelFill !== void 0 && { labelFill: props.labelFill },
17357
+ ...props.labelAlign !== void 0 && { labelAlign: props.labelAlign },
17358
+ ...props.labelVerticalAlign !== void 0 && { labelVerticalAlign: props.labelVerticalAlign },
17359
+ ...props.labelLetterSpacing !== void 0 && { labelLetterSpacing: props.labelLetterSpacing },
17360
+ ...props.labelLineHeight !== void 0 && { labelLineHeight: props.labelLineHeight },
17361
+ ...props.labelPaddingX !== void 0 && { labelPaddingX: props.labelPaddingX },
17362
+ ...props.labelPaddingY !== void 0 && { labelPaddingY: props.labelPaddingY }
15697
17363
  } });
15698
17364
  }
15699
17365
  static updateNodeState(prevNodeState, nextProps) {
15700
17366
  return mergeExceptArrays(prevNodeState, { props: {
15701
17367
  x: nextProps.x,
15702
17368
  y: nextProps.y,
17369
+ ...nextProps.width !== void 0 && { width: nextProps.width },
17370
+ ...nextProps.height !== void 0 && { height: nextProps.height },
15703
17371
  sides: nextProps.sides,
15704
- radius: nextProps.radius,
17372
+ points: nextProps.points,
17373
+ innerRect: nextProps.innerRect,
15705
17374
  rotation: nextProps.rotation,
15706
17375
  fill: nextProps.fill,
15707
17376
  ...nextProps.stroke && { stroke: nextProps.stroke },
15708
- ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth }
17377
+ ...nextProps.strokeWidth !== void 0 && { strokeWidth: nextProps.strokeWidth },
17378
+ ...nextProps.labelText !== void 0 && { labelText: nextProps.labelText },
17379
+ ...nextProps.labelFontFamily !== void 0 && { labelFontFamily: nextProps.labelFontFamily },
17380
+ ...nextProps.labelFontSize !== void 0 && { labelFontSize: nextProps.labelFontSize },
17381
+ ...nextProps.labelFontStyle !== void 0 && { labelFontStyle: nextProps.labelFontStyle },
17382
+ ...nextProps.labelFontVariant !== void 0 && { labelFontVariant: nextProps.labelFontVariant },
17383
+ ...nextProps.labelFill !== void 0 && { labelFill: nextProps.labelFill },
17384
+ ...nextProps.labelAlign !== void 0 && { labelAlign: nextProps.labelAlign },
17385
+ ...nextProps.labelVerticalAlign !== void 0 && { labelVerticalAlign: nextProps.labelVerticalAlign },
17386
+ ...nextProps.labelLetterSpacing !== void 0 && { labelLetterSpacing: nextProps.labelLetterSpacing },
17387
+ ...nextProps.labelLineHeight !== void 0 && { labelLineHeight: nextProps.labelLineHeight },
17388
+ ...nextProps.labelPaddingX !== void 0 && { labelPaddingX: nextProps.labelPaddingX },
17389
+ ...nextProps.labelPaddingY !== void 0 && { labelPaddingY: nextProps.labelPaddingY }
15709
17390
  } });
15710
17391
  }
15711
17392
  static getSchema() {
15712
17393
  const baseSchema = super.getSchema();
15713
17394
  const nodeSchema = baseSchema.extend({
15714
- type: z.literal(WEAVE_REGULAR_POLYGON_NODE_TYPE).describe(`Type of the node, for a regular polygon node it will always be "${WEAVE_REGULAR_POLYGON_NODE_TYPE}"`),
17395
+ type: z.literal(WEAVE_POLYGON_NODE_TYPE).describe(`Type of the node, for a polygon node it will always be "${WEAVE_POLYGON_NODE_TYPE}"`),
15715
17396
  props: baseSchema.shape.props.extend({
15716
- nodeType: z.literal(WEAVE_REGULAR_POLYGON_NODE_TYPE).describe(`Type of the node, for a regular polygon node it will always be "${WEAVE_REGULAR_POLYGON_NODE_TYPE}"`),
15717
- sides: z.number().describe("Number of sides of the regular polygon, must be 3 or more"),
15718
- radius: z.number().describe("Radius of the regular polygon in pixels, distance from the center to any vertex"),
15719
- fill: z.string().describe("Fill color of the regular polygon in hex format with alpha channel (e.g. #RRGGBBAA)"),
15720
- stroke: z.string().describe("Stroke color of the regular polygon in hex format with alpha channel (e.g. #RRGGBBAA)"),
15721
- strokeWidth: z.number().describe("Stroke width of the regular polygon in pixels"),
15722
- strokeScaleEnabled: z.boolean().describe("Whether the regular polygon stroke width should scale when the node is scaled. Defaults to true.")
17397
+ nodeType: z.literal(WEAVE_POLYGON_NODE_TYPE).describe(`Type of the node, for a polygon node it will always be "${WEAVE_POLYGON_NODE_TYPE}"`),
17398
+ sides: z.number().describe("Number of sides of the polygon (3 or more)"),
17399
+ width: z.number().optional().describe("Visual width of the polygon in pixels (= maxX of vertices). Setting this rescales vertices proportionally."),
17400
+ height: z.number().optional().describe("Visual height of the polygon in pixels (= maxY of vertices). Setting this rescales vertices proportionally."),
17401
+ points: z.array(z.object({
17402
+ x: z.number(),
17403
+ y: z.number()
17404
+ })).describe("Vertex positions of the polygon in group-local pixel space"),
17405
+ innerRect: z.object({
17406
+ tl: z.object({
17407
+ x: z.number(),
17408
+ y: z.number()
17409
+ }),
17410
+ tr: z.object({
17411
+ x: z.number(),
17412
+ y: z.number()
17413
+ }),
17414
+ bl: z.object({
17415
+ x: z.number(),
17416
+ y: z.number()
17417
+ }),
17418
+ br: z.object({
17419
+ x: z.number(),
17420
+ y: z.number()
17421
+ })
17422
+ }).describe("Largest inscribed axis-aligned rectangle inside the polygon (used for label bounds)"),
17423
+ fill: z.string().describe("Fill color of the polygon in hex format with alpha channel (e.g. #RRGGBBAA)"),
17424
+ stroke: z.string().describe("Stroke color of the polygon in hex format with alpha channel (e.g. #RRGGBBAA)"),
17425
+ strokeWidth: z.number().describe("Stroke width of the polygon in pixels"),
17426
+ strokeScaleEnabled: z.boolean().describe("Whether the polygon stroke width should scale when the node is scaled. Defaults to false."),
17427
+ labelText: z.string().optional().describe("Text label displayed inside the polygon"),
17428
+ labelFontFamily: z.string().optional().describe("Font family for the label text"),
17429
+ labelFontSize: z.number().optional().describe("Font size for the label text in pixels"),
17430
+ labelFontStyle: z.string().optional().describe("Font style for the label text (e.g. \"normal\", \"bold\", \"italic\", \"bold italic\")"),
17431
+ labelFontVariant: z.string().optional().describe("Font variant for the label text (e.g. \"normal\", \"small-caps\")"),
17432
+ labelFill: z.string().optional().describe("Color of the label text in hex format (e.g. #RRGGBBAA)"),
17433
+ labelAlign: z.string().optional().describe("Horizontal alignment of the label text (\"left\", \"center\", \"right\")"),
17434
+ labelVerticalAlign: z.string().optional().describe("Vertical alignment of the label text (\"top\", \"middle\", \"bottom\")"),
17435
+ labelLetterSpacing: z.number().optional().describe("Letter spacing for the label text in pixels"),
17436
+ labelLineHeight: z.number().optional().describe("Line height multiplier for the label text"),
17437
+ labelPaddingX: z.number().optional().describe("Horizontal inset (padding) in pixels applied on each side of the label"),
17438
+ labelPaddingY: z.number().optional().describe("Vertical inset (padding) in pixels applied on top and bottom of the label")
15723
17439
  })
15724
17440
  });
15725
17441
  return nodeSchema;
@@ -16225,9 +17941,48 @@ var WeaveStrokeNode = class extends WeaveNode {
16225
17941
  result.push(pts[pts.length - 1]);
16226
17942
  return result;
16227
17943
  }
17944
+ drawRoundCap(ctx, a, b, color) {
17945
+ const cx = (a.x + b.x) / 2;
17946
+ const cy = (a.y + b.y) / 2;
17947
+ const r = Math.hypot(a.x - b.x, a.y - b.y) / 2;
17948
+ ctx.beginPath();
17949
+ ctx.fillStyle = color;
17950
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
17951
+ ctx.fill();
17952
+ }
17953
+ /**
17954
+ * Draws a filled polygon from the accumulated left/right outline points of a
17955
+ * dash segment and adds round caps at both ends.
17956
+ * NOTE: mutates `rightSide` via Array.reverse() — callers must not reuse it after this call.
17957
+ */
17958
+ drawDashPolygon(ctx, leftSide, rightSide, color) {
17959
+ const capStartL = leftSide[0];
17960
+ const capStartR = rightSide[0];
17961
+ const capEndL = leftSide.at(-1);
17962
+ const capEndR = rightSide.at(-1);
17963
+ const smoothLeft = this.getSplinePoints(leftSide, 4);
17964
+ const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
17965
+ ctx.beginPath();
17966
+ ctx.fillStyle = color;
17967
+ ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
17968
+ for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
17969
+ for (const p of smoothRight) ctx.lineTo(p.x, p.y);
17970
+ ctx.closePath();
17971
+ ctx.fill();
17972
+ this.drawRoundCap(ctx, capStartL, capStartR, color);
17973
+ this.drawRoundCap(ctx, capEndL, capEndR, color);
17974
+ }
16228
17975
  drawRibbonWithDash(ctx, pts, baseW, color, dash) {
16229
17976
  if (!pts) return;
16230
- if (pts.length < 2) return;
17977
+ if (pts.length < 2) {
17978
+ const pt = pts[0];
17979
+ const r = Math.max(baseW * pt.pressure / 2, .5);
17980
+ ctx.beginPath();
17981
+ ctx.fillStyle = color;
17982
+ ctx.arc(pt.x, pt.y, r, 0, Math.PI * 2);
17983
+ ctx.fill();
17984
+ return;
17985
+ }
16231
17986
  const filtered = this.resamplePoints(pts, 2);
16232
17987
  const centerline = this.getSplinePoints(filtered, 8);
16233
17988
  let dashIndex = 0;
@@ -16280,17 +18035,7 @@ var WeaveStrokeNode = class extends WeaveNode {
16280
18035
  }
16281
18036
  dashRemaining -= step;
16282
18037
  if (dashRemaining <= 0) {
16283
- if (dashOn && leftSide.length && rightSide.length) {
16284
- const smoothLeft = this.getSplinePoints(leftSide, 4);
16285
- const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
16286
- ctx.beginPath();
16287
- ctx.fillStyle = color;
16288
- ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
16289
- for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
16290
- for (const p of smoothRight) ctx.lineTo(p.x, p.y);
16291
- ctx.closePath();
16292
- ctx.fill();
16293
- }
18038
+ if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
16294
18039
  leftSide = [];
16295
18040
  rightSide = [];
16296
18041
  dashOn = !dashOn;
@@ -16300,17 +18045,7 @@ var WeaveStrokeNode = class extends WeaveNode {
16300
18045
  traveled += step;
16301
18046
  }
16302
18047
  }
16303
- if (dashOn && leftSide.length && rightSide.length) {
16304
- const smoothLeft = this.getSplinePoints(leftSide, 4);
16305
- const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
16306
- ctx.beginPath();
16307
- ctx.fillStyle = color;
16308
- ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
16309
- for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
16310
- for (const p of smoothRight) ctx.lineTo(p.x, p.y);
16311
- ctx.closePath();
16312
- ctx.fill();
16313
- }
18048
+ if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
16314
18049
  }
16315
18050
  drawShape(ctx, shape) {
16316
18051
  const strokeElements = shape.getAttrs().strokeElements;
@@ -16327,8 +18062,6 @@ var WeaveStrokeNode = class extends WeaveNode {
16327
18062
  sceneFunc: (ctx, shape) => {
16328
18063
  this.drawShape(ctx, shape);
16329
18064
  },
16330
- lineCap: "round",
16331
- lineJoin: "round",
16332
18065
  dashEnabled: false,
16333
18066
  hitFunc: (context, shape) => {
16334
18067
  context.beginPath();
@@ -21601,6 +23334,7 @@ var WeaveRectangleToolAction = class extends WeaveAction {
21601
23334
  if (node) selectionPlugin.setSelectedNodes([node]);
21602
23335
  this.instance.triggerAction(SELECTION_TOOL_ACTION_NAME);
21603
23336
  }
23337
+ if (this.tempRectNode) this.tempRectNode.destroy();
21604
23338
  this.rectId = null;
21605
23339
  this.tempRectNode = null;
21606
23340
  this.moved = false;
@@ -22610,34 +24344,40 @@ var WeaveBrushToolAction = class extends WeaveAction {
22610
24344
  if (nodeHandler) nodeHandler.onUpdate(tempStroke, tempStroke.getAttrs());
22611
24345
  }
22612
24346
  }
24347
+ finalizeStroke(tempStroke, nodeHandler) {
24348
+ const box = this.getBoundingBox(tempStroke.getAttrs().strokeElements);
24349
+ let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
24350
+ if (this.predictedCount > 0) {
24351
+ newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
24352
+ this.predictedCount = 0;
24353
+ }
24354
+ newStrokeElements = newStrokeElements.map((point) => ({
24355
+ ...point,
24356
+ x: point.x - box.x,
24357
+ y: point.y - box.y
24358
+ }));
24359
+ const compressedPoints = simplify(newStrokeElements, 1, true);
24360
+ const sw = tempStroke.getAttrs().strokeWidth ?? 1;
24361
+ const finalWidth = Math.max(box.width, sw);
24362
+ const finalHeight = Math.max(box.height, sw);
24363
+ const finalX = box.width === 0 ? box.x - sw / 2 : box.x;
24364
+ const finalY = box.height === 0 ? box.y - sw / 2 : box.y;
24365
+ tempStroke.setAttrs({
24366
+ width: finalWidth,
24367
+ height: finalHeight,
24368
+ x: finalX,
24369
+ y: finalY,
24370
+ strokeElements: compressedPoints
24371
+ });
24372
+ const realNode = this.instance.getStage().findOne(`#${tempStroke.getAttrs().id}`);
24373
+ if (realNode) realNode.destroy();
24374
+ if (tempStroke.getAttrs().strokeElements.length >= 1) this.instance.addNode(nodeHandler.serialize(tempStroke), this.container?.getAttrs().id);
24375
+ }
22613
24376
  handleEndStroke() {
22614
24377
  const tempStroke = this.instance.getStage().findOne(`#${this.strokeId}`);
22615
24378
  if (tempStroke) {
22616
24379
  const nodeHandler = this.instance.getNodeHandler("stroke");
22617
- if (nodeHandler) {
22618
- const box = this.getBoundingBox(tempStroke.getAttrs().strokeElements);
22619
- let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
22620
- if (this.predictedCount > 0) {
22621
- newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
22622
- this.predictedCount = 0;
22623
- }
22624
- newStrokeElements = newStrokeElements.map((point) => ({
22625
- ...point,
22626
- x: point.x - box.x,
22627
- y: point.y - box.y
22628
- }));
22629
- const compressedPoints = simplify(newStrokeElements, 1, true);
22630
- tempStroke.setAttrs({
22631
- width: box.width,
22632
- height: box.height,
22633
- x: box.x,
22634
- y: box.y,
22635
- strokeElements: compressedPoints
22636
- });
22637
- const realNode = this.instance.getStage().findOne(`#${tempStroke.getAttrs().id}`);
22638
- if (realNode) realNode.destroy();
22639
- if (tempStroke.getAttrs().strokeElements.length >= 3) this.instance.addNode(nodeHandler.serialize(tempStroke), this.container?.getAttrs().id);
22640
- }
24380
+ if (nodeHandler) this.finalizeStroke(tempStroke, nodeHandler);
22641
24381
  this.clickPoint = null;
22642
24382
  this.setCursor();
22643
24383
  this.setFocusStage();
@@ -24705,6 +26445,137 @@ var WeaveRegularPolygonToolAction = class extends WeaveAction {
24705
26445
  }
24706
26446
  };
24707
26447
 
26448
+ //#endregion
26449
+ //#region src/actions/polygon-tool/constants.ts
26450
+ const POLYGON_TOOL_ACTION_NAME = "polygonTool";
26451
+ const POLYGON_TOOL_STATE = {
26452
+ ["IDLE"]: "idle",
26453
+ ["ADDING"]: "adding",
26454
+ ["ADDED"]: "added"
26455
+ };
26456
+
26457
+ //#endregion
26458
+ //#region src/actions/polygon-tool/polygon-tool.ts
26459
+ var WeavePolygonToolAction = class extends WeaveAction {
26460
+ initialized = false;
26461
+ onPropsChange = void 0;
26462
+ onInit = void 0;
26463
+ constructor(preset) {
26464
+ super();
26465
+ this.preset = preset ?? "pentagon";
26466
+ this.initialize();
26467
+ }
26468
+ initialize() {
26469
+ this.initialized = false;
26470
+ this.state = POLYGON_TOOL_STATE.IDLE;
26471
+ this.polygonId = null;
26472
+ this.props = this.initProps();
26473
+ }
26474
+ getName() {
26475
+ return POLYGON_TOOL_ACTION_NAME;
26476
+ }
26477
+ initProps() {
26478
+ return {
26479
+ opacity: 1,
26480
+ fill: "#ffffffff",
26481
+ stroke: "#000000ff",
26482
+ strokeWidth: 1
26483
+ };
26484
+ }
26485
+ getPolygonsPresets() {
26486
+ return WEAVE_POLYGON_PRESETS;
26487
+ }
26488
+ getPolygonPreset() {
26489
+ return this.preset;
26490
+ }
26491
+ setPolygonPreset(preset) {
26492
+ this.preset = preset;
26493
+ }
26494
+ setupEvents() {
26495
+ const stage = this.instance.getStage();
26496
+ window.addEventListener("keydown", (e) => {
26497
+ if ((e.code === "Enter" || e.code === "Escape") && this.instance.getActiveAction() === POLYGON_TOOL_ACTION_NAME) this.cancelAction();
26498
+ }, { signal: this.instance.getEventsController().signal });
26499
+ stage.on("pointermove", () => {
26500
+ if (this.state === POLYGON_TOOL_STATE.IDLE) return;
26501
+ this.setCursor();
26502
+ });
26503
+ stage.on("pointerdown", (e) => {
26504
+ this.setTapStart(e);
26505
+ if (this.state !== POLYGON_TOOL_STATE.ADDING) return;
26506
+ this.handleAdding();
26507
+ });
26508
+ this.initialized = true;
26509
+ }
26510
+ setState(state) {
26511
+ this.state = state;
26512
+ }
26513
+ addPolygon() {
26514
+ this.setCursor();
26515
+ this.setFocusStage();
26516
+ this.instance.emitEvent("onAddingPolygon");
26517
+ this.setState(POLYGON_TOOL_STATE.ADDING);
26518
+ }
26519
+ handleAdding() {
26520
+ const { mousePoint, container } = this.instance.getMousePointer();
26521
+ this.polygonId = v4_default();
26522
+ const presetDef = WEAVE_POLYGON_PRESETS[this.preset];
26523
+ const scaleFactor = this.props.scaleFactor ?? 1;
26524
+ const { points, innerRect, width, height } = instantiatePreset(presetDef, presetDef.defaultWidth * scaleFactor, presetDef.defaultHeight * scaleFactor);
26525
+ const nodeHandler = this.instance.getNodeHandler(WEAVE_POLYGON_NODE_TYPE);
26526
+ if (nodeHandler) {
26527
+ const node = nodeHandler.create(this.polygonId, {
26528
+ ...this.props,
26529
+ x: mousePoint?.x ?? 0,
26530
+ y: mousePoint?.y ?? 0,
26531
+ width,
26532
+ height,
26533
+ sides: presetDef.sides,
26534
+ points,
26535
+ innerRect
26536
+ });
26537
+ this.instance.addNode(node, container?.getAttrs().id);
26538
+ }
26539
+ this.instance.emitEvent("onAddedPolygon");
26540
+ this.cancelAction();
26541
+ }
26542
+ trigger(cancelAction, params) {
26543
+ if (!this.instance) throw new Error("Instance not defined");
26544
+ if (!this.initialized) this.setupEvents();
26545
+ this.preset = params?.presetId ?? "pentagon";
26546
+ const stage = this.instance.getStage();
26547
+ stage.container().tabIndex = 1;
26548
+ stage.container().focus();
26549
+ this.cancelAction = cancelAction;
26550
+ const selectionPlugin = this.instance.getPlugin("nodesSelection");
26551
+ if (selectionPlugin) selectionPlugin.setSelectedNodes([]);
26552
+ this.props = this.initProps();
26553
+ this.addPolygon();
26554
+ }
26555
+ cleanup() {
26556
+ const stage = this.instance.getStage();
26557
+ stage.container().style.cursor = "default";
26558
+ const selectionPlugin = this.instance.getPlugin("nodesSelection");
26559
+ if (selectionPlugin) {
26560
+ const node = stage.findOne(`#${this.polygonId}`);
26561
+ if (node) selectionPlugin.setSelectedNodes([node]);
26562
+ this.instance.triggerAction(SELECTION_TOOL_ACTION_NAME);
26563
+ }
26564
+ this.polygonId = null;
26565
+ this.setState(POLYGON_TOOL_STATE.IDLE);
26566
+ }
26567
+ setCursor() {
26568
+ const stage = this.instance.getStage();
26569
+ stage.container().style.cursor = "crosshair";
26570
+ }
26571
+ setFocusStage() {
26572
+ const stage = this.instance.getStage();
26573
+ stage.container().tabIndex = 1;
26574
+ stage.container().blur();
26575
+ stage.container().focus();
26576
+ }
26577
+ };
26578
+
24708
26579
  //#endregion
24709
26580
  //#region src/actions/frame-tool/constants.ts
24710
26581
  const FRAME_TOOL_ACTION_NAME = "frameTool";
@@ -30817,14 +32688,20 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
30817
32688
  }
30818
32689
  return updatedBox;
30819
32690
  };
30820
- const bindedBoundingBoxFunc = boundingBoxFunc.bind(this);
30821
- tr.boundBoxFunc(bindedBoundingBoxFunc);
32691
+ const snapBoundingBoxFunc = boundingBoxFunc.bind(this);
32692
+ const newBoundFunc = (oldBox, newBox) => {
32693
+ const mainBoundBoxFunc = nodesSelectionPlugin.getBoundBoxFunc();
32694
+ const actualBox = mainBoundBoxFunc(oldBox, newBox);
32695
+ if (actualBox === oldBox) return actualBox;
32696
+ return snapBoundingBoxFunc(oldBox, newBox);
32697
+ };
32698
+ tr.boundBoxFunc(newBoundFunc);
30822
32699
  }
30823
32700
  transformEndHandler() {
30824
32701
  const nodesSelectionPlugin = this.getNodesSelectionPlugin();
30825
32702
  if (nodesSelectionPlugin) {
30826
32703
  const tr = nodesSelectionPlugin.getTransformer();
30827
- tr.boundBoxFunc(void 0);
32704
+ tr.boundBoxFunc(nodesSelectionPlugin.getBoundBoxFunc());
30828
32705
  }
30829
32706
  this.snappingGuides = [];
30830
32707
  }
@@ -31033,4 +32910,4 @@ const setupCanvasBackend = async () => {
31033
32910
  };
31034
32911
 
31035
32912
  //#endregion
31036
- 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, 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_RECTANGLE_NODE_TYPE, WEAVE_REGULAR_POLYGON_NODE_TYPE, 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_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_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, 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, canComposite, clearContainerTargets, containerOverCursor, containsNodeDeep, defaultInitialState, downscaleImageFile, downscaleImageFromURL, getBoundingBox, getDownscaleRatio, getExportBoundingBox, getImageSizeFromFile, getSelectedNodesMetadata, getStageClickPoint, getTargetAndSkipNodes, getTargetedNode, getTopmostShadowHost, getVisibleNodes, getVisibleNodesInViewport, hasFrames, hasImages, intersectArrays, isIOS, isInShadowDOM, isNodeInSelection, isNumber, isServer, loadImageSource, memoize, mergeExceptArrays, moveNodeToContainer, moveNodeToContainerNT, resetScale, setupCanvasBackend, setupSkiaBackend };
32913
+ 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, 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_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, 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, spreadLabelProps };