@inditextech/weave-sdk 5.0.0-SNAPSHOT.366.1 → 5.0.0-SNAPSHOT.377.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
@@ -9482,7 +9482,7 @@ var WeaveRegisterManager = class {
9482
9482
 
9483
9483
  //#endregion
9484
9484
  //#region package.json
9485
- var version = "5.0.0-SNAPSHOT.366.1";
9485
+ var version = "5.0.0-SNAPSHOT.377.1";
9486
9486
 
9487
9487
  //#endregion
9488
9488
  //#region src/managers/setup.ts
@@ -12671,7 +12671,8 @@ const WEAVE_STAGE_TEXT_EDITION_MODE = "text-edition";
12671
12671
  const WEAVE_TEXT_NODE_DEFAULT_CONFIG = {
12672
12672
  transform: { ...WEAVE_NODES_SELECTION_DEFAULT_CONFIG.selection },
12673
12673
  outline: { enabled: false },
12674
- cursor: { color: "#000000" }
12674
+ cursor: { color: "#000000" },
12675
+ edition: { borderSize: 2 }
12675
12676
  };
12676
12677
  const TEXT_LAYOUT = {
12677
12678
  ["SMART"]: "smart",
@@ -12967,7 +12968,8 @@ var WeaveTextNode = class extends WeaveNode {
12967
12968
  if (!this.textArea || !this.textAreaContainer) return;
12968
12969
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART && !textNode.getAttrs().smartFixedWidth) {
12969
12970
  const { width: textAreaWidth } = this.textRenderedSize(this.textArea.value, textNode);
12970
- const width = (textAreaWidth + 2) * textNode.getAbsoluteScale().x / this.instance.getStage().scaleX();
12971
+ const borderSize = this.config.edition.borderSize;
12972
+ const width = (textAreaWidth + borderSize * 2) * textNode.getAbsoluteScale().x / this.instance.getStage().scaleX();
12971
12973
  this.textAreaContainer.style.width = width + "px";
12972
12974
  }
12973
12975
  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 +13068,15 @@ var WeaveTextNode = class extends WeaveNode {
13066
13068
  this.textAreaContainer.style.top = position.y * upscaleScale + "px";
13067
13069
  this.textAreaContainer.style.left = position.x * upscaleScale + "px";
13068
13070
  if (textNode.getAttrs().layout === TEXT_LAYOUT.SMART && !textNode.getAttrs().smartFixedWidth) {
13071
+ const borderSize$1 = this.config.edition.borderSize;
13069
13072
  const rect = textNode.getClientRect({ relativeTo: stage });
13070
- this.textAreaContainer.style.width = (rect.width + 2) * textNode.getAbsoluteScale().x + "px";
13073
+ this.textAreaContainer.style.width = (rect.width + borderSize$1 * 2) * textNode.getAbsoluteScale().x + "px";
13071
13074
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13072
13075
  }
13073
13076
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL) {
13077
+ const borderSize$1 = this.config.edition.borderSize;
13074
13078
  const rect = textNode.getClientRect({ relativeTo: stage });
13075
- this.textAreaContainer.style.width = (rect.width + 2) * textNode.getAbsoluteScale().x + "px";
13079
+ this.textAreaContainer.style.width = (rect.width + borderSize$1 * 2) * textNode.getAbsoluteScale().x + "px";
13076
13080
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13077
13081
  }
13078
13082
  if (textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.SMART && textNode.getAttrs().smartFixedWidth) {
@@ -13085,7 +13089,9 @@ var WeaveTextNode = class extends WeaveNode {
13085
13089
  this.textAreaContainer.style.width = (textNode.width() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13086
13090
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13087
13091
  }
13088
- this.textAreaContainer.style.border = "solid 1px #1e40af";
13092
+ const size = this.textRenderedSize(textNode.text(), textNode);
13093
+ const borderSize = this.config.edition.borderSize;
13094
+ this.textAreaContainer.style.border = `solid ${borderSize}px #1e40af`;
13089
13095
  this.textArea.style.position = "absolute";
13090
13096
  this.textArea.style.top = "0px";
13091
13097
  this.textArea.style.left = "0px";
@@ -13094,11 +13100,12 @@ var WeaveTextNode = class extends WeaveNode {
13094
13100
  this.textArea.style.scrollBehavior = "auto";
13095
13101
  this.textArea.style.caretColor = "black";
13096
13102
  this.textArea.style.width = "100%";
13103
+ this.textArea.style.height = `${size.height}px`;
13097
13104
  this.textArea.style.minHeight = "auto";
13098
13105
  this.textArea.style.margin = "0px";
13099
13106
  this.textArea.style.padding = "0px";
13100
13107
  this.textArea.style.paddingTop = "0px";
13101
- this.textArea.style.boxSizing = "border-box";
13108
+ this.textArea.style.boxSizing = "content-box";
13102
13109
  this.textArea.style.overflow = "hidden";
13103
13110
  this.textArea.style.background = "transparent";
13104
13111
  this.textArea.style.border = "none";
@@ -13112,8 +13119,8 @@ var WeaveTextNode = class extends WeaveNode {
13112
13119
  this.textArea.style.backgroundColor = "transparent";
13113
13120
  this.textAreaContainer.style.transformOrigin = "left top";
13114
13121
  this.mimicTextNode(textNode);
13115
- this.textArea.style.left = `-1px`;
13116
- this.textArea.style.top = `-1px`;
13122
+ this.textArea.style.left = `${-borderSize}px`;
13123
+ this.textArea.style.top = `${-borderSize + (size.height - this.textArea.offsetHeight)}px`;
13117
13124
  this.textArea.onfocus = () => {
13118
13125
  this.textAreaDomResize(textNode);
13119
13126
  };
@@ -13159,7 +13166,10 @@ var WeaveTextNode = class extends WeaveNode {
13159
13166
  const width = textAreaWidth / this.instance.getStage().scaleX();
13160
13167
  textNode.width(width);
13161
13168
  }
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));
13169
+ if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) {
13170
+ const size$1 = this.textRenderedSize(this.textArea.value, textNode);
13171
+ textNode.height(size$1.height * (1 / textNode.getAbsoluteScale().x));
13172
+ }
13163
13173
  };
13164
13174
  const handleKeyDown = (e) => {
13165
13175
  if (this.textArea && textNode && e.code === "Escape") {
@@ -16226,9 +16236,48 @@ var WeaveStrokeNode = class extends WeaveNode {
16226
16236
  result.push(pts[pts.length - 1]);
16227
16237
  return result;
16228
16238
  }
16239
+ drawRoundCap(ctx, a, b, color) {
16240
+ const cx = (a.x + b.x) / 2;
16241
+ const cy = (a.y + b.y) / 2;
16242
+ const r = Math.hypot(a.x - b.x, a.y - b.y) / 2;
16243
+ ctx.beginPath();
16244
+ ctx.fillStyle = color;
16245
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
16246
+ ctx.fill();
16247
+ }
16248
+ /**
16249
+ * Draws a filled polygon from the accumulated left/right outline points of a
16250
+ * dash segment and adds round caps at both ends.
16251
+ * NOTE: mutates `rightSide` via Array.reverse() — callers must not reuse it after this call.
16252
+ */
16253
+ drawDashPolygon(ctx, leftSide, rightSide, color) {
16254
+ const capStartL = leftSide[0];
16255
+ const capStartR = rightSide[0];
16256
+ const capEndL = leftSide.at(-1);
16257
+ const capEndR = rightSide.at(-1);
16258
+ const smoothLeft = this.getSplinePoints(leftSide, 4);
16259
+ const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
16260
+ ctx.beginPath();
16261
+ ctx.fillStyle = color;
16262
+ ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
16263
+ for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
16264
+ for (const p of smoothRight) ctx.lineTo(p.x, p.y);
16265
+ ctx.closePath();
16266
+ ctx.fill();
16267
+ this.drawRoundCap(ctx, capStartL, capStartR, color);
16268
+ this.drawRoundCap(ctx, capEndL, capEndR, color);
16269
+ }
16229
16270
  drawRibbonWithDash(ctx, pts, baseW, color, dash) {
16230
16271
  if (!pts) return;
16231
- if (pts.length < 2) return;
16272
+ if (pts.length < 2) {
16273
+ const pt = pts[0];
16274
+ const r = Math.max(baseW * pt.pressure / 2, .5);
16275
+ ctx.beginPath();
16276
+ ctx.fillStyle = color;
16277
+ ctx.arc(pt.x, pt.y, r, 0, Math.PI * 2);
16278
+ ctx.fill();
16279
+ return;
16280
+ }
16232
16281
  const filtered = this.resamplePoints(pts, 2);
16233
16282
  const centerline = this.getSplinePoints(filtered, 8);
16234
16283
  let dashIndex = 0;
@@ -16281,17 +16330,7 @@ var WeaveStrokeNode = class extends WeaveNode {
16281
16330
  }
16282
16331
  dashRemaining -= step;
16283
16332
  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
- }
16333
+ if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
16295
16334
  leftSide = [];
16296
16335
  rightSide = [];
16297
16336
  dashOn = !dashOn;
@@ -16301,17 +16340,7 @@ var WeaveStrokeNode = class extends WeaveNode {
16301
16340
  traveled += step;
16302
16341
  }
16303
16342
  }
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
- }
16343
+ if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
16315
16344
  }
16316
16345
  drawShape(ctx, shape) {
16317
16346
  const strokeElements = shape.getAttrs().strokeElements;
@@ -16328,8 +16357,6 @@ var WeaveStrokeNode = class extends WeaveNode {
16328
16357
  sceneFunc: (ctx, shape) => {
16329
16358
  this.drawShape(ctx, shape);
16330
16359
  },
16331
- lineCap: "round",
16332
- lineJoin: "round",
16333
16360
  dashEnabled: false,
16334
16361
  hitFunc: (context, shape) => {
16335
16362
  context.beginPath();
@@ -22611,34 +22638,40 @@ var WeaveBrushToolAction = class extends WeaveAction {
22611
22638
  if (nodeHandler) nodeHandler.onUpdate(tempStroke, tempStroke.getAttrs());
22612
22639
  }
22613
22640
  }
22641
+ finalizeStroke(tempStroke, nodeHandler) {
22642
+ const box = this.getBoundingBox(tempStroke.getAttrs().strokeElements);
22643
+ let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
22644
+ if (this.predictedCount > 0) {
22645
+ newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
22646
+ this.predictedCount = 0;
22647
+ }
22648
+ newStrokeElements = newStrokeElements.map((point) => ({
22649
+ ...point,
22650
+ x: point.x - box.x,
22651
+ y: point.y - box.y
22652
+ }));
22653
+ const compressedPoints = simplify(newStrokeElements, 1, true);
22654
+ const sw = tempStroke.getAttrs().strokeWidth ?? 1;
22655
+ const finalWidth = Math.max(box.width, sw);
22656
+ const finalHeight = Math.max(box.height, sw);
22657
+ const finalX = box.width === 0 ? box.x - sw / 2 : box.x;
22658
+ const finalY = box.height === 0 ? box.y - sw / 2 : box.y;
22659
+ tempStroke.setAttrs({
22660
+ width: finalWidth,
22661
+ height: finalHeight,
22662
+ x: finalX,
22663
+ y: finalY,
22664
+ strokeElements: compressedPoints
22665
+ });
22666
+ const realNode = this.instance.getStage().findOne(`#${tempStroke.getAttrs().id}`);
22667
+ if (realNode) realNode.destroy();
22668
+ if (tempStroke.getAttrs().strokeElements.length >= 1) this.instance.addNode(nodeHandler.serialize(tempStroke), this.container?.getAttrs().id);
22669
+ }
22614
22670
  handleEndStroke() {
22615
22671
  const tempStroke = this.instance.getStage().findOne(`#${this.strokeId}`);
22616
22672
  if (tempStroke) {
22617
22673
  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
- }
22674
+ if (nodeHandler) this.finalizeStroke(tempStroke, nodeHandler);
22642
22675
  this.clickPoint = null;
22643
22676
  this.setCursor();
22644
22677
  this.setFocusStage();
package/dist/sdk.node.js CHANGED
@@ -9481,7 +9481,7 @@ var WeaveRegisterManager = class {
9481
9481
 
9482
9482
  //#endregion
9483
9483
  //#region package.json
9484
- var version = "5.0.0-SNAPSHOT.366.1";
9484
+ var version = "5.0.0-SNAPSHOT.377.1";
9485
9485
 
9486
9486
  //#endregion
9487
9487
  //#region src/managers/setup.ts
@@ -12670,7 +12670,8 @@ const WEAVE_STAGE_TEXT_EDITION_MODE = "text-edition";
12670
12670
  const WEAVE_TEXT_NODE_DEFAULT_CONFIG = {
12671
12671
  transform: { ...WEAVE_NODES_SELECTION_DEFAULT_CONFIG.selection },
12672
12672
  outline: { enabled: false },
12673
- cursor: { color: "#000000" }
12673
+ cursor: { color: "#000000" },
12674
+ edition: { borderSize: 2 }
12674
12675
  };
12675
12676
  const TEXT_LAYOUT = {
12676
12677
  ["SMART"]: "smart",
@@ -12966,7 +12967,8 @@ var WeaveTextNode = class extends WeaveNode {
12966
12967
  if (!this.textArea || !this.textAreaContainer) return;
12967
12968
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART && !textNode.getAttrs().smartFixedWidth) {
12968
12969
  const { width: textAreaWidth } = this.textRenderedSize(this.textArea.value, textNode);
12969
- const width = (textAreaWidth + 2) * textNode.getAbsoluteScale().x / this.instance.getStage().scaleX();
12970
+ const borderSize = this.config.edition.borderSize;
12971
+ const width = (textAreaWidth + borderSize * 2) * textNode.getAbsoluteScale().x / this.instance.getStage().scaleX();
12970
12972
  this.textAreaContainer.style.width = width + "px";
12971
12973
  }
12972
12974
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) {
@@ -13065,13 +13067,15 @@ var WeaveTextNode = class extends WeaveNode {
13065
13067
  this.textAreaContainer.style.top = position.y * upscaleScale + "px";
13066
13068
  this.textAreaContainer.style.left = position.x * upscaleScale + "px";
13067
13069
  if (textNode.getAttrs().layout === TEXT_LAYOUT.SMART && !textNode.getAttrs().smartFixedWidth) {
13070
+ const borderSize$1 = this.config.edition.borderSize;
13068
13071
  const rect = textNode.getClientRect({ relativeTo: stage });
13069
- this.textAreaContainer.style.width = (rect.width + 2) * textNode.getAbsoluteScale().x + "px";
13072
+ this.textAreaContainer.style.width = (rect.width + borderSize$1 * 2) * textNode.getAbsoluteScale().x + "px";
13070
13073
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13071
13074
  }
13072
13075
  if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL) {
13076
+ const borderSize$1 = this.config.edition.borderSize;
13073
13077
  const rect = textNode.getClientRect({ relativeTo: stage });
13074
- this.textAreaContainer.style.width = (rect.width + 2) * textNode.getAbsoluteScale().x + "px";
13078
+ this.textAreaContainer.style.width = (rect.width + borderSize$1 * 2) * textNode.getAbsoluteScale().x + "px";
13075
13079
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13076
13080
  }
13077
13081
  if (textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.SMART && textNode.getAttrs().smartFixedWidth) {
@@ -13084,7 +13088,9 @@ var WeaveTextNode = class extends WeaveNode {
13084
13088
  this.textAreaContainer.style.width = (textNode.width() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13085
13089
  this.textAreaContainer.style.height = (textNode.height() - textNode.padding() * 2) * textNode.getAbsoluteScale().x + "px";
13086
13090
  }
13087
- this.textAreaContainer.style.border = "solid 1px #1e40af";
13091
+ const size = this.textRenderedSize(textNode.text(), textNode);
13092
+ const borderSize = this.config.edition.borderSize;
13093
+ this.textAreaContainer.style.border = `solid ${borderSize}px #1e40af`;
13088
13094
  this.textArea.style.position = "absolute";
13089
13095
  this.textArea.style.top = "0px";
13090
13096
  this.textArea.style.left = "0px";
@@ -13093,11 +13099,12 @@ var WeaveTextNode = class extends WeaveNode {
13093
13099
  this.textArea.style.scrollBehavior = "auto";
13094
13100
  this.textArea.style.caretColor = "black";
13095
13101
  this.textArea.style.width = "100%";
13102
+ this.textArea.style.height = `${size.height}px`;
13096
13103
  this.textArea.style.minHeight = "auto";
13097
13104
  this.textArea.style.margin = "0px";
13098
13105
  this.textArea.style.padding = "0px";
13099
13106
  this.textArea.style.paddingTop = "0px";
13100
- this.textArea.style.boxSizing = "border-box";
13107
+ this.textArea.style.boxSizing = "content-box";
13101
13108
  this.textArea.style.overflow = "hidden";
13102
13109
  this.textArea.style.background = "transparent";
13103
13110
  this.textArea.style.border = "none";
@@ -13111,8 +13118,8 @@ var WeaveTextNode = class extends WeaveNode {
13111
13118
  this.textArea.style.backgroundColor = "transparent";
13112
13119
  this.textAreaContainer.style.transformOrigin = "left top";
13113
13120
  this.mimicTextNode(textNode);
13114
- this.textArea.style.left = `-1px`;
13115
- this.textArea.style.top = `-1px`;
13121
+ this.textArea.style.left = `${-borderSize}px`;
13122
+ this.textArea.style.top = `${-borderSize + (size.height - this.textArea.offsetHeight)}px`;
13116
13123
  this.textArea.onfocus = () => {
13117
13124
  this.textAreaDomResize(textNode);
13118
13125
  };
@@ -13158,7 +13165,10 @@ var WeaveTextNode = class extends WeaveNode {
13158
13165
  const width = textAreaWidth / this.instance.getStage().scaleX();
13159
13166
  textNode.width(width);
13160
13167
  }
13161
- if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) textNode.height(this.textArea.scrollHeight * (1 / textNode.getAbsoluteScale().x));
13168
+ if (!textNode.getAttrs().layout || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_HEIGHT || textNode.getAttrs().layout === TEXT_LAYOUT.AUTO_ALL || textNode.getAttrs().layout === TEXT_LAYOUT.SMART) {
13169
+ const size$1 = this.textRenderedSize(this.textArea.value, textNode);
13170
+ textNode.height(size$1.height * (1 / textNode.getAbsoluteScale().x));
13171
+ }
13162
13172
  };
13163
13173
  const handleKeyDown = (e) => {
13164
13174
  if (this.textArea && textNode && e.code === "Escape") {
@@ -16225,9 +16235,48 @@ var WeaveStrokeNode = class extends WeaveNode {
16225
16235
  result.push(pts[pts.length - 1]);
16226
16236
  return result;
16227
16237
  }
16238
+ drawRoundCap(ctx, a, b, color) {
16239
+ const cx = (a.x + b.x) / 2;
16240
+ const cy = (a.y + b.y) / 2;
16241
+ const r = Math.hypot(a.x - b.x, a.y - b.y) / 2;
16242
+ ctx.beginPath();
16243
+ ctx.fillStyle = color;
16244
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
16245
+ ctx.fill();
16246
+ }
16247
+ /**
16248
+ * Draws a filled polygon from the accumulated left/right outline points of a
16249
+ * dash segment and adds round caps at both ends.
16250
+ * NOTE: mutates `rightSide` via Array.reverse() — callers must not reuse it after this call.
16251
+ */
16252
+ drawDashPolygon(ctx, leftSide, rightSide, color) {
16253
+ const capStartL = leftSide[0];
16254
+ const capStartR = rightSide[0];
16255
+ const capEndL = leftSide.at(-1);
16256
+ const capEndR = rightSide.at(-1);
16257
+ const smoothLeft = this.getSplinePoints(leftSide, 4);
16258
+ const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
16259
+ ctx.beginPath();
16260
+ ctx.fillStyle = color;
16261
+ ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
16262
+ for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
16263
+ for (const p of smoothRight) ctx.lineTo(p.x, p.y);
16264
+ ctx.closePath();
16265
+ ctx.fill();
16266
+ this.drawRoundCap(ctx, capStartL, capStartR, color);
16267
+ this.drawRoundCap(ctx, capEndL, capEndR, color);
16268
+ }
16228
16269
  drawRibbonWithDash(ctx, pts, baseW, color, dash) {
16229
16270
  if (!pts) return;
16230
- if (pts.length < 2) return;
16271
+ if (pts.length < 2) {
16272
+ const pt = pts[0];
16273
+ const r = Math.max(baseW * pt.pressure / 2, .5);
16274
+ ctx.beginPath();
16275
+ ctx.fillStyle = color;
16276
+ ctx.arc(pt.x, pt.y, r, 0, Math.PI * 2);
16277
+ ctx.fill();
16278
+ return;
16279
+ }
16231
16280
  const filtered = this.resamplePoints(pts, 2);
16232
16281
  const centerline = this.getSplinePoints(filtered, 8);
16233
16282
  let dashIndex = 0;
@@ -16280,17 +16329,7 @@ var WeaveStrokeNode = class extends WeaveNode {
16280
16329
  }
16281
16330
  dashRemaining -= step;
16282
16331
  if (dashRemaining <= 0) {
16283
- if (dashOn && leftSide.length && rightSide.length) {
16284
- const smoothLeft = this.getSplinePoints(leftSide, 4);
16285
- const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
16286
- ctx.beginPath();
16287
- ctx.fillStyle = color;
16288
- ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
16289
- for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
16290
- for (const p of smoothRight) ctx.lineTo(p.x, p.y);
16291
- ctx.closePath();
16292
- ctx.fill();
16293
- }
16332
+ if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
16294
16333
  leftSide = [];
16295
16334
  rightSide = [];
16296
16335
  dashOn = !dashOn;
@@ -16300,17 +16339,7 @@ var WeaveStrokeNode = class extends WeaveNode {
16300
16339
  traveled += step;
16301
16340
  }
16302
16341
  }
16303
- if (dashOn && leftSide.length && rightSide.length) {
16304
- const smoothLeft = this.getSplinePoints(leftSide, 4);
16305
- const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
16306
- ctx.beginPath();
16307
- ctx.fillStyle = color;
16308
- ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
16309
- for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
16310
- for (const p of smoothRight) ctx.lineTo(p.x, p.y);
16311
- ctx.closePath();
16312
- ctx.fill();
16313
- }
16342
+ if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
16314
16343
  }
16315
16344
  drawShape(ctx, shape) {
16316
16345
  const strokeElements = shape.getAttrs().strokeElements;
@@ -16327,8 +16356,6 @@ var WeaveStrokeNode = class extends WeaveNode {
16327
16356
  sceneFunc: (ctx, shape) => {
16328
16357
  this.drawShape(ctx, shape);
16329
16358
  },
16330
- lineCap: "round",
16331
- lineJoin: "round",
16332
16359
  dashEnabled: false,
16333
16360
  hitFunc: (context, shape) => {
16334
16361
  context.beginPath();
@@ -22610,34 +22637,40 @@ var WeaveBrushToolAction = class extends WeaveAction {
22610
22637
  if (nodeHandler) nodeHandler.onUpdate(tempStroke, tempStroke.getAttrs());
22611
22638
  }
22612
22639
  }
22640
+ finalizeStroke(tempStroke, nodeHandler) {
22641
+ const box = this.getBoundingBox(tempStroke.getAttrs().strokeElements);
22642
+ let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
22643
+ if (this.predictedCount > 0) {
22644
+ newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
22645
+ this.predictedCount = 0;
22646
+ }
22647
+ newStrokeElements = newStrokeElements.map((point) => ({
22648
+ ...point,
22649
+ x: point.x - box.x,
22650
+ y: point.y - box.y
22651
+ }));
22652
+ const compressedPoints = simplify(newStrokeElements, 1, true);
22653
+ const sw = tempStroke.getAttrs().strokeWidth ?? 1;
22654
+ const finalWidth = Math.max(box.width, sw);
22655
+ const finalHeight = Math.max(box.height, sw);
22656
+ const finalX = box.width === 0 ? box.x - sw / 2 : box.x;
22657
+ const finalY = box.height === 0 ? box.y - sw / 2 : box.y;
22658
+ tempStroke.setAttrs({
22659
+ width: finalWidth,
22660
+ height: finalHeight,
22661
+ x: finalX,
22662
+ y: finalY,
22663
+ strokeElements: compressedPoints
22664
+ });
22665
+ const realNode = this.instance.getStage().findOne(`#${tempStroke.getAttrs().id}`);
22666
+ if (realNode) realNode.destroy();
22667
+ if (tempStroke.getAttrs().strokeElements.length >= 1) this.instance.addNode(nodeHandler.serialize(tempStroke), this.container?.getAttrs().id);
22668
+ }
22613
22669
  handleEndStroke() {
22614
22670
  const tempStroke = this.instance.getStage().findOne(`#${this.strokeId}`);
22615
22671
  if (tempStroke) {
22616
22672
  const nodeHandler = this.instance.getNodeHandler("stroke");
22617
- if (nodeHandler) {
22618
- const box = this.getBoundingBox(tempStroke.getAttrs().strokeElements);
22619
- let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
22620
- if (this.predictedCount > 0) {
22621
- newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
22622
- this.predictedCount = 0;
22623
- }
22624
- newStrokeElements = newStrokeElements.map((point) => ({
22625
- ...point,
22626
- x: point.x - box.x,
22627
- y: point.y - box.y
22628
- }));
22629
- const compressedPoints = simplify(newStrokeElements, 1, true);
22630
- tempStroke.setAttrs({
22631
- width: box.width,
22632
- height: box.height,
22633
- x: box.x,
22634
- y: box.y,
22635
- strokeElements: compressedPoints
22636
- });
22637
- const realNode = this.instance.getStage().findOne(`#${tempStroke.getAttrs().id}`);
22638
- if (realNode) realNode.destroy();
22639
- if (tempStroke.getAttrs().strokeElements.length >= 3) this.instance.addNode(nodeHandler.serialize(tempStroke), this.container?.getAttrs().id);
22640
- }
22673
+ if (nodeHandler) this.finalizeStroke(tempStroke, nodeHandler);
22641
22674
  this.clickPoint = null;
22642
22675
  this.setCursor();
22643
22676
  this.setFocusStage();