@codemirror/view 6.43.4 → 6.43.6

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,21 @@
1
+ ## 6.43.6 (2026-07-06)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix another tile tree corruption bug that could occur on zero-length content updates.
6
+
7
+ ## 6.43.5 (2026-07-04)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix an issue what would cause `domAtPos` to return positions on the wrong side of widgets.
12
+
13
+ Fix an issue that could cause the DOM cursor to be drawn on the wrong side of a collapsing decoration.
14
+
15
+ Don't return off-by-one coordinate positions at the edge of right-to-left text node in Safari.
16
+
17
+ Don't abort momentum scrolling on iOS by stabilizing the scroll position.
18
+
1
19
  ## 6.43.4 (2026-06-27)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -514,8 +514,11 @@ function scanFor(node, off, targetNode, targetOff, dir) {
514
514
  function maxOffset(node) {
515
515
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
516
516
  }
517
- function flattenRect(rect, left) {
518
- let x = left ? rect.left : rect.right;
517
+ function flattenRect(rect, toLeft) {
518
+ let { left, right } = rect;
519
+ if (left == right)
520
+ return rect;
521
+ let x = toLeft ? left : right;
519
522
  return { left: x, right: x, top: rect.top, bottom: rect.bottom };
520
523
  }
521
524
  function windowRect(win) {
@@ -1780,7 +1783,7 @@ class Tile {
1780
1783
  return this.posBefore(tile) + tile.length;
1781
1784
  }
1782
1785
  covers(side) { return true; }
1783
- coordsIn(pos, side) { return null; }
1786
+ coordsIn(pos, side, rtl) { return null; }
1784
1787
  domPosFor(off, side) {
1785
1788
  let index = domIndex(this.dom);
1786
1789
  let after = this.length ? off > 0 : side > 0;
@@ -1976,7 +1979,7 @@ class LineTile extends CompositeTile {
1976
1979
  if (child.isComposite()) {
1977
1980
  scan(child, pos - off);
1978
1981
  }
1979
- else if ((!after || after.isHidden && (side > 0 || forCoords && onSameLine(after, child))) &&
1982
+ else if ((!after || after.isHidden && (side > 0 && !(after.flags & 32 /* TileFlag.After */) || forCoords && onSameLine(after, child))) &&
1980
1983
  (end > pos || (child.flags & 32 /* TileFlag.After */))) {
1981
1984
  after = child;
1982
1985
  afterOff = pos - off;
@@ -1993,11 +1996,11 @@ class LineTile extends CompositeTile {
1993
1996
  let target = ((side < 0 ? before : after) || before || after);
1994
1997
  return target ? { tile: target, offset: target == before ? beforeOff : afterOff } : null;
1995
1998
  }
1996
- coordsIn(pos, side) {
1999
+ coordsIn(pos, side, rtl) {
1997
2000
  let found = this.resolveInline(pos, side, true);
1998
2001
  if (!found)
1999
2002
  return fallbackRect(this);
2000
- return found.tile.coordsIn(Math.max(0, found.offset), side);
2003
+ return found.tile.coordsIn(Math.max(0, found.offset), side, rtl);
2001
2004
  }
2002
2005
  domIn(pos, side) {
2003
2006
  let found = this.resolveInline(pos, side);
@@ -2061,7 +2064,7 @@ class TextTile extends Tile {
2061
2064
  }
2062
2065
  isText() { return true; }
2063
2066
  toString() { return JSON.stringify(this.text); }
2064
- coordsIn(pos, side) {
2067
+ coordsIn(pos, side, rtl) {
2065
2068
  let length = this.dom.nodeValue.length;
2066
2069
  if (pos > length)
2067
2070
  pos = length;
@@ -2071,7 +2074,7 @@ class TextTile extends Tile {
2071
2074
  if (pos) {
2072
2075
  from--;
2073
2076
  flatten = 1;
2074
- } // FIXME this is wrong in RTL text
2077
+ }
2075
2078
  else if (to < length) {
2076
2079
  to++;
2077
2080
  flatten = -1;
@@ -2090,7 +2093,7 @@ class TextTile extends Tile {
2090
2093
  let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
2091
2094
  if (browser.safari && !flatten && rect.width == 0)
2092
2095
  rect = Array.prototype.find.call(rects, r => r.width) || rect;
2093
- return flatten ? flattenRect(rect, flatten < 0) : rect || null;
2096
+ return rtl == null ? rect : flattenRect(rect, (flatten ? flatten > 0 : side < 0) == rtl);
2094
2097
  }
2095
2098
  static of(text, dom) {
2096
2099
  let tile = new TextTile(dom || document.createTextNode(text), text);
@@ -2166,7 +2169,10 @@ class WidgetBufferTile extends Tile {
2166
2169
  }
2167
2170
  get isHidden() { return true; }
2168
2171
  get overrideDOMText() { return state.Text.empty; }
2169
- coordsIn(pos) { return this.dom.getBoundingClientRect(); }
2172
+ coordsIn(pos, side, rtl) {
2173
+ let rect = this.dom.getBoundingClientRect();
2174
+ return rtl == null ? rect : flattenRect(rect, (side > 0) == rtl);
2175
+ }
2170
2176
  }
2171
2177
  // Represents a position in the tile tree.
2172
2178
  class TilePointer {
@@ -2528,8 +2534,8 @@ class TileCache {
2528
2534
  find(cls, test, type = 2 /* Reused.DOM */) {
2529
2535
  let i = cls.bucket;
2530
2536
  let bucket = this.buckets[i], off = this.index[i];
2531
- for (let j = bucket.length - 1; j >= 0; j--) {
2532
- // Look at the most recently added items first (last-in, first-out)
2537
+ for (let j = 0; j < bucket.length; j++) {
2538
+ // Look at the most oldest items first (first-in, first-out)
2533
2539
  let index = (j + off) % bucket.length, tile = bucket[index];
2534
2540
  if ((!test || test(tile)) && !this.reused.has(tile)) {
2535
2541
  bucket.splice(index, 1);
@@ -2732,7 +2738,7 @@ class TileUpdate {
2732
2738
  }
2733
2739
  emit(from, to) {
2734
2740
  let pendingLineAttrs = null;
2735
- let b = this.builder, markCount = 0;
2741
+ let b = this.builder, markCount = -1;
2736
2742
  let openEnd = state.RangeSet.spans(this.decorations, from, to, {
2737
2743
  point: (from, to, deco, active, openStart, index) => {
2738
2744
  if (deco instanceof PointDecoration) {
@@ -2786,7 +2792,8 @@ class TileUpdate {
2786
2792
  markCount = active.length;
2787
2793
  }
2788
2794
  });
2789
- this.openWidget = openEnd > markCount;
2795
+ if (markCount > -1)
2796
+ this.openWidget = openEnd > markCount;
2790
2797
  if (!this.openWidget)
2791
2798
  b.addLineStartIfNotCovered(pendingLineAttrs);
2792
2799
  this.openMarks = openEnd;
@@ -3195,7 +3202,7 @@ class DocView {
3195
3202
  domAtPos(pos, side) {
3196
3203
  let { tile, offset } = this.tile.resolveBlock(pos, side);
3197
3204
  if (tile.isWidget())
3198
- return tile.domPosFor(pos, side);
3205
+ return tile.domPosFor(offset, side);
3199
3206
  return tile.domIn(offset, side);
3200
3207
  }
3201
3208
  inlineDOMNearPos(pos, side) {
@@ -3232,14 +3239,16 @@ class DocView {
3232
3239
  after = null;
3233
3240
  return before && side < 0 || !after ? before.domIn(beforeOff, side) : after.domIn(afterOff, side);
3234
3241
  }
3235
- coordsAt(pos, side) {
3242
+ // Get the coord of the element at the given side of the given
3243
+ // position. If rtl is given, flatten it using that text direction.
3244
+ coordsAt(pos, side, rtl) {
3236
3245
  let { tile, offset } = this.tile.resolveBlock(pos, side);
3237
3246
  if (tile.isWidget()) {
3238
3247
  if (tile.widget instanceof BlockGapWidget)
3239
3248
  return null;
3240
3249
  return tile.coordsInWidget(offset, side, true);
3241
3250
  }
3242
- return tile.coordsIn(offset, side);
3251
+ return tile.coordsIn(offset, side, rtl);
3243
3252
  }
3244
3253
  lineAt(pos, side) {
3245
3254
  let { tile } = this.tile.resolveBlock(pos, side);
@@ -3413,7 +3422,6 @@ class DocView {
3413
3422
  this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
3414
3423
  }
3415
3424
  scrollIntoView(target) {
3416
- var _a;
3417
3425
  if (target.isSnapshot) {
3418
3426
  let ref = this.view.viewState.lineBlockAt(target.range.head);
3419
3427
  this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
@@ -3430,7 +3438,7 @@ class DocView {
3430
3438
  }
3431
3439
  }
3432
3440
  let { range } = target;
3433
- let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
3441
+ let rect = this.coordsAt(range.head, range.assoc || (range.head > range.anchor ? -1 : 1)), other;
3434
3442
  if (!rect)
3435
3443
  return;
3436
3444
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
@@ -4472,6 +4480,7 @@ class InputState {
4472
4480
  this.view = view;
4473
4481
  this.lastKeyCode = 0;
4474
4482
  this.lastKeyTime = 0;
4483
+ this.touchActive = false;
4475
4484
  this.lastTouchTime = 0;
4476
4485
  this.lastTouchX = 0;
4477
4486
  this.lastTouchY = 0;
@@ -4483,6 +4492,10 @@ class InputState {
4483
4492
  // (after which we retroactively handle them and reset the DOM) to
4484
4493
  // avoid messing up the virtual keyboard state.
4485
4494
  this.pendingIOSKey = undefined;
4495
+ // Set to a time stap by scroll events when touch isn't active on
4496
+ // iOS, to work around an issue where Safari will abort the scroll
4497
+ // momentum if we set scrollTop
4498
+ this.lastIOSMomentumScroll = 0;
4486
4499
  // When enabled (>-1), tab presses are not given to key handlers,
4487
4500
  // leaving the browser's default behavior. If >0, the mode expires
4488
4501
  // at that timestamp, and any other keypress clears it.
@@ -4913,8 +4926,11 @@ function doPaste(view, input) {
4913
4926
  });
4914
4927
  }
4915
4928
  observers.scroll = view => {
4916
- view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4917
- view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4929
+ let iState = view.inputState;
4930
+ iState.lastScrollTop = view.scrollDOM.scrollTop;
4931
+ iState.lastScrollLeft = view.scrollDOM.scrollLeft;
4932
+ if (browser.ios && !iState.touchActive)
4933
+ iState.lastIOSMomentumScroll = Date.now();
4918
4934
  };
4919
4935
  observers.wheel = observers.mousewheel = view => {
4920
4936
  view.inputState.lastWheelEvent = Date.now();
@@ -4927,6 +4943,7 @@ handlers.keydown = (view, event) => {
4927
4943
  };
4928
4944
  observers.touchstart = (view, e) => {
4929
4945
  let iState = view.inputState, touch = e.targetTouches[0];
4946
+ iState.touchActive = true;
4930
4947
  iState.lastTouchTime = Date.now();
4931
4948
  if (touch) {
4932
4949
  iState.lastTouchX = touch.clientX;
@@ -4937,6 +4954,9 @@ observers.touchstart = (view, e) => {
4937
4954
  observers.touchmove = view => {
4938
4955
  view.inputState.setSelectionOrigin("select.pointer");
4939
4956
  };
4957
+ observers.touchend = (view, e) => {
4958
+ view.inputState.touchActive = false;
4959
+ };
4940
4960
  handlers.mousedown = (view, event) => {
4941
4961
  view.observer.flush();
4942
4962
  if (view.inputState.lastTouchTime > Date.now() - 2000)
@@ -8199,6 +8219,7 @@ class EditorView {
8199
8219
  this.viewState.lineBlockAt(scrollAnchorPos).top;
8200
8220
  let diff = (newAnchorHeight - scrollAnchorHeight) / this.scaleY;
8201
8221
  if ((diff > 1 || diff < -1) &&
8222
+ !(browser.ios && this.inputState.lastIOSMomentumScroll > Date.now() - 100) &&
8202
8223
  (scroll == this.scrollDOM || this.hasFocus ||
8203
8224
  Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
8204
8225
  scrollOffset = scrollOffset + diff;
@@ -8490,12 +8511,9 @@ class EditorView {
8490
8511
  */
8491
8512
  coordsAtPos(pos, side = 1) {
8492
8513
  this.readMeasured();
8493
- let rect = this.docView.coordsAt(pos, side);
8494
- if (!rect || rect.left == rect.right)
8495
- return rect;
8496
8514
  let line = this.state.doc.lineAt(pos), order = this.bidiSpans(line);
8497
8515
  let span = order[BidiSpan.find(order, pos - line.from, -1, side)];
8498
- return flattenRect(rect, (span.dir == exports.Direction.LTR) == (side > 0));
8516
+ return this.docView.coordsAt(pos, side, span.dir == exports.Direction.RTL);
8499
8517
  }
8500
8518
  /**
8501
8519
  Return the rectangle around a given character. If `pos` does not
package/dist/index.js CHANGED
@@ -511,8 +511,11 @@ function scanFor(node, off, targetNode, targetOff, dir) {
511
511
  function maxOffset(node) {
512
512
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
513
513
  }
514
- function flattenRect(rect, left) {
515
- let x = left ? rect.left : rect.right;
514
+ function flattenRect(rect, toLeft) {
515
+ let { left, right } = rect;
516
+ if (left == right)
517
+ return rect;
518
+ let x = toLeft ? left : right;
516
519
  return { left: x, right: x, top: rect.top, bottom: rect.bottom };
517
520
  }
518
521
  function windowRect(win) {
@@ -1776,7 +1779,7 @@ class Tile {
1776
1779
  return this.posBefore(tile) + tile.length;
1777
1780
  }
1778
1781
  covers(side) { return true; }
1779
- coordsIn(pos, side) { return null; }
1782
+ coordsIn(pos, side, rtl) { return null; }
1780
1783
  domPosFor(off, side) {
1781
1784
  let index = domIndex(this.dom);
1782
1785
  let after = this.length ? off > 0 : side > 0;
@@ -1972,7 +1975,7 @@ class LineTile extends CompositeTile {
1972
1975
  if (child.isComposite()) {
1973
1976
  scan(child, pos - off);
1974
1977
  }
1975
- else if ((!after || after.isHidden && (side > 0 || forCoords && onSameLine(after, child))) &&
1978
+ else if ((!after || after.isHidden && (side > 0 && !(after.flags & 32 /* TileFlag.After */) || forCoords && onSameLine(after, child))) &&
1976
1979
  (end > pos || (child.flags & 32 /* TileFlag.After */))) {
1977
1980
  after = child;
1978
1981
  afterOff = pos - off;
@@ -1989,11 +1992,11 @@ class LineTile extends CompositeTile {
1989
1992
  let target = ((side < 0 ? before : after) || before || after);
1990
1993
  return target ? { tile: target, offset: target == before ? beforeOff : afterOff } : null;
1991
1994
  }
1992
- coordsIn(pos, side) {
1995
+ coordsIn(pos, side, rtl) {
1993
1996
  let found = this.resolveInline(pos, side, true);
1994
1997
  if (!found)
1995
1998
  return fallbackRect(this);
1996
- return found.tile.coordsIn(Math.max(0, found.offset), side);
1999
+ return found.tile.coordsIn(Math.max(0, found.offset), side, rtl);
1997
2000
  }
1998
2001
  domIn(pos, side) {
1999
2002
  let found = this.resolveInline(pos, side);
@@ -2057,7 +2060,7 @@ class TextTile extends Tile {
2057
2060
  }
2058
2061
  isText() { return true; }
2059
2062
  toString() { return JSON.stringify(this.text); }
2060
- coordsIn(pos, side) {
2063
+ coordsIn(pos, side, rtl) {
2061
2064
  let length = this.dom.nodeValue.length;
2062
2065
  if (pos > length)
2063
2066
  pos = length;
@@ -2067,7 +2070,7 @@ class TextTile extends Tile {
2067
2070
  if (pos) {
2068
2071
  from--;
2069
2072
  flatten = 1;
2070
- } // FIXME this is wrong in RTL text
2073
+ }
2071
2074
  else if (to < length) {
2072
2075
  to++;
2073
2076
  flatten = -1;
@@ -2086,7 +2089,7 @@ class TextTile extends Tile {
2086
2089
  let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
2087
2090
  if (browser.safari && !flatten && rect.width == 0)
2088
2091
  rect = Array.prototype.find.call(rects, r => r.width) || rect;
2089
- return flatten ? flattenRect(rect, flatten < 0) : rect || null;
2092
+ return rtl == null ? rect : flattenRect(rect, (flatten ? flatten > 0 : side < 0) == rtl);
2090
2093
  }
2091
2094
  static of(text, dom) {
2092
2095
  let tile = new TextTile(dom || document.createTextNode(text), text);
@@ -2162,7 +2165,10 @@ class WidgetBufferTile extends Tile {
2162
2165
  }
2163
2166
  get isHidden() { return true; }
2164
2167
  get overrideDOMText() { return Text.empty; }
2165
- coordsIn(pos) { return this.dom.getBoundingClientRect(); }
2168
+ coordsIn(pos, side, rtl) {
2169
+ let rect = this.dom.getBoundingClientRect();
2170
+ return rtl == null ? rect : flattenRect(rect, (side > 0) == rtl);
2171
+ }
2166
2172
  }
2167
2173
  // Represents a position in the tile tree.
2168
2174
  class TilePointer {
@@ -2524,8 +2530,8 @@ class TileCache {
2524
2530
  find(cls, test, type = 2 /* Reused.DOM */) {
2525
2531
  let i = cls.bucket;
2526
2532
  let bucket = this.buckets[i], off = this.index[i];
2527
- for (let j = bucket.length - 1; j >= 0; j--) {
2528
- // Look at the most recently added items first (last-in, first-out)
2533
+ for (let j = 0; j < bucket.length; j++) {
2534
+ // Look at the most oldest items first (first-in, first-out)
2529
2535
  let index = (j + off) % bucket.length, tile = bucket[index];
2530
2536
  if ((!test || test(tile)) && !this.reused.has(tile)) {
2531
2537
  bucket.splice(index, 1);
@@ -2728,7 +2734,7 @@ class TileUpdate {
2728
2734
  }
2729
2735
  emit(from, to) {
2730
2736
  let pendingLineAttrs = null;
2731
- let b = this.builder, markCount = 0;
2737
+ let b = this.builder, markCount = -1;
2732
2738
  let openEnd = RangeSet.spans(this.decorations, from, to, {
2733
2739
  point: (from, to, deco, active, openStart, index) => {
2734
2740
  if (deco instanceof PointDecoration) {
@@ -2782,7 +2788,8 @@ class TileUpdate {
2782
2788
  markCount = active.length;
2783
2789
  }
2784
2790
  });
2785
- this.openWidget = openEnd > markCount;
2791
+ if (markCount > -1)
2792
+ this.openWidget = openEnd > markCount;
2786
2793
  if (!this.openWidget)
2787
2794
  b.addLineStartIfNotCovered(pendingLineAttrs);
2788
2795
  this.openMarks = openEnd;
@@ -3191,7 +3198,7 @@ class DocView {
3191
3198
  domAtPos(pos, side) {
3192
3199
  let { tile, offset } = this.tile.resolveBlock(pos, side);
3193
3200
  if (tile.isWidget())
3194
- return tile.domPosFor(pos, side);
3201
+ return tile.domPosFor(offset, side);
3195
3202
  return tile.domIn(offset, side);
3196
3203
  }
3197
3204
  inlineDOMNearPos(pos, side) {
@@ -3228,14 +3235,16 @@ class DocView {
3228
3235
  after = null;
3229
3236
  return before && side < 0 || !after ? before.domIn(beforeOff, side) : after.domIn(afterOff, side);
3230
3237
  }
3231
- coordsAt(pos, side) {
3238
+ // Get the coord of the element at the given side of the given
3239
+ // position. If rtl is given, flatten it using that text direction.
3240
+ coordsAt(pos, side, rtl) {
3232
3241
  let { tile, offset } = this.tile.resolveBlock(pos, side);
3233
3242
  if (tile.isWidget()) {
3234
3243
  if (tile.widget instanceof BlockGapWidget)
3235
3244
  return null;
3236
3245
  return tile.coordsInWidget(offset, side, true);
3237
3246
  }
3238
- return tile.coordsIn(offset, side);
3247
+ return tile.coordsIn(offset, side, rtl);
3239
3248
  }
3240
3249
  lineAt(pos, side) {
3241
3250
  let { tile } = this.tile.resolveBlock(pos, side);
@@ -3409,7 +3418,6 @@ class DocView {
3409
3418
  this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
3410
3419
  }
3411
3420
  scrollIntoView(target) {
3412
- var _a;
3413
3421
  if (target.isSnapshot) {
3414
3422
  let ref = this.view.viewState.lineBlockAt(target.range.head);
3415
3423
  this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
@@ -3426,7 +3434,7 @@ class DocView {
3426
3434
  }
3427
3435
  }
3428
3436
  let { range } = target;
3429
- let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
3437
+ let rect = this.coordsAt(range.head, range.assoc || (range.head > range.anchor ? -1 : 1)), other;
3430
3438
  if (!rect)
3431
3439
  return;
3432
3440
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
@@ -4468,6 +4476,7 @@ class InputState {
4468
4476
  this.view = view;
4469
4477
  this.lastKeyCode = 0;
4470
4478
  this.lastKeyTime = 0;
4479
+ this.touchActive = false;
4471
4480
  this.lastTouchTime = 0;
4472
4481
  this.lastTouchX = 0;
4473
4482
  this.lastTouchY = 0;
@@ -4479,6 +4488,10 @@ class InputState {
4479
4488
  // (after which we retroactively handle them and reset the DOM) to
4480
4489
  // avoid messing up the virtual keyboard state.
4481
4490
  this.pendingIOSKey = undefined;
4491
+ // Set to a time stap by scroll events when touch isn't active on
4492
+ // iOS, to work around an issue where Safari will abort the scroll
4493
+ // momentum if we set scrollTop
4494
+ this.lastIOSMomentumScroll = 0;
4482
4495
  // When enabled (>-1), tab presses are not given to key handlers,
4483
4496
  // leaving the browser's default behavior. If >0, the mode expires
4484
4497
  // at that timestamp, and any other keypress clears it.
@@ -4909,8 +4922,11 @@ function doPaste(view, input) {
4909
4922
  });
4910
4923
  }
4911
4924
  observers.scroll = view => {
4912
- view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4913
- view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4925
+ let iState = view.inputState;
4926
+ iState.lastScrollTop = view.scrollDOM.scrollTop;
4927
+ iState.lastScrollLeft = view.scrollDOM.scrollLeft;
4928
+ if (browser.ios && !iState.touchActive)
4929
+ iState.lastIOSMomentumScroll = Date.now();
4914
4930
  };
4915
4931
  observers.wheel = observers.mousewheel = view => {
4916
4932
  view.inputState.lastWheelEvent = Date.now();
@@ -4923,6 +4939,7 @@ handlers.keydown = (view, event) => {
4923
4939
  };
4924
4940
  observers.touchstart = (view, e) => {
4925
4941
  let iState = view.inputState, touch = e.targetTouches[0];
4942
+ iState.touchActive = true;
4926
4943
  iState.lastTouchTime = Date.now();
4927
4944
  if (touch) {
4928
4945
  iState.lastTouchX = touch.clientX;
@@ -4933,6 +4950,9 @@ observers.touchstart = (view, e) => {
4933
4950
  observers.touchmove = view => {
4934
4951
  view.inputState.setSelectionOrigin("select.pointer");
4935
4952
  };
4953
+ observers.touchend = (view, e) => {
4954
+ view.inputState.touchActive = false;
4955
+ };
4936
4956
  handlers.mousedown = (view, event) => {
4937
4957
  view.observer.flush();
4938
4958
  if (view.inputState.lastTouchTime > Date.now() - 2000)
@@ -8194,6 +8214,7 @@ class EditorView {
8194
8214
  this.viewState.lineBlockAt(scrollAnchorPos).top;
8195
8215
  let diff = (newAnchorHeight - scrollAnchorHeight) / this.scaleY;
8196
8216
  if ((diff > 1 || diff < -1) &&
8217
+ !(browser.ios && this.inputState.lastIOSMomentumScroll > Date.now() - 100) &&
8197
8218
  (scroll == this.scrollDOM || this.hasFocus ||
8198
8219
  Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
8199
8220
  scrollOffset = scrollOffset + diff;
@@ -8485,12 +8506,9 @@ class EditorView {
8485
8506
  */
8486
8507
  coordsAtPos(pos, side = 1) {
8487
8508
  this.readMeasured();
8488
- let rect = this.docView.coordsAt(pos, side);
8489
- if (!rect || rect.left == rect.right)
8490
- return rect;
8491
8509
  let line = this.state.doc.lineAt(pos), order = this.bidiSpans(line);
8492
8510
  let span = order[BidiSpan.find(order, pos - line.from, -1, side)];
8493
- return flattenRect(rect, (span.dir == Direction.LTR) == (side > 0));
8511
+ return this.docView.coordsAt(pos, side, span.dir == Direction.RTL);
8494
8512
  }
8495
8513
  /**
8496
8514
  Return the rectangle around a given character. If `pos` does not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.43.4",
3
+ "version": "6.43.6",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",