@codemirror/view 6.43.7 → 6.43.9

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,17 @@
1
+ ## 6.43.9 (2026-08-16)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where the scroll position would incorrectly be moved up when a document scrolled to the bottom would lose height.
6
+
7
+ ## 6.43.8 (2026-08-04)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix an issue that could cause identical `posAtCoords` queries to return different results.
12
+
13
+ Fix an issue where scrolling a position into view vertically on Android or iOS could cause horizontal scrolling motion.
14
+
1
15
  ## 6.43.7 (2026-07-27)
2
16
 
3
17
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -685,6 +685,22 @@ class DOMSelectionState {
685
685
  this.focusOffset = focusOffset;
686
686
  }
687
687
  }
688
+ function getScrollStack(target) {
689
+ let stack = [];
690
+ for (let cur = target; cur; cur = cur.nodeType == 11 ? cur.host : cur.parentNode) {
691
+ if (cur.nodeType == 1)
692
+ stack.push({ node: cur, left: cur.scrollLeft, top: cur.scrollTop });
693
+ }
694
+ return stack;
695
+ }
696
+ function restoreScrollStack(stack, vert = true) {
697
+ for (let { node, left, top } of stack) {
698
+ if (vert && node.scrollTop != top)
699
+ node.scrollTop = top;
700
+ if (node.scrollLeft != left)
701
+ node.scrollLeft = left;
702
+ }
703
+ }
688
704
  let preventScrollSupported = null;
689
705
  // Safari 26 breaks preventScroll support
690
706
  if (browser.safari && browser.safari_version >= 26)
@@ -696,12 +712,7 @@ function focusPreventScroll(dom) {
696
712
  return dom.setActive(); // in IE
697
713
  if (preventScrollSupported)
698
714
  return dom.focus(preventScrollSupported);
699
- let stack = [];
700
- for (let cur = dom; cur; cur = cur.parentNode) {
701
- stack.push(cur, cur.scrollTop, cur.scrollLeft);
702
- if (cur == cur.ownerDocument)
703
- break;
704
- }
715
+ let stack = getScrollStack(dom);
705
716
  dom.focus(preventScrollSupported == null ? {
706
717
  get preventScroll() {
707
718
  preventScrollSupported = { preventScroll: true };
@@ -710,13 +721,7 @@ function focusPreventScroll(dom) {
710
721
  } : undefined);
711
722
  if (!preventScrollSupported) {
712
723
  preventScrollSupported = false;
713
- for (let i = 0; i < stack.length;) {
714
- let elt = stack[i++], top = stack[i++], left = stack[i++];
715
- if (elt.scrollTop != top)
716
- elt.scrollTop = top;
717
- if (elt.scrollLeft != left)
718
- elt.scrollLeft = left;
719
- }
724
+ restoreScrollStack(stack);
720
725
  }
721
726
  }
722
727
  let scratchRange;
@@ -3462,11 +3467,14 @@ class DocView {
3462
3467
  // can affect it. So this tries to kludge around the problem by
3463
3468
  // calling scrollIntoView on the scroll target's line.
3464
3469
  if (window.visualViewport && window.innerHeight - window.visualViewport.height > 1 &&
3465
- (rect.top > window.pageYOffset + window.visualViewport.offsetTop + window.visualViewport.height ||
3466
- rect.bottom < window.pageYOffset + window.visualViewport.offsetTop)) {
3470
+ (rect.top > window.visualViewport.offsetTop + window.visualViewport.height ||
3471
+ rect.bottom < window.visualViewport.offsetTop)) {
3467
3472
  let line = this.view.docView.lineAt(range.head, 1);
3468
- if (line)
3473
+ if (line) {
3474
+ let stack = getScrollStack(line.dom);
3469
3475
  line.dom.scrollIntoView({ block: "nearest" });
3476
+ restoreScrollStack(stack, false);
3477
+ }
3470
3478
  }
3471
3479
  }
3472
3480
  lineHasWidget(pos) {
@@ -3888,23 +3896,22 @@ class InlineCoordsScan {
3888
3896
  search: while (lo < hi) {
3889
3897
  let dist = hi - lo, mid = (lo + hi) >> 1;
3890
3898
  adjust: if (seen.has(mid)) {
3891
- let scan = lo + Math.floor(Math.random() * dist);
3892
- for (let i = 0; i < dist; i++) {
3899
+ for (let i = 1; i < dist; i++) {
3900
+ let scan = mid + i;
3901
+ if (scan >= hi)
3902
+ scan -= dist;
3893
3903
  if (!seen.has(scan)) {
3894
3904
  mid = scan;
3895
3905
  break adjust;
3896
3906
  }
3897
- scan++;
3898
- if (scan == hi)
3899
- scan = lo; // Wrap around
3900
3907
  }
3901
3908
  break search; // No index found, we're done
3902
3909
  }
3903
3910
  seen.add(mid);
3904
- let rects = getRects(mid);
3911
+ let rects = getRects(mid), side = 0;
3905
3912
  if (rects)
3906
3913
  for (let i = 0; i < rects.length; i++) {
3907
- let rect = rects[i], side = 0;
3914
+ let rect = rects[i];
3908
3915
  // Ignore empty rectangles when there are other rectangles
3909
3916
  if (rect.width == 0 && rects.length > 1)
3910
3917
  continue;
@@ -3929,12 +3936,12 @@ class InlineCoordsScan {
3929
3936
  if (off)
3930
3937
  side = (off < 0) == (this.baseDir == exports.Direction.LTR) ? -1 : 1;
3931
3938
  }
3932
- // Narrow binary search when it is safe to do so
3933
- if (side == -1 && (!bidi || this.baseDirAt(positions[mid], 1)))
3934
- hi = mid;
3935
- else if (side == 1 && (!bidi || this.baseDirAt(positions[mid + 1], -1)))
3936
- lo = mid + 1;
3937
3939
  }
3940
+ // Narrow binary search when it is safe to do so
3941
+ if (side == -1 && (!bidi || this.baseDirAt(positions[mid], 1)))
3942
+ hi = mid;
3943
+ else if (side == 1 && (!bidi || this.baseDirAt(positions[mid + 1], -1)))
3944
+ lo = mid + 1;
3938
3945
  }
3939
3946
  // If no element with y overlap is found, find the nearest element
3940
3947
  // on the y axis, move this.y into it, and retry the scan.
@@ -8229,10 +8236,12 @@ class EditorView {
8229
8236
  (scroll == this.scrollDOM || this.hasFocus ||
8230
8237
  Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
8231
8238
  scrollOffset = scrollOffset + diff;
8232
- if (scroll)
8233
- scroll.scrollTop += diff;
8234
- else
8239
+ if (!scroll)
8235
8240
  this.win.scrollBy(0, diff);
8241
+ else if (scrollAnchorPos < 0)
8242
+ scroll.scrollTop = scroll.scrollHeight;
8243
+ else
8244
+ scroll.scrollTop += diff;
8236
8245
  scrollAnchorHeight = -1;
8237
8246
  continue;
8238
8247
  }
@@ -9597,7 +9606,8 @@ const selectionLayer = layer({
9597
9606
  },
9598
9607
  class: "cm-selectionLayer"
9599
9608
  });
9600
- const selectionBg = browser.gecko && browser.gecko_version >= 153 ? "#ffffff01" : "transparent";
9609
+ // https://discuss.codemirror.net/t/firefox-153-ignores-transparent-selection-styling/9838
9610
+ const selectionBg = browser.gecko && browser.gecko_version == 153 ? "#ffffff01" : "transparent";
9601
9611
  const hideNativeSelection = state.Prec.highest(EditorView.theme({
9602
9612
  ".cm-line": {
9603
9613
  "& ::selection, &::selection": { backgroundColor: `${selectionBg} !important` },
package/dist/index.js CHANGED
@@ -682,6 +682,22 @@ class DOMSelectionState {
682
682
  this.focusOffset = focusOffset;
683
683
  }
684
684
  }
685
+ function getScrollStack(target) {
686
+ let stack = [];
687
+ for (let cur = target; cur; cur = cur.nodeType == 11 ? cur.host : cur.parentNode) {
688
+ if (cur.nodeType == 1)
689
+ stack.push({ node: cur, left: cur.scrollLeft, top: cur.scrollTop });
690
+ }
691
+ return stack;
692
+ }
693
+ function restoreScrollStack(stack, vert = true) {
694
+ for (let { node, left, top } of stack) {
695
+ if (vert && node.scrollTop != top)
696
+ node.scrollTop = top;
697
+ if (node.scrollLeft != left)
698
+ node.scrollLeft = left;
699
+ }
700
+ }
685
701
  let preventScrollSupported = null;
686
702
  // Safari 26 breaks preventScroll support
687
703
  if (browser.safari && browser.safari_version >= 26)
@@ -693,12 +709,7 @@ function focusPreventScroll(dom) {
693
709
  return dom.setActive(); // in IE
694
710
  if (preventScrollSupported)
695
711
  return dom.focus(preventScrollSupported);
696
- let stack = [];
697
- for (let cur = dom; cur; cur = cur.parentNode) {
698
- stack.push(cur, cur.scrollTop, cur.scrollLeft);
699
- if (cur == cur.ownerDocument)
700
- break;
701
- }
712
+ let stack = getScrollStack(dom);
702
713
  dom.focus(preventScrollSupported == null ? {
703
714
  get preventScroll() {
704
715
  preventScrollSupported = { preventScroll: true };
@@ -707,13 +718,7 @@ function focusPreventScroll(dom) {
707
718
  } : undefined);
708
719
  if (!preventScrollSupported) {
709
720
  preventScrollSupported = false;
710
- for (let i = 0; i < stack.length;) {
711
- let elt = stack[i++], top = stack[i++], left = stack[i++];
712
- if (elt.scrollTop != top)
713
- elt.scrollTop = top;
714
- if (elt.scrollLeft != left)
715
- elt.scrollLeft = left;
716
- }
721
+ restoreScrollStack(stack);
717
722
  }
718
723
  }
719
724
  let scratchRange;
@@ -3458,11 +3463,14 @@ class DocView {
3458
3463
  // can affect it. So this tries to kludge around the problem by
3459
3464
  // calling scrollIntoView on the scroll target's line.
3460
3465
  if (window.visualViewport && window.innerHeight - window.visualViewport.height > 1 &&
3461
- (rect.top > window.pageYOffset + window.visualViewport.offsetTop + window.visualViewport.height ||
3462
- rect.bottom < window.pageYOffset + window.visualViewport.offsetTop)) {
3466
+ (rect.top > window.visualViewport.offsetTop + window.visualViewport.height ||
3467
+ rect.bottom < window.visualViewport.offsetTop)) {
3463
3468
  let line = this.view.docView.lineAt(range.head, 1);
3464
- if (line)
3469
+ if (line) {
3470
+ let stack = getScrollStack(line.dom);
3465
3471
  line.dom.scrollIntoView({ block: "nearest" });
3472
+ restoreScrollStack(stack, false);
3473
+ }
3466
3474
  }
3467
3475
  }
3468
3476
  lineHasWidget(pos) {
@@ -3884,23 +3892,22 @@ class InlineCoordsScan {
3884
3892
  search: while (lo < hi) {
3885
3893
  let dist = hi - lo, mid = (lo + hi) >> 1;
3886
3894
  adjust: if (seen.has(mid)) {
3887
- let scan = lo + Math.floor(Math.random() * dist);
3888
- for (let i = 0; i < dist; i++) {
3895
+ for (let i = 1; i < dist; i++) {
3896
+ let scan = mid + i;
3897
+ if (scan >= hi)
3898
+ scan -= dist;
3889
3899
  if (!seen.has(scan)) {
3890
3900
  mid = scan;
3891
3901
  break adjust;
3892
3902
  }
3893
- scan++;
3894
- if (scan == hi)
3895
- scan = lo; // Wrap around
3896
3903
  }
3897
3904
  break search; // No index found, we're done
3898
3905
  }
3899
3906
  seen.add(mid);
3900
- let rects = getRects(mid);
3907
+ let rects = getRects(mid), side = 0;
3901
3908
  if (rects)
3902
3909
  for (let i = 0; i < rects.length; i++) {
3903
- let rect = rects[i], side = 0;
3910
+ let rect = rects[i];
3904
3911
  // Ignore empty rectangles when there are other rectangles
3905
3912
  if (rect.width == 0 && rects.length > 1)
3906
3913
  continue;
@@ -3925,12 +3932,12 @@ class InlineCoordsScan {
3925
3932
  if (off)
3926
3933
  side = (off < 0) == (this.baseDir == Direction.LTR) ? -1 : 1;
3927
3934
  }
3928
- // Narrow binary search when it is safe to do so
3929
- if (side == -1 && (!bidi || this.baseDirAt(positions[mid], 1)))
3930
- hi = mid;
3931
- else if (side == 1 && (!bidi || this.baseDirAt(positions[mid + 1], -1)))
3932
- lo = mid + 1;
3933
3935
  }
3936
+ // Narrow binary search when it is safe to do so
3937
+ if (side == -1 && (!bidi || this.baseDirAt(positions[mid], 1)))
3938
+ hi = mid;
3939
+ else if (side == 1 && (!bidi || this.baseDirAt(positions[mid + 1], -1)))
3940
+ lo = mid + 1;
3934
3941
  }
3935
3942
  // If no element with y overlap is found, find the nearest element
3936
3943
  // on the y axis, move this.y into it, and retry the scan.
@@ -8224,10 +8231,12 @@ class EditorView {
8224
8231
  (scroll == this.scrollDOM || this.hasFocus ||
8225
8232
  Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
8226
8233
  scrollOffset = scrollOffset + diff;
8227
- if (scroll)
8228
- scroll.scrollTop += diff;
8229
- else
8234
+ if (!scroll)
8230
8235
  this.win.scrollBy(0, diff);
8236
+ else if (scrollAnchorPos < 0)
8237
+ scroll.scrollTop = scroll.scrollHeight;
8238
+ else
8239
+ scroll.scrollTop += diff;
8231
8240
  scrollAnchorHeight = -1;
8232
8241
  continue;
8233
8242
  }
@@ -9592,7 +9601,8 @@ const selectionLayer = /*@__PURE__*/layer({
9592
9601
  },
9593
9602
  class: "cm-selectionLayer"
9594
9603
  });
9595
- const selectionBg = browser.gecko && browser.gecko_version >= 153 ? "#ffffff01" : "transparent";
9604
+ // https://discuss.codemirror.net/t/firefox-153-ignores-transparent-selection-styling/9838
9605
+ const selectionBg = browser.gecko && browser.gecko_version == 153 ? "#ffffff01" : "transparent";
9596
9606
  const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme({
9597
9607
  ".cm-line": {
9598
9608
  "& ::selection, &::selection": { backgroundColor: `${selectionBg} !important` },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.43.7",
3
+ "version": "6.43.9",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",