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