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