@inditextech/weave-sdk 5.7.0-SNAPSHOT.595.1 → 5.7.0-SNAPSHOT.596.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
@@ -9505,7 +9505,7 @@ var WeaveRegisterManager = class {
9505
9505
  };
9506
9506
  //#endregion
9507
9507
  //#region package.json
9508
- var version = "5.7.0-SNAPSHOT.595.1";
9508
+ var version = "5.7.0-SNAPSHOT.596.1";
9509
9509
  //#endregion
9510
9510
  //#region src/managers/setup.ts
9511
9511
  var WeaveSetupManager = class {
@@ -14415,7 +14415,7 @@ function normalizeRuns(runs) {
14415
14415
  const out = [];
14416
14416
  for (const run of runs) {
14417
14417
  if (run.t === "") continue;
14418
- const last = out[out.length - 1];
14418
+ const last = out.at(-1);
14419
14419
  if (last && sameMarks(last.m, run.m)) last.t += run.t;
14420
14420
  else out.push({
14421
14421
  t: run.t,
@@ -14490,7 +14490,7 @@ function replaceRange(blocks, start, end, insert) {
14490
14490
  const suffixBlocks = [];
14491
14491
  let inheritedMarks;
14492
14492
  let pos = 0;
14493
- blocks.forEach((block, i) => {
14493
+ blocks.forEach((block) => {
14494
14494
  const len = blockText(block).length;
14495
14495
  const bStart = pos;
14496
14496
  const bEnd = pos + len;
@@ -14555,6 +14555,43 @@ const now = () => typeof performance !== "undefined" ? performance.now() : Date.
14555
14555
  const WS = /\s/;
14556
14556
  const MAX_CACHE = 2e4;
14557
14557
  const MAX_CACHED_CHUNK = 256;
14558
+ function flattenBlock(block) {
14559
+ const graphemes = [];
14560
+ const runOf = [];
14561
+ for (let ri = 0; ri < block.runs.length; ri++) for (const g of splitGraphemes(block.runs[ri].t)) {
14562
+ graphemes.push(g);
14563
+ runOf.push(ri);
14564
+ }
14565
+ return {
14566
+ graphemes,
14567
+ runOf
14568
+ };
14569
+ }
14570
+ function trimTrailingSpace(graphemes, start, end) {
14571
+ let e = end;
14572
+ while (e > start && WS.test(graphemes[e - 1])) e--;
14573
+ return e;
14574
+ }
14575
+ function skipSpace(graphemes, from) {
14576
+ let i = from;
14577
+ while (i < graphemes.length && WS.test(graphemes[i])) i++;
14578
+ return i;
14579
+ }
14580
+ const isNum = (v) => v !== void 0 && !Number.isNaN(v);
14581
+ /** Konva's word wrap: break after the last space/dash that fits (0 = no break). */
14582
+ function wordWrapIndex(graphemes, start, matchLen, matchWidth, maxWidth) {
14583
+ const nextChar = graphemes[start + matchLen];
14584
+ if ((nextChar === " " || nextChar === "-") && matchWidth <= maxWidth) return matchLen;
14585
+ const seg = graphemes.slice(start, start + matchLen);
14586
+ return Math.max(seg.lastIndexOf(" "), seg.lastIndexOf("-")) + 1;
14587
+ }
14588
+ function applyAlign(lines, align, width, padding) {
14589
+ if (align === "left") return;
14590
+ for (const line of lines) {
14591
+ const free = width - line.width - padding * 2;
14592
+ line.offsetX = align === "center" ? free / 2 : free;
14593
+ }
14594
+ }
14558
14595
  var TextLayoutEngine = class {
14559
14596
  measure;
14560
14597
  cache = /* @__PURE__ */ new Map();
@@ -14612,8 +14649,8 @@ var TextLayoutEngine = class {
14612
14649
  layout(blocks, style, bounds) {
14613
14650
  const font = fontString(style);
14614
14651
  const padding = bounds.padding;
14615
- const fixedWidth = bounds.width !== void 0 && !Number.isNaN(bounds.width);
14616
- const fixedHeight = bounds.height !== void 0 && !Number.isNaN(bounds.height);
14652
+ const fixedWidth = isNum(bounds.width);
14653
+ const fixedHeight = isNum(bounds.height);
14617
14654
  const maxWidth = (bounds.width ?? 0) - padding * 2;
14618
14655
  const maxHeightPx = (bounds.height ?? 0) - padding * 2;
14619
14656
  const lineHeightPx = style.lineHeight * style.fontSize;
@@ -14623,9 +14660,8 @@ var TextLayoutEngine = class {
14623
14660
  let relaid = 0;
14624
14661
  const lines = [];
14625
14662
  let textWidth = 0;
14626
- let currentHeightPx = 0;
14627
14663
  let offset = 0;
14628
- outer: for (let p = 0; p < blocks.length; p++) {
14664
+ for (let p = 0; p < blocks.length; p++) {
14629
14665
  const block = blocks[p];
14630
14666
  const sig = `${sigBase}|${JSON.stringify(block.runs)}`;
14631
14667
  let entry = prev[p]?.sig === sig ? prev[p] : this.pool.get(sig);
@@ -14634,27 +14670,14 @@ var TextLayoutEngine = class {
14634
14670
  relaid++;
14635
14671
  }
14636
14672
  next.push(entry);
14637
- for (const pl of entry.lines) {
14638
- const text = entry.graphemes.slice(pl.startG, pl.endG).join("");
14639
- const segments = this.buildSegments(entry, block, pl, font, style);
14640
- const width = segments.reduce((a, s) => a + s.width, 0);
14641
- const startChar = entry.graphemes.slice(0, pl.startG).join("").length;
14642
- lines.push({
14643
- paragraph: p,
14644
- start: offset + startChar,
14645
- end: offset + startChar + text.length,
14646
- text,
14647
- width,
14648
- measuredWidth: pl.measuredWidth,
14649
- offsetX: 0,
14650
- y: lines.length * lineHeightPx,
14651
- segments,
14652
- lastInParagraph: pl.lastInParagraph
14653
- });
14654
- textWidth = Math.max(textWidth, pl.measuredWidth);
14655
- currentHeightPx += lineHeightPx;
14656
- if (fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx) break outer;
14657
- }
14673
+ const emitted = this.emitLines(entry, block, p, offset, lines, {
14674
+ font,
14675
+ style,
14676
+ lineHeightPx,
14677
+ maxHeightPx: fixedHeight ? maxHeightPx : void 0
14678
+ });
14679
+ textWidth = Math.max(textWidth, emitted.maxWidth);
14680
+ if (emitted.stopped) break;
14658
14681
  offset += blockText(block).length + 1;
14659
14682
  }
14660
14683
  this.paragraphs = next;
@@ -14662,10 +14685,7 @@ var TextLayoutEngine = class {
14662
14685
  this.lastParagraphsRelaid = relaid;
14663
14686
  const width = fixedWidth ? bounds.width : textWidth + padding * 2;
14664
14687
  const height = fixedHeight ? bounds.height : lines.length * lineHeightPx + padding * 2;
14665
- if (style.align !== "left") for (const line of lines) {
14666
- const free = width - line.width - padding * 2;
14667
- line.offsetX = style.align === "center" ? free / 2 : free;
14668
- }
14688
+ applyAlign(lines, style.align, width, padding);
14669
14689
  return {
14670
14690
  lines,
14671
14691
  textWidth,
@@ -14675,7 +14695,38 @@ var TextLayoutEngine = class {
14675
14695
  height
14676
14696
  };
14677
14697
  }
14678
- rangeWidth(entry, runs, from, to, font, spacing) {
14698
+ /** Append a paragraph's lines to `lines`; stops once a fixed height is full. */
14699
+ emitLines(entry, block, paragraph, offset, lines, opts) {
14700
+ const { font, style, lineHeightPx, maxHeightPx } = opts;
14701
+ let maxWidth = 0;
14702
+ for (const pl of entry.lines) {
14703
+ const text = entry.graphemes.slice(pl.startG, pl.endG).join("");
14704
+ const segments = this.buildSegments(entry, block, pl, font, style);
14705
+ const startChar = entry.graphemes.slice(0, pl.startG).join("").length;
14706
+ lines.push({
14707
+ paragraph,
14708
+ start: offset + startChar,
14709
+ end: offset + startChar + text.length,
14710
+ text,
14711
+ width: segments.reduce((a, s) => a + s.width, 0),
14712
+ measuredWidth: pl.measuredWidth,
14713
+ offsetX: 0,
14714
+ y: lines.length * lineHeightPx,
14715
+ segments,
14716
+ lastInParagraph: pl.lastInParagraph
14717
+ });
14718
+ maxWidth = Math.max(maxWidth, pl.measuredWidth);
14719
+ if (maxHeightPx !== void 0 && (lines.length + 1) * lineHeightPx > maxHeightPx) return {
14720
+ stopped: true,
14721
+ maxWidth
14722
+ };
14723
+ }
14724
+ return {
14725
+ stopped: false,
14726
+ maxWidth
14727
+ };
14728
+ }
14729
+ rangeWidth(entry, from, to, font, spacing) {
14679
14730
  let width = 0;
14680
14731
  let i = from;
14681
14732
  while (i < to) {
@@ -14687,15 +14738,39 @@ var TextLayoutEngine = class {
14687
14738
  }
14688
14739
  return width;
14689
14740
  }
14741
+ /**
14742
+ * Longest prefix of graphemes[start..n) that fits `maxWidth`, found with an
14743
+ * exponential probe (bounds the work to about one line) plus a binary
14744
+ * search, like Konva. Always consumes at least one grapheme.
14745
+ */
14746
+ longestFit(start, n, maxWidth, width) {
14747
+ let low = 0;
14748
+ let high = n - start;
14749
+ let probe = Math.min(high, 32);
14750
+ while (probe < high && width(start, start + probe) <= maxWidth) probe = Math.min(high, probe * 2);
14751
+ high = probe;
14752
+ let matchLen = 0;
14753
+ let matchWidth = 0;
14754
+ while (low < high) {
14755
+ const mid = low + high >>> 1;
14756
+ const w = width(start, start + mid + 1);
14757
+ if (w <= maxWidth) {
14758
+ low = mid + 1;
14759
+ matchLen = mid + 1;
14760
+ matchWidth = w;
14761
+ } else high = mid;
14762
+ }
14763
+ if (matchLen === 0) {
14764
+ matchLen = 1;
14765
+ matchWidth = width(start, start + 1);
14766
+ }
14767
+ return {
14768
+ matchLen,
14769
+ matchWidth
14770
+ };
14771
+ }
14690
14772
  layoutParagraph(block, sig, font, style, fixedWidth, maxWidth) {
14691
- const graphemes = [];
14692
- const runOf = [];
14693
- block.runs.forEach((run, ri) => {
14694
- for (const g of splitGraphemes(run.t)) {
14695
- graphemes.push(g);
14696
- runOf.push(ri);
14697
- }
14698
- });
14773
+ const { graphemes, runOf } = flattenBlock(block);
14699
14774
  const entry = {
14700
14775
  sig,
14701
14776
  graphemes,
@@ -14704,7 +14779,7 @@ var TextLayoutEngine = class {
14704
14779
  };
14705
14780
  const n = graphemes.length;
14706
14781
  const spacing = style.letterSpacing;
14707
- const width = (a, b) => this.rangeWidth(entry, block.runs, a, b, font, spacing);
14782
+ const width = (a, b) => this.rangeWidth(entry, a, b, font, spacing);
14708
14783
  const shouldWrap = style.wrap !== "none";
14709
14784
  const wrapAtWord = style.wrap !== "char" && shouldWrap;
14710
14785
  const whole = width(0, n);
@@ -14719,26 +14794,7 @@ var TextLayoutEngine = class {
14719
14794
  }
14720
14795
  let start = 0;
14721
14796
  while (start < n) {
14722
- let low = 0;
14723
- let high = n - start;
14724
- let probe = Math.min(high, 32);
14725
- while (probe < high && width(start, start + probe) <= maxWidth) probe = Math.min(high, probe * 2);
14726
- high = probe;
14727
- let matchLen = 0;
14728
- let matchWidth = 0;
14729
- while (low < high) {
14730
- const mid = low + high >>> 1;
14731
- const w = width(start, start + mid + 1);
14732
- if (w <= maxWidth) {
14733
- low = mid + 1;
14734
- matchLen = mid + 1;
14735
- matchWidth = w;
14736
- } else high = mid;
14737
- }
14738
- if (matchLen === 0) {
14739
- matchLen = 1;
14740
- matchWidth = width(start, start + 1);
14741
- }
14797
+ let { matchLen, matchWidth } = this.longestFit(start, n, maxWidth, width);
14742
14798
  if (start + matchLen >= n) {
14743
14799
  entry.lines.push({
14744
14800
  startG: start,
@@ -14749,32 +14805,22 @@ var TextLayoutEngine = class {
14749
14805
  return entry;
14750
14806
  }
14751
14807
  if (wrapAtWord) {
14752
- const nextChar = graphemes[start + matchLen];
14753
- const nextIsSpaceOrDash = nextChar === " " || nextChar === "-";
14754
- let wrapIndex;
14755
- if (nextIsSpaceOrDash && matchWidth <= maxWidth) wrapIndex = matchLen;
14756
- else {
14757
- const seg = graphemes.slice(start, start + matchLen);
14758
- wrapIndex = Math.max(seg.lastIndexOf(" "), seg.lastIndexOf("-")) + 1;
14759
- }
14760
- if (wrapIndex > 0) {
14761
- matchLen = wrapIndex;
14808
+ const wrapped = wordWrapIndex(graphemes, start, matchLen, matchWidth, maxWidth);
14809
+ if (wrapped > 0) {
14810
+ matchLen = wrapped;
14762
14811
  matchWidth = width(start, start + matchLen);
14763
14812
  }
14764
14813
  }
14765
- let end = start + matchLen;
14766
- let trimmedEnd = end;
14767
- while (trimmedEnd > start && WS.test(graphemes[trimmedEnd - 1])) trimmedEnd--;
14814
+ const end = start + matchLen;
14768
14815
  entry.lines.push({
14769
14816
  startG: start,
14770
- endG: trimmedEnd,
14817
+ endG: trimTrailingSpace(graphemes, start, end),
14771
14818
  measuredWidth: matchWidth,
14772
14819
  lastInParagraph: false
14773
14820
  });
14774
- while (end < n && WS.test(graphemes[end])) end++;
14775
- start = end;
14821
+ start = skipSpace(graphemes, end);
14776
14822
  }
14777
- const last = entry.lines[entry.lines.length - 1];
14823
+ const last = entry.lines.at(-1);
14778
14824
  if (last) last.lastInParagraph = true;
14779
14825
  return entry;
14780
14826
  }
@@ -14836,8 +14882,6 @@ var WeaveTextV2Shape = class extends Konva.Shape {
14836
14882
  this.on(LAYOUT_ATTRS.map((a) => `${a}Change.konva`).join(" "), () => {
14837
14883
  this.layoutCache = null;
14838
14884
  });
14839
- this.sceneFunc(this._paint);
14840
- this.hitFunc(this._hit);
14841
14885
  }
14842
14886
  text(value) {
14843
14887
  if (value === void 0) return typeof this.attrs.text === "string" ? this.attrs.text : blocksToText(this.getBlocks());
@@ -15029,18 +15073,12 @@ var WeaveTextV2Shape = class extends Konva.Shape {
15029
15073
  y2: Math.max(...pts.map((p) => p.y))
15030
15074
  };
15031
15075
  }
15032
- _paint = (ctx, shape) => {
15076
+ _sceneFunc = (ctx, shape) => {
15033
15077
  const self = shape;
15034
15078
  const layout = self.ensureLayout();
15035
15079
  if (layout.lines.length === 0) return;
15036
- const fontSize = self.fontSize();
15037
15080
  const padding = self.padding();
15038
15081
  const lineHeightPx = layout.lineHeightPx;
15039
- const decoration = self.textDecoration();
15040
- const underline = decoration.includes("underline");
15041
- const lineThrough = decoration.includes("line-through");
15042
- const spacing = self.letterSpacing();
15043
- const fill = self.fill();
15044
15082
  let baseline = "middle";
15045
15083
  let translateY = lineHeightPx / 2;
15046
15084
  if (!Konva.legacyTextRendering) {
@@ -15051,62 +15089,63 @@ var WeaveTextV2Shape = class extends Konva.Shape {
15051
15089
  ctx.setAttr("font", fontString(self.style()));
15052
15090
  ctx.setAttr("textBaseline", baseline);
15053
15091
  ctx.setAttr("textAlign", "left");
15054
- let alignY = 0;
15055
- const va = self.verticalAlign();
15056
- const used = layout.lines.length * lineHeightPx + padding * 2;
15057
- if (va === "middle") alignY = (self.getHeight() - used) / 2;
15058
- else if (va === "bottom") alignY = self.getHeight() - used;
15092
+ const alignY = self.verticalOffset(layout);
15059
15093
  ctx.translate(padding, alignY + padding);
15060
15094
  const range = self.visibleRange();
15061
- const paintSelf = self;
15095
+ const top = alignY + padding;
15062
15096
  for (let n = 0; n < layout.lines.length; n++) {
15063
- const line = layout.lines[n];
15064
- const y = translateY + n * lineHeightPx;
15065
- if (range && (alignY + padding + n * lineHeightPx + lineHeightPx < range.y1 - lineHeightPx || alignY + padding + n * lineHeightPx > range.y2 + lineHeightPx)) continue;
15066
- ctx.save();
15067
- const baseX = line.offsetX;
15068
- if (underline) {
15069
- ctx.save();
15070
- ctx.beginPath();
15071
- const uy = y + Math.round(fontSize / 4);
15072
- ctx.moveTo(baseX, uy);
15073
- ctx.lineTo(baseX + Math.round(line.width), uy);
15074
- ctx.lineWidth = fontSize / 15;
15075
- ctx.strokeStyle = fill;
15076
- ctx.stroke();
15077
- ctx.restore();
15078
- }
15079
- for (const seg of line.segments) {
15080
- let x = baseX + seg.x;
15081
- if (spacing !== 0) for (const ch of Array.from(seg.t)) {
15082
- paintSelf._partialText = ch;
15083
- paintSelf._partialTextX = x;
15084
- paintSelf._partialTextY = y;
15085
- ctx.fillStrokeShape(self);
15086
- x += self.measureSize(ch).width + spacing;
15087
- }
15088
- else {
15089
- paintSelf._partialText = seg.t;
15090
- paintSelf._partialTextX = x;
15091
- paintSelf._partialTextY = y;
15092
- ctx.fillStrokeShape(self);
15093
- }
15094
- }
15095
- if (lineThrough) {
15096
- ctx.save();
15097
- ctx.beginPath();
15098
- const ly = y - Math.round(fontSize / 4);
15099
- ctx.moveTo(baseX, ly);
15100
- ctx.lineTo(baseX + Math.round(line.width), ly);
15101
- ctx.lineWidth = fontSize / 15;
15102
- ctx.strokeStyle = fill;
15103
- ctx.stroke();
15104
- ctx.restore();
15105
- }
15106
- ctx.restore();
15097
+ const lineTop = top + n * lineHeightPx;
15098
+ if (range && (lineTop + lineHeightPx < range.y1 - lineHeightPx || lineTop > range.y2 + lineHeightPx)) continue;
15099
+ self.paintLine(ctx, layout.lines[n], translateY + n * lineHeightPx);
15107
15100
  }
15108
15101
  };
15109
- _hit = (ctx, shape) => {
15102
+ /** Vertical offset of the text block inside the box (verticalAlign). */
15103
+ verticalOffset(layout) {
15104
+ const used = layout.lines.length * layout.lineHeightPx + this.padding() * 2;
15105
+ const va = this.verticalAlign();
15106
+ if (va === "middle") return (this.getHeight() - used) / 2;
15107
+ if (va === "bottom") return this.getHeight() - used;
15108
+ return 0;
15109
+ }
15110
+ paintLine(ctx, line, y) {
15111
+ const decoration = this.textDecoration();
15112
+ const fontSize = this.fontSize();
15113
+ ctx.save();
15114
+ if (decoration.includes("underline")) this.strokeDecoration(ctx, line, y + Math.round(fontSize / 4));
15115
+ for (const seg of line.segments) this.paintSegment(ctx, seg.t, line.offsetX + seg.x, y);
15116
+ if (decoration.includes("line-through")) this.strokeDecoration(ctx, line, y - Math.round(fontSize / 4));
15117
+ ctx.restore();
15118
+ }
15119
+ strokeDecoration(ctx, line, y) {
15120
+ ctx.save();
15121
+ ctx.beginPath();
15122
+ ctx.moveTo(line.offsetX, y);
15123
+ ctx.lineTo(line.offsetX + Math.round(line.width), y);
15124
+ ctx.lineWidth = this.fontSize() / 15;
15125
+ ctx.strokeStyle = this.fill();
15126
+ ctx.stroke();
15127
+ ctx.restore();
15128
+ }
15129
+ paintSegment(ctx, text, x, y) {
15130
+ const spacing = this.letterSpacing();
15131
+ if (spacing === 0) {
15132
+ this.fillText(ctx, text, x, y);
15133
+ return;
15134
+ }
15135
+ let cx = x;
15136
+ for (const ch of Array.from(text)) {
15137
+ this.fillText(ctx, ch, cx, y);
15138
+ cx += this.measureSize(ch).width + spacing;
15139
+ }
15140
+ }
15141
+ fillText(ctx, text, x, y) {
15142
+ const self = this;
15143
+ self._partialText = text;
15144
+ self._partialTextX = x;
15145
+ self._partialTextY = y;
15146
+ ctx.fillStrokeShape(this);
15147
+ }
15148
+ _hitFunc = (ctx, shape) => {
15110
15149
  ctx.beginPath();
15111
15150
  const self = shape;
15112
15151
  ctx.rect(0, 0, self.getWidth(), self.getHeight());
@@ -15214,7 +15253,7 @@ function lineIndexAt(layout, offset) {
15214
15253
  if (l.start <= offset) fallback = i;
15215
15254
  if (offset >= l.start && offset <= l.end) {
15216
15255
  const next = lines[i + 1];
15217
- if (offset === l.end && !l.lastInParagraph && next && next.start === offset) continue;
15256
+ if (offset === l.end && !l.lastInParagraph && next?.start === offset) continue;
15218
15257
  return i;
15219
15258
  }
15220
15259
  }
@@ -15263,10 +15302,7 @@ function offsetInLine(line, x, measure) {
15263
15302
  function moveVertical(layout, offset, dir, desiredX, measure) {
15264
15303
  const j = lineIndexAt(layout, offset) + dir;
15265
15304
  if (j < 0) return 0;
15266
- if (j >= layout.lines.length) {
15267
- const last = layout.lines[layout.lines.length - 1];
15268
- return last ? last.end : 0;
15269
- }
15305
+ if (j >= layout.lines.length) return layout.lines.at(-1)?.end ?? 0;
15270
15306
  return offsetInLine(layout.lines[j], desiredX, measure);
15271
15307
  }
15272
15308
  function lineHome(layout, offset) {
@@ -15496,9 +15532,10 @@ var TextEditSession = class {
15496
15532
  capture: true
15497
15533
  });
15498
15534
  window.weaveTextEditing = {
15499
- ...window.weaveTextEditing ?? {},
15535
+ ...window.weaveTextEditing,
15500
15536
  [this.id]: this.id
15501
15537
  };
15538
+ if (this.host.selectAllOnStart) this.model.selectAll();
15502
15539
  this.syncTextarea();
15503
15540
  ta.focus();
15504
15541
  this.positionTextarea();
@@ -15602,41 +15639,43 @@ var TextEditSession = class {
15602
15639
  this.afterSelectionChange();
15603
15640
  }
15604
15641
  };
15605
- onKeyDown = (e) => {
15606
- e.stopPropagation();
15642
+ /** Keys the session handles itself; everything else stays native. */
15643
+ handleSessionKey(e) {
15607
15644
  const mod = e.ctrlKey || e.metaKey;
15608
15645
  const key = e.key.toLowerCase();
15609
- if (e.key === "Escape") {
15610
- e.preventDefault();
15611
- this.host.onRequestExit("escape");
15612
- return;
15613
- }
15614
- if (mod && key === "z" && !e.shiftKey) {
15615
- e.preventDefault();
15616
- this.undo();
15617
- return;
15646
+ const isUndo = mod && key === "z" && !e.shiftKey;
15647
+ const isRedo = mod && (key === "y" || key === "z" && e.shiftKey);
15648
+ if (e.key === "Escape") this.host.onRequestExit("escape");
15649
+ else if (isUndo) this.undo();
15650
+ else if (isRedo) this.redo();
15651
+ else if (e.key === "Tab") return true;
15652
+ else return false;
15653
+ return true;
15654
+ }
15655
+ handleNavigationKey(e) {
15656
+ if (e.ctrlKey || e.metaKey) return false;
15657
+ if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !e.altKey) {
15658
+ this.moveVertically(e.key === "ArrowUp" ? -1 : 1, e.shiftKey);
15659
+ return true;
15618
15660
  }
15619
- if (mod && (key === "y" || key === "z" && e.shiftKey)) {
15620
- e.preventDefault();
15621
- this.redo();
15622
- return;
15661
+ if (e.key === "Home" || e.key === "End") {
15662
+ const layout = this.target.getLayout();
15663
+ const to = e.key === "Home" ? lineHome(layout, this.model.focus) : lineEnd(layout, this.model.focus);
15664
+ this.model.moveTo(to, e.shiftKey);
15665
+ this.afterSelectionChange();
15666
+ return true;
15623
15667
  }
15624
- if (e.key === "Tab") {
15668
+ return false;
15669
+ }
15670
+ onKeyDown = (e) => {
15671
+ e.stopPropagation();
15672
+ if (this.handleSessionKey(e)) {
15625
15673
  e.preventDefault();
15626
15674
  return;
15627
15675
  }
15628
15676
  if (this.composing) return;
15629
- if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !mod && !e.altKey) {
15677
+ if (this.handleNavigationKey(e)) {
15630
15678
  e.preventDefault();
15631
- this.moveVertically(e.key === "ArrowUp" ? -1 : 1, e.shiftKey);
15632
- return;
15633
- }
15634
- if ((e.key === "Home" || e.key === "End") && !mod) {
15635
- e.preventDefault();
15636
- const layout = this.target.getLayout();
15637
- const to = e.key === "Home" ? lineHome(layout, this.model.focus) : lineEnd(layout, this.model.focus);
15638
- this.model.moveTo(to, e.shiftKey);
15639
- this.afterSelectionChange();
15640
15679
  return;
15641
15680
  }
15642
15681
  setTimeout(this.syncFromTextarea, 0);
@@ -15650,7 +15689,7 @@ var TextEditSession = class {
15650
15689
  };
15651
15690
  moveVertically(dir, extend) {
15652
15691
  const layout = this.target.getLayout();
15653
- if (this.model.desiredX === null) this.model.desiredX = caretRect(layout, this.model.focus, this.target.measure).x;
15692
+ this.model.desiredX ??= caretRect(layout, this.model.focus, this.target.measure).x;
15654
15693
  const to = moveVertical(layout, this.model.focus, dir, this.model.desiredX, this.target.measure);
15655
15694
  this.model.moveTo(to, extend, true);
15656
15695
  this.afterSelectionChange();
@@ -15668,11 +15707,17 @@ var TextEditSession = class {
15668
15707
  while (s < max - p && oldText[oldText.length - 1 - s] === newText[newText.length - 1 - s]) s++;
15669
15708
  const inserted = newText.slice(p, newText.length - s);
15670
15709
  const inputType = e.inputType ?? "";
15671
- const kind = this.composing || inputType.includes("Composition") ? "ime" : inputType.startsWith("delete") ? "delete" : inputType === "insertFromPaste" ? "paste" : "type";
15710
+ const kind = this.editKindFor(inputType);
15672
15711
  this.model.replaceRange(p, oldText.length - s, inserted, kind);
15673
15712
  this.model.anchor = this.model.focus = Math.min(ta.selectionEnd, this.model.getText().length);
15674
15713
  this.afterContentChange(inserted.length);
15675
15714
  };
15715
+ editKindFor(inputType) {
15716
+ if (this.composing || inputType.includes("Composition")) return "ime";
15717
+ if (inputType.startsWith("delete")) return "delete";
15718
+ if (inputType === "insertFromPaste") return "paste";
15719
+ return "type";
15720
+ }
15676
15721
  onPaste = (e) => {
15677
15722
  e.stopPropagation();
15678
15723
  e.preventDefault();
@@ -15819,6 +15864,17 @@ var TextEditSession = class {
15819
15864
  const o = this.target.getContentOffset();
15820
15865
  ctx.save();
15821
15866
  ctx.transform(rel.m[0], rel.m[1], rel.m[2], rel.m[3], rel.m[4], rel.m[5]);
15867
+ if (this.host.outlineColor) {
15868
+ const scale = Math.hypot(abs.m[0], abs.m[1]) || 1;
15869
+ const lw = (this.host.outlineWidth ?? 1) / scale;
15870
+ const { width, height } = this.target.getSize();
15871
+ ctx.setAttr("strokeStyle", this.host.outlineColor);
15872
+ ctx.setAttr("lineWidth", lw);
15873
+ ctx.beginPath();
15874
+ const pad = (this.host.outlinePadding ?? 0) / scale;
15875
+ ctx.rect(-pad, -pad, width + pad * 2, height + pad * 2);
15876
+ ctx.stroke();
15877
+ }
15822
15878
  ctx.translate(o.x, o.y);
15823
15879
  const { start, end } = this.model.getSelection();
15824
15880
  if (start !== end) {
@@ -15862,6 +15918,12 @@ const WEAVE_TEXT_V2_SIZE_WARNING_THRESHOLD = 2e5;
15862
15918
  const WEAVE_TEXT_V2_SELECTION_COLOR = "rgba(66, 133, 244, 0.35)";
15863
15919
  const WEAVE_TEXT_V2_DEFAULT_CONFIG = {
15864
15920
  ...WEAVE_TEXT_NODE_DEFAULT_CONFIG,
15921
+ edition: { outline: {
15922
+ enabled: true,
15923
+ color: "#0074ff",
15924
+ width: 1,
15925
+ padding: 0
15926
+ } },
15865
15927
  sizeWarning: { threshold: WEAVE_TEXT_V2_SIZE_WARNING_THRESHOLD }
15866
15928
  };
15867
15929
  //#endregion
@@ -15873,6 +15935,8 @@ const CORNER_ANCHORS = [
15873
15935
  "bottom-right"
15874
15936
  ];
15875
15937
  const SIDE_ANCHORS = ["middle-right", "middle-left"];
15938
+ const CORNER_ANCHOR_SET = new Set(CORNER_ANCHORS);
15939
+ const SIDE_ANCHOR_SET = new Set(SIDE_ANCHORS);
15876
15940
  /**
15877
15941
  * Drop-in replacement for `WeaveTextNode`. Same `nodeType` (`'text'`), so
15878
15942
  * existing documents load unchanged; register it INSTEAD of `WeaveTextNode`
@@ -16106,8 +16170,14 @@ var WeaveTextV2Node = class extends WeaveNode {
16106
16170
  y: p.y * sy
16107
16171
  };
16108
16172
  },
16173
+ selectAllOnStart: !create,
16109
16174
  caretColor: this.config.cursor.color,
16110
16175
  selectionColor: WEAVE_TEXT_V2_SELECTION_COLOR,
16176
+ ...this.config.edition.outline.enabled && {
16177
+ outlineColor: this.config.edition.outline.color,
16178
+ outlineWidth: this.config.edition.outline.width,
16179
+ outlinePadding: this.config.edition.outline.padding
16180
+ },
16111
16181
  signal: this.instance.getEventsController().signal,
16112
16182
  onChange: (blocks) => this.checkSize(shape, blocks),
16113
16183
  onRequestExit: () => this.exitEditMode(shape)
@@ -16173,8 +16243,8 @@ var WeaveTextV2Node = class extends WeaveNode {
16173
16243
  }
16174
16244
  setupTransform(text) {
16175
16245
  let actualAnchor = void 0;
16176
- const isSide = () => SIDE_ANCHORS.includes(actualAnchor ?? "");
16177
- const isCorner = () => CORNER_ANCHORS.includes(actualAnchor ?? "");
16246
+ const isSide = () => SIDE_ANCHOR_SET.has(actualAnchor ?? "");
16247
+ const isCorner = () => CORNER_ANCHOR_SET.has(actualAnchor ?? "");
16178
16248
  const autoWidthLayout = () => [TEXT_LAYOUT.AUTO_HEIGHT, TEXT_LAYOUT.SMART].includes(text.getAttrs().layout);
16179
16249
  text.on("transformstart", (e) => {
16180
16250
  const ctrlOrMeta = e.evt.ctrlKey || e.evt.metaKey;
@@ -16271,6 +16341,7 @@ var WeaveTextV2Node = class extends WeaveNode {
16271
16341
  serialize(instance) {
16272
16342
  const attrs = instance.getAttrs();
16273
16343
  const cleanedAttrs = { ...attrs };
16344
+ for (const key of Object.keys(cleanedAttrs)) if (typeof cleanedAttrs[key] === "function") delete cleanedAttrs[key];
16274
16345
  delete cleanedAttrs.mutexLocked;
16275
16346
  delete cleanedAttrs.mutexUserId;
16276
16347
  delete cleanedAttrs.draggable;