@codemirror/view 6.38.2 → 6.38.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 6.38.3 (2025-09-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Work around a rendering bug in Mobile Safari by completely hiding empty layers.
6
+
7
+ Fix vertical cursor motion in Chrome around decorations with bottom borders or margins.
8
+
9
+ Fix an issue that caused mark decorations longer than 512 characters to needlessly be split.
10
+
11
+ Move the cursor out of atomic ranges when text input happens.
12
+
1
13
  ## 6.38.2 (2025-09-01)
2
14
 
3
15
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1786,13 +1786,14 @@ class ContentBuilder {
1786
1786
  this.textOff = 0;
1787
1787
  }
1788
1788
  }
1789
- let take = Math.min(this.text.length - this.textOff, length, 512 /* T.Chunk */);
1789
+ let remaining = Math.min(this.text.length - this.textOff, length);
1790
+ let take = Math.min(remaining, 512 /* T.Chunk */);
1790
1791
  this.flushBuffer(active.slice(active.length - openStart));
1791
1792
  this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1792
1793
  this.atCursorPos = true;
1793
1794
  this.textOff += take;
1794
1795
  length -= take;
1795
- openStart = 0;
1796
+ openStart = remaining <= take ? 0 : active.length;
1796
1797
  }
1797
1798
  }
1798
1799
  span(from, to, active, openStart) {
@@ -3607,14 +3608,13 @@ function posAtCoords(view, coords, precise, bias = -1) {
3607
3608
  }
3608
3609
  else if (doc.caretRangeFromPoint) {
3609
3610
  let range = doc.caretRangeFromPoint(x, y);
3610
- if (range) {
3611
+ if (range)
3611
3612
  ({ startContainer: node, startOffset: offset } = range);
3612
- if (!view.contentDOM.contains(node) ||
3613
- browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3614
- browser.chrome && isSuspiciousChromeCaretResult(node, offset, x))
3615
- node = undefined;
3616
- }
3617
3613
  }
3614
+ if (node && (!view.contentDOM.contains(node) ||
3615
+ browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3616
+ browser.chrome && isSuspiciousChromeCaretResult(node, offset, x)))
3617
+ node = undefined;
3618
3618
  // Chrome will return offsets into <input> elements without child
3619
3619
  // nodes, which will lead to a null deref below, so clip the
3620
3620
  // offset to the node size.
@@ -3650,11 +3650,7 @@ function posAtCoordsImprecise(view, contentRect, block, x, y) {
3650
3650
  let content = view.state.sliceDoc(block.from, block.to);
3651
3651
  return block.from + state.findColumn(content, into, view.state.tabSize);
3652
3652
  }
3653
- // In case of a high line height, Safari's caretRangeFromPoint treats
3654
- // the space between lines as belonging to the last character of the
3655
- // line before. This is used to detect such a result so that it can be
3656
- // ignored (issue #401).
3657
- function isSuspiciousSafariCaretResult(node, offset, x) {
3653
+ function isEndOfLineBefore(node, offset, x) {
3658
3654
  let len, scan = node;
3659
3655
  if (node.nodeType != 3 || offset != (len = node.nodeValue.length))
3660
3656
  return false;
@@ -3674,10 +3670,17 @@ function isSuspiciousSafariCaretResult(node, offset, x) {
3674
3670
  }
3675
3671
  return textRange(node, len - 1, len).getBoundingClientRect().right > x;
3676
3672
  }
3673
+ // In case of a high line height, Safari's caretRangeFromPoint treats
3674
+ // the space between lines as belonging to the last character of the
3675
+ // line before. This is used to detect such a result so that it can be
3676
+ // ignored (issue #401).
3677
+ function isSuspiciousSafariCaretResult(node, offset, x) {
3678
+ return isEndOfLineBefore(node, offset, x);
3679
+ }
3677
3680
  // Chrome will move positions between lines to the start of the next line
3678
3681
  function isSuspiciousChromeCaretResult(node, offset, x) {
3679
3682
  if (offset != 0)
3680
- return false;
3683
+ return isEndOfLineBefore(node, offset, x);
3681
3684
  for (let cur = node;;) {
3682
3685
  let parent = cur.parentNode;
3683
3686
  if (!parent || parent.nodeType != 1 || parent.firstChild != cur)
@@ -4103,8 +4106,20 @@ function applyDOMChangeInner(view, change, newSel, lastKey = -1) {
4103
4106
  return true;
4104
4107
  }
4105
4108
  function applyDefaultInsert(view, change, newSel) {
4106
- let tr, startState = view.state, sel = startState.selection.main;
4107
- if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
4109
+ let tr, startState = view.state, sel = startState.selection.main, inAtomic = -1;
4110
+ if (change.from == change.to && change.from < sel.from || change.from > sel.to) {
4111
+ let side = change.from < sel.from ? -1 : 1, pos = side < 0 ? sel.from : sel.to;
4112
+ let moved = skipAtomicRanges(startState.facet(atomicRanges).map(f => f(view)), pos, side);
4113
+ if (change.from == moved)
4114
+ inAtomic = moved;
4115
+ }
4116
+ if (inAtomic > -1) {
4117
+ tr = {
4118
+ changes: change,
4119
+ selection: state.EditorSelection.cursor(change.from + change.insert.length, -1)
4120
+ };
4121
+ }
4122
+ else if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
4108
4123
  (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
4109
4124
  view.inputState.composing < 0) {
4110
4125
  let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
@@ -9083,6 +9098,8 @@ class LayerView {
9083
9098
  old = next;
9084
9099
  }
9085
9100
  this.drawn = markers;
9101
+ if (browser.ios) // Issue #1600
9102
+ this.dom.style.display = this.dom.firstChild ? "" : "none";
9086
9103
  }
9087
9104
  }
9088
9105
  destroy() {
package/dist/index.js CHANGED
@@ -1783,13 +1783,14 @@ class ContentBuilder {
1783
1783
  this.textOff = 0;
1784
1784
  }
1785
1785
  }
1786
- let take = Math.min(this.text.length - this.textOff, length, 512 /* T.Chunk */);
1786
+ let remaining = Math.min(this.text.length - this.textOff, length);
1787
+ let take = Math.min(remaining, 512 /* T.Chunk */);
1787
1788
  this.flushBuffer(active.slice(active.length - openStart));
1788
1789
  this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1789
1790
  this.atCursorPos = true;
1790
1791
  this.textOff += take;
1791
1792
  length -= take;
1792
- openStart = 0;
1793
+ openStart = remaining <= take ? 0 : active.length;
1793
1794
  }
1794
1795
  }
1795
1796
  span(from, to, active, openStart) {
@@ -3603,14 +3604,13 @@ function posAtCoords(view, coords, precise, bias = -1) {
3603
3604
  }
3604
3605
  else if (doc.caretRangeFromPoint) {
3605
3606
  let range = doc.caretRangeFromPoint(x, y);
3606
- if (range) {
3607
+ if (range)
3607
3608
  ({ startContainer: node, startOffset: offset } = range);
3608
- if (!view.contentDOM.contains(node) ||
3609
- browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3610
- browser.chrome && isSuspiciousChromeCaretResult(node, offset, x))
3611
- node = undefined;
3612
- }
3613
3609
  }
3610
+ if (node && (!view.contentDOM.contains(node) ||
3611
+ browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
3612
+ browser.chrome && isSuspiciousChromeCaretResult(node, offset, x)))
3613
+ node = undefined;
3614
3614
  // Chrome will return offsets into <input> elements without child
3615
3615
  // nodes, which will lead to a null deref below, so clip the
3616
3616
  // offset to the node size.
@@ -3646,11 +3646,7 @@ function posAtCoordsImprecise(view, contentRect, block, x, y) {
3646
3646
  let content = view.state.sliceDoc(block.from, block.to);
3647
3647
  return block.from + findColumn(content, into, view.state.tabSize);
3648
3648
  }
3649
- // In case of a high line height, Safari's caretRangeFromPoint treats
3650
- // the space between lines as belonging to the last character of the
3651
- // line before. This is used to detect such a result so that it can be
3652
- // ignored (issue #401).
3653
- function isSuspiciousSafariCaretResult(node, offset, x) {
3649
+ function isEndOfLineBefore(node, offset, x) {
3654
3650
  let len, scan = node;
3655
3651
  if (node.nodeType != 3 || offset != (len = node.nodeValue.length))
3656
3652
  return false;
@@ -3670,10 +3666,17 @@ function isSuspiciousSafariCaretResult(node, offset, x) {
3670
3666
  }
3671
3667
  return textRange(node, len - 1, len).getBoundingClientRect().right > x;
3672
3668
  }
3669
+ // In case of a high line height, Safari's caretRangeFromPoint treats
3670
+ // the space between lines as belonging to the last character of the
3671
+ // line before. This is used to detect such a result so that it can be
3672
+ // ignored (issue #401).
3673
+ function isSuspiciousSafariCaretResult(node, offset, x) {
3674
+ return isEndOfLineBefore(node, offset, x);
3675
+ }
3673
3676
  // Chrome will move positions between lines to the start of the next line
3674
3677
  function isSuspiciousChromeCaretResult(node, offset, x) {
3675
3678
  if (offset != 0)
3676
- return false;
3679
+ return isEndOfLineBefore(node, offset, x);
3677
3680
  for (let cur = node;;) {
3678
3681
  let parent = cur.parentNode;
3679
3682
  if (!parent || parent.nodeType != 1 || parent.firstChild != cur)
@@ -4099,8 +4102,20 @@ function applyDOMChangeInner(view, change, newSel, lastKey = -1) {
4099
4102
  return true;
4100
4103
  }
4101
4104
  function applyDefaultInsert(view, change, newSel) {
4102
- let tr, startState = view.state, sel = startState.selection.main;
4103
- if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
4105
+ let tr, startState = view.state, sel = startState.selection.main, inAtomic = -1;
4106
+ if (change.from == change.to && change.from < sel.from || change.from > sel.to) {
4107
+ let side = change.from < sel.from ? -1 : 1, pos = side < 0 ? sel.from : sel.to;
4108
+ let moved = skipAtomicRanges(startState.facet(atomicRanges).map(f => f(view)), pos, side);
4109
+ if (change.from == moved)
4110
+ inAtomic = moved;
4111
+ }
4112
+ if (inAtomic > -1) {
4113
+ tr = {
4114
+ changes: change,
4115
+ selection: EditorSelection.cursor(change.from + change.insert.length, -1)
4116
+ };
4117
+ }
4118
+ else if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
4104
4119
  (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
4105
4120
  view.inputState.composing < 0) {
4106
4121
  let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
@@ -9078,6 +9093,8 @@ class LayerView {
9078
9093
  old = next;
9079
9094
  }
9080
9095
  this.drawn = markers;
9096
+ if (browser.ios) // Issue #1600
9097
+ this.dom.style.display = this.dom.firstChild ? "" : "none";
9081
9098
  }
9082
9099
  }
9083
9100
  destroy() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.38.2",
3
+ "version": "6.38.3",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",