@inditextech/weave-sdk 5.3.0 → 5.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sdk.node.js CHANGED
@@ -4679,7 +4679,15 @@ function handleClickOrTap(ctx, e) {
4679
4679
  nodeTargeted.dblClick();
4680
4680
  return;
4681
4681
  }
4682
- if (e.evt.ctrlKey || e.evt.metaKey) return;
4682
+ const isCtrlOrCmdPressed = e.evt.ctrlKey || e.evt.metaKey;
4683
+ if (!e.evt.shiftKey) {
4684
+ const wasSelectedBeforeThisClick = tr.nodes().findIndex((node) => node.getAttrs().id === nodeTargeted.getAttrs().id) !== -1;
4685
+ nodeTargeted.click({
4686
+ wasSelected: wasSelectedBeforeThisClick,
4687
+ ctrlOrMetaPressed: isCtrlOrCmdPressed
4688
+ });
4689
+ }
4690
+ if (isCtrlOrCmdPressed) return;
4683
4691
  if (!metaPressed) {
4684
4692
  tr.nodes([nodeTargeted]);
4685
4693
  tr.show();
@@ -5948,6 +5956,7 @@ const augmentKonvaNodeClass = (config) => {
5948
5956
  Konva.Node.prototype.closeCrop = function() {};
5949
5957
  Konva.Node.prototype.resetCrop = function() {};
5950
5958
  Konva.Node.prototype.dblClick = function() {};
5959
+ Konva.Node.prototype.click = function() {};
5951
5960
  Konva.Node.prototype.allowedAnchors = function() {
5952
5961
  return [];
5953
5962
  };
@@ -9460,7 +9469,7 @@ var WeaveRegisterManager = class {
9460
9469
  };
9461
9470
  //#endregion
9462
9471
  //#region package.json
9463
- var version = "5.3.0";
9472
+ var version = "5.4.1";
9464
9473
  //#endregion
9465
9474
  //#region src/managers/setup.ts
9466
9475
  var WeaveSetupManager = class {
@@ -13390,8 +13399,13 @@ const WEAVE_TEXT_NODE_DEFAULT_CONFIG = {
13390
13399
  transform: { ...WEAVE_NODES_SELECTION_DEFAULT_CONFIG.selection },
13391
13400
  outline: { enabled: false },
13392
13401
  cursor: { color: "#000000" },
13393
- edition: { borderSize: 2 }
13402
+ edition: { borderSize: 2 },
13403
+ link: {
13404
+ defaultColor: "#1155ccff",
13405
+ hoverColor: "#3d7be0ff"
13406
+ }
13394
13407
  };
13408
+ const WEAVE_TEXT_LINK_ALLOWED_PROTOCOLS = ["http:", "https:"];
13395
13409
  const TEXT_LAYOUT = {
13396
13410
  ["SMART"]: "smart",
13397
13411
  ["AUTO_ALL"]: "auto-all",
@@ -13400,6 +13414,21 @@ const TEXT_LAYOUT = {
13400
13414
  };
13401
13415
  //#endregion
13402
13416
  //#region src/nodes/text/text.ts
13417
+ /**
13418
+ * Validates that a link is an absolute http(s) URL. Used both by the zod
13419
+ * schema (getSchema()) and defensively again at click-time (setLink() and
13420
+ * the pointerclick handler), since the value is user-authored content
13421
+ * rendered and clicked on by other collaborators — schemes like
13422
+ * `javascript:` or `data:` must never reach `window.open`.
13423
+ */
13424
+ function isValidTextLink(value) {
13425
+ try {
13426
+ const url = new URL(value);
13427
+ return WEAVE_TEXT_LINK_ALLOWED_PROTOCOLS.includes(url.protocol);
13428
+ } catch {
13429
+ return false;
13430
+ }
13431
+ }
13403
13432
  var WeaveTextNode = class extends WeaveNode {
13404
13433
  config;
13405
13434
  nodeType = WEAVE_TEXT_NODE_TYPE;
@@ -13463,6 +13492,8 @@ var WeaveTextNode = class extends WeaveNode {
13463
13492
  }
13464
13493
  }
13465
13494
  onRender(props) {
13495
+ const linkPreviousFill = props.link ? props.linkPreviousFill ?? props.fill : props.linkPreviousFill;
13496
+ const linkPreviousTextDecoration = props.link ? props.linkPreviousTextDecoration ?? props.textDecoration : props.linkPreviousTextDecoration;
13466
13497
  const text = new Konva.Text({
13467
13498
  ...props,
13468
13499
  name: "node",
@@ -13472,6 +13503,12 @@ var WeaveTextNode = class extends WeaveNode {
13472
13503
  stroke: this.config.outline.color,
13473
13504
  strokeWidth: this.config.outline.width,
13474
13505
  fillAfterStrokeEnabled: true
13506
+ },
13507
+ ...props.link && {
13508
+ textDecoration: "underline",
13509
+ fill: this.config.link.defaultColor,
13510
+ linkPreviousFill,
13511
+ linkPreviousTextDecoration
13475
13512
  }
13476
13513
  });
13477
13514
  this.setupDefaultNodeAugmentation(text);
@@ -13530,6 +13567,7 @@ var WeaveTextNode = class extends WeaveNode {
13530
13567
  this.triggerEditMode(text);
13531
13568
  };
13532
13569
  text.setAttr("triggerEditMode", this.triggerEditMode.bind(this));
13570
+ this.setupLinkBehavior(text);
13533
13571
  let actualAnchor = void 0;
13534
13572
  text.on("transformstart", (e) => {
13535
13573
  const isCtrlOrMetaPressed = e.evt.ctrlKey || e.evt.metaKey;
@@ -13626,6 +13664,9 @@ var WeaveTextNode = class extends WeaveNode {
13626
13664
  return text;
13627
13665
  }
13628
13666
  onUpdate(nodeInstance, nextProps) {
13667
+ const isLinked = Boolean(nextProps.link);
13668
+ const linkPreviousFill = nextProps.link ? nextProps.linkPreviousFill ?? nextProps.fill : nextProps.linkPreviousFill;
13669
+ const linkPreviousTextDecoration = nextProps.link ? nextProps.linkPreviousTextDecoration ?? nextProps.textDecoration : nextProps.linkPreviousTextDecoration;
13629
13670
  nodeInstance.setAttrs({
13630
13671
  ...nextProps,
13631
13672
  ...!this.config.outline.enabled && { strokeEnabled: false },
@@ -13634,6 +13675,13 @@ var WeaveTextNode = class extends WeaveNode {
13634
13675
  stroke: this.config.outline.color,
13635
13676
  strokeWidth: this.config.outline.width,
13636
13677
  fillAfterStrokeEnabled: true
13678
+ },
13679
+ link: nextProps.link,
13680
+ linkPreviousFill: isLinked ? linkPreviousFill : void 0,
13681
+ linkPreviousTextDecoration: isLinked ? linkPreviousTextDecoration : void 0,
13682
+ ...isLinked && {
13683
+ textDecoration: "underline",
13684
+ fill: this.config.link.defaultColor
13637
13685
  }
13638
13686
  });
13639
13687
  let width = nextProps.width;
@@ -13675,6 +13723,10 @@ var WeaveTextNode = class extends WeaveNode {
13675
13723
  delete cleanedAttrs.overridesMouseControl;
13676
13724
  delete cleanedAttrs.shouldUpdateOnTransform;
13677
13725
  delete cleanedAttrs.dragBoundFunc;
13726
+ if (cleanedAttrs.link) {
13727
+ if (typeof cleanedAttrs.linkPreviousFill === "string") cleanedAttrs.fill = cleanedAttrs.linkPreviousFill;
13728
+ if (typeof cleanedAttrs.linkPreviousTextDecoration === "string") cleanedAttrs.textDecoration = cleanedAttrs.linkPreviousTextDecoration;
13729
+ }
13678
13730
  return {
13679
13731
  key: attrs.id ?? "",
13680
13732
  type: attrs.nodeType,
@@ -14080,6 +14132,94 @@ var WeaveTextNode = class extends WeaveNode {
14080
14132
  this.createTextAreaDOM(textNode, areaPosition);
14081
14133
  this.instance.emitEvent("onEnterTextNodeEditMode", { node: textNode });
14082
14134
  }
14135
+ /**
14136
+ * Wires the hover colour swap, link cursor and click-to-open behaviour for
14137
+ * a rendered text node. Runs on top of (not instead of) the generic
14138
+ * selection-hover-halo handling in setupDefaultNodeEvents()/node.ts, the
14139
+ * same way connector.ts layers its own handleMouseover/handleMouseout.
14140
+ */
14141
+ setupLinkBehavior(text) {
14142
+ text.handleMouseover = () => {
14143
+ if (!text.getAttrs().link) return;
14144
+ text.fill(this.config.link.hoverColor);
14145
+ text.getLayer()?.batchDraw();
14146
+ };
14147
+ text.handleMouseout = () => {
14148
+ if (!text.getAttrs().link) return;
14149
+ text.fill(this.config.link.defaultColor);
14150
+ text.getLayer()?.batchDraw();
14151
+ };
14152
+ text.defineMousePointer = () => {
14153
+ return text.getAttrs().link ? "pointer" : "default";
14154
+ };
14155
+ let linkClickTimeout;
14156
+ const clearLinkClickTimeout = () => {
14157
+ if (linkClickTimeout) {
14158
+ clearTimeout(linkClickTimeout);
14159
+ linkClickTimeout = void 0;
14160
+ }
14161
+ };
14162
+ const previousDblClick = text.dblClick.bind(text);
14163
+ text.dblClick = () => {
14164
+ clearLinkClickTimeout();
14165
+ previousDblClick();
14166
+ };
14167
+ text.click = ({ wasSelected, ctrlOrMetaPressed }) => {
14168
+ if (!text.getAttrs().link) return;
14169
+ if (ctrlOrMetaPressed) {
14170
+ clearLinkClickTimeout();
14171
+ this.openTextLink(text);
14172
+ return;
14173
+ }
14174
+ if (wasSelected) {
14175
+ clearLinkClickTimeout();
14176
+ linkClickTimeout = setTimeout(() => {
14177
+ linkClickTimeout = void 0;
14178
+ if (!this.editing) this.openTextLink(text);
14179
+ }, Konva.dblClickWindow);
14180
+ }
14181
+ };
14182
+ }
14183
+ openTextLink(textNode) {
14184
+ if (this.instance.isServerSide()) return;
14185
+ const link = textNode.getAttrs().link;
14186
+ if (typeof link !== "string" || !isValidTextLink(link)) return;
14187
+ window.open(link, "_blank", "noopener,noreferrer");
14188
+ }
14189
+ /**
14190
+ * Public API to read/set/remove the URL a text node links to. Setting an
14191
+ * invalid or non-http(s) URL is a no-op (see isValidTextLink()).
14192
+ *
14193
+ * The node's real fill/textDecoration are captured once into
14194
+ * `linkPreviousFill`/`linkPreviousTextDecoration` the first time a link is
14195
+ * rendered (see onRender()/onUpdate()) and restored by serialize()
14196
+ * whenever the node is re-serialized while linked — so they survive any
14197
+ * number of resizes/drags/edits while the link is active. removeLink()
14198
+ * relies on that: it omits `link` *and* those two bookkeeping props from
14199
+ * the serialized props entirely, so all three are deleted from shared
14200
+ * state (see updateYjsMapFromObject in managers/state.ts) rather than
14201
+ * persisted as `undefined`, and the node's original fill/text decoration
14202
+ * reappear immediately.
14203
+ */
14204
+ getLink(nodeInstance) {
14205
+ const link = nodeInstance.getAttrs().link;
14206
+ return typeof link === "string" ? link : void 0;
14207
+ }
14208
+ setLink(nodeInstance, url) {
14209
+ if (!isValidTextLink(url)) return;
14210
+ const serialized = this.serialize(nodeInstance);
14211
+ serialized.props.linkPreviousFill = serialized.props.fill;
14212
+ serialized.props.linkPreviousTextDecoration = serialized.props.textDecoration;
14213
+ serialized.props.link = url;
14214
+ this.instance.updateNode(serialized);
14215
+ }
14216
+ removeLink(nodeInstance) {
14217
+ const serialized = this.serialize(nodeInstance);
14218
+ delete serialized.props.link;
14219
+ delete serialized.props.linkPreviousFill;
14220
+ delete serialized.props.linkPreviousTextDecoration;
14221
+ this.instance.updateNode(serialized);
14222
+ }
14083
14223
  onDestroyInstance() {
14084
14224
  super.onDestroyInstance();
14085
14225
  if (!this.instance.isServerSide() && this.keyPressHandler) {
@@ -14145,7 +14285,8 @@ var WeaveTextNode = class extends WeaveNode {
14145
14285
  ...props.strokeEnabled && { strokeEnabled: props.strokeEnabled },
14146
14286
  ...props.stroke && { stroke: props.stroke },
14147
14287
  ...props.strokeWidth && { strokeWidth: props.strokeWidth },
14148
- ...props.fillAfterStrokeEnabled && { fillAfterStrokeEnabled: props.fillAfterStrokeEnabled }
14288
+ ...props.fillAfterStrokeEnabled && { fillAfterStrokeEnabled: props.fillAfterStrokeEnabled },
14289
+ ...props.link && { link: props.link }
14149
14290
  } });
14150
14291
  }
14151
14292
  static updateNodeState(prevNodeState, nextProps) {
@@ -14170,7 +14311,8 @@ var WeaveTextNode = class extends WeaveNode {
14170
14311
  ...nextProps.strokeEnabled && { strokeEnabled: nextProps.strokeEnabled },
14171
14312
  ...nextProps.stroke && { stroke: nextProps.stroke },
14172
14313
  ...nextProps.strokeWidth && { strokeWidth: nextProps.strokeWidth },
14173
- ...nextProps.fillAfterStrokeEnabled && { fillAfterStrokeEnabled: nextProps.fillAfterStrokeEnabled }
14314
+ ...nextProps.fillAfterStrokeEnabled && { fillAfterStrokeEnabled: nextProps.fillAfterStrokeEnabled },
14315
+ ...nextProps.link && { link: nextProps.link }
14174
14316
  } });
14175
14317
  }
14176
14318
  static getSchema() {
@@ -14204,6 +14346,9 @@ var WeaveTextNode = class extends WeaveNode {
14204
14346
  ]).default("top").describe("Vertical alignment of the text, can be 'top', 'middle' or 'bottom'."),
14205
14347
  fill: z.string().default("#000000ff").describe("Fill color of the text in hex format with alpha channel (e.g. #RRGGBBAA)."),
14206
14348
  text: z.string().default("text").describe("The actual text content of the node."),
14349
+ link: z.string().url().refine((value) => isValidTextLink(value), { message: "Link must be an absolute http:// or https:// URL." }).optional().describe("External URL the whole text of the node links to. When set, the text is forced underlined and coloured with the configured link colours regardless of fill/textDecoration, is clickable, and opens the URL in a new tab. Only http(s) URLs are accepted. Omit/remove to make the node plain text again."),
14350
+ linkPreviousFill: z.string().optional().describe("Internal bookkeeping: the fill colour the node had right before a link was set, captured automatically so it can be restored when the link is removed. Managed automatically — do not set directly."),
14351
+ linkPreviousTextDecoration: z.string().optional().describe("Internal bookkeeping: the textDecoration the node had right before a link was set, captured automatically so it can be restored when the link is removed. Managed automatically — do not set directly."),
14207
14352
  strokeEnabled: z.boolean().default(false).describe("Whether the text outline is enabled."),
14208
14353
  stroke: z.string().optional().default("#d6d6d6").describe("Color of the text outline in hex format with alpha channel (e.g. #RRGGBBAA)."),
14209
14354
  strokeWidth: z.number().optional().default(2).describe("Width of the text outline in pixels."),
@@ -17873,21 +18018,29 @@ var WeaveFrameNode = class extends WeaveNode {
17873
18018
  const WEAVE_STROKE_NODE_TYPE = "stroke";
17874
18019
  const WEAVE_STROKE_NODE_DEFAULT_CONFIG = {
17875
18020
  splineResolution: 8,
18021
+ splineTargetEdge: 1,
18022
+ splineMaxSteps: 48,
17876
18023
  resamplingSpacing: 2,
17877
18024
  isEraser: false
17878
18025
  };
17879
18026
  //#endregion
17880
18027
  //#region src/nodes/stroke/stroke.ts
17881
- var WeaveStrokeNode = class extends WeaveNode {
18028
+ var WeaveStrokeNode = class WeaveStrokeNode extends WeaveNode {
17882
18029
  config;
17883
18030
  nodeType = WEAVE_STROKE_NODE_TYPE;
17884
18031
  initialize = void 0;
18032
+ /**
18033
+ * Below this chord deviation (in canvas units) a spline span is treated as
18034
+ * straight and emitted as a single edge. Subdividing a straight span costs
18035
+ * vertices without changing any pixel.
18036
+ */
18037
+ static SPLINE_FLATNESS_EPSILON = .08;
17885
18038
  constructor(params) {
17886
18039
  super();
17887
18040
  const { config } = params ?? {};
17888
18041
  this.config = mergeExceptArrays(WEAVE_STROKE_NODE_DEFAULT_CONFIG, config);
17889
18042
  }
17890
- resamplePoints(pts, minDist = 2) {
18043
+ resamplePoints(pts, minDist = this.config.resamplingSpacing) {
17891
18044
  if (pts.length < 2) return pts;
17892
18045
  const result = [pts[0]];
17893
18046
  let last = pts[0];
@@ -17901,29 +18054,73 @@ var WeaveStrokeNode = class extends WeaveNode {
17901
18054
  }
17902
18055
  return result;
17903
18056
  }
17904
- getSplinePoints(pts, resolution = 8) {
18057
+ /** Perpendicular distance from `p` to the segment `a`-`b`. */
18058
+ distanceToSegment(p, a, b) {
18059
+ const dx = b.x - a.x;
18060
+ const dy = b.y - a.y;
18061
+ const lengthSq = dx * dx + dy * dy;
18062
+ let t = lengthSq ? ((p.x - a.x) * dx + (p.y - a.y) * dy) / lengthSq : 0;
18063
+ t = Math.max(0, Math.min(1, t));
18064
+ return Math.hypot(p.x - (a.x + dx * t), p.y - (a.y + dy * t));
18065
+ }
18066
+ /**
18067
+ * Evaluates the uniform Catmull-Rom basis for one 4-point window at `t`.
18068
+ */
18069
+ catmullRomAt(p0, p1, p2, p3, t) {
18070
+ const tt = t * t;
18071
+ const ttt = tt * t;
18072
+ const q1 = -ttt + 2 * tt - t;
18073
+ const q2 = 3 * ttt - 5 * tt + 2;
18074
+ const q3 = -3 * ttt + 4 * tt + t;
18075
+ const q4 = ttt - tt;
18076
+ return {
18077
+ x: (q1 * p0.x + q2 * p1.x + q3 * p2.x + q4 * p3.x) / 2,
18078
+ y: (q1 * p0.y + q2 * p1.y + q3 * p2.y + q4 * p3.y) / 2,
18079
+ pressure: (q1 * p0.pressure + q2 * p1.pressure + q3 * p2.pressure + q4 * p3.pressure) / 2
18080
+ };
18081
+ }
18082
+ /**
18083
+ * Samples a Catmull-Rom spline through `pts`, subdividing each span by its
18084
+ * ARC LENGTH rather than by a fixed step count.
18085
+ *
18086
+ * A fixed count is the reason a finished stroke looks faceted while the
18087
+ * one being drawn looks smooth: both render with the same number of
18088
+ * subdivisions per span, but a live stroke's control points sit ~1-2px
18089
+ * apart while a finalized stroke's - after Douglas-Peucker simplification
18090
+ * - sit a median 9-28px apart. The same 8 subdivisions therefore produce
18091
+ * sub-pixel edges live and edges up to ~15px long once finalized, with
18092
+ * visible turns between them.
18093
+ *
18094
+ * `resolution` is now the MINIMUM number of subdivisions for a curved
18095
+ * span (so dense, live-drawn spans behave exactly as before); long spans
18096
+ * get proportionally more, capped so pathological input cannot explode
18097
+ * the vertex count. Spans whose midpoint deviates from their chord by
18098
+ * less than `SPLINE_FLATNESS_EPSILON` are effectively straight and are
18099
+ * emitted as a single edge - subdividing a straight line adds vertices
18100
+ * without changing a pixel.
18101
+ */
18102
+ getSplinePoints(pts, resolution = this.config.splineResolution) {
18103
+ if (pts.length < 2) return [...pts];
18104
+ const targetEdge = this.config.splineTargetEdge;
18105
+ const maxSteps = this.config.splineMaxSteps;
17905
18106
  const result = [];
17906
18107
  for (let i = -1; i < pts.length - 2; i++) {
17907
18108
  const p0 = pts[Math.max(i, 0)];
17908
18109
  const p1 = pts[i + 1];
17909
18110
  const p2 = pts[i + 2];
17910
18111
  const p3 = pts[Math.min(i + 3, pts.length - 1)];
17911
- for (let t = 0; t < 1; t += 1 / resolution) {
17912
- const tt = t * t;
17913
- const ttt = tt * t;
17914
- const q1 = -ttt + 2 * tt - t;
17915
- const q2 = 3 * ttt - 5 * tt + 2;
17916
- const q3 = -3 * ttt + 4 * tt + t;
17917
- const q4 = ttt - tt;
17918
- const x = (q1 * p0.x + q2 * p1.x + q3 * p2.x + q4 * p3.x) / 2;
17919
- const y = (q1 * p0.y + q2 * p1.y + q3 * p2.y + q4 * p3.y) / 2;
17920
- const pressure = (q1 * p0.pressure + q2 * p1.pressure + q3 * p2.pressure + q4 * p3.pressure) / 2;
17921
- result.push({
17922
- x,
17923
- y,
17924
- pressure
17925
- });
17926
- }
18112
+ const chord = Math.hypot(p2.x - p1.x, p2.y - p1.y);
18113
+ let deviation = 0;
18114
+ for (const probe of [
18115
+ .25,
18116
+ .5,
18117
+ .75
18118
+ ]) {
18119
+ const at = this.catmullRomAt(p0, p1, p2, p3, probe);
18120
+ deviation = Math.max(deviation, this.distanceToSegment(at, p1, p2));
18121
+ }
18122
+ const steps = deviation < WeaveStrokeNode.SPLINE_FLATNESS_EPSILON ? 1 : Math.min(Math.max(Math.ceil(chord / targetEdge), resolution), maxSteps);
18123
+ for (let s = 0; s < steps; s++) result.push(this.catmullRomAt(p0, p1, p2, p3, s / steps));
17927
18124
  }
17928
18125
  result.push(pts[pts.length - 1]);
17929
18126
  return result;
@@ -17941,19 +18138,26 @@ var WeaveStrokeNode = class extends WeaveNode {
17941
18138
  * Draws a filled polygon from the accumulated left/right outline points of a
17942
18139
  * dash segment and adds round caps at both ends.
17943
18140
  * NOTE: mutates `rightSide` via Array.reverse() — callers must not reuse it after this call.
18141
+ *
18142
+ * The outline is NOT re-smoothed here. It used to be, back when the
18143
+ * centerline was sampled at a fixed subdivision count and could be coarse;
18144
+ * now that the centerline is subdivided by arc length, its offset curves
18145
+ * are already smooth, and measurement showed the extra spline pass cost
18146
+ * roughly half of the node's total render time while changing the drawn
18147
+ * result by well under a pixel (95th-percentile outline edge 1.72px vs
18148
+ * 1.76px; byte-identical on long, gently-curved strokes).
17944
18149
  */
17945
18150
  drawDashPolygon(ctx, leftSide, rightSide, color) {
17946
18151
  const capStartL = leftSide[0];
17947
18152
  const capStartR = rightSide[0];
17948
18153
  const capEndL = leftSide.at(-1);
17949
18154
  const capEndR = rightSide.at(-1);
17950
- const smoothLeft = this.getSplinePoints(leftSide, 4);
17951
- const smoothRight = this.getSplinePoints(rightSide.reverse(), 4);
18155
+ const outlineRight = rightSide.reverse();
17952
18156
  ctx.beginPath();
17953
18157
  ctx.fillStyle = color;
17954
- ctx.moveTo(smoothLeft[0].x, smoothLeft[0].y);
17955
- for (const p of smoothLeft) ctx.lineTo(p.x, p.y);
17956
- for (const p of smoothRight) ctx.lineTo(p.x, p.y);
18158
+ ctx.moveTo(leftSide[0].x, leftSide[0].y);
18159
+ for (const p of leftSide) ctx.lineTo(p.x, p.y);
18160
+ for (const p of outlineRight) ctx.lineTo(p.x, p.y);
17957
18161
  ctx.closePath();
17958
18162
  ctx.fill();
17959
18163
  this.drawRoundCap(ctx, capStartL, capStartR, color);
@@ -17970,13 +18174,23 @@ var WeaveStrokeNode = class extends WeaveNode {
17970
18174
  ctx.fill();
17971
18175
  return;
17972
18176
  }
17973
- const filtered = this.resamplePoints(pts, 2);
17974
- const centerline = this.getSplinePoints(filtered, 8);
18177
+ const filtered = this.resamplePoints(pts, this.config.resamplingSpacing);
18178
+ const centerline = this.getSplinePoints(filtered, this.config.splineResolution);
17975
18179
  let dashIndex = 0;
17976
18180
  let dashOn = true;
17977
18181
  let dashRemaining = dash.length ? dash[0] : Infinity;
17978
18182
  let leftSide = [];
17979
18183
  let rightSide = [];
18184
+ let pendingEndLeft = null;
18185
+ let pendingEndRight = null;
18186
+ const flushRun = () => {
18187
+ if (!dashOn || !leftSide.length || !rightSide.length) return;
18188
+ if (pendingEndLeft && pendingEndRight) {
18189
+ leftSide.push(pendingEndLeft);
18190
+ rightSide.push(pendingEndRight);
18191
+ }
18192
+ this.drawDashPolygon(ctx, leftSide, rightSide, color);
18193
+ };
17980
18194
  for (let i = 0; i < centerline.length - 1; i++) {
17981
18195
  const p0 = centerline[i];
17982
18196
  const p1 = centerline[i + 1];
@@ -18009,22 +18223,24 @@ var WeaveStrokeNode = class extends WeaveNode {
18009
18223
  y: y0 - ny * pw0,
18010
18224
  pressure: 1
18011
18225
  });
18012
- leftSide.push({
18226
+ pendingEndLeft = {
18013
18227
  x: x1 + nx * pw1,
18014
18228
  y: y1 + ny * pw1,
18015
18229
  pressure: 1
18016
- });
18017
- rightSide.push({
18230
+ };
18231
+ pendingEndRight = {
18018
18232
  x: x1 - nx * pw1,
18019
18233
  y: y1 - ny * pw1,
18020
18234
  pressure: 1
18021
- });
18235
+ };
18022
18236
  }
18023
18237
  dashRemaining -= step;
18024
18238
  if (dashRemaining <= 0) {
18025
- if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
18239
+ flushRun();
18026
18240
  leftSide = [];
18027
18241
  rightSide = [];
18242
+ pendingEndLeft = null;
18243
+ pendingEndRight = null;
18028
18244
  dashOn = !dashOn;
18029
18245
  dashIndex = (dashIndex + 1) % dash.length;
18030
18246
  dashRemaining = dash[dashIndex];
@@ -18032,7 +18248,7 @@ var WeaveStrokeNode = class extends WeaveNode {
18032
18248
  traveled += step;
18033
18249
  }
18034
18250
  }
18035
- if (dashOn && leftSide.length && rightSide.length) this.drawDashPolygon(ctx, leftSide, rightSide, color);
18251
+ flushRun();
18036
18252
  }
18037
18253
  drawShape(ctx, shape) {
18038
18254
  const strokeElements = shape.getAttrs().strokeElements;
@@ -24036,7 +24252,22 @@ const BRUSH_TOOL_STATE = {
24036
24252
  ["IDLE"]: "idle",
24037
24253
  ["DEFINE_STROKE"]: "defineStroke"
24038
24254
  };
24039
- const BRUSH_TOOL_DEFAULT_CONFIG = { interpolationSteps: 10 };
24255
+ /**
24256
+ * Suffix applied to a finished stroke's preview node while it is briefly
24257
+ * kept on screen alongside the persisted node, so the two cannot collide on
24258
+ * the same id.
24259
+ */
24260
+ const PREVIEW_ID_SUFFIX = "--preview";
24261
+ /**
24262
+ * How many frames the preview of a finished stroke may wait for its
24263
+ * persisted counterpart to be rendered before it is removed regardless.
24264
+ * Bounds the handover so a preview can never be orphaned on screen.
24265
+ */
24266
+ const PREVIEW_HANDOVER_MAX_FRAMES = 30;
24267
+ const BRUSH_TOOL_DEFAULT_CONFIG = {
24268
+ interpolationSteps: 10,
24269
+ simplifyTolerance: .5
24270
+ };
24040
24271
  //#endregion
24041
24272
  //#region src/actions/brush-tool/brush-tool.ts
24042
24273
  var WeaveBrushToolAction = class extends WeaveAction {
@@ -24053,6 +24284,7 @@ var WeaveBrushToolAction = class extends WeaveAction {
24053
24284
  prevTouchAction;
24054
24285
  penActive = false;
24055
24286
  lastSmoothedPressure = .5;
24287
+ hasPressureSample = false;
24056
24288
  lastPointerPos = null;
24057
24289
  lastPointerTime = 0;
24058
24290
  predictedCount = 0;
@@ -24084,26 +24316,84 @@ var WeaveBrushToolAction = class extends WeaveAction {
24084
24316
  opacity: 1
24085
24317
  };
24086
24318
  }
24087
- getEventPressure(e) {
24319
+ /**
24320
+ * Velocity-adaptive exponential smoothing of a single raw pressure sample.
24321
+ *
24322
+ * This is the one place pressure smoothing happens. Every sample that
24323
+ * becomes a stroke point must go through it - including the
24324
+ * high-frequency coalesced samples a stylus reports (see
24325
+ * `handlePointerMove`), which previously bypassed it entirely and so
24326
+ * landed in the stroke with their raw value, no pressure floor, and
24327
+ * without advancing the smoothing state for later samples.
24328
+ *
24329
+ * Timing intentionally uses `performance.now()` (not the event's own
24330
+ * `timeStamp`), matching the original behaviour: the two are not
24331
+ * guaranteed to share a time origin, and mixing them corrupts the
24332
+ * velocity term that drives `alpha`.
24333
+ *
24334
+ * The first sample of a stroke SEEDS the filter with its own value
24335
+ * rather than being blended toward the neutral 0.5 baseline. Blending
24336
+ * from a constant gives every stroke a warm-up transient: the filter
24337
+ * starts at 0.5 and converges toward the pressure actually being
24338
+ * applied, so a stroke drawn lighter than 0.5 renders visibly fatter at
24339
+ * its start (and heavier than 0.5, thinner) until the filter catches
24340
+ * up. Seeding removes that transient entirely - smoothing then only
24341
+ * ever acts between real samples, which is its actual purpose.
24342
+ */
24343
+ smoothPressureSample(raw, clientX, clientY) {
24088
24344
  const now = performance.now();
24089
24345
  let velocity = 0;
24090
- if (this.lastPointerPos && now - this.lastPointerTime > 0) {
24091
- const dx = e.evt.clientX - this.lastPointerPos.x;
24092
- const dy = e.evt.clientY - this.lastPointerPos.y;
24346
+ const hasPosition = Number.isFinite(clientX) && Number.isFinite(clientY);
24347
+ if (hasPosition && this.lastPointerPos && now - this.lastPointerTime > 0) {
24348
+ const dx = clientX - this.lastPointerPos.x;
24349
+ const dy = clientY - this.lastPointerPos.y;
24093
24350
  velocity = Math.hypot(dx, dy) / (now - this.lastPointerTime) * 1e3;
24094
24351
  }
24095
- this.lastPointerPos = {
24096
- x: e.evt.clientX,
24097
- y: e.evt.clientY
24352
+ if (hasPosition) this.lastPointerPos = {
24353
+ x: clientX,
24354
+ y: clientY
24098
24355
  };
24099
24356
  this.lastPointerTime = now;
24357
+ const safeRaw = Number.isFinite(raw) ? raw : .5;
24358
+ if (!this.hasPressureSample) {
24359
+ this.hasPressureSample = true;
24360
+ this.lastSmoothedPressure = safeRaw;
24361
+ return Math.max(this.lastSmoothedPressure, .15);
24362
+ }
24100
24363
  const alpha = Math.min(Math.max(velocity / 1500, .15), .6);
24101
- let raw;
24102
- if (e.evt.pointerType === "pen") raw = e.evt.pressure || .5;
24103
- else raw = .5;
24104
- this.lastSmoothedPressure = alpha * raw + (1 - alpha) * this.lastSmoothedPressure;
24364
+ const smoothed = alpha * safeRaw + (1 - alpha) * this.lastSmoothedPressure;
24365
+ this.lastSmoothedPressure = Number.isFinite(smoothed) ? smoothed : safeRaw;
24105
24366
  return Math.max(this.lastSmoothedPressure, .15);
24106
24367
  }
24368
+ /**
24369
+ * Raw pressure a pointer sample reports: the device's own reading for a
24370
+ * pen, a neutral constant for devices that do not sense pressure.
24371
+ */
24372
+ getRawPressure(pointerType, pressure) {
24373
+ if (pointerType === "pen") return (typeof pressure === "number" ? pressure : 0) || .5;
24374
+ return .5;
24375
+ }
24376
+ getEventPressure(e) {
24377
+ return this.smoothPressureSample(this.getRawPressure(e.evt.pointerType, e.evt.pressure), e.evt.clientX, e.evt.clientY);
24378
+ }
24379
+ /**
24380
+ * Resets the pressure-smoothing state (smoothed pressure, last known pointer
24381
+ * position/time, and predicted-point bookkeeping) to a neutral baseline.
24382
+ *
24383
+ * This state is scoped to a single stroke: it must be reset before the
24384
+ * first pressure sample of a new stroke is computed (in `handlePointerDown`,
24385
+ * ahead of the `getEventPressure` call for point 0), not only afterwards -
24386
+ * otherwise that first sample can inherit smoothing state left behind by
24387
+ * the previous stroke (its ending pressure, pointer position and time),
24388
+ * inflating the very first point's pressure.
24389
+ */
24390
+ resetPressureSmoothingState() {
24391
+ this.lastSmoothedPressure = .5;
24392
+ this.hasPressureSample = false;
24393
+ this.lastPointerPos = null;
24394
+ this.lastPointerTime = 0;
24395
+ this.predictedCount = 0;
24396
+ }
24107
24397
  setupEvents() {
24108
24398
  const stage = this.instance.getStage();
24109
24399
  this.prevTouchAction = stage.container().style.touchAction;
@@ -24135,6 +24425,7 @@ var WeaveBrushToolAction = class extends WeaveAction {
24135
24425
  if (e?.evt?.button !== 0) return;
24136
24426
  if (e.evt.pointerType === "touch" && this.penActive) return;
24137
24427
  if (e.evt.pointerType === "pen") this.penActive = true;
24428
+ this.resetPressureSmoothingState();
24138
24429
  const pointPressure = this.getEventPressure(e);
24139
24430
  this.handleStartStroke(pointPressure);
24140
24431
  e.evt.stopPropagation();
@@ -24147,16 +24438,21 @@ var WeaveBrushToolAction = class extends WeaveAction {
24147
24438
  if (this.getZoomPlugin()?.isPinching()) return;
24148
24439
  const coalescedEvents = e.evt.getCoalescedEvents ? e.evt.getCoalescedEvents() : [];
24149
24440
  if (coalescedEvents.length > 1) {
24150
- for (const ce of coalescedEvents) {
24151
- const pointPressure = ce.pointerType === "pen" && typeof ce.pressure === "number" ? ce.pressure : .5;
24152
- this.handleMovement(pointPressure, void 0, false);
24153
- }
24441
+ const samples = coalescedEvents.map((ce) => ({
24442
+ pressure: this.smoothPressureSample(this.getRawPressure(ce.pointerType, ce.pressure), ce.clientX, ce.clientY),
24443
+ isPredicted: false
24444
+ }));
24154
24445
  const predictedEvents = e.evt.getPredictedEvents ? e.evt.getPredictedEvents() : [];
24155
24446
  if (predictedEvents.length > 0) {
24156
24447
  const last = predictedEvents[predictedEvents.length - 1];
24157
- const predPressure = last.pointerType === "pen" && typeof last.pressure === "number" ? last.pressure : .5;
24158
- this.handleMovement(predPressure, last, true);
24448
+ const predPressure = Math.max(.15 * this.getRawPressure(last.pointerType, last.pressure) + .85 * this.lastSmoothedPressure, .15);
24449
+ samples.push({
24450
+ pressure: predPressure,
24451
+ sourceEvent: last,
24452
+ isPredicted: true
24453
+ });
24159
24454
  }
24455
+ this.applyMovementSamples(samples);
24160
24456
  } else {
24161
24457
  const pointPressure = this.getEventPressure(e);
24162
24458
  this.handleMovement(pointPressure, void 0, false);
@@ -24202,10 +24498,7 @@ var WeaveBrushToolAction = class extends WeaveAction {
24202
24498
  };
24203
24499
  }
24204
24500
  handleStartStroke(pressure) {
24205
- this.lastSmoothedPressure = .5;
24206
- this.lastPointerPos = null;
24207
- this.lastPointerTime = 0;
24208
- this.predictedCount = 0;
24501
+ this.resetPressureSmoothingState();
24209
24502
  const { mousePoint, container, measureContainer } = this.instance.getMousePointer();
24210
24503
  this.clickPoint = mousePoint;
24211
24504
  this.container = container;
@@ -24234,36 +24527,60 @@ var WeaveBrushToolAction = class extends WeaveAction {
24234
24527
  }
24235
24528
  this.setState(BRUSH_TOOL_STATE.DEFINE_STROKE);
24236
24529
  }
24237
- handleMovement(pressure, predictedEvent, isPredicted = false) {
24530
+ /**
24531
+ * Appends one or more pointer samples to the stroke in a single
24532
+ * read-modify-write, then renders once.
24533
+ *
24534
+ * Batching matters: a stylus reports a burst of coalesced samples per
24535
+ * frame, and applying them one at a time meant copying the whole point
24536
+ * array, recomputing the whole bounding box and issuing a full redraw
24537
+ * once PER SAMPLE. That is O(n) work repeated ~10x a frame, i.e.
24538
+ * quadratic in stroke length - which is why drawing fast could stall for
24539
+ * seconds. Here that O(n) work happens once per event instead.
24540
+ */
24541
+ applyMovementSamples(samples) {
24238
24542
  if (this.state !== BRUSH_TOOL_STATE.DEFINE_STROKE) return;
24543
+ if (samples.length === 0) return;
24239
24544
  const stage = this.instance.getStage();
24240
- const tempStroke = this.instance.getStage().findOne(`#${this.strokeId}`);
24241
- if (this.measureContainer && tempStroke) {
24242
- if (predictedEvent) stage.setPointersPositions(predictedEvent);
24545
+ const tempStroke = stage.findOne(`#${this.strokeId}`);
24546
+ if (!this.measureContainer || !tempStroke) return;
24547
+ let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
24548
+ for (const sample of samples) {
24549
+ if (sample.sourceEvent) stage.setPointersPositions(sample.sourceEvent);
24243
24550
  const { mousePoint } = this.instance.getMousePointerRelativeToContainer(this.measureContainer);
24244
24551
  const currentPoint = {
24245
24552
  x: mousePoint.x - tempStroke.x(),
24246
24553
  y: mousePoint.y - tempStroke.y(),
24247
- pressure
24554
+ pressure: sample.pressure
24248
24555
  };
24249
- let newStrokeElements = [...tempStroke.getAttrs().strokeElements];
24250
- if (!isPredicted && this.predictedCount > 0) {
24556
+ if (!sample.isPredicted && this.predictedCount > 0) {
24251
24557
  newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
24252
24558
  this.predictedCount = 0;
24253
24559
  }
24560
+ if (!sample.isPredicted && newStrokeElements.length === 1) newStrokeElements[0] = {
24561
+ ...newStrokeElements[0],
24562
+ pressure: currentPoint.pressure
24563
+ };
24254
24564
  newStrokeElements.push(currentPoint);
24255
- if (isPredicted) this.predictedCount++;
24256
- const box = this.getBoundingBox(newStrokeElements);
24257
- tempStroke.setAttrs({
24258
- width: box.width,
24259
- height: box.height,
24260
- x: 0,
24261
- y: 0,
24262
- strokeElements: newStrokeElements
24263
- });
24264
- const nodeHandler = this.instance.getNodeHandler("stroke");
24265
- if (nodeHandler) nodeHandler.onUpdate(tempStroke, tempStroke.getAttrs());
24565
+ if (sample.isPredicted) this.predictedCount++;
24266
24566
  }
24567
+ const box = this.getBoundingBox(newStrokeElements);
24568
+ tempStroke.setAttrs({
24569
+ width: box.width,
24570
+ height: box.height,
24571
+ x: 0,
24572
+ y: 0,
24573
+ strokeElements: newStrokeElements
24574
+ });
24575
+ const nodeHandler = this.instance.getNodeHandler("stroke");
24576
+ if (nodeHandler) nodeHandler.onUpdate(tempStroke, tempStroke.getAttrs());
24577
+ }
24578
+ handleMovement(pressure, predictedEvent, isPredicted = false) {
24579
+ this.applyMovementSamples([{
24580
+ pressure,
24581
+ sourceEvent: predictedEvent,
24582
+ isPredicted
24583
+ }]);
24267
24584
  }
24268
24585
  finalizeStroke(tempStroke, nodeHandler) {
24269
24586
  const box = this.getBoundingBox(tempStroke.getAttrs().strokeElements);
@@ -24272,12 +24589,19 @@ var WeaveBrushToolAction = class extends WeaveAction {
24272
24589
  newStrokeElements = newStrokeElements.slice(0, -1 * this.predictedCount);
24273
24590
  this.predictedCount = 0;
24274
24591
  }
24592
+ if (newStrokeElements.length >= 2) {
24593
+ const lastIndex = newStrokeElements.length - 1;
24594
+ newStrokeElements[lastIndex] = {
24595
+ ...newStrokeElements[lastIndex],
24596
+ pressure: newStrokeElements[lastIndex - 1].pressure
24597
+ };
24598
+ }
24275
24599
  newStrokeElements = newStrokeElements.map((point) => ({
24276
24600
  ...point,
24277
24601
  x: point.x - box.x,
24278
24602
  y: point.y - box.y
24279
24603
  }));
24280
- const compressedPoints = simplify(newStrokeElements, 1, true);
24604
+ const compressedPoints = simplify(newStrokeElements, this.config.simplifyTolerance, true);
24281
24605
  const sw = tempStroke.getAttrs().strokeWidth ?? 1;
24282
24606
  const finalWidth = Math.max(box.width, sw);
24283
24607
  const finalHeight = Math.max(box.height, sw);
@@ -24290,9 +24614,44 @@ var WeaveBrushToolAction = class extends WeaveAction {
24290
24614
  y: finalY,
24291
24615
  strokeElements: compressedPoints
24292
24616
  });
24293
- const realNode = this.instance.getStage().findOne(`#${tempStroke.getAttrs().id}`);
24294
- if (realNode) realNode.destroy();
24295
- if (tempStroke.getAttrs().strokeElements.length >= 1) this.instance.addNode(nodeHandler.serialize(tempStroke), this.container?.getAttrs().id);
24617
+ const strokeNodeId = tempStroke.getAttrs().id ?? "";
24618
+ const serializedNode = nodeHandler.serialize(tempStroke);
24619
+ const hasPoints = tempStroke.getAttrs().strokeElements.length >= 1;
24620
+ const preview = this.instance.getStage().findOne(`#${strokeNodeId}`);
24621
+ if (!hasPoints) {
24622
+ preview?.destroy();
24623
+ return;
24624
+ }
24625
+ if (preview) preview.setAttrs({ id: `${strokeNodeId}${PREVIEW_ID_SUFFIX}` });
24626
+ this.instance.addNode(serializedNode, this.container?.getAttrs().id);
24627
+ this.removePreviewWhenRendered(preview, strokeNodeId);
24628
+ }
24629
+ /**
24630
+ * Destroys the finished stroke's preview node once its persisted
24631
+ * counterpart has actually been rendered onto the stage, so the stroke is
24632
+ * never absent from the canvas between the two.
24633
+ *
24634
+ * Falls back to destroying immediately where no frame scheduler exists
24635
+ * (non-browser hosts), and gives up waiting after a bounded number of
24636
+ * frames so a preview can never be orphaned if the node never arrives.
24637
+ */
24638
+ removePreviewWhenRendered(preview, realNodeId) {
24639
+ if (!preview) return;
24640
+ const scheduleFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : void 0;
24641
+ if (!scheduleFrame) {
24642
+ preview.destroy();
24643
+ return;
24644
+ }
24645
+ let framesWaited = 0;
24646
+ const check = () => {
24647
+ if (this.instance.getStage().findOne(`#${realNodeId}`) || framesWaited >= 30) {
24648
+ preview.destroy();
24649
+ return;
24650
+ }
24651
+ framesWaited++;
24652
+ scheduleFrame(check);
24653
+ };
24654
+ check();
24296
24655
  }
24297
24656
  handleEndStroke() {
24298
24657
  const tempStroke = this.instance.getStage().findOne(`#${this.strokeId}`);
@@ -28154,7 +28513,7 @@ const POOL_SIZE_MULTIPLIER = 128;
28154
28513
  let pool;
28155
28514
  let poolOffset;
28156
28515
  let fillPool = (bytes) => {
28157
- if (bytes < 0) throw new RangeError("Wrong ID size");
28516
+ if (bytes < 0 || bytes > 1024) throw new RangeError("Wrong ID size");
28158
28517
  try {
28159
28518
  if (!pool || pool.length < bytes) {
28160
28519
  pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
@@ -32935,4 +33294,4 @@ const setupCanvasBackend = async () => {
32935
33294
  await import("konva/canvas-backend");
32936
33295
  };
32937
33296
  //#endregion
32938
- 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, buildAncestorGroupIds, canComposite, clearContainerTargets, computeEllipseLabelMinSize, computePolygonLabelMinSize, computeRectangleLabelMinSize, containerOverCursor, containsNodeDeep, defaultInitialState, downscaleImageFile, downscaleImageFromURL, getBoundingBox, getDownscaleRatio, getExportBoundingBox, getImageSizeFromFile, getSelectedNodesMetadata, getShapeLabelSchemaFields, getStageClickPoint, getTargetAndSkipNodes, getTargetedNode, getTopmostShadowHost, getVisibleNodes, getVisibleNodesInViewport, hasFrames, hasImages, instantiatePreset, intersectArrays, isIOS, isInShadowDOM, isNodeInSelection, isNumber, isServer, labelId, loadImageSource, memoize, mergeExceptArrays, moveNodeToContainer, moveNodeToContainerNT, resetScale, setupCanvasBackend, setupSkiaBackend, sleep, spreadLabelProps };
33297
+ export { ALIGN_NODES_ALIGN_TO, ALIGN_NODES_TOOL_ACTION_NAME, ALIGN_NODES_TOOL_STATE, BRUSH_TOOL_ACTION_NAME, BRUSH_TOOL_DEFAULT_CONFIG, BRUSH_TOOL_STATE, CONNECTOR_TOOL_ACTION_NAME, CONNECTOR_TOOL_DEFAULT_CONFIG, CONNECTOR_TOOL_STATE, COPY_PASTE_NODES_PLUGIN_STATE, DEFAULT_GUIDE_TOOL_ACTION_CONFIG, DEFAULT_SNAPPING_MANAGER_CONFIG, ELLIPSE_TOOL_ACTION_NAME, ELLIPSE_TOOL_STATE, ERASER_TOOL_ACTION_NAME, ERASER_TOOL_STATE, FRAME_TOOL_ACTION_NAME, FRAME_TOOL_STATE, GUIDE_DISTANCE_NAME, GUIDE_DISTANCE_ORIGIN, GUIDE_KIND, GUIDE_NAME, GUIDE_ORIENTATION, GUIDE_STATE, GUIDE_TOOL_ACTION_NAME, GUIDE_TOOL_STATE, LINE_TOOL_ACTION_NAME, LINE_TOOL_DEFAULT_CONFIG, LINE_TOOL_STATE, MEASURE_TOOL_ACTION_NAME, MEASURE_TOOL_STATE, MOVE_ORIENTATION, MOVE_TOOL_ACTION_NAME, MOVE_TOOL_STATE, PEN_TOOL_ACTION_NAME, PEN_TOOL_STATE, POLYGON_TOOL_ACTION_NAME, POLYGON_TOOL_STATE, PREVIEW_HANDOVER_MAX_FRAMES, PREVIEW_ID_SUFFIX, RECTANGLE_TOOL_ACTION_NAME, RECTANGLE_TOOL_STATE, REGULAR_POLYGON_TOOL_ACTION_NAME, REGULAR_POLYGON_TOOL_STATE, SELECTION_TOOL_ACTION_NAME, SELECTION_TOOL_STATE, STAGE_MINIMAP_DEFAULT_CONFIG, STAR_TOOL_ACTION_NAME, STAR_TOOL_STATE, TEXT_LAYOUT, TEXT_TOOL_ACTION_NAME, TEXT_TOOL_STATE, VIDEO_TOOL_ACTION_NAME, VIDEO_TOOL_STATE, WEAVE_ARROW_NODE_TYPE, WEAVE_ARROW_TOOL_ACTION_NAME, WEAVE_ARROW_TOOL_STATE, WEAVE_COMMENTS_RENDERER_KEY, WEAVE_COMMENTS_TOOL_LAYER_ID, WEAVE_COMMENT_CREATE_ACTION, WEAVE_COMMENT_NODE_ACTION, WEAVE_COMMENT_NODE_DEFAULTS, WEAVE_COMMENT_NODE_TYPE, WEAVE_COMMENT_STATUS, WEAVE_COMMENT_TOOL_ACTION_NAME, WEAVE_COMMENT_TOOL_DEFAULT_CONFIG, WEAVE_COMMENT_TOOL_STATE, WEAVE_COMMENT_VIEW_ACTION, WEAVE_CONNECTOR_NODE_ANCHOR_ORIGIN, WEAVE_CONNECTOR_NODE_DECORATOR_TYPE, WEAVE_CONNECTOR_NODE_DEFAULT_CONFIG, WEAVE_CONNECTOR_NODE_LINE_ORIGIN, WEAVE_CONNECTOR_NODE_LINE_TYPE, WEAVE_CONNECTOR_NODE_TYPE, WEAVE_COPY_PASTE_CONFIG_DEFAULT, WEAVE_COPY_PASTE_NODES_KEY, WEAVE_COPY_PASTE_PASTE_CATCHER_ID, WEAVE_COPY_PASTE_PASTE_MODES, WEAVE_DEFAULT_USER_INFO_FUNCTION, WEAVE_ELLIPSE_NODE_TYPE, WEAVE_FRAME_DEFAULT_BACKGROUND_COLOR, WEAVE_FRAME_NODE_DEFAULT_CONFIG, WEAVE_FRAME_NODE_DEFAULT_PROPS, WEAVE_FRAME_NODE_TYPE, WEAVE_GRID_DEFAULT_CONFIG, WEAVE_GRID_DOT_TYPES, WEAVE_GRID_LAYER_ID, WEAVE_GRID_TYPES, WEAVE_GROUP_NODE_TYPE, WEAVE_IMAGES_TOOL_ACTION_NAME, WEAVE_IMAGES_TOOL_DEFAULT_CONFIG, WEAVE_IMAGES_TOOL_STATE, WEAVE_IMAGES_TOOL_UPLOAD_TYPE, WEAVE_IMAGE_CROP_ANCHOR_POSITION, WEAVE_IMAGE_CROP_END_TYPE, WEAVE_IMAGE_DEFAULT_CONFIG, WEAVE_IMAGE_NODE_TYPE, WEAVE_IMAGE_TOOL_ACTION_NAME, WEAVE_IMAGE_TOOL_CONFIG_DEFAULT, WEAVE_IMAGE_TOOL_STATE, WEAVE_IMAGE_TOOL_UPLOAD_TYPE, WEAVE_LAYER_NODE_TYPE, WEAVE_LINE_NODE_DEFAULT_CONFIG, WEAVE_LINE_NODE_TYPE, WEAVE_MEASURE_NODE_DEFAULT_CONFIG, WEAVE_MEASURE_NODE_TYPE, WEAVE_MEASURE_TOOL_DEFAULT_CONFIG, WEAVE_NODES_MULTI_SELECTION_FEEDBACK_PLUGIN_DEFAULT_CONFIG, WEAVE_NODES_MULTI_SELECTION_FEEDBACK_PLUGIN_KEY, WEAVE_NODES_MULTI_SELECTION_FEEDBACK_PLUGIN_LAYER_ID, WEAVE_NODES_SELECTION_DEFAULT_CONFIG, WEAVE_NODES_SELECTION_KEY, WEAVE_NODES_SELECTION_LAYER_ID, WEAVE_NODES_SNAPPING_PLUGIN_KEY, WEAVE_POLYGON_NODE_TYPE, WEAVE_POLYGON_PRESETS, WEAVE_RECTANGLE_NODE_TYPE, WEAVE_REGULAR_POLYGON_NODE_TYPE, WEAVE_SHAPE_LABEL_DEFAULTS, WEAVE_STAGE_DEFAULT_MODE, WEAVE_STAGE_DROP_AREA_KEY, WEAVE_STAGE_GRID_PLUGIN_KEY, WEAVE_STAGE_IMAGE_CROPPING_MODE, WEAVE_STAGE_KEYBOARD_MOVE_DEFAULT_CONFIG, WEAVE_STAGE_KEYBOARD_MOVE_KEY, WEAVE_STAGE_KEYBOARD_MOVE_ORIENTATION, WEAVE_STAGE_MINIMAP_KEY, WEAVE_STAGE_NODE_TYPE, WEAVE_STAGE_PANNING_DEFAULT_CONFIG, WEAVE_STAGE_PANNING_KEY, WEAVE_STAGE_PANNING_THROTTLE_MS, WEAVE_STAGE_SHAPE_LABEL_EDITION_MODE, WEAVE_STAGE_TEXT_EDITION_MODE, WEAVE_STAGE_ZOOM_DEFAULT_CONFIG, WEAVE_STAGE_ZOOM_KEY, WEAVE_STAGE_ZOOM_TYPE, WEAVE_STAR_NODE_TYPE, WEAVE_STROKE_NODE_DEFAULT_CONFIG, WEAVE_STROKE_NODE_TYPE, WEAVE_STROKE_SINGLE_NODE_DEFAULT_CONFIG, WEAVE_STROKE_SINGLE_NODE_TIP_SIDE, WEAVE_STROKE_SINGLE_NODE_TIP_TYPE, WEAVE_STROKE_SINGLE_NODE_TYPE, WEAVE_STROKE_TOOL_ACTION_NAME, WEAVE_STROKE_TOOL_ACTION_NAME_ALIASES, WEAVE_STROKE_TOOL_DEFAULT_CONFIG, WEAVE_STROKE_TOOL_STATE, WEAVE_TEXT_LINK_ALLOWED_PROTOCOLS, WEAVE_TEXT_NODE_DEFAULT_CONFIG, WEAVE_TEXT_NODE_TYPE, WEAVE_USERS_POINTERS_CONFIG_DEFAULT_PROPS, WEAVE_USERS_POINTERS_KEY, WEAVE_USERS_PRESENCE_CONFIG_DEFAULT_PROPS, WEAVE_USERS_PRESENCE_PLUGIN_KEY, WEAVE_USERS_SELECTION_KEY, WEAVE_USER_POINTER_KEY, WEAVE_USER_PRESENCE_KEY, WEAVE_USER_SELECTION_KEY, WEAVE_VIDEO_DEFAULT_CONFIG, WEAVE_VIDEO_NODE_TYPE, Weave, WeaveAction, WeaveAlignNodesToolAction, WeaveArrowNode, WeaveArrowToolAction, WeaveBrushToolAction, WeaveCommentNode, WeaveCommentToolAction, WeaveCommentsRendererPlugin, WeaveConnectedUsersPlugin, WeaveConnectorNode, WeaveConnectorToolAction, WeaveContextMenuPlugin, WeaveCopyPasteNodesPlugin, WeaveEllipseNode, WeaveEllipseToolAction, WeaveEraserToolAction, WeaveExportNodesToolAction, WeaveExportStageToolAction, WeaveFitToScreenToolAction, WeaveFitToSelectionToolAction, WeaveFrameNode, WeaveFrameToolAction, WeaveGroupNode, WeaveGuideToolAction, WeaveImageNode, WeaveImageToolAction, WeaveImagesToolAction, WeaveLayerNode, WeaveLineNode, WeaveLineToolAction, WeaveMeasureNode, WeaveMeasureToolAction, WeaveMoveToolAction, WeaveNode, WeaveNodesMultiSelectionFeedbackPlugin, WeaveNodesSelectionPlugin, WeaveNodesSnappingPlugin, WeavePenToolAction, WeavePlugin, WeavePolygonNode, WeavePolygonToolAction, WeaveRectangleNode, WeaveRectangleToolAction, WeaveRegularPolygonNode, WeaveRegularPolygonToolAction, WeaveRenderer, WeaveSelectionToolAction, WeaveStageDropAreaPlugin, WeaveStageGridPlugin, WeaveStageKeyboardMovePlugin, WeaveStageMinimapPlugin, WeaveStageNode, WeaveStagePanningPlugin, WeaveStageResizePlugin, WeaveStageZoomPlugin, WeaveStarNode, WeaveStarToolAction, WeaveStateManipulation, WeaveStore, WeaveStrokeNode, WeaveStrokeSingleNode, WeaveStrokeToolAction, WeaveTextNode, WeaveTextToolAction, WeaveUsersPointersPlugin, WeaveUsersPresencePlugin, WeaveUsersSelectionPlugin, WeaveVideoNode, WeaveVideoToolAction, WeaveZoomInToolAction, WeaveZoomOutToolAction, buildAncestorGroupIds, canComposite, clearContainerTargets, computeEllipseLabelMinSize, computePolygonLabelMinSize, computeRectangleLabelMinSize, containerOverCursor, containsNodeDeep, defaultInitialState, downscaleImageFile, downscaleImageFromURL, getBoundingBox, getDownscaleRatio, getExportBoundingBox, getImageSizeFromFile, getSelectedNodesMetadata, getShapeLabelSchemaFields, getStageClickPoint, getTargetAndSkipNodes, getTargetedNode, getTopmostShadowHost, getVisibleNodes, getVisibleNodesInViewport, hasFrames, hasImages, instantiatePreset, intersectArrays, isIOS, isInShadowDOM, isNodeInSelection, isNumber, isServer, labelId, loadImageSource, memoize, mergeExceptArrays, moveNodeToContainer, moveNodeToContainerNT, resetScale, setupCanvasBackend, setupSkiaBackend, sleep, spreadLabelProps };