@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/types.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 {
@@ -14455,7 +14455,7 @@ function normalizeRuns(runs) {
14455
14455
  const out = [];
14456
14456
  for (const run of runs) {
14457
14457
  if (run.t === "") continue;
14458
- const last = out[out.length - 1];
14458
+ const last = out.at(-1);
14459
14459
  if (last && sameMarks(last.m, run.m)) last.t += run.t;
14460
14460
  else out.push({
14461
14461
  t: run.t,
@@ -14530,7 +14530,7 @@ function replaceRange(blocks, start, end, insert) {
14530
14530
  const suffixBlocks = [];
14531
14531
  let inheritedMarks;
14532
14532
  let pos = 0;
14533
- blocks.forEach((block, i) => {
14533
+ blocks.forEach((block) => {
14534
14534
  const len = blockText(block).length;
14535
14535
  const bStart = pos;
14536
14536
  const bEnd = pos + len;
@@ -14595,6 +14595,43 @@ const now = () => typeof performance !== "undefined" ? performance.now() : Date.
14595
14595
  const WS = /\s/;
14596
14596
  const MAX_CACHE = 2e4;
14597
14597
  const MAX_CACHED_CHUNK = 256;
14598
+ function flattenBlock(block) {
14599
+ const graphemes = [];
14600
+ const runOf = [];
14601
+ for (let ri = 0; ri < block.runs.length; ri++) for (const g of splitGraphemes(block.runs[ri].t)) {
14602
+ graphemes.push(g);
14603
+ runOf.push(ri);
14604
+ }
14605
+ return {
14606
+ graphemes,
14607
+ runOf
14608
+ };
14609
+ }
14610
+ function trimTrailingSpace(graphemes, start, end) {
14611
+ let e = end;
14612
+ while (e > start && WS.test(graphemes[e - 1])) e--;
14613
+ return e;
14614
+ }
14615
+ function skipSpace(graphemes, from) {
14616
+ let i = from;
14617
+ while (i < graphemes.length && WS.test(graphemes[i])) i++;
14618
+ return i;
14619
+ }
14620
+ const isNum = (v) => v !== void 0 && !Number.isNaN(v);
14621
+ /** Konva's word wrap: break after the last space/dash that fits (0 = no break). */
14622
+ function wordWrapIndex(graphemes, start, matchLen, matchWidth, maxWidth) {
14623
+ const nextChar = graphemes[start + matchLen];
14624
+ if ((nextChar === " " || nextChar === "-") && matchWidth <= maxWidth) return matchLen;
14625
+ const seg = graphemes.slice(start, start + matchLen);
14626
+ return Math.max(seg.lastIndexOf(" "), seg.lastIndexOf("-")) + 1;
14627
+ }
14628
+ function applyAlign(lines, align, width, padding) {
14629
+ if (align === "left") return;
14630
+ for (const line of lines) {
14631
+ const free = width - line.width - padding * 2;
14632
+ line.offsetX = align === "center" ? free / 2 : free;
14633
+ }
14634
+ }
14598
14635
  var TextLayoutEngine = class {
14599
14636
  measure;
14600
14637
  cache = /* @__PURE__ */ new Map();
@@ -14652,8 +14689,8 @@ var TextLayoutEngine = class {
14652
14689
  layout(blocks, style, bounds) {
14653
14690
  const font = fontString(style);
14654
14691
  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);
14692
+ const fixedWidth = isNum(bounds.width);
14693
+ const fixedHeight = isNum(bounds.height);
14657
14694
  const maxWidth = (bounds.width ?? 0) - padding * 2;
14658
14695
  const maxHeightPx = (bounds.height ?? 0) - padding * 2;
14659
14696
  const lineHeightPx = style.lineHeight * style.fontSize;
@@ -14663,9 +14700,8 @@ var TextLayoutEngine = class {
14663
14700
  let relaid = 0;
14664
14701
  const lines = [];
14665
14702
  let textWidth = 0;
14666
- let currentHeightPx = 0;
14667
14703
  let offset = 0;
14668
- outer: for (let p = 0; p < blocks.length; p++) {
14704
+ for (let p = 0; p < blocks.length; p++) {
14669
14705
  const block = blocks[p];
14670
14706
  const sig = `${sigBase}|${JSON.stringify(block.runs)}`;
14671
14707
  let entry = prev[p]?.sig === sig ? prev[p] : this.pool.get(sig);
@@ -14674,27 +14710,14 @@ var TextLayoutEngine = class {
14674
14710
  relaid++;
14675
14711
  }
14676
14712
  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
- }
14713
+ const emitted = this.emitLines(entry, block, p, offset, lines, {
14714
+ font,
14715
+ style,
14716
+ lineHeightPx,
14717
+ maxHeightPx: fixedHeight ? maxHeightPx : void 0
14718
+ });
14719
+ textWidth = Math.max(textWidth, emitted.maxWidth);
14720
+ if (emitted.stopped) break;
14698
14721
  offset += blockText(block).length + 1;
14699
14722
  }
14700
14723
  this.paragraphs = next;
@@ -14702,10 +14725,7 @@ var TextLayoutEngine = class {
14702
14725
  this.lastParagraphsRelaid = relaid;
14703
14726
  const width = fixedWidth ? bounds.width : textWidth + padding * 2;
14704
14727
  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
- }
14728
+ applyAlign(lines, style.align, width, padding);
14709
14729
  return {
14710
14730
  lines,
14711
14731
  textWidth,
@@ -14715,7 +14735,38 @@ var TextLayoutEngine = class {
14715
14735
  height
14716
14736
  };
14717
14737
  }
14718
- rangeWidth(entry, runs, from, to, font, spacing) {
14738
+ /** Append a paragraph's lines to `lines`; stops once a fixed height is full. */
14739
+ emitLines(entry, block, paragraph, offset, lines, opts) {
14740
+ const { font, style, lineHeightPx, maxHeightPx } = opts;
14741
+ let maxWidth = 0;
14742
+ for (const pl of entry.lines) {
14743
+ const text = entry.graphemes.slice(pl.startG, pl.endG).join("");
14744
+ const segments = this.buildSegments(entry, block, pl, font, style);
14745
+ const startChar = entry.graphemes.slice(0, pl.startG).join("").length;
14746
+ lines.push({
14747
+ paragraph,
14748
+ start: offset + startChar,
14749
+ end: offset + startChar + text.length,
14750
+ text,
14751
+ width: segments.reduce((a, s) => a + s.width, 0),
14752
+ measuredWidth: pl.measuredWidth,
14753
+ offsetX: 0,
14754
+ y: lines.length * lineHeightPx,
14755
+ segments,
14756
+ lastInParagraph: pl.lastInParagraph
14757
+ });
14758
+ maxWidth = Math.max(maxWidth, pl.measuredWidth);
14759
+ if (maxHeightPx !== void 0 && (lines.length + 1) * lineHeightPx > maxHeightPx) return {
14760
+ stopped: true,
14761
+ maxWidth
14762
+ };
14763
+ }
14764
+ return {
14765
+ stopped: false,
14766
+ maxWidth
14767
+ };
14768
+ }
14769
+ rangeWidth(entry, from, to, font, spacing) {
14719
14770
  let width = 0;
14720
14771
  let i = from;
14721
14772
  while (i < to) {
@@ -14727,15 +14778,39 @@ var TextLayoutEngine = class {
14727
14778
  }
14728
14779
  return width;
14729
14780
  }
14781
+ /**
14782
+ * Longest prefix of graphemes[start..n) that fits `maxWidth`, found with an
14783
+ * exponential probe (bounds the work to about one line) plus a binary
14784
+ * search, like Konva. Always consumes at least one grapheme.
14785
+ */
14786
+ longestFit(start, n, maxWidth, width) {
14787
+ let low = 0;
14788
+ let high = n - start;
14789
+ let probe = Math.min(high, 32);
14790
+ while (probe < high && width(start, start + probe) <= maxWidth) probe = Math.min(high, probe * 2);
14791
+ high = probe;
14792
+ let matchLen = 0;
14793
+ let matchWidth = 0;
14794
+ while (low < high) {
14795
+ const mid = low + high >>> 1;
14796
+ const w = width(start, start + mid + 1);
14797
+ if (w <= maxWidth) {
14798
+ low = mid + 1;
14799
+ matchLen = mid + 1;
14800
+ matchWidth = w;
14801
+ } else high = mid;
14802
+ }
14803
+ if (matchLen === 0) {
14804
+ matchLen = 1;
14805
+ matchWidth = width(start, start + 1);
14806
+ }
14807
+ return {
14808
+ matchLen,
14809
+ matchWidth
14810
+ };
14811
+ }
14730
14812
  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
- });
14813
+ const { graphemes, runOf } = flattenBlock(block);
14739
14814
  const entry = {
14740
14815
  sig,
14741
14816
  graphemes,
@@ -14744,7 +14819,7 @@ var TextLayoutEngine = class {
14744
14819
  };
14745
14820
  const n = graphemes.length;
14746
14821
  const spacing = style.letterSpacing;
14747
- const width = (a, b) => this.rangeWidth(entry, block.runs, a, b, font, spacing);
14822
+ const width = (a, b) => this.rangeWidth(entry, a, b, font, spacing);
14748
14823
  const shouldWrap = style.wrap !== "none";
14749
14824
  const wrapAtWord = style.wrap !== "char" && shouldWrap;
14750
14825
  const whole = width(0, n);
@@ -14759,26 +14834,7 @@ var TextLayoutEngine = class {
14759
14834
  }
14760
14835
  let start = 0;
14761
14836
  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
- }
14837
+ let { matchLen, matchWidth } = this.longestFit(start, n, maxWidth, width);
14782
14838
  if (start + matchLen >= n) {
14783
14839
  entry.lines.push({
14784
14840
  startG: start,
@@ -14789,32 +14845,22 @@ var TextLayoutEngine = class {
14789
14845
  return entry;
14790
14846
  }
14791
14847
  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;
14848
+ const wrapped = wordWrapIndex(graphemes, start, matchLen, matchWidth, maxWidth);
14849
+ if (wrapped > 0) {
14850
+ matchLen = wrapped;
14802
14851
  matchWidth = width(start, start + matchLen);
14803
14852
  }
14804
14853
  }
14805
- let end = start + matchLen;
14806
- let trimmedEnd = end;
14807
- while (trimmedEnd > start && WS.test(graphemes[trimmedEnd - 1])) trimmedEnd--;
14854
+ const end = start + matchLen;
14808
14855
  entry.lines.push({
14809
14856
  startG: start,
14810
- endG: trimmedEnd,
14857
+ endG: trimTrailingSpace(graphemes, start, end),
14811
14858
  measuredWidth: matchWidth,
14812
14859
  lastInParagraph: false
14813
14860
  });
14814
- while (end < n && WS.test(graphemes[end])) end++;
14815
- start = end;
14861
+ start = skipSpace(graphemes, end);
14816
14862
  }
14817
- const last = entry.lines[entry.lines.length - 1];
14863
+ const last = entry.lines.at(-1);
14818
14864
  if (last) last.lastInParagraph = true;
14819
14865
  return entry;
14820
14866
  }
@@ -14876,8 +14922,6 @@ var WeaveTextV2Shape = class extends Konva.Shape {
14876
14922
  this.on(LAYOUT_ATTRS.map((a) => `${a}Change.konva`).join(" "), () => {
14877
14923
  this.layoutCache = null;
14878
14924
  });
14879
- this.sceneFunc(this._paint);
14880
- this.hitFunc(this._hit);
14881
14925
  }
14882
14926
  text(value) {
14883
14927
  if (value === void 0) return typeof this.attrs.text === "string" ? this.attrs.text : blocksToText(this.getBlocks());
@@ -15069,18 +15113,12 @@ var WeaveTextV2Shape = class extends Konva.Shape {
15069
15113
  y2: Math.max(...pts.map((p) => p.y))
15070
15114
  };
15071
15115
  }
15072
- _paint = (ctx, shape) => {
15116
+ _sceneFunc = (ctx, shape) => {
15073
15117
  const self = shape;
15074
15118
  const layout = self.ensureLayout();
15075
15119
  if (layout.lines.length === 0) return;
15076
- const fontSize = self.fontSize();
15077
15120
  const padding = self.padding();
15078
15121
  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
15122
  let baseline = "middle";
15085
15123
  let translateY = lineHeightPx / 2;
15086
15124
  if (!Konva.legacyTextRendering) {
@@ -15091,62 +15129,63 @@ var WeaveTextV2Shape = class extends Konva.Shape {
15091
15129
  ctx.setAttr("font", fontString(self.style()));
15092
15130
  ctx.setAttr("textBaseline", baseline);
15093
15131
  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;
15132
+ const alignY = self.verticalOffset(layout);
15099
15133
  ctx.translate(padding, alignY + padding);
15100
15134
  const range = self.visibleRange();
15101
- const paintSelf = self;
15135
+ const top = alignY + padding;
15102
15136
  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();
15137
+ const lineTop = top + n * lineHeightPx;
15138
+ if (range && (lineTop + lineHeightPx < range.y1 - lineHeightPx || lineTop > range.y2 + lineHeightPx)) continue;
15139
+ self.paintLine(ctx, layout.lines[n], translateY + n * lineHeightPx);
15147
15140
  }
15148
15141
  };
15149
- _hit = (ctx, shape) => {
15142
+ /** Vertical offset of the text block inside the box (verticalAlign). */
15143
+ verticalOffset(layout) {
15144
+ const used = layout.lines.length * layout.lineHeightPx + this.padding() * 2;
15145
+ const va = this.verticalAlign();
15146
+ if (va === "middle") return (this.getHeight() - used) / 2;
15147
+ if (va === "bottom") return this.getHeight() - used;
15148
+ return 0;
15149
+ }
15150
+ paintLine(ctx, line, y) {
15151
+ const decoration = this.textDecoration();
15152
+ const fontSize = this.fontSize();
15153
+ ctx.save();
15154
+ if (decoration.includes("underline")) this.strokeDecoration(ctx, line, y + Math.round(fontSize / 4));
15155
+ for (const seg of line.segments) this.paintSegment(ctx, seg.t, line.offsetX + seg.x, y);
15156
+ if (decoration.includes("line-through")) this.strokeDecoration(ctx, line, y - Math.round(fontSize / 4));
15157
+ ctx.restore();
15158
+ }
15159
+ strokeDecoration(ctx, line, y) {
15160
+ ctx.save();
15161
+ ctx.beginPath();
15162
+ ctx.moveTo(line.offsetX, y);
15163
+ ctx.lineTo(line.offsetX + Math.round(line.width), y);
15164
+ ctx.lineWidth = this.fontSize() / 15;
15165
+ ctx.strokeStyle = this.fill();
15166
+ ctx.stroke();
15167
+ ctx.restore();
15168
+ }
15169
+ paintSegment(ctx, text, x, y) {
15170
+ const spacing = this.letterSpacing();
15171
+ if (spacing === 0) {
15172
+ this.fillText(ctx, text, x, y);
15173
+ return;
15174
+ }
15175
+ let cx = x;
15176
+ for (const ch of Array.from(text)) {
15177
+ this.fillText(ctx, ch, cx, y);
15178
+ cx += this.measureSize(ch).width + spacing;
15179
+ }
15180
+ }
15181
+ fillText(ctx, text, x, y) {
15182
+ const self = this;
15183
+ self._partialText = text;
15184
+ self._partialTextX = x;
15185
+ self._partialTextY = y;
15186
+ ctx.fillStrokeShape(this);
15187
+ }
15188
+ _hitFunc = (ctx, shape) => {
15150
15189
  ctx.beginPath();
15151
15190
  const self = shape;
15152
15191
  ctx.rect(0, 0, self.getWidth(), self.getHeight());
@@ -15254,7 +15293,7 @@ function lineIndexAt(layout, offset) {
15254
15293
  if (l.start <= offset) fallback = i;
15255
15294
  if (offset >= l.start && offset <= l.end) {
15256
15295
  const next = lines[i + 1];
15257
- if (offset === l.end && !l.lastInParagraph && next && next.start === offset) continue;
15296
+ if (offset === l.end && !l.lastInParagraph && next?.start === offset) continue;
15258
15297
  return i;
15259
15298
  }
15260
15299
  }
@@ -15303,10 +15342,7 @@ function offsetInLine(line, x, measure) {
15303
15342
  function moveVertical(layout, offset, dir, desiredX, measure) {
15304
15343
  const j = lineIndexAt(layout, offset) + dir;
15305
15344
  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
- }
15345
+ if (j >= layout.lines.length) return layout.lines.at(-1)?.end ?? 0;
15310
15346
  return offsetInLine(layout.lines[j], desiredX, measure);
15311
15347
  }
15312
15348
  function lineHome(layout, offset) {
@@ -15536,9 +15572,10 @@ var TextEditSession = class {
15536
15572
  capture: true
15537
15573
  });
15538
15574
  window.weaveTextEditing = {
15539
- ...window.weaveTextEditing ?? {},
15575
+ ...window.weaveTextEditing,
15540
15576
  [this.id]: this.id
15541
15577
  };
15578
+ if (this.host.selectAllOnStart) this.model.selectAll();
15542
15579
  this.syncTextarea();
15543
15580
  ta.focus();
15544
15581
  this.positionTextarea();
@@ -15642,41 +15679,43 @@ var TextEditSession = class {
15642
15679
  this.afterSelectionChange();
15643
15680
  }
15644
15681
  };
15645
- onKeyDown = (e) => {
15646
- e.stopPropagation();
15682
+ /** Keys the session handles itself; everything else stays native. */
15683
+ handleSessionKey(e) {
15647
15684
  const mod = e.ctrlKey || e.metaKey;
15648
15685
  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;
15686
+ const isUndo = mod && key === "z" && !e.shiftKey;
15687
+ const isRedo = mod && (key === "y" || key === "z" && e.shiftKey);
15688
+ if (e.key === "Escape") this.host.onRequestExit("escape");
15689
+ else if (isUndo) this.undo();
15690
+ else if (isRedo) this.redo();
15691
+ else if (e.key === "Tab") return true;
15692
+ else return false;
15693
+ return true;
15694
+ }
15695
+ handleNavigationKey(e) {
15696
+ if (e.ctrlKey || e.metaKey) return false;
15697
+ if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !e.altKey) {
15698
+ this.moveVertically(e.key === "ArrowUp" ? -1 : 1, e.shiftKey);
15699
+ return true;
15658
15700
  }
15659
- if (mod && (key === "y" || key === "z" && e.shiftKey)) {
15660
- e.preventDefault();
15661
- this.redo();
15662
- return;
15701
+ if (e.key === "Home" || e.key === "End") {
15702
+ const layout = this.target.getLayout();
15703
+ const to = e.key === "Home" ? lineHome(layout, this.model.focus) : lineEnd(layout, this.model.focus);
15704
+ this.model.moveTo(to, e.shiftKey);
15705
+ this.afterSelectionChange();
15706
+ return true;
15663
15707
  }
15664
- if (e.key === "Tab") {
15708
+ return false;
15709
+ }
15710
+ onKeyDown = (e) => {
15711
+ e.stopPropagation();
15712
+ if (this.handleSessionKey(e)) {
15665
15713
  e.preventDefault();
15666
15714
  return;
15667
15715
  }
15668
15716
  if (this.composing) return;
15669
- if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !mod && !e.altKey) {
15717
+ if (this.handleNavigationKey(e)) {
15670
15718
  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
15719
  return;
15681
15720
  }
15682
15721
  setTimeout(this.syncFromTextarea, 0);
@@ -15690,7 +15729,7 @@ var TextEditSession = class {
15690
15729
  };
15691
15730
  moveVertically(dir, extend) {
15692
15731
  const layout = this.target.getLayout();
15693
- if (this.model.desiredX === null) this.model.desiredX = caretRect(layout, this.model.focus, this.target.measure).x;
15732
+ this.model.desiredX ??= caretRect(layout, this.model.focus, this.target.measure).x;
15694
15733
  const to = moveVertical(layout, this.model.focus, dir, this.model.desiredX, this.target.measure);
15695
15734
  this.model.moveTo(to, extend, true);
15696
15735
  this.afterSelectionChange();
@@ -15708,11 +15747,17 @@ var TextEditSession = class {
15708
15747
  while (s < max - p && oldText[oldText.length - 1 - s] === newText[newText.length - 1 - s]) s++;
15709
15748
  const inserted = newText.slice(p, newText.length - s);
15710
15749
  const inputType = e.inputType ?? "";
15711
- const kind = this.composing || inputType.includes("Composition") ? "ime" : inputType.startsWith("delete") ? "delete" : inputType === "insertFromPaste" ? "paste" : "type";
15750
+ const kind = this.editKindFor(inputType);
15712
15751
  this.model.replaceRange(p, oldText.length - s, inserted, kind);
15713
15752
  this.model.anchor = this.model.focus = Math.min(ta.selectionEnd, this.model.getText().length);
15714
15753
  this.afterContentChange(inserted.length);
15715
15754
  };
15755
+ editKindFor(inputType) {
15756
+ if (this.composing || inputType.includes("Composition")) return "ime";
15757
+ if (inputType.startsWith("delete")) return "delete";
15758
+ if (inputType === "insertFromPaste") return "paste";
15759
+ return "type";
15760
+ }
15716
15761
  onPaste = (e) => {
15717
15762
  e.stopPropagation();
15718
15763
  e.preventDefault();
@@ -15859,6 +15904,17 @@ var TextEditSession = class {
15859
15904
  const o = this.target.getContentOffset();
15860
15905
  ctx.save();
15861
15906
  ctx.transform(rel.m[0], rel.m[1], rel.m[2], rel.m[3], rel.m[4], rel.m[5]);
15907
+ if (this.host.outlineColor) {
15908
+ const scale = Math.hypot(abs.m[0], abs.m[1]) || 1;
15909
+ const lw = (this.host.outlineWidth ?? 1) / scale;
15910
+ const { width, height } = this.target.getSize();
15911
+ ctx.setAttr("strokeStyle", this.host.outlineColor);
15912
+ ctx.setAttr("lineWidth", lw);
15913
+ ctx.beginPath();
15914
+ const pad = (this.host.outlinePadding ?? 0) / scale;
15915
+ ctx.rect(-pad, -pad, width + pad * 2, height + pad * 2);
15916
+ ctx.stroke();
15917
+ }
15862
15918
  ctx.translate(o.x, o.y);
15863
15919
  const { start, end } = this.model.getSelection();
15864
15920
  if (start !== end) {
@@ -15902,6 +15958,12 @@ const WEAVE_TEXT_V2_SIZE_WARNING_THRESHOLD = 2e5;
15902
15958
  const WEAVE_TEXT_V2_SELECTION_COLOR = "rgba(66, 133, 244, 0.35)";
15903
15959
  const WEAVE_TEXT_V2_DEFAULT_CONFIG = {
15904
15960
  ...WEAVE_TEXT_NODE_DEFAULT_CONFIG,
15961
+ edition: { outline: {
15962
+ enabled: true,
15963
+ color: "#0074ff",
15964
+ width: 1,
15965
+ padding: 0
15966
+ } },
15905
15967
  sizeWarning: { threshold: WEAVE_TEXT_V2_SIZE_WARNING_THRESHOLD }
15906
15968
  };
15907
15969
  //#endregion
@@ -15913,6 +15975,8 @@ const CORNER_ANCHORS = [
15913
15975
  "bottom-right"
15914
15976
  ];
15915
15977
  const SIDE_ANCHORS = ["middle-right", "middle-left"];
15978
+ const CORNER_ANCHOR_SET = new Set(CORNER_ANCHORS);
15979
+ const SIDE_ANCHOR_SET = new Set(SIDE_ANCHORS);
15916
15980
  /**
15917
15981
  * Drop-in replacement for `WeaveTextNode`. Same `nodeType` (`'text'`), so
15918
15982
  * existing documents load unchanged; register it INSTEAD of `WeaveTextNode`
@@ -16146,8 +16210,14 @@ var WeaveTextV2Node = class extends WeaveNode {
16146
16210
  y: p.y * sy
16147
16211
  };
16148
16212
  },
16213
+ selectAllOnStart: !create,
16149
16214
  caretColor: this.config.cursor.color,
16150
16215
  selectionColor: WEAVE_TEXT_V2_SELECTION_COLOR,
16216
+ ...this.config.edition.outline.enabled && {
16217
+ outlineColor: this.config.edition.outline.color,
16218
+ outlineWidth: this.config.edition.outline.width,
16219
+ outlinePadding: this.config.edition.outline.padding
16220
+ },
16151
16221
  signal: this.instance.getEventsController().signal,
16152
16222
  onChange: (blocks) => this.checkSize(shape, blocks),
16153
16223
  onRequestExit: () => this.exitEditMode(shape)
@@ -16213,8 +16283,8 @@ var WeaveTextV2Node = class extends WeaveNode {
16213
16283
  }
16214
16284
  setupTransform(text) {
16215
16285
  let actualAnchor = void 0;
16216
- const isSide = () => SIDE_ANCHORS.includes(actualAnchor ?? "");
16217
- const isCorner = () => CORNER_ANCHORS.includes(actualAnchor ?? "");
16286
+ const isSide = () => SIDE_ANCHOR_SET.has(actualAnchor ?? "");
16287
+ const isCorner = () => CORNER_ANCHOR_SET.has(actualAnchor ?? "");
16218
16288
  const autoWidthLayout = () => [TEXT_LAYOUT.AUTO_HEIGHT, TEXT_LAYOUT.SMART].includes(text.getAttrs().layout);
16219
16289
  text.on("transformstart", (e) => {
16220
16290
  const ctrlOrMeta = e.evt.ctrlKey || e.evt.metaKey;
@@ -16311,6 +16381,7 @@ var WeaveTextV2Node = class extends WeaveNode {
16311
16381
  serialize(instance) {
16312
16382
  const attrs = instance.getAttrs();
16313
16383
  const cleanedAttrs = { ...attrs };
16384
+ for (const key of Object.keys(cleanedAttrs)) if (typeof cleanedAttrs[key] === "function") delete cleanedAttrs[key];
16314
16385
  delete cleanedAttrs.mutexLocked;
16315
16386
  delete cleanedAttrs.mutexUserId;
16316
16387
  delete cleanedAttrs.draggable;