@inditextech/weave-sdk 5.6.0 → 5.7.0-SNAPSHOT.595.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/types.js CHANGED
@@ -9502,7 +9502,7 @@ var WeaveRegisterManager = class {
9502
9502
  };
9503
9503
  //#endregion
9504
9504
  //#region package.json
9505
- var version = "5.6.0";
9505
+ var version = "5.7.0-SNAPSHOT.595.1";
9506
9506
  //#endregion
9507
9507
  //#region src/managers/setup.ts
9508
9508
  var WeaveSetupManager = class {
@@ -14439,6 +14439,1991 @@ var WeaveTextNode = class extends WeaveNode {
14439
14439
  }
14440
14440
  };
14441
14441
  //#endregion
14442
+ //#region src/nodes/text-v2/model.ts
14443
+ function isEmptyMarks(m) {
14444
+ return !m || Object.keys(m).length === 0;
14445
+ }
14446
+ function sameMarks(a, b) {
14447
+ if (isEmptyMarks(a) && isEmptyMarks(b)) return true;
14448
+ if (isEmptyMarks(a) || isEmptyMarks(b)) return false;
14449
+ const ka = Object.keys(a);
14450
+ const kb = Object.keys(b);
14451
+ return ka.length === kb.length && ka.every((k) => a[k] === b[k]);
14452
+ }
14453
+ /** Merge adjacent runs with equal marks and drop empty runs (keeps >= 0). */
14454
+ function normalizeRuns(runs) {
14455
+ const out = [];
14456
+ for (const run of runs) {
14457
+ if (run.t === "") continue;
14458
+ const last = out[out.length - 1];
14459
+ if (last && sameMarks(last.m, run.m)) last.t += run.t;
14460
+ else out.push({
14461
+ t: run.t,
14462
+ ...isEmptyMarks(run.m) ? {} : { m: run.m }
14463
+ });
14464
+ }
14465
+ return out;
14466
+ }
14467
+ /**
14468
+ * Synthesize blocks from legacy plain `text`. One run per paragraph; a
14469
+ * legacy whole-text `link` becomes a full-range `link` mark.
14470
+ */
14471
+ function synthesizeBlocks(text, link) {
14472
+ return (text ?? "").split("\n").map((paragraph) => ({ runs: paragraph === "" ? [] : [{
14473
+ t: paragraph,
14474
+ ...link ? { m: { link } } : {}
14475
+ }] }));
14476
+ }
14477
+ /** Resolve blocks of a node: stored `blocks` or lazily synthesized (no write). */
14478
+ function resolveBlocks(attrs) {
14479
+ if (Array.isArray(attrs.blocks) && attrs.blocks.length > 0) return attrs.blocks;
14480
+ return synthesizeBlocks(attrs.text ?? "", attrs.link);
14481
+ }
14482
+ function blockText(block) {
14483
+ return block.runs.map((r) => r.t).join("");
14484
+ }
14485
+ /** Derived mirror for the `text` attribute. */
14486
+ function blocksToText(blocks) {
14487
+ return blocks.map(blockText).join("\n");
14488
+ }
14489
+ /** Slice runs of a block between local offsets [from, to). */
14490
+ function sliceRuns(runs, from, to) {
14491
+ const out = [];
14492
+ let pos = 0;
14493
+ for (const run of runs) {
14494
+ const start = pos;
14495
+ const end = pos + run.t.length;
14496
+ pos = end;
14497
+ const s = Math.max(from, start);
14498
+ const e = Math.min(to, end);
14499
+ if (s < e) out.push({
14500
+ t: run.t.slice(s - start, e - start),
14501
+ m: run.m
14502
+ });
14503
+ }
14504
+ return out;
14505
+ }
14506
+ /** Marks of the run at (or immediately before) a local offset. */
14507
+ function marksAt(runs, offset) {
14508
+ let pos = 0;
14509
+ let found;
14510
+ for (const run of runs) {
14511
+ const end = pos + run.t.length;
14512
+ if (offset > pos && offset <= end) return run.m;
14513
+ if (offset === pos && !found) found = run.m;
14514
+ pos = end;
14515
+ }
14516
+ return found;
14517
+ }
14518
+ /**
14519
+ * Replace the global range [start, end) (newline counts as 1 char between
14520
+ * blocks) with plain `insert`. Marks on untouched text are preserved; the
14521
+ * inserted text inherits marks of the character before `start`.
14522
+ */
14523
+ function replaceRange(blocks, start, end, insert) {
14524
+ const total = blocksToText(blocks).length;
14525
+ start = Math.max(0, Math.min(start, total));
14526
+ end = Math.max(start, Math.min(end, total));
14527
+ let before = [];
14528
+ let after = [];
14529
+ const prefixBlocks = [];
14530
+ const suffixBlocks = [];
14531
+ let inheritedMarks;
14532
+ let pos = 0;
14533
+ blocks.forEach((block, i) => {
14534
+ const len = blockText(block).length;
14535
+ const bStart = pos;
14536
+ const bEnd = pos + len;
14537
+ pos = bEnd + 1;
14538
+ if (bEnd < start) prefixBlocks.push(block);
14539
+ else if (bStart > end) suffixBlocks.push(block);
14540
+ else {
14541
+ if (start >= bStart && start <= bEnd) {
14542
+ const local = start - bStart;
14543
+ before = sliceRuns(block.runs, 0, local);
14544
+ inheritedMarks = marksAt(block.runs, local);
14545
+ }
14546
+ if (end >= bStart && end <= bEnd) after = sliceRuns(block.runs, end - bStart, len);
14547
+ }
14548
+ });
14549
+ const lines = insert.split("\n");
14550
+ const mk = (t) => t === "" ? [] : [{
14551
+ t,
14552
+ ...isEmptyMarks(inheritedMarks) ? {} : { m: inheritedMarks }
14553
+ }];
14554
+ const middle = [];
14555
+ lines.forEach((line, idx) => {
14556
+ let runs = mk(line);
14557
+ if (idx === 0) runs = [...before, ...runs];
14558
+ if (idx === lines.length - 1) runs = [...runs, ...after];
14559
+ middle.push({ runs: normalizeRuns(runs) });
14560
+ });
14561
+ return [
14562
+ ...prefixBlocks,
14563
+ ...middle,
14564
+ ...suffixBlocks
14565
+ ];
14566
+ }
14567
+ //#endregion
14568
+ //#region src/nodes/text-v2/layout/engine.ts
14569
+ /** Quote multi-word families like `Konva` does so the font shorthand parses. */
14570
+ function normalizeFontFamily(fontFamily) {
14571
+ return fontFamily.split(",").map((family) => {
14572
+ family = family.trim();
14573
+ const hasSpace = family.includes(" ");
14574
+ const hasQuotes = family.includes("\"") || family.includes("'");
14575
+ return hasSpace && !hasQuotes ? `"${family}"` : family;
14576
+ }).join(", ");
14577
+ }
14578
+ function fontString(s) {
14579
+ return `${s.fontStyle} ${s.fontVariant} ${s.fontSize}px ${normalizeFontFamily(s.fontFamily)}`;
14580
+ }
14581
+ let sharedContext = null;
14582
+ /** Default measurer: works on browser, node-canvas and skia-canvas backends. */
14583
+ const canvasMeasurer = (font, text) => {
14584
+ sharedContext ??= Konva.Util.createCanvasElement().getContext("2d");
14585
+ sharedContext.font = font;
14586
+ return sharedContext.measureText(text).width;
14587
+ };
14588
+ const graphemeSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
14589
+ function splitGraphemes(text) {
14590
+ if (text === "") return [];
14591
+ if (graphemeSegmenter) return Array.from(graphemeSegmenter.segment(text), (s) => s.segment);
14592
+ return Array.from(text);
14593
+ }
14594
+ const now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
14595
+ const WS = /\s/;
14596
+ const MAX_CACHE = 2e4;
14597
+ const MAX_CACHED_CHUNK = 256;
14598
+ var TextLayoutEngine = class {
14599
+ measure;
14600
+ cache = /* @__PURE__ */ new Map();
14601
+ paragraphs = [];
14602
+ /** Entries by signature, so paragraphs that shifted index are reused. */
14603
+ pool = /* @__PURE__ */ new Map();
14604
+ lastParagraphsRelaid = 0;
14605
+ constructor(measurer = canvasMeasurer) {
14606
+ this.measure = measurer;
14607
+ }
14608
+ /** Number of paragraphs recomputed by the last `layout()` call. */
14609
+ getLastRelaidCount() {
14610
+ return this.lastParagraphsRelaid;
14611
+ }
14612
+ clearCaches() {
14613
+ this.cache.clear();
14614
+ this.paragraphs = [];
14615
+ this.pool.clear();
14616
+ }
14617
+ paragraphSig(block, style, bounds) {
14618
+ const fixedWidth = bounds.width !== void 0 && !Number.isNaN(bounds.width);
14619
+ const maxWidth = (bounds.width ?? 0) - bounds.padding * 2;
14620
+ return `${fontString(style)}|${style.letterSpacing}|${style.wrap}|${fixedWidth ? maxWidth : "auto"}|${JSON.stringify(block.runs)}`;
14621
+ }
14622
+ /**
14623
+ * Pre-compute paragraph layouts for `blocks` within a time budget so a
14624
+ * following `layout()` is cheap. Call repeatedly until it returns true.
14625
+ * Lets callers time-slice very large content (e.g. bulk paste).
14626
+ */
14627
+ warm(blocks, style, bounds, budgetMs) {
14628
+ const font = fontString(style);
14629
+ const fixedWidth = bounds.width !== void 0 && !Number.isNaN(bounds.width);
14630
+ const maxWidth = (bounds.width ?? 0) - bounds.padding * 2;
14631
+ const deadline = now() + budgetMs;
14632
+ for (const block of blocks) {
14633
+ const sig = this.paragraphSig(block, style, bounds);
14634
+ if (this.pool.has(sig)) continue;
14635
+ this.pool.set(sig, this.layoutParagraph(block, sig, font, style, fixedWidth, maxWidth));
14636
+ if (now() >= deadline) return false;
14637
+ }
14638
+ return true;
14639
+ }
14640
+ measureChunk(font, text, spacing) {
14641
+ if (text === "") return 0;
14642
+ if (text.length > MAX_CACHED_CHUNK) return this.measure(font, text) + text.length * spacing;
14643
+ const key = `${font}\u0000${text}`;
14644
+ let w = this.cache.get(key);
14645
+ if (w === void 0) {
14646
+ w = this.measure(font, text);
14647
+ if (this.cache.size >= MAX_CACHE) this.cache.clear();
14648
+ this.cache.set(key, w);
14649
+ }
14650
+ return w + text.length * spacing;
14651
+ }
14652
+ layout(blocks, style, bounds) {
14653
+ const font = fontString(style);
14654
+ const padding = bounds.padding;
14655
+ const fixedWidth = bounds.width !== void 0 && !Number.isNaN(bounds.width);
14656
+ const fixedHeight = bounds.height !== void 0 && !Number.isNaN(bounds.height);
14657
+ const maxWidth = (bounds.width ?? 0) - padding * 2;
14658
+ const maxHeightPx = (bounds.height ?? 0) - padding * 2;
14659
+ const lineHeightPx = style.lineHeight * style.fontSize;
14660
+ const sigBase = `${font}|${style.letterSpacing}|${style.wrap}|${fixedWidth ? maxWidth : "auto"}`;
14661
+ const prev = this.paragraphs;
14662
+ const next = [];
14663
+ let relaid = 0;
14664
+ const lines = [];
14665
+ let textWidth = 0;
14666
+ let currentHeightPx = 0;
14667
+ let offset = 0;
14668
+ outer: for (let p = 0; p < blocks.length; p++) {
14669
+ const block = blocks[p];
14670
+ const sig = `${sigBase}|${JSON.stringify(block.runs)}`;
14671
+ let entry = prev[p]?.sig === sig ? prev[p] : this.pool.get(sig);
14672
+ if (!entry) {
14673
+ entry = this.layoutParagraph(block, sig, font, style, fixedWidth, maxWidth);
14674
+ relaid++;
14675
+ }
14676
+ next.push(entry);
14677
+ for (const pl of entry.lines) {
14678
+ const text = entry.graphemes.slice(pl.startG, pl.endG).join("");
14679
+ const segments = this.buildSegments(entry, block, pl, font, style);
14680
+ const width = segments.reduce((a, s) => a + s.width, 0);
14681
+ const startChar = entry.graphemes.slice(0, pl.startG).join("").length;
14682
+ lines.push({
14683
+ paragraph: p,
14684
+ start: offset + startChar,
14685
+ end: offset + startChar + text.length,
14686
+ text,
14687
+ width,
14688
+ measuredWidth: pl.measuredWidth,
14689
+ offsetX: 0,
14690
+ y: lines.length * lineHeightPx,
14691
+ segments,
14692
+ lastInParagraph: pl.lastInParagraph
14693
+ });
14694
+ textWidth = Math.max(textWidth, pl.measuredWidth);
14695
+ currentHeightPx += lineHeightPx;
14696
+ if (fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx) break outer;
14697
+ }
14698
+ offset += blockText(block).length + 1;
14699
+ }
14700
+ this.paragraphs = next;
14701
+ this.pool = new Map(next.map((e) => [e.sig, e]));
14702
+ this.lastParagraphsRelaid = relaid;
14703
+ const width = fixedWidth ? bounds.width : textWidth + padding * 2;
14704
+ const height = fixedHeight ? bounds.height : lines.length * lineHeightPx + padding * 2;
14705
+ if (style.align !== "left") for (const line of lines) {
14706
+ const free = width - line.width - padding * 2;
14707
+ line.offsetX = style.align === "center" ? free / 2 : free;
14708
+ }
14709
+ return {
14710
+ lines,
14711
+ textWidth,
14712
+ textHeight: style.fontSize,
14713
+ lineHeightPx,
14714
+ width,
14715
+ height
14716
+ };
14717
+ }
14718
+ rangeWidth(entry, runs, from, to, font, spacing) {
14719
+ let width = 0;
14720
+ let i = from;
14721
+ while (i < to) {
14722
+ const run = entry.runOf[i];
14723
+ let j = i;
14724
+ while (j < to && entry.runOf[j] === run) j++;
14725
+ width += this.measureChunk(font, entry.graphemes.slice(i, j).join(""), spacing);
14726
+ i = j;
14727
+ }
14728
+ return width;
14729
+ }
14730
+ layoutParagraph(block, sig, font, style, fixedWidth, maxWidth) {
14731
+ const graphemes = [];
14732
+ const runOf = [];
14733
+ block.runs.forEach((run, ri) => {
14734
+ for (const g of splitGraphemes(run.t)) {
14735
+ graphemes.push(g);
14736
+ runOf.push(ri);
14737
+ }
14738
+ });
14739
+ const entry = {
14740
+ sig,
14741
+ graphemes,
14742
+ runOf,
14743
+ lines: []
14744
+ };
14745
+ const n = graphemes.length;
14746
+ const spacing = style.letterSpacing;
14747
+ const width = (a, b) => this.rangeWidth(entry, block.runs, a, b, font, spacing);
14748
+ const shouldWrap = style.wrap !== "none";
14749
+ const wrapAtWord = style.wrap !== "char" && shouldWrap;
14750
+ const whole = width(0, n);
14751
+ if (!fixedWidth || !shouldWrap || whole <= maxWidth || n === 0) {
14752
+ entry.lines.push({
14753
+ startG: 0,
14754
+ endG: n,
14755
+ measuredWidth: whole,
14756
+ lastInParagraph: true
14757
+ });
14758
+ return entry;
14759
+ }
14760
+ let start = 0;
14761
+ while (start < n) {
14762
+ let low = 0;
14763
+ let high = n - start;
14764
+ let probe = Math.min(high, 32);
14765
+ while (probe < high && width(start, start + probe) <= maxWidth) probe = Math.min(high, probe * 2);
14766
+ high = probe;
14767
+ let matchLen = 0;
14768
+ let matchWidth = 0;
14769
+ while (low < high) {
14770
+ const mid = low + high >>> 1;
14771
+ const w = width(start, start + mid + 1);
14772
+ if (w <= maxWidth) {
14773
+ low = mid + 1;
14774
+ matchLen = mid + 1;
14775
+ matchWidth = w;
14776
+ } else high = mid;
14777
+ }
14778
+ if (matchLen === 0) {
14779
+ matchLen = 1;
14780
+ matchWidth = width(start, start + 1);
14781
+ }
14782
+ if (start + matchLen >= n) {
14783
+ entry.lines.push({
14784
+ startG: start,
14785
+ endG: n,
14786
+ measuredWidth: matchWidth,
14787
+ lastInParagraph: true
14788
+ });
14789
+ return entry;
14790
+ }
14791
+ if (wrapAtWord) {
14792
+ const nextChar = graphemes[start + matchLen];
14793
+ const nextIsSpaceOrDash = nextChar === " " || nextChar === "-";
14794
+ let wrapIndex;
14795
+ if (nextIsSpaceOrDash && matchWidth <= maxWidth) wrapIndex = matchLen;
14796
+ else {
14797
+ const seg = graphemes.slice(start, start + matchLen);
14798
+ wrapIndex = Math.max(seg.lastIndexOf(" "), seg.lastIndexOf("-")) + 1;
14799
+ }
14800
+ if (wrapIndex > 0) {
14801
+ matchLen = wrapIndex;
14802
+ matchWidth = width(start, start + matchLen);
14803
+ }
14804
+ }
14805
+ let end = start + matchLen;
14806
+ let trimmedEnd = end;
14807
+ while (trimmedEnd > start && WS.test(graphemes[trimmedEnd - 1])) trimmedEnd--;
14808
+ entry.lines.push({
14809
+ startG: start,
14810
+ endG: trimmedEnd,
14811
+ measuredWidth: matchWidth,
14812
+ lastInParagraph: false
14813
+ });
14814
+ while (end < n && WS.test(graphemes[end])) end++;
14815
+ start = end;
14816
+ }
14817
+ const last = entry.lines[entry.lines.length - 1];
14818
+ if (last) last.lastInParagraph = true;
14819
+ return entry;
14820
+ }
14821
+ buildSegments(entry, block, pl, font, style) {
14822
+ const segments = [];
14823
+ let x = 0;
14824
+ let i = pl.startG;
14825
+ while (i < pl.endG) {
14826
+ const run = entry.runOf[i];
14827
+ let j = i;
14828
+ while (j < pl.endG && entry.runOf[j] === run) j++;
14829
+ const t = entry.graphemes.slice(i, j).join("");
14830
+ const width = this.measureChunk(font, t, style.letterSpacing);
14831
+ segments.push({
14832
+ t,
14833
+ m: block.runs[run].m,
14834
+ x,
14835
+ width
14836
+ });
14837
+ x += width;
14838
+ i = j;
14839
+ }
14840
+ return segments;
14841
+ }
14842
+ };
14843
+ //#endregion
14844
+ //#region src/nodes/text-v2/shape.ts
14845
+ const LAYOUT_ATTRS = [
14846
+ "text",
14847
+ "blocks",
14848
+ "link",
14849
+ "fontSize",
14850
+ "fontFamily",
14851
+ "fontStyle",
14852
+ "fontVariant",
14853
+ "lineHeight",
14854
+ "letterSpacing",
14855
+ "wrap",
14856
+ "align",
14857
+ "padding",
14858
+ "width",
14859
+ "height"
14860
+ ];
14861
+ /**
14862
+ * Canvas shape that lays out and paints blocks/runs text with the
14863
+ * `TextLayoutEngine`. Exposes Konva.Text-compatible getters/setters so
14864
+ * code written against `Konva.Text` keeps working.
14865
+ */
14866
+ var WeaveTextV2Shape = class extends Konva.Shape {
14867
+ engine = new TextLayoutEngine();
14868
+ naturalEngine = new TextLayoutEngine();
14869
+ layoutCache = null;
14870
+ blocksKey = {};
14871
+ blocksCache = [];
14872
+ viewport = null;
14873
+ constructor(config) {
14874
+ super(config);
14875
+ this.className = "WeaveTextV2Shape";
14876
+ this.on(LAYOUT_ATTRS.map((a) => `${a}Change.konva`).join(" "), () => {
14877
+ this.layoutCache = null;
14878
+ });
14879
+ this.sceneFunc(this._paint);
14880
+ this.hitFunc(this._hit);
14881
+ }
14882
+ text(value) {
14883
+ if (value === void 0) return typeof this.attrs.text === "string" ? this.attrs.text : blocksToText(this.getBlocks());
14884
+ return this.setText(value);
14885
+ }
14886
+ setText(value) {
14887
+ this._setAttr("blocks", void 0);
14888
+ this._setAttr("text", value == null ? "" : String(value));
14889
+ return this;
14890
+ }
14891
+ /** Resolved blocks: stored or lazily synthesized (never written back). */
14892
+ getBlocks() {
14893
+ const k = this.blocksKey;
14894
+ const { blocks, text, link } = this.attrs;
14895
+ if (k.b !== blocks || k.t !== text || k.l !== link) {
14896
+ this.blocksCache = resolveBlocks({
14897
+ blocks,
14898
+ text,
14899
+ link
14900
+ });
14901
+ this.blocksKey = {
14902
+ b: blocks,
14903
+ t: text,
14904
+ l: link
14905
+ };
14906
+ }
14907
+ return this.blocksCache;
14908
+ }
14909
+ /** Replace content with blocks, keeping the `text` mirror in sync. */
14910
+ setBlocks(blocks) {
14911
+ this._setAttr("blocks", blocks);
14912
+ this._setAttr("text", blocksToText(blocks));
14913
+ return this;
14914
+ }
14915
+ fontSize(v) {
14916
+ return this.accessor("fontSize", 12, v);
14917
+ }
14918
+ fontFamily(v) {
14919
+ return this.accessor("fontFamily", "Arial", v);
14920
+ }
14921
+ fontStyle(v) {
14922
+ return this.accessor("fontStyle", "normal", v);
14923
+ }
14924
+ fontVariant(v) {
14925
+ return this.accessor("fontVariant", "normal", v);
14926
+ }
14927
+ lineHeight(v) {
14928
+ return this.accessor("lineHeight", 1, v);
14929
+ }
14930
+ letterSpacing(v) {
14931
+ return this.accessor("letterSpacing", 0, v);
14932
+ }
14933
+ padding(v) {
14934
+ return this.accessor("padding", 0, v);
14935
+ }
14936
+ align(v) {
14937
+ return this.accessor("align", "left", v);
14938
+ }
14939
+ verticalAlign(v) {
14940
+ return this.accessor("verticalAlign", "top", v);
14941
+ }
14942
+ wrap(v) {
14943
+ return this.accessor("wrap", "word", v);
14944
+ }
14945
+ textDecoration(v) {
14946
+ return this.accessor("textDecoration", "", v);
14947
+ }
14948
+ accessor(name, fallback, value) {
14949
+ if (value === void 0) return this.attrs[name] ?? fallback;
14950
+ this.setAttr(name, value);
14951
+ return this;
14952
+ }
14953
+ getTextWidth() {
14954
+ return this.ensureLayout().textWidth;
14955
+ }
14956
+ getTextHeight() {
14957
+ return this.fontSize();
14958
+ }
14959
+ measureSize(text) {
14960
+ return {
14961
+ width: canvasMeasurer(fontString(this.style()), text),
14962
+ height: this.fontSize()
14963
+ };
14964
+ }
14965
+ getWidth() {
14966
+ const w = this.attrs.width;
14967
+ return w === void 0 || w === "auto" || Number.isNaN(w) ? this.ensureLayout().width : w;
14968
+ }
14969
+ getHeight() {
14970
+ const h = this.attrs.height;
14971
+ return h === void 0 || h === "auto" || Number.isNaN(h) ? this.ensureLayout().height : h;
14972
+ }
14973
+ style() {
14974
+ return {
14975
+ fontSize: this.fontSize(),
14976
+ fontFamily: this.fontFamily(),
14977
+ fontStyle: this.fontStyle(),
14978
+ fontVariant: this.fontVariant(),
14979
+ lineHeight: this.lineHeight(),
14980
+ letterSpacing: this.letterSpacing(),
14981
+ wrap: this.wrap(),
14982
+ align: this.align()
14983
+ };
14984
+ }
14985
+ /** Lazy, cached layout. Size accessors and painting force it. */
14986
+ ensureLayout() {
14987
+ if (!this.layoutCache) {
14988
+ const w = this.attrs.width;
14989
+ const h = this.attrs.height;
14990
+ const num = (v) => typeof v === "number" && !Number.isNaN(v) ? v : void 0;
14991
+ this.layoutCache = this.engine.layout(this.getBlocks(), this.style(), {
14992
+ width: num(w),
14993
+ height: num(h),
14994
+ padding: this.padding()
14995
+ });
14996
+ }
14997
+ return this.layoutCache;
14998
+ }
14999
+ /** Size the text would take with no width constraint (for auto layouts). */
15000
+ measureNaturalSize() {
15001
+ const out = this.naturalEngine.layout(this.getBlocks(), this.style(), {
15002
+ width: void 0,
15003
+ height: void 0,
15004
+ padding: this.padding()
15005
+ });
15006
+ return {
15007
+ width: out.width,
15008
+ height: out.height
15009
+ };
15010
+ }
15011
+ /** Rendered width of a string with this shape's font and letter spacing. */
15012
+ measureText(text) {
15013
+ return canvasMeasurer(fontString(this.style()), text) + text.length * this.letterSpacing();
15014
+ }
15015
+ /** Offset of the first line inside the box (padding + vertical align). */
15016
+ getContentOffset() {
15017
+ const padding = this.padding();
15018
+ const layout = this.ensureLayout();
15019
+ const used = layout.lines.length * layout.lineHeightPx + padding * 2;
15020
+ const va = this.verticalAlign();
15021
+ let alignY = 0;
15022
+ if (va === "middle") alignY = (this.getHeight() - used) / 2;
15023
+ else if (va === "bottom") alignY = this.getHeight() - used;
15024
+ return {
15025
+ x: padding,
15026
+ y: alignY + padding
15027
+ };
15028
+ }
15029
+ /** Pre-compute layout for candidate blocks within a time budget. */
15030
+ warmLayout(blocks, budgetMs) {
15031
+ const num = (v) => typeof v === "number" && !Number.isNaN(v) ? v : void 0;
15032
+ return this.engine.warm(blocks, this.style(), {
15033
+ width: num(this.attrs.width),
15034
+ height: num(this.attrs.height),
15035
+ padding: this.padding()
15036
+ }, budgetMs);
15037
+ }
15038
+ /** Optional visible y-range in local coordinates, used for line culling. */
15039
+ setViewport(range) {
15040
+ this.viewport = range;
15041
+ }
15042
+ visibleRange() {
15043
+ if (this.viewport) return this.viewport;
15044
+ const stage = this.getStage();
15045
+ if (!stage) return null;
15046
+ const inv = this.getAbsoluteTransform().copy().invert();
15047
+ const w = stage.width();
15048
+ const h = stage.height();
15049
+ const pts = [
15050
+ inv.point({
15051
+ x: 0,
15052
+ y: 0
15053
+ }),
15054
+ inv.point({
15055
+ x: w,
15056
+ y: 0
15057
+ }),
15058
+ inv.point({
15059
+ x: 0,
15060
+ y: h
15061
+ }),
15062
+ inv.point({
15063
+ x: w,
15064
+ y: h
15065
+ })
15066
+ ];
15067
+ return {
15068
+ y1: Math.min(...pts.map((p) => p.y)),
15069
+ y2: Math.max(...pts.map((p) => p.y))
15070
+ };
15071
+ }
15072
+ _paint = (ctx, shape) => {
15073
+ const self = shape;
15074
+ const layout = self.ensureLayout();
15075
+ if (layout.lines.length === 0) return;
15076
+ const fontSize = self.fontSize();
15077
+ const padding = self.padding();
15078
+ const lineHeightPx = layout.lineHeightPx;
15079
+ const decoration = self.textDecoration();
15080
+ const underline = decoration.includes("underline");
15081
+ const lineThrough = decoration.includes("line-through");
15082
+ const spacing = self.letterSpacing();
15083
+ const fill = self.fill();
15084
+ let baseline = "middle";
15085
+ let translateY = lineHeightPx / 2;
15086
+ if (!Konva.legacyTextRendering) {
15087
+ const m = self.measureMetrics();
15088
+ baseline = "alphabetic";
15089
+ translateY = (m.ascent - m.descent) / 2 + lineHeightPx / 2;
15090
+ }
15091
+ ctx.setAttr("font", fontString(self.style()));
15092
+ ctx.setAttr("textBaseline", baseline);
15093
+ ctx.setAttr("textAlign", "left");
15094
+ let alignY = 0;
15095
+ const va = self.verticalAlign();
15096
+ const used = layout.lines.length * lineHeightPx + padding * 2;
15097
+ if (va === "middle") alignY = (self.getHeight() - used) / 2;
15098
+ else if (va === "bottom") alignY = self.getHeight() - used;
15099
+ ctx.translate(padding, alignY + padding);
15100
+ const range = self.visibleRange();
15101
+ const paintSelf = self;
15102
+ for (let n = 0; n < layout.lines.length; n++) {
15103
+ const line = layout.lines[n];
15104
+ const y = translateY + n * lineHeightPx;
15105
+ if (range && (alignY + padding + n * lineHeightPx + lineHeightPx < range.y1 - lineHeightPx || alignY + padding + n * lineHeightPx > range.y2 + lineHeightPx)) continue;
15106
+ ctx.save();
15107
+ const baseX = line.offsetX;
15108
+ if (underline) {
15109
+ ctx.save();
15110
+ ctx.beginPath();
15111
+ const uy = y + Math.round(fontSize / 4);
15112
+ ctx.moveTo(baseX, uy);
15113
+ ctx.lineTo(baseX + Math.round(line.width), uy);
15114
+ ctx.lineWidth = fontSize / 15;
15115
+ ctx.strokeStyle = fill;
15116
+ ctx.stroke();
15117
+ ctx.restore();
15118
+ }
15119
+ for (const seg of line.segments) {
15120
+ let x = baseX + seg.x;
15121
+ if (spacing !== 0) for (const ch of Array.from(seg.t)) {
15122
+ paintSelf._partialText = ch;
15123
+ paintSelf._partialTextX = x;
15124
+ paintSelf._partialTextY = y;
15125
+ ctx.fillStrokeShape(self);
15126
+ x += self.measureSize(ch).width + spacing;
15127
+ }
15128
+ else {
15129
+ paintSelf._partialText = seg.t;
15130
+ paintSelf._partialTextX = x;
15131
+ paintSelf._partialTextY = y;
15132
+ ctx.fillStrokeShape(self);
15133
+ }
15134
+ }
15135
+ if (lineThrough) {
15136
+ ctx.save();
15137
+ ctx.beginPath();
15138
+ const ly = y - Math.round(fontSize / 4);
15139
+ ctx.moveTo(baseX, ly);
15140
+ ctx.lineTo(baseX + Math.round(line.width), ly);
15141
+ ctx.lineWidth = fontSize / 15;
15142
+ ctx.strokeStyle = fill;
15143
+ ctx.stroke();
15144
+ ctx.restore();
15145
+ }
15146
+ ctx.restore();
15147
+ }
15148
+ };
15149
+ _hit = (ctx, shape) => {
15150
+ ctx.beginPath();
15151
+ const self = shape;
15152
+ ctx.rect(0, 0, self.getWidth(), self.getHeight());
15153
+ ctx.closePath();
15154
+ ctx.fillStrokeShape(shape);
15155
+ };
15156
+ metricsCache = null;
15157
+ /** Font ascent/descent used for baseline placement (parity with Konva.Text). */
15158
+ measureMetrics() {
15159
+ const font = fontString(this.style());
15160
+ if (this.metricsCache?.key === font) return this.metricsCache;
15161
+ const ctx = Konva.Util.createCanvasElement().getContext("2d");
15162
+ ctx.font = font;
15163
+ const m = ctx.measureText("M");
15164
+ const scale = this.fontSize() / 100;
15165
+ const ascent = m.fontBoundingBoxAscent ?? 91 * scale;
15166
+ const descent = m.fontBoundingBoxDescent ?? 21 * scale;
15167
+ this.metricsCache = {
15168
+ key: font,
15169
+ ascent,
15170
+ descent
15171
+ };
15172
+ return this.metricsCache;
15173
+ }
15174
+ };
15175
+ Object.assign(WeaveTextV2Shape.prototype, {
15176
+ _fillFunc(ctx) {
15177
+ ctx.fillText(this._partialText, this._partialTextX, this._partialTextY);
15178
+ },
15179
+ _strokeFunc(ctx) {
15180
+ ctx.setAttr("miterLimit", 2);
15181
+ ctx.strokeText(this._partialText, this._partialTextX, this._partialTextY);
15182
+ }
15183
+ });
15184
+ //#endregion
15185
+ //#region src/nodes/text-v2/edit/geometry.ts
15186
+ const graphemeSeg = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
15187
+ const wordSeg = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "word" }) : null;
15188
+ function boundaries(text, seg) {
15189
+ if (!seg) return Array.from({ length: text.length + 1 }, (_, i) => i);
15190
+ const out = [0];
15191
+ for (const s of seg.segment(text)) out.push(s.index + s.segment.length);
15192
+ return out;
15193
+ }
15194
+ function prevGrapheme(text, offset) {
15195
+ if (offset <= 0) return 0;
15196
+ const b = boundaries(text, graphemeSeg);
15197
+ for (let i = b.length - 1; i >= 0; i--) if (b[i] < offset) return b[i];
15198
+ return 0;
15199
+ }
15200
+ function nextGrapheme(text, offset) {
15201
+ if (offset >= text.length) return text.length;
15202
+ const b = boundaries(text, graphemeSeg);
15203
+ for (const v of b) if (v > offset) return v;
15204
+ return text.length;
15205
+ }
15206
+ /** Word segment [start, end) containing (or adjacent to) the offset. */
15207
+ function wordRangeAt(text, offset) {
15208
+ if (text === "") return [0, 0];
15209
+ offset = Math.max(0, Math.min(offset, text.length));
15210
+ if (!wordSeg) {
15211
+ let s = offset;
15212
+ let e = offset;
15213
+ while (s > 0 && /\S/.test(text[s - 1])) s--;
15214
+ while (e < text.length && /\S/.test(text[e])) e++;
15215
+ return [s, e];
15216
+ }
15217
+ let last = [offset, offset];
15218
+ for (const seg of wordSeg.segment(text)) {
15219
+ const s = seg.index;
15220
+ const e = s + seg.segment.length;
15221
+ last = [s, e];
15222
+ if (offset < e) return [s, e];
15223
+ }
15224
+ return last;
15225
+ }
15226
+ /** Paragraph range [start, end) (no trailing newline) containing offset. */
15227
+ function paragraphRangeAt(text, offset) {
15228
+ offset = Math.max(0, Math.min(offset, text.length));
15229
+ const s = text.lastIndexOf("\n", offset - 1) + 1;
15230
+ let e = text.indexOf("\n", offset);
15231
+ if (e === -1) e = text.length;
15232
+ return [s, e];
15233
+ }
15234
+ /** Previous word start (Ctrl+Left semantics). */
15235
+ function prevWordBoundary(text, offset) {
15236
+ let i = offset;
15237
+ while (i > 0 && /\s/.test(text[i - 1])) i--;
15238
+ if (i === 0) return 0;
15239
+ return wordRangeAt(text, i - 1)[0];
15240
+ }
15241
+ /** Next word end (Ctrl+Right semantics). */
15242
+ function nextWordBoundary(text, offset) {
15243
+ let i = offset;
15244
+ while (i < text.length && /\s/.test(text[i])) i++;
15245
+ if (i >= text.length) return text.length;
15246
+ return wordRangeAt(text, i)[1];
15247
+ }
15248
+ function lineIndexAt(layout, offset) {
15249
+ const lines = layout.lines;
15250
+ if (lines.length === 0) return 0;
15251
+ let fallback = 0;
15252
+ for (let i = 0; i < lines.length; i++) {
15253
+ const l = lines[i];
15254
+ if (l.start <= offset) fallback = i;
15255
+ if (offset >= l.start && offset <= l.end) {
15256
+ const next = lines[i + 1];
15257
+ if (offset === l.end && !l.lastInParagraph && next && next.start === offset) continue;
15258
+ return i;
15259
+ }
15260
+ }
15261
+ return fallback;
15262
+ }
15263
+ function lineX(line, offset, measure) {
15264
+ const k = Math.max(0, Math.min(offset - line.start, line.text.length));
15265
+ return line.offsetX + (k === 0 ? 0 : measure(line.text.slice(0, k)));
15266
+ }
15267
+ /** Caret rectangle in text-local coordinates (before padding/valign offset). */
15268
+ function caretRect(layout, offset, measure) {
15269
+ const i = lineIndexAt(layout, offset);
15270
+ const line = layout.lines[i];
15271
+ if (!line) return {
15272
+ x: 0,
15273
+ y: 0,
15274
+ height: layout.lineHeightPx,
15275
+ line: 0
15276
+ };
15277
+ return {
15278
+ x: lineX(line, offset, measure),
15279
+ y: i * layout.lineHeightPx,
15280
+ height: layout.lineHeightPx,
15281
+ line: i
15282
+ };
15283
+ }
15284
+ /** Offset closest to a point in text-local coordinates. */
15285
+ function offsetFromPoint(layout, point, measure) {
15286
+ const lines = layout.lines;
15287
+ if (lines.length === 0) return 0;
15288
+ return offsetInLine(lines[Math.max(0, Math.min(lines.length - 1, Math.floor(point.y / layout.lineHeightPx)))], point.x, measure);
15289
+ }
15290
+ function offsetInLine(line, x, measure) {
15291
+ const rel = x - line.offsetX;
15292
+ if (rel <= 0 || line.text === "") return line.start;
15293
+ const b = boundaries(line.text, graphemeSeg);
15294
+ let prevW = 0;
15295
+ for (let k = 1; k < b.length; k++) {
15296
+ const w = measure(line.text.slice(0, b[k]));
15297
+ if (rel < (prevW + w) / 2) return line.start + b[k - 1];
15298
+ prevW = w;
15299
+ }
15300
+ return line.end;
15301
+ }
15302
+ /** Move one visual line up/down keeping a desired x. */
15303
+ function moveVertical(layout, offset, dir, desiredX, measure) {
15304
+ const j = lineIndexAt(layout, offset) + dir;
15305
+ if (j < 0) return 0;
15306
+ if (j >= layout.lines.length) {
15307
+ const last = layout.lines[layout.lines.length - 1];
15308
+ return last ? last.end : 0;
15309
+ }
15310
+ return offsetInLine(layout.lines[j], desiredX, measure);
15311
+ }
15312
+ function lineHome(layout, offset) {
15313
+ return layout.lines[lineIndexAt(layout, offset)]?.start ?? 0;
15314
+ }
15315
+ function lineEnd(layout, offset) {
15316
+ return layout.lines[lineIndexAt(layout, offset)]?.end ?? 0;
15317
+ }
15318
+ /** Selection rectangles (one per touched line) in text-local coordinates. */
15319
+ function selectionRects(layout, from, to, measure) {
15320
+ if (from > to) [from, to] = [to, from];
15321
+ if (from === to) return [];
15322
+ const out = [];
15323
+ layout.lines.forEach((line, i) => {
15324
+ const hasNewline = line.lastInParagraph;
15325
+ const lineFrom = Math.max(from, line.start);
15326
+ const lineTo = Math.min(to, hasNewline ? line.end + 1 : line.end);
15327
+ if (lineTo <= lineFrom && !(line.start === line.end && from <= line.start && to > line.start)) return;
15328
+ const x1 = lineX(line, Math.min(lineFrom, line.end), measure);
15329
+ let x2 = lineX(line, Math.min(lineTo, line.end), measure);
15330
+ if (hasNewline && to > line.end) x2 += Math.max(4, layout.textHeight / 3);
15331
+ if (x2 <= x1) return;
15332
+ out.push({
15333
+ x: x1,
15334
+ y: i * layout.lineHeightPx,
15335
+ width: x2 - x1,
15336
+ height: layout.lineHeightPx
15337
+ });
15338
+ });
15339
+ return out;
15340
+ }
15341
+ //#endregion
15342
+ //#region src/nodes/text-v2/edit/model.ts
15343
+ /**
15344
+ * Session-local edit state: content + selection + undo/redo with
15345
+ * coalescing. Typing and deleting merge into one entry while they continue
15346
+ * in the same direction without a selection move; paste and IME composition
15347
+ * are always one entry each. Independent of Konva and the DOM.
15348
+ */
15349
+ var TextEditModel = class {
15350
+ blocks;
15351
+ anchor = 0;
15352
+ focus = 0;
15353
+ /** x kept while moving vertically; reset by any other move. */
15354
+ desiredX = null;
15355
+ undoStack = [];
15356
+ redoStack = [];
15357
+ lastKind = null;
15358
+ lastEditEnd = -1;
15359
+ composing = false;
15360
+ constructor(blocks) {
15361
+ this.blocks = blocks;
15362
+ const len = blocksToText(blocks).length;
15363
+ this.anchor = this.focus = len;
15364
+ }
15365
+ getBlocks() {
15366
+ return this.blocks;
15367
+ }
15368
+ getText() {
15369
+ return blocksToText(this.blocks);
15370
+ }
15371
+ getSelection() {
15372
+ return {
15373
+ start: Math.min(this.anchor, this.focus),
15374
+ end: Math.max(this.anchor, this.focus)
15375
+ };
15376
+ }
15377
+ getSelectedText() {
15378
+ const { start, end } = this.getSelection();
15379
+ return this.getText().slice(start, end);
15380
+ }
15381
+ canUndo() {
15382
+ return this.undoStack.length > 0;
15383
+ }
15384
+ canRedo() {
15385
+ return this.redoStack.length > 0;
15386
+ }
15387
+ setSelection(anchor, focus) {
15388
+ const len = this.getText().length;
15389
+ this.anchor = Math.max(0, Math.min(anchor, len));
15390
+ this.focus = Math.max(0, Math.min(focus, len));
15391
+ this.breakCoalescing();
15392
+ }
15393
+ moveTo(offset, extend = false, keepDesiredX = false) {
15394
+ const len = this.getText().length;
15395
+ const o = Math.max(0, Math.min(offset, len));
15396
+ this.focus = o;
15397
+ if (!extend) this.anchor = o;
15398
+ if (!keepDesiredX) this.desiredX = null;
15399
+ this.breakCoalescing();
15400
+ }
15401
+ selectAll() {
15402
+ this.anchor = 0;
15403
+ this.focus = this.getText().length;
15404
+ this.breakCoalescing();
15405
+ }
15406
+ breakCoalescing() {
15407
+ if (!this.composing) this.lastKind = null;
15408
+ }
15409
+ beginComposition() {
15410
+ this.composing = true;
15411
+ this.lastKind = null;
15412
+ }
15413
+ endComposition() {
15414
+ this.composing = false;
15415
+ this.lastKind = null;
15416
+ }
15417
+ snapshot() {
15418
+ return {
15419
+ blocks: this.blocks,
15420
+ anchor: this.anchor,
15421
+ focus: this.focus
15422
+ };
15423
+ }
15424
+ record(kind, editStart) {
15425
+ if (!(this.lastKind === kind && (kind === "type" || kind === "delete" || kind === "ime") && (kind === "ime" || this.lastEditEnd === editStart || kind === "delete"))) {
15426
+ this.undoStack.push(this.snapshot());
15427
+ if (this.undoStack.length > 500) this.undoStack.shift();
15428
+ }
15429
+ this.redoStack = [];
15430
+ this.lastKind = kind;
15431
+ }
15432
+ /** Replace [start,end) with plain text and put the caret after it. */
15433
+ replaceRange(start, end, text, kind) {
15434
+ const len = this.getText().length;
15435
+ start = Math.max(0, Math.min(start, len));
15436
+ end = Math.max(start, Math.min(end, len));
15437
+ if (start === end && text === "") return false;
15438
+ this.record(kind, start);
15439
+ this.blocks = replaceRange(this.blocks, start, end, text);
15440
+ this.anchor = this.focus = start + text.length;
15441
+ this.lastEditEnd = this.focus;
15442
+ this.desiredX = null;
15443
+ return true;
15444
+ }
15445
+ replaceSelection(text, kind) {
15446
+ const { start, end } = this.getSelection();
15447
+ return this.replaceRange(start, end, text, kind);
15448
+ }
15449
+ backspace(byWord = false) {
15450
+ const { start, end } = this.getSelection();
15451
+ if (start !== end) return this.replaceRange(start, end, "", "delete");
15452
+ if (start === 0) return false;
15453
+ const text = this.getText();
15454
+ const from = byWord ? prevWordBoundary(text, start) : prevGrapheme(text, start);
15455
+ return this.replaceRange(from, start, "", "delete");
15456
+ }
15457
+ del(byWord = false) {
15458
+ const { start, end } = this.getSelection();
15459
+ if (start !== end) return this.replaceRange(start, end, "", "delete");
15460
+ const text = this.getText();
15461
+ if (start >= text.length) return false;
15462
+ const to = byWord ? nextWordBoundary(text, start) : nextGrapheme(text, start);
15463
+ return this.replaceRange(start, to, "", "delete");
15464
+ }
15465
+ undo() {
15466
+ const prev = this.undoStack.pop();
15467
+ if (!prev) return false;
15468
+ this.redoStack.push(this.snapshot());
15469
+ this.restore(prev);
15470
+ return true;
15471
+ }
15472
+ redo() {
15473
+ const next = this.redoStack.pop();
15474
+ if (!next) return false;
15475
+ this.undoStack.push(this.snapshot());
15476
+ this.restore(next);
15477
+ return true;
15478
+ }
15479
+ restore(s) {
15480
+ this.blocks = s.blocks;
15481
+ this.anchor = s.anchor;
15482
+ this.focus = s.focus;
15483
+ this.lastKind = null;
15484
+ this.desiredX = null;
15485
+ }
15486
+ };
15487
+ const SLICE_BUDGET_MS = 8;
15488
+ const BLINK_MS = 530;
15489
+ var TextEditSession = class {
15490
+ model;
15491
+ target;
15492
+ host;
15493
+ id;
15494
+ abort = new AbortController();
15495
+ textarea = null;
15496
+ overlay = null;
15497
+ blinkTimer;
15498
+ caretOn = true;
15499
+ dragging = false;
15500
+ composing = false;
15501
+ pendingTimer;
15502
+ pendingFlush = false;
15503
+ destroyed = false;
15504
+ initialJson;
15505
+ constructor(target, host, id = "text-edit-session") {
15506
+ this.target = target;
15507
+ this.host = host;
15508
+ this.id = id;
15509
+ this.model = new TextEditModel(target.getBlocks());
15510
+ this.initialJson = JSON.stringify(this.model.getBlocks());
15511
+ }
15512
+ start() {
15513
+ const signal = this.abort.signal;
15514
+ host_signal(this.host.signal, this.abort);
15515
+ this.createTextarea();
15516
+ this.createOverlay();
15517
+ const ta = this.textarea;
15518
+ const opts = { signal };
15519
+ ta.addEventListener("keydown", this.onKeyDown, opts);
15520
+ ta.addEventListener("keyup", this.onKeyUp, opts);
15521
+ ta.addEventListener("beforeinput", this.onBeforeInput, opts);
15522
+ ta.addEventListener("input", this.onInput, opts);
15523
+ ta.addEventListener("paste", this.onPaste, opts);
15524
+ ta.addEventListener("compositionstart", this.onCompositionStart, opts);
15525
+ ta.addEventListener("compositionend", this.onCompositionEnd, opts);
15526
+ ta.addEventListener("select", this.syncFromTextarea, opts);
15527
+ ta.ownerDocument.addEventListener("selectionchange", this.syncFromTextarea, opts);
15528
+ window.addEventListener("pointerdown", this.onPointerDown, {
15529
+ signal,
15530
+ capture: true
15531
+ });
15532
+ window.addEventListener("pointermove", this.onPointerMove, { signal });
15533
+ window.addEventListener("pointerup", this.onPointerUp, { signal });
15534
+ window.addEventListener("mousedown", this.onMouseDown, {
15535
+ signal,
15536
+ capture: true
15537
+ });
15538
+ window.weaveTextEditing = {
15539
+ ...window.weaveTextEditing ?? {},
15540
+ [this.id]: this.id
15541
+ };
15542
+ this.syncTextarea();
15543
+ ta.focus();
15544
+ this.positionTextarea();
15545
+ this.startBlink();
15546
+ this.repaint();
15547
+ }
15548
+ /** Final content and whether it differs from when the session started. */
15549
+ getResult() {
15550
+ return {
15551
+ blocks: this.model.getBlocks(),
15552
+ changed: JSON.stringify(this.model.getBlocks()) !== this.initialJson
15553
+ };
15554
+ }
15555
+ destroy() {
15556
+ if (this.destroyed) return;
15557
+ this.destroyed = true;
15558
+ this.flushPending();
15559
+ this.abort.abort();
15560
+ clearInterval(this.blinkTimer);
15561
+ clearTimeout(this.pendingTimer);
15562
+ this.textarea?.remove();
15563
+ this.textarea = null;
15564
+ const layer = this.overlay?.getLayer();
15565
+ this.overlay?.destroy();
15566
+ this.overlay = null;
15567
+ layer?.batchDraw();
15568
+ const w = window;
15569
+ if (w.weaveTextEditing) delete w.weaveTextEditing[this.id];
15570
+ }
15571
+ focus() {
15572
+ this.textarea?.focus();
15573
+ }
15574
+ createTextarea() {
15575
+ const ta = this.host.container.ownerDocument.createElement("textarea");
15576
+ ta.setAttribute("autocapitalize", "off");
15577
+ ta.setAttribute("autocomplete", "off");
15578
+ ta.setAttribute("autocorrect", "off");
15579
+ ta.setAttribute("spellcheck", "false");
15580
+ ta.setAttribute("wrap", "off");
15581
+ ta.setAttribute("aria-label", "Text editor");
15582
+ ta.tabIndex = 1;
15583
+ Object.assign(ta.style, {
15584
+ position: "absolute",
15585
+ left: "0px",
15586
+ top: "0px",
15587
+ width: "1px",
15588
+ height: "1em",
15589
+ margin: "0",
15590
+ padding: "0",
15591
+ border: "0",
15592
+ outline: "none",
15593
+ resize: "none",
15594
+ overflow: "hidden",
15595
+ whiteSpace: "pre",
15596
+ opacity: "0",
15597
+ fontSize: "16px",
15598
+ background: "transparent",
15599
+ color: "transparent",
15600
+ caretColor: "transparent",
15601
+ pointerEvents: "none",
15602
+ zIndex: "1"
15603
+ });
15604
+ this.host.container.appendChild(ta);
15605
+ this.textarea = ta;
15606
+ }
15607
+ syncTextarea() {
15608
+ const ta = this.textarea;
15609
+ if (!ta || this.composing) return;
15610
+ const text = this.model.getText();
15611
+ if (ta.value !== text) ta.value = text;
15612
+ const { start, end } = this.model.getSelection();
15613
+ const backward = this.model.focus < this.model.anchor;
15614
+ if (ta.selectionStart !== start || ta.selectionEnd !== end) ta.setSelectionRange(start, end, backward ? "backward" : "forward");
15615
+ }
15616
+ positionTextarea() {
15617
+ const ta = this.textarea;
15618
+ if (!ta) return;
15619
+ const c = this.caretStagePoint();
15620
+ const p = this.host.stageToContainer(c);
15621
+ ta.style.left = `${p.x}px`;
15622
+ ta.style.top = `${p.y}px`;
15623
+ }
15624
+ caretStagePoint() {
15625
+ const r = caretRect(this.target.getLayout(), this.model.focus, this.target.measure);
15626
+ const o = this.target.getContentOffset();
15627
+ return this.target.getAbsoluteTransform().point({
15628
+ x: r.x + o.x,
15629
+ y: r.y + o.y
15630
+ });
15631
+ }
15632
+ syncFromTextarea = () => {
15633
+ const ta = this.textarea;
15634
+ if (!ta || this.composing || this.destroyed) return;
15635
+ if (ta.getRootNode().activeElement !== ta) return;
15636
+ if (ta.value !== this.model.getText()) return;
15637
+ const backward = ta.selectionDirection === "backward";
15638
+ const a = backward ? ta.selectionEnd : ta.selectionStart;
15639
+ const f = backward ? ta.selectionStart : ta.selectionEnd;
15640
+ if (a !== this.model.anchor || f !== this.model.focus) {
15641
+ this.model.setSelection(a, f);
15642
+ this.afterSelectionChange();
15643
+ }
15644
+ };
15645
+ onKeyDown = (e) => {
15646
+ e.stopPropagation();
15647
+ const mod = e.ctrlKey || e.metaKey;
15648
+ const key = e.key.toLowerCase();
15649
+ if (e.key === "Escape") {
15650
+ e.preventDefault();
15651
+ this.host.onRequestExit("escape");
15652
+ return;
15653
+ }
15654
+ if (mod && key === "z" && !e.shiftKey) {
15655
+ e.preventDefault();
15656
+ this.undo();
15657
+ return;
15658
+ }
15659
+ if (mod && (key === "y" || key === "z" && e.shiftKey)) {
15660
+ e.preventDefault();
15661
+ this.redo();
15662
+ return;
15663
+ }
15664
+ if (e.key === "Tab") {
15665
+ e.preventDefault();
15666
+ return;
15667
+ }
15668
+ if (this.composing) return;
15669
+ if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !mod && !e.altKey) {
15670
+ e.preventDefault();
15671
+ this.moveVertically(e.key === "ArrowUp" ? -1 : 1, e.shiftKey);
15672
+ return;
15673
+ }
15674
+ if ((e.key === "Home" || e.key === "End") && !mod) {
15675
+ e.preventDefault();
15676
+ const layout = this.target.getLayout();
15677
+ const to = e.key === "Home" ? lineHome(layout, this.model.focus) : lineEnd(layout, this.model.focus);
15678
+ this.model.moveTo(to, e.shiftKey);
15679
+ this.afterSelectionChange();
15680
+ return;
15681
+ }
15682
+ setTimeout(this.syncFromTextarea, 0);
15683
+ };
15684
+ onKeyUp = (e) => {
15685
+ e.stopPropagation();
15686
+ this.syncFromTextarea();
15687
+ };
15688
+ onBeforeInput = (e) => {
15689
+ if (e.inputType === "historyUndo" || e.inputType === "historyRedo") e.preventDefault();
15690
+ };
15691
+ moveVertically(dir, extend) {
15692
+ const layout = this.target.getLayout();
15693
+ if (this.model.desiredX === null) this.model.desiredX = caretRect(layout, this.model.focus, this.target.measure).x;
15694
+ const to = moveVertical(layout, this.model.focus, dir, this.model.desiredX, this.target.measure);
15695
+ this.model.moveTo(to, extend, true);
15696
+ this.afterSelectionChange();
15697
+ }
15698
+ onInput = (e) => {
15699
+ const ta = this.textarea;
15700
+ if (!ta) return;
15701
+ const oldText = this.model.getText();
15702
+ const newText = ta.value;
15703
+ if (oldText === newText) return;
15704
+ let p = 0;
15705
+ const max = Math.min(oldText.length, newText.length);
15706
+ while (p < max && oldText[p] === newText[p]) p++;
15707
+ let s = 0;
15708
+ while (s < max - p && oldText[oldText.length - 1 - s] === newText[newText.length - 1 - s]) s++;
15709
+ const inserted = newText.slice(p, newText.length - s);
15710
+ const inputType = e.inputType ?? "";
15711
+ const kind = this.composing || inputType.includes("Composition") ? "ime" : inputType.startsWith("delete") ? "delete" : inputType === "insertFromPaste" ? "paste" : "type";
15712
+ this.model.replaceRange(p, oldText.length - s, inserted, kind);
15713
+ this.model.anchor = this.model.focus = Math.min(ta.selectionEnd, this.model.getText().length);
15714
+ this.afterContentChange(inserted.length);
15715
+ };
15716
+ onPaste = (e) => {
15717
+ e.stopPropagation();
15718
+ e.preventDefault();
15719
+ const raw = e.clipboardData?.getData("text/plain") ?? "";
15720
+ if (raw === "") return;
15721
+ this.insertText(raw.replace(/\r\n?/g, "\n"), "paste");
15722
+ };
15723
+ onCompositionStart = () => {
15724
+ this.composing = true;
15725
+ this.model.beginComposition();
15726
+ };
15727
+ onCompositionEnd = () => {
15728
+ this.composing = false;
15729
+ this.model.endComposition();
15730
+ this.syncFromTextarea();
15731
+ };
15732
+ /** Programmatic insert at the selection (paste, tests, host actions). */
15733
+ insertText(text, kind = "paste") {
15734
+ if (this.model.replaceSelection(text, kind)) {
15735
+ this.syncTextarea();
15736
+ this.afterContentChange(text.length);
15737
+ }
15738
+ }
15739
+ undo() {
15740
+ this.flushPending();
15741
+ if (this.model.undo()) {
15742
+ this.syncTextarea();
15743
+ this.afterContentChange(0, true);
15744
+ }
15745
+ }
15746
+ redo() {
15747
+ this.flushPending();
15748
+ if (this.model.redo()) {
15749
+ this.syncTextarea();
15750
+ this.afterContentChange(0, true);
15751
+ }
15752
+ }
15753
+ toLocal(e) {
15754
+ const stagePt = this.host.clientToStage(e.clientX, e.clientY);
15755
+ const p = this.target.getAbsoluteTransform().copy().invert().point(stagePt);
15756
+ const o = this.target.getContentOffset();
15757
+ return {
15758
+ x: p.x - o.x,
15759
+ y: p.y - o.y
15760
+ };
15761
+ }
15762
+ isInside(local) {
15763
+ const o = this.target.getContentOffset();
15764
+ const { width, height } = this.target.getSize();
15765
+ return local.x + o.x >= 0 && local.y + o.y >= 0 && local.x + o.x <= width && local.y + o.y <= height;
15766
+ }
15767
+ onMouseDown = (e) => {
15768
+ if (this.isInside(this.toLocal(e))) e.preventDefault();
15769
+ };
15770
+ onPointerDown = (e) => {
15771
+ if (e.button !== 0 && e.pointerType === "mouse") return;
15772
+ if (this.textarea && e.target === this.textarea) return;
15773
+ const local = this.toLocal(e);
15774
+ if (!this.isInside(local)) {
15775
+ e.stopPropagation();
15776
+ this.host.onRequestExit("outside");
15777
+ return;
15778
+ }
15779
+ e.stopPropagation();
15780
+ e.preventDefault();
15781
+ const offset = offsetFromPoint(this.target.getLayout(), local, this.target.measure);
15782
+ const text = this.model.getText();
15783
+ if (e.detail >= 3) {
15784
+ const [s, en] = paragraphRangeAt(text, offset);
15785
+ this.model.setSelection(s, en);
15786
+ } else if (e.detail === 2) {
15787
+ const [s, en] = wordRangeAt(text, offset);
15788
+ this.model.setSelection(s, en);
15789
+ } else {
15790
+ this.model.moveTo(offset, e.shiftKey);
15791
+ this.dragging = true;
15792
+ }
15793
+ this.syncTextarea();
15794
+ this.afterSelectionChange();
15795
+ this.textarea?.focus();
15796
+ };
15797
+ onPointerMove = (e) => {
15798
+ if (!this.dragging) return;
15799
+ const local = this.toLocal(e);
15800
+ const offset = offsetFromPoint(this.target.getLayout(), local, this.target.measure);
15801
+ this.model.moveTo(offset, true);
15802
+ this.syncTextarea();
15803
+ this.afterSelectionChange();
15804
+ };
15805
+ onPointerUp = () => {
15806
+ if (!this.dragging) return;
15807
+ this.dragging = false;
15808
+ this.textarea?.focus();
15809
+ };
15810
+ afterSelectionChange() {
15811
+ this.syncTextarea();
15812
+ this.positionTextarea();
15813
+ this.resetBlink();
15814
+ this.repaint();
15815
+ }
15816
+ afterContentChange(insertedLength, immediate = false) {
15817
+ this.flushPending();
15818
+ if (!immediate && insertedLength > 2e4 && typeof this.target.warmLayout === "function") {
15819
+ this.pendingFlush = true;
15820
+ this.sliceLayout();
15821
+ } else this.applyContent();
15822
+ this.resetBlink();
15823
+ }
15824
+ sliceLayout = () => {
15825
+ if (!this.pendingFlush || this.destroyed) return;
15826
+ if (this.target.warmLayout(this.model.getBlocks(), SLICE_BUDGET_MS)) this.flushPending();
15827
+ else this.pendingTimer = setTimeout(this.sliceLayout, 0);
15828
+ };
15829
+ flushPending() {
15830
+ if (!this.pendingFlush) return;
15831
+ this.pendingFlush = false;
15832
+ clearTimeout(this.pendingTimer);
15833
+ this.applyContent();
15834
+ }
15835
+ applyContent() {
15836
+ this.target.setBlocks(this.model.getBlocks());
15837
+ this.host.onChange?.(this.model.getBlocks());
15838
+ this.positionTextarea();
15839
+ this.target.requestRedraw();
15840
+ this.repaint();
15841
+ }
15842
+ createOverlay() {
15843
+ const layer = this.host.utilityLayer;
15844
+ if (!layer) return;
15845
+ const overlay = new Konva.Shape({
15846
+ name: "text-v2-edit-overlay",
15847
+ listening: false,
15848
+ sceneFunc: (ctx, shape) => this.paintOverlay(ctx, shape)
15849
+ });
15850
+ layer.add(overlay);
15851
+ overlay.moveToTop();
15852
+ this.overlay = overlay;
15853
+ }
15854
+ paintOverlay(ctx, shape) {
15855
+ if (this.destroyed || this.pendingFlush) return;
15856
+ const layout = this.target.getLayout();
15857
+ const abs = this.target.getAbsoluteTransform();
15858
+ const rel = shape.getAbsoluteTransform().copy().invert().multiply(abs);
15859
+ const o = this.target.getContentOffset();
15860
+ ctx.save();
15861
+ ctx.transform(rel.m[0], rel.m[1], rel.m[2], rel.m[3], rel.m[4], rel.m[5]);
15862
+ ctx.translate(o.x, o.y);
15863
+ const { start, end } = this.model.getSelection();
15864
+ if (start !== end) {
15865
+ ctx.setAttr("fillStyle", this.host.selectionColor);
15866
+ for (const r of selectionRects(layout, start, end, this.target.measure)) ctx.fillRect(r.x, r.y, r.width, r.height);
15867
+ } else if (this.caretOn) {
15868
+ const c = caretRect(layout, this.model.focus, this.target.measure);
15869
+ const scale = Math.hypot(abs.m[0], abs.m[1]) || 1;
15870
+ const w = Math.max(1, 1.5) / scale;
15871
+ ctx.setAttr("fillStyle", this.host.caretColor);
15872
+ ctx.fillRect(c.x - w / 2, c.y, w, c.height);
15873
+ }
15874
+ ctx.restore();
15875
+ }
15876
+ repaint() {
15877
+ this.overlay?.getLayer()?.batchDraw();
15878
+ }
15879
+ startBlink() {
15880
+ this.blinkTimer = setInterval(() => {
15881
+ this.caretOn = !this.caretOn;
15882
+ this.repaint();
15883
+ }, BLINK_MS);
15884
+ }
15885
+ resetBlink() {
15886
+ this.caretOn = true;
15887
+ if (this.blinkTimer) {
15888
+ clearInterval(this.blinkTimer);
15889
+ this.startBlink();
15890
+ }
15891
+ }
15892
+ };
15893
+ function host_signal(signal, abort) {
15894
+ if (!signal) return;
15895
+ if (signal.aborted) abort.abort();
15896
+ else signal.addEventListener("abort", () => abort.abort(), { once: true });
15897
+ }
15898
+ //#endregion
15899
+ //#region src/nodes/text-v2/constants.ts
15900
+ /** Default threshold for `onTextSizeWarning` (chars of serialized blocks). */
15901
+ const WEAVE_TEXT_V2_SIZE_WARNING_THRESHOLD = 2e5;
15902
+ const WEAVE_TEXT_V2_SELECTION_COLOR = "rgba(66, 133, 244, 0.35)";
15903
+ const WEAVE_TEXT_V2_DEFAULT_CONFIG = {
15904
+ ...WEAVE_TEXT_NODE_DEFAULT_CONFIG,
15905
+ sizeWarning: { threshold: WEAVE_TEXT_V2_SIZE_WARNING_THRESHOLD }
15906
+ };
15907
+ //#endregion
15908
+ //#region src/nodes/text-v2/text-v2.ts
15909
+ const CORNER_ANCHORS = [
15910
+ "top-left",
15911
+ "top-right",
15912
+ "bottom-left",
15913
+ "bottom-right"
15914
+ ];
15915
+ const SIDE_ANCHORS = ["middle-right", "middle-left"];
15916
+ /**
15917
+ * Drop-in replacement for `WeaveTextNode`. Same `nodeType` (`'text'`), so
15918
+ * existing documents load unchanged; register it INSTEAD of `WeaveTextNode`
15919
+ * (handlers are keyed by node type). Renders through `WeaveTextV2Shape`
15920
+ * (blocks/runs model + layout engine) instead of `Konva.Text`.
15921
+ */
15922
+ var WeaveTextV2Node = class extends WeaveNode {
15923
+ config;
15924
+ nodeType = WEAVE_TEXT_NODE_TYPE;
15925
+ createNode = false;
15926
+ editing = false;
15927
+ editingNodeId = null;
15928
+ session = null;
15929
+ sizeWarned = false;
15930
+ nodeRenderedAddedRegistered = false;
15931
+ keyPressHandler;
15932
+ constructor(params) {
15933
+ super();
15934
+ this.config = (0, import_merge.default)({}, WEAVE_TEXT_V2_DEFAULT_CONFIG, params?.config);
15935
+ }
15936
+ initialize() {
15937
+ this.keyPressHandler = void 0;
15938
+ this.editing = false;
15939
+ this.editingNodeId = null;
15940
+ this.session = null;
15941
+ this.nodeRenderedAddedRegistered = false;
15942
+ }
15943
+ /**
15944
+ * Resolve width/height for the layout mode. Auto modes size from the
15945
+ * engine's natural measure (independent of zoom, unlike the DOM-based
15946
+ * measure of the legacy node).
15947
+ */
15948
+ resolveSize(shape, props) {
15949
+ let width = props.width;
15950
+ let height = props.height;
15951
+ if (props.layout === TEXT_LAYOUT.AUTO_ALL) {
15952
+ const nat = shape.measureNaturalSize();
15953
+ width = nat.width + 2;
15954
+ height = nat.height + 2;
15955
+ }
15956
+ if (props.layout === TEXT_LAYOUT.SMART && !props.smartFixedWidth) {
15957
+ width = Math.max(1, shape.measureNaturalSize().width * 1.01);
15958
+ height = void 0;
15959
+ }
15960
+ if (props.layout === TEXT_LAYOUT.SMART && props.smartFixedWidth) height = void 0;
15961
+ if (props.layout === TEXT_LAYOUT.AUTO_HEIGHT) height = void 0;
15962
+ return {
15963
+ width,
15964
+ height
15965
+ };
15966
+ }
15967
+ measureMultilineText(shape) {
15968
+ return () => {
15969
+ const nat = shape.measureNaturalSize();
15970
+ return {
15971
+ width: Math.max(1, nat.width),
15972
+ height: Math.max(1, nat.height)
15973
+ };
15974
+ };
15975
+ }
15976
+ linkOverrides(props) {
15977
+ return {
15978
+ linkPreviousFill: props.link ? props.linkPreviousFill ?? props.fill : props.linkPreviousFill,
15979
+ linkPreviousTextDecoration: props.link ? props.linkPreviousTextDecoration ?? props.textDecoration : props.linkPreviousTextDecoration
15980
+ };
15981
+ }
15982
+ outlineProps() {
15983
+ return this.config.outline.enabled ? {
15984
+ strokeEnabled: true,
15985
+ stroke: this.config.outline.color,
15986
+ strokeWidth: this.config.outline.width,
15987
+ fillAfterStrokeEnabled: true
15988
+ } : { strokeEnabled: false };
15989
+ }
15990
+ onRender(props) {
15991
+ const { linkPreviousFill, linkPreviousTextDecoration } = this.linkOverrides(props);
15992
+ const text = new WeaveTextV2Shape({
15993
+ ...props,
15994
+ name: "node",
15995
+ ...this.outlineProps(),
15996
+ ...props.link && {
15997
+ textDecoration: "underline",
15998
+ fill: this.config.link.defaultColor,
15999
+ linkPreviousFill,
16000
+ linkPreviousTextDecoration
16001
+ }
16002
+ });
16003
+ this.setupDefaultNodeAugmentation(text);
16004
+ const defaultTransformerProperties = this.defaultGetTransformerProperties(this.config.transform);
16005
+ text.getTransformerProperties = function() {
16006
+ const layout = this.getAttrs().layout;
16007
+ if (layout === TEXT_LAYOUT.SMART) return {
16008
+ ...defaultTransformerProperties,
16009
+ ignoreStroke: true,
16010
+ resizeEnabled: true,
16011
+ keepRatio: false,
16012
+ enabledAnchors: []
16013
+ };
16014
+ if (layout === TEXT_LAYOUT.AUTO_ALL) return {
16015
+ ...defaultTransformerProperties,
16016
+ ignoreStroke: true,
16017
+ resizeEnabled: false,
16018
+ enabledAnchors: []
16019
+ };
16020
+ if (layout === TEXT_LAYOUT.AUTO_HEIGHT) return {
16021
+ ...defaultTransformerProperties,
16022
+ ignoreStroke: true,
16023
+ resizeEnabled: true,
16024
+ enabledAnchors: SIDE_ANCHORS
16025
+ };
16026
+ return defaultTransformerProperties;
16027
+ };
16028
+ text.allowedAnchors = function() {
16029
+ const layout = this.getAttrs().layout;
16030
+ if (layout === TEXT_LAYOUT.SMART) return [...CORNER_ANCHORS, ...SIDE_ANCHORS];
16031
+ if (layout === TEXT_LAYOUT.AUTO_ALL) return [];
16032
+ if (layout === TEXT_LAYOUT.AUTO_HEIGHT) return SIDE_ANCHORS;
16033
+ return [
16034
+ ...CORNER_ANCHORS,
16035
+ "top-center",
16036
+ "bottom-center",
16037
+ ...SIDE_ANCHORS
16038
+ ];
16039
+ };
16040
+ text.setAttrs({ measureMultilineText: this.measureMultilineText(text) });
16041
+ this.setupDefaultNodeEvents(text, { performScaleReset: false });
16042
+ text.dblClick = () => {
16043
+ if (this.editing) return;
16044
+ if (!(this.isSelecting() && this.isNodeSelected(text))) return;
16045
+ this.triggerEditMode(text);
16046
+ };
16047
+ text.setAttr("triggerEditMode", this.triggerEditMode.bind(this));
16048
+ this.setupLinkBehavior(text);
16049
+ this.setupTransform(text);
16050
+ if (!this.nodeRenderedAddedRegistered) {
16051
+ this.instance.addEventListener("onNodeRenderedAdded", (node) => {
16052
+ if (this.editing && this.editingNodeId !== null && node.id() === this.editingNodeId && node.getAttr("cancelEditMode")) node.getAttr("cancelEditMode")?.();
16053
+ });
16054
+ this.nodeRenderedAddedRegistered = true;
16055
+ }
16056
+ this.registerKeyPress();
16057
+ return text;
16058
+ }
16059
+ handleKeyPress = (e) => {
16060
+ if (e.code !== "Enter" || this.instance.getActiveAction() !== "selectionTool" || this.editing) return;
16061
+ e.preventDefault();
16062
+ const selected = this.getNodesSelectionPlugin()?.getSelectedNodes() ?? [];
16063
+ if (this.isSelecting() && selected.length === 1 && selected[0].getAttrs().nodeType === "text") this.triggerEditMode(selected[0]);
16064
+ };
16065
+ registerKeyPress() {
16066
+ if (this.instance.isServerSide() || this.keyPressHandler) return;
16067
+ this.keyPressHandler = this.handleKeyPress.bind(this);
16068
+ window.addEventListener("keypress", this.keyPressHandler, { signal: this.instance.getEventsController().signal });
16069
+ }
16070
+ onAdd() {
16071
+ this.registerKeyPress();
16072
+ }
16073
+ onDestroyInstance() {
16074
+ super.onDestroyInstance();
16075
+ this.session?.destroy();
16076
+ this.session = null;
16077
+ if (!this.instance.isServerSide() && this.keyPressHandler) {
16078
+ window.removeEventListener("keypress", this.keyPressHandler);
16079
+ this.keyPressHandler = void 0;
16080
+ }
16081
+ }
16082
+ /** Re-apply layout-driven width/height after live content changes. */
16083
+ applySize(shape) {
16084
+ const { width, height } = this.resolveSize(shape, shape.getAttrs());
16085
+ shape.setAttrs({
16086
+ width,
16087
+ height
16088
+ });
16089
+ }
16090
+ editTarget(shape) {
16091
+ return {
16092
+ getBlocks: () => shape.getBlocks(),
16093
+ setBlocks: (blocks) => {
16094
+ shape.setBlocks(blocks);
16095
+ this.applySize(shape);
16096
+ },
16097
+ getLayout: () => shape.ensureLayout(),
16098
+ measure: (t) => shape.measureText(t),
16099
+ getAbsoluteTransform: () => shape.getAbsoluteTransform(),
16100
+ getContentOffset: () => shape.getContentOffset(),
16101
+ getSize: () => ({
16102
+ width: shape.getWidth(),
16103
+ height: shape.getHeight()
16104
+ }),
16105
+ getFontSize: () => shape.fontSize(),
16106
+ requestRedraw: () => shape.getLayer()?.batchDraw(),
16107
+ warmLayout: (blocks, budget) => shape.warmLayout(blocks, budget)
16108
+ };
16109
+ }
16110
+ triggerEditMode(shape, create = false) {
16111
+ if (create) this.createNode = true;
16112
+ if (!this.instance.setMutexLock({
16113
+ nodeIds: [shape.id()],
16114
+ operation: "text-edit"
16115
+ })) return;
16116
+ this.editing = true;
16117
+ this.editingNodeId = shape.id();
16118
+ this.sizeWarned = false;
16119
+ const selectionPlugin = this.instance.getPlugin("nodesSelection");
16120
+ if (selectionPlugin) {
16121
+ const tr = selectionPlugin.getTransformer();
16122
+ this.instance.disablePlugin("nodesSelection");
16123
+ tr.hide();
16124
+ }
16125
+ const stage = this.instance.getStage();
16126
+ const container = stage.container();
16127
+ const rect = () => container.getBoundingClientRect();
16128
+ this.session = new TextEditSession(this.editTarget(shape), {
16129
+ utilityLayer: this.instance.getUtilityLayer(),
16130
+ container,
16131
+ clientToStage: (cx, cy) => {
16132
+ const r = rect();
16133
+ const sx = r.width ? stage.width() / r.width : 1;
16134
+ const sy = r.height ? stage.height() / r.height : 1;
16135
+ return {
16136
+ x: (cx - r.left) * sx,
16137
+ y: (cy - r.top) * sy
16138
+ };
16139
+ },
16140
+ stageToContainer: (p) => {
16141
+ const r = rect();
16142
+ const sx = r.width ? r.width / stage.width() : 1;
16143
+ const sy = r.height ? r.height / stage.height() : 1;
16144
+ return {
16145
+ x: p.x * sx,
16146
+ y: p.y * sy
16147
+ };
16148
+ },
16149
+ caretColor: this.config.cursor.color,
16150
+ selectionColor: WEAVE_TEXT_V2_SELECTION_COLOR,
16151
+ signal: this.instance.getEventsController().signal,
16152
+ onChange: (blocks) => this.checkSize(shape, blocks),
16153
+ onRequestExit: () => this.exitEditMode(shape)
16154
+ }, shape.id());
16155
+ shape.setAttr("cancelEditMode", () => this.exitEditMode(shape));
16156
+ stage.mode(WEAVE_STAGE_TEXT_EDITION_MODE);
16157
+ this.session.start();
16158
+ this.instance.emitEvent("onEnterTextNodeEditMode", { node: shape });
16159
+ }
16160
+ checkSize(shape, blocks) {
16161
+ const threshold = this.config.sizeWarning.threshold;
16162
+ if (blocksToText(blocks).length * 8 < threshold) {
16163
+ this.sizeWarned = false;
16164
+ return;
16165
+ }
16166
+ const size = JSON.stringify(blocks).length;
16167
+ if (size > threshold && !this.sizeWarned) {
16168
+ this.sizeWarned = true;
16169
+ this.instance.emitEvent("onTextSizeWarning", {
16170
+ node: shape,
16171
+ size,
16172
+ threshold
16173
+ });
16174
+ } else if (size <= threshold) this.sizeWarned = false;
16175
+ }
16176
+ exitEditMode(shape) {
16177
+ const session = this.session;
16178
+ if (!session || !this.editing) return;
16179
+ const { changed } = session.getResult();
16180
+ session.destroy();
16181
+ this.session = null;
16182
+ this.instance.releaseMutexLock();
16183
+ const stage = this.instance.getStage();
16184
+ stage.mode(WEAVE_STAGE_DEFAULT_MODE);
16185
+ this.editing = false;
16186
+ this.editingNodeId = null;
16187
+ shape.setAttr("cancelEditMode", void 0);
16188
+ this.commitNode(shape, changed);
16189
+ const liveNode = stage.findOne(`#${shape.id()}`) ?? shape;
16190
+ const selectionPlugin = this.instance.getPlugin("nodesSelection");
16191
+ if (selectionPlugin) {
16192
+ this.instance.enablePlugin("nodesSelection");
16193
+ selectionPlugin.setSelectedNodes([liveNode]);
16194
+ this.instance.triggerAction(SELECTION_TOOL_ACTION_NAME);
16195
+ }
16196
+ stage.container().tabIndex = 1;
16197
+ stage.container().click();
16198
+ stage.container().focus();
16199
+ this.instance.emitEvent("onExitTextNodeEditMode", { node: liveNode });
16200
+ }
16201
+ /** One `updateNode` per session; none when content is unchanged. */
16202
+ commitNode(shape, changed) {
16203
+ const live = this.instance.getStage().findOne(`#${shape.id()}`) ?? shape;
16204
+ if (this.createNode) {
16205
+ const serialized = this.serialize(live);
16206
+ if (live.text() !== "") {
16207
+ const container = live.getParent();
16208
+ live.destroy();
16209
+ this.instance.addNode(serialized, container?.getAttrs().id);
16210
+ } else live.destroy();
16211
+ } else if (changed) this.instance.updateNode(this.serialize(live));
16212
+ this.createNode = false;
16213
+ }
16214
+ setupTransform(text) {
16215
+ let actualAnchor = void 0;
16216
+ const isSide = () => SIDE_ANCHORS.includes(actualAnchor ?? "");
16217
+ const isCorner = () => CORNER_ANCHORS.includes(actualAnchor ?? "");
16218
+ const autoWidthLayout = () => [TEXT_LAYOUT.AUTO_HEIGHT, TEXT_LAYOUT.SMART].includes(text.getAttrs().layout);
16219
+ text.on("transformstart", (e) => {
16220
+ const ctrlOrMeta = e.evt.ctrlKey || e.evt.metaKey;
16221
+ this.instance.emitEvent("onTransform", e.target);
16222
+ actualAnchor = this.getNodesSelectionPlugin()?.getTransformer()?.getActiveAnchor();
16223
+ const layout = text.getAttrs().layout;
16224
+ this.getNodesSelectionPlugin()?.getTransformer()?.keepRatio(layout === TEXT_LAYOUT.SMART && isCorner() || layout === TEXT_LAYOUT.FIXED && ctrlOrMeta);
16225
+ if (autoWidthLayout() && isSide()) {
16226
+ text.wrap("word");
16227
+ text.scaleY(1);
16228
+ text.height(void 0);
16229
+ }
16230
+ e.cancelBubble = true;
16231
+ });
16232
+ text.on("transform", () => {
16233
+ if (autoWidthLayout() && isSide()) {
16234
+ text.width(text.width() * text.scaleX());
16235
+ text.scaleX(1);
16236
+ text.scaleY(1);
16237
+ text.height(void 0);
16238
+ text.getLayer()?.batchDraw();
16239
+ }
16240
+ const layout = text.getAttrs().layout;
16241
+ if (this.isSelecting() && this.isNodeSelected(text) && (layout !== TEXT_LAYOUT.SMART || !isCorner())) {
16242
+ text.width(text.width() * text.scaleX());
16243
+ resetScale(text);
16244
+ text.fontSize(text.fontSize() * text.scaleY());
16245
+ }
16246
+ text.setAttr("shouldUpdateOnTransform", false);
16247
+ text.getLayer()?.batchDraw();
16248
+ });
16249
+ text.on("transformend", () => {
16250
+ this.instance.emitEvent("onTransform", null);
16251
+ let definedSmartWidth = false;
16252
+ let smartFixedWidth = text.getAttr("smartFixedWidth") ?? false;
16253
+ const layout = text.getAttrs().layout;
16254
+ if (layout !== TEXT_LAYOUT.SMART && !isCorner()) this.scaleReset(text);
16255
+ if (layout === TEXT_LAYOUT.SMART && isCorner()) text.setAttrs({
16256
+ width: Math.ceil(text.width() * text.scaleX()),
16257
+ height: Math.ceil(text.height() * text.scaleY()),
16258
+ fontSize: text.fontSize() * text.scaleY(),
16259
+ scaleX: 1,
16260
+ scaleY: 1
16261
+ });
16262
+ if (autoWidthLayout() && isSide() && !smartFixedWidth && !definedSmartWidth) {
16263
+ text.setAttr("smartFixedWidth", true);
16264
+ smartFixedWidth = true;
16265
+ definedSmartWidth = true;
16266
+ text.width(Math.ceil(text.width() * text.scaleX()));
16267
+ text.scaleX(1);
16268
+ text.scaleY(1);
16269
+ text.height(void 0);
16270
+ text.getLayer()?.batchDraw();
16271
+ text.height(Math.ceil(text.height()));
16272
+ }
16273
+ if (layout === TEXT_LAYOUT.SMART && isSide() && smartFixedWidth && !definedSmartWidth) {
16274
+ text.width(Math.ceil(text.width() * text.scaleX()));
16275
+ text.scaleX(1);
16276
+ }
16277
+ this.instance.updateNode(this.serialize(text));
16278
+ });
16279
+ }
16280
+ onUpdate(nodeInstance, nextProps) {
16281
+ const shape = nodeInstance;
16282
+ if (this.editing && this.editingNodeId === shape.id()) nextProps = {
16283
+ ...nextProps,
16284
+ text: shape.attrs.text,
16285
+ blocks: shape.attrs.blocks
16286
+ };
16287
+ const isLinked = Boolean(nextProps.link);
16288
+ const { linkPreviousFill, linkPreviousTextDecoration } = this.linkOverrides(nextProps);
16289
+ shape.setAttrs({
16290
+ ...nextProps,
16291
+ ...this.outlineProps(),
16292
+ link: nextProps.link,
16293
+ linkPreviousFill: isLinked ? linkPreviousFill : void 0,
16294
+ linkPreviousTextDecoration: isLinked ? linkPreviousTextDecoration : void 0,
16295
+ ...isLinked && {
16296
+ textDecoration: "underline",
16297
+ fill: this.config.link.defaultColor
16298
+ }
16299
+ });
16300
+ if (nextProps.blocks === void 0 && shape.attrs.blocks !== void 0) shape.setAttr("blocks", void 0);
16301
+ const { width, height } = this.resolveSize(shape, nextProps);
16302
+ shape.setAttrs({
16303
+ width,
16304
+ height
16305
+ });
16306
+ if (!this.editing) {
16307
+ const selection = this.getNodesSelectionPlugin();
16308
+ if (selection) selection.setSelectedNodes(selection.getSelectedNodes());
16309
+ }
16310
+ }
16311
+ serialize(instance) {
16312
+ const attrs = instance.getAttrs();
16313
+ const cleanedAttrs = { ...attrs };
16314
+ delete cleanedAttrs.mutexLocked;
16315
+ delete cleanedAttrs.mutexUserId;
16316
+ delete cleanedAttrs.draggable;
16317
+ delete cleanedAttrs.triggerEditMode;
16318
+ delete cleanedAttrs.cancelEditMode;
16319
+ delete cleanedAttrs.measureMultilineText;
16320
+ delete cleanedAttrs.overridesMouseControl;
16321
+ delete cleanedAttrs.shouldUpdateOnTransform;
16322
+ delete cleanedAttrs.dragBoundFunc;
16323
+ if (cleanedAttrs.link) {
16324
+ if (typeof cleanedAttrs.linkPreviousFill === "string") cleanedAttrs.fill = cleanedAttrs.linkPreviousFill;
16325
+ if (typeof cleanedAttrs.linkPreviousTextDecoration === "string") cleanedAttrs.textDecoration = cleanedAttrs.linkPreviousTextDecoration;
16326
+ }
16327
+ return {
16328
+ key: attrs.id ?? "",
16329
+ type: attrs.nodeType,
16330
+ props: {
16331
+ ...cleanedAttrs,
16332
+ isCloned: void 0,
16333
+ isCloneOrigin: void 0,
16334
+ id: attrs.id ?? "",
16335
+ nodeType: attrs.nodeType,
16336
+ children: []
16337
+ }
16338
+ };
16339
+ }
16340
+ setupLinkBehavior(text) {
16341
+ text.handleMouseover = () => {
16342
+ if (!text.getAttrs().link) return;
16343
+ text.fill(this.config.link.hoverColor);
16344
+ text.getLayer()?.batchDraw();
16345
+ };
16346
+ text.handleMouseout = () => {
16347
+ if (!text.getAttrs().link) return;
16348
+ text.fill(this.config.link.defaultColor);
16349
+ text.getLayer()?.batchDraw();
16350
+ };
16351
+ text.defineMousePointer = () => text.getAttrs().link ? "pointer" : "default";
16352
+ let linkClickTimeout;
16353
+ const clearLinkClickTimeout = () => {
16354
+ if (linkClickTimeout) {
16355
+ clearTimeout(linkClickTimeout);
16356
+ linkClickTimeout = void 0;
16357
+ }
16358
+ };
16359
+ const previousDblClick = text.dblClick?.bind(text) ?? (() => {});
16360
+ text.dblClick = () => {
16361
+ clearLinkClickTimeout();
16362
+ previousDblClick();
16363
+ };
16364
+ text.click = ({ wasSelected, ctrlOrMetaPressed }) => {
16365
+ if (!text.getAttrs().link) return;
16366
+ if (ctrlOrMetaPressed) {
16367
+ clearLinkClickTimeout();
16368
+ this.openTextLink(text);
16369
+ return;
16370
+ }
16371
+ if (wasSelected) {
16372
+ clearLinkClickTimeout();
16373
+ linkClickTimeout = setTimeout(() => {
16374
+ linkClickTimeout = void 0;
16375
+ this.openTextLink(text);
16376
+ }, Konva.dblClickWindow);
16377
+ }
16378
+ };
16379
+ }
16380
+ openTextLink(textNode) {
16381
+ if (this.instance.isServerSide()) return;
16382
+ const link = textNode.getAttrs().link;
16383
+ if (typeof link !== "string" || !isValidTextLink(link)) return;
16384
+ window.open(link, "_blank", "noopener,noreferrer");
16385
+ }
16386
+ getLink(nodeInstance) {
16387
+ const link = nodeInstance.getAttrs().link;
16388
+ return typeof link === "string" ? link : void 0;
16389
+ }
16390
+ setLink(nodeInstance, url) {
16391
+ if (!isValidTextLink(url)) return;
16392
+ const serialized = this.serialize(nodeInstance);
16393
+ serialized.props.linkPreviousFill = serialized.props.fill;
16394
+ serialized.props.linkPreviousTextDecoration = serialized.props.textDecoration;
16395
+ serialized.props.link = url;
16396
+ this.instance.updateNode(serialized);
16397
+ }
16398
+ removeLink(nodeInstance) {
16399
+ const serialized = this.serialize(nodeInstance);
16400
+ delete serialized.props.link;
16401
+ delete serialized.props.linkPreviousFill;
16402
+ delete serialized.props.linkPreviousTextDecoration;
16403
+ this.instance.updateNode(serialized);
16404
+ }
16405
+ resetSmartLayout(textNode) {
16406
+ textNode.setAttr("smartFixedWidth", void 0);
16407
+ textNode.width(Math.max(1, textNode.measureNaturalSize().width * 1.01));
16408
+ this.instance.updateNode(this.serialize(textNode));
16409
+ }
16410
+ static defaultState(nodeId, params) {
16411
+ return WeaveTextNode.defaultState(nodeId, params);
16412
+ }
16413
+ static addNodeState(defaultNodeState, props) {
16414
+ return mergeExceptArrays(WeaveTextNode.addNodeState(defaultNodeState, props), { props: { ...props.blocks && { blocks: props.blocks } } });
16415
+ }
16416
+ static updateNodeState(prevNodeState, nextProps) {
16417
+ const next = WeaveTextNode.updateNodeState(prevNodeState, nextProps);
16418
+ if (nextProps.blocks) next.props.blocks = nextProps.blocks;
16419
+ else delete next.props.blocks;
16420
+ return next;
16421
+ }
16422
+ static getSchema() {
16423
+ return WeaveTextNode.getSchema();
16424
+ }
16425
+ };
16426
+ //#endregion
14442
16427
  //#region src/nodes/image/constants.ts
14443
16428
  const WEAVE_IMAGE_NODE_TYPE = "image";
14444
16429
  const WEAVE_STAGE_IMAGE_CROPPING_MODE = "image-cropping";
@@ -33586,6 +35571,6 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
33586
35571
  }
33587
35572
  };
33588
35573
  //#endregion
33589
- 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, 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, setupCanvasBackend, setupSkiaBackend, sleep, spreadLabelProps, weavejsToYjsBinary };
35574
+ 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, TextLayoutEngine, 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, WeaveTextV2Node, WeaveTextV2Shape, WeaveUsersPointersPlugin, WeaveUsersPresencePlugin, WeaveUsersSelectionPlugin, WeaveVideoNode, WeaveVideoToolAction, WeaveZoomInToolAction, WeaveZoomOutToolAction, buildAncestorGroupIds, 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, setupCanvasBackend, setupSkiaBackend, sleep, spreadLabelProps, weavejsToYjsBinary };
33590
35575
 
33591
35576
  //# sourceMappingURL=types.js.map