@codemirror/view 6.39.3 → 6.39.4

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.39.4 (2025-12-12)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where paste events handlers on Chrome could fail to run when pasting on a blank line.
6
+
7
+ Fix a regression causing the native cursor to get stuck before block widgets with side>0.
8
+
9
+ Fix a crash in content DOM building after a block widget.
10
+
11
+ Fix a bug in `posAtCoords` that would in some circumstances make it return positions on the wrong side of a block widget.
12
+
1
13
  ## 6.39.3 (2025-12-11)
2
14
 
3
15
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1991,7 +1991,7 @@ class LineTile extends CompositeTile {
1991
1991
  if (this.dom.contains(tile.dom)) {
1992
1992
  if (tile.isText())
1993
1993
  return new DOMPos(tile.dom, Math.min(tile.dom.nodeValue.length, offset));
1994
- return tile.domPosFor(offset, side);
1994
+ return tile.domPosFor(offset, tile.flags & 16 /* TileFlag.Before */ ? 1 : tile.flags & 32 /* TileFlag.After */ ? -1 : side);
1995
1995
  }
1996
1996
  let parent = found.tile.parent, saw = false;
1997
1997
  for (let ch of parent.children) {
@@ -2623,12 +2623,14 @@ class TileUpdate {
2623
2623
  this.builder.addBlockWidget(widget);
2624
2624
  }
2625
2625
  else {
2626
+ this.builder.ensureLine(null);
2626
2627
  this.builder.addInlineWidget(widget, activeMarks, openMarks);
2627
2628
  openMarks = activeMarks.length;
2628
2629
  }
2629
2630
  }
2630
2631
  }
2631
2632
  else if (tile.isText()) {
2633
+ this.builder.ensureLine(null);
2632
2634
  if (!from && to == tile.length) {
2633
2635
  this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
2634
2636
  }
@@ -2647,6 +2649,7 @@ class TileUpdate {
2647
2649
  this.cache.add(tile);
2648
2650
  }
2649
2651
  else if (tile instanceof MarkTile) {
2652
+ this.builder.ensureLine(null);
2650
2653
  this.builder.addMark(tile, activeMarks, openMarks);
2651
2654
  this.cache.reused.set(tile, 1 /* Reused.Full */);
2652
2655
  openMarks = activeMarks.length;
@@ -3074,7 +3077,7 @@ class DocView {
3074
3077
  let { anchorNode, anchorOffset } = view.observer.selectionRange;
3075
3078
  if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
3076
3079
  return;
3077
- let line = this.lineAt(cursor.head);
3080
+ let line = this.lineAt(cursor.head, cursor.assoc);
3078
3081
  if (!line)
3079
3082
  return;
3080
3083
  let lineStart = line.posAtStart;
@@ -3153,7 +3156,7 @@ class DocView {
3153
3156
  let after, afterOff = -1, afterBad = false;
3154
3157
  this.tile.blockTiles((tile, off) => {
3155
3158
  if (tile.isWidget()) {
3156
- if ((tile.flags & 32 /* TileFlag.After */) && before)
3159
+ if ((tile.flags & 32 /* TileFlag.After */) && off >= pos)
3157
3160
  return true;
3158
3161
  if (tile.flags & 16 /* TileFlag.Before */)
3159
3162
  beforeBad = true;
@@ -3191,8 +3194,8 @@ class DocView {
3191
3194
  }
3192
3195
  return tile.coordsIn(offset, side);
3193
3196
  }
3194
- lineAt(pos) {
3195
- let { tile } = this.tile.resolveBlock(pos, 1);
3197
+ lineAt(pos, side) {
3198
+ let { tile } = this.tile.resolveBlock(pos, side);
3196
3199
  return tile.isLine() ? tile : null;
3197
3200
  }
3198
3201
  coordsForChar(pos) {
@@ -3738,7 +3741,10 @@ function posAtCoords(view, coords, precise, scanY) {
3738
3741
  if (block.type != exports.BlockType.Text)
3739
3742
  return yOffset < (block.top + block.bottom) / 2 ? new PosAssoc(block.from, 1) : new PosAssoc(block.to, -1);
3740
3743
  // Here we know we're in a line, so run the logic for inline layout
3741
- return posAtCoordsInline(view, view.docView.lineAt(block.from), block.from, x, y);
3744
+ let line = view.docView.lineAt(block.from, 2);
3745
+ if (!line || line.length != block.length)
3746
+ line = view.docView.lineAt(block.from, -2);
3747
+ return posAtCoordsInline(view, line, block.from, x, y);
3742
3748
  }
3743
3749
  // Scan through the rectangles for the content of a tile, finding the
3744
3750
  // one closest to the given coordinates, prefering closeness in Y over
@@ -4657,7 +4663,8 @@ function eventBelongsToEditor(view, event) {
4657
4663
  if (event.defaultPrevented)
4658
4664
  return false;
4659
4665
  for (let node = event.target, tile; node != view.contentDOM; node = node.parentNode)
4660
- if (!node || node.nodeType == 11 || ((tile = Tile.get(node)) && tile.isWidget() && tile.widget.ignoreEvent(event)))
4666
+ if (!node || node.nodeType == 11 ||
4667
+ ((tile = Tile.get(node)) && tile.isWidget() && !tile.isHidden && tile.widget.ignoreEvent(event)))
4661
4668
  return false;
4662
4669
  return true;
4663
4670
  }
@@ -4776,42 +4783,13 @@ function rangeForClick(view, pos, bias, type) {
4776
4783
  return groupAt(view.state, pos, bias);
4777
4784
  }
4778
4785
  else { // Triple click
4779
- let visual = view.docView.lineAt(pos), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
4786
+ let visual = view.docView.lineAt(pos, bias), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
4780
4787
  let from = visual ? visual.posAtStart : line.from, to = visual ? visual.posAtEnd : line.to;
4781
4788
  if (to < view.state.doc.length && to == line.to)
4782
4789
  to++;
4783
4790
  return state.EditorSelection.range(from, to);
4784
4791
  }
4785
4792
  }
4786
- let inside = (x, y, rect) => y >= rect.top && y <= rect.bottom && x >= rect.left && x <= rect.right;
4787
- // Try to determine, for the given coordinates, associated with the
4788
- // given position, whether they are related to the element before or
4789
- // the element after the position.
4790
- function findPositionSide(view, pos, x, y) {
4791
- let line = view.docView.lineAt(pos);
4792
- if (!line)
4793
- return 1;
4794
- let off = pos - line.posAtStart;
4795
- // Line boundaries point into the line
4796
- if (off == 0)
4797
- return 1;
4798
- if (off == line.length)
4799
- return -1;
4800
- // Positions on top of an element point at that element
4801
- let before = line.coordsIn(off, -1);
4802
- if (before && inside(x, y, before))
4803
- return -1;
4804
- let after = line.coordsIn(off, 1);
4805
- if (after && inside(x, y, after))
4806
- return 1;
4807
- // This is probably a line wrap point. Pick before if the point is
4808
- // above its bottom.
4809
- return before && before.bottom >= y ? -1 : 1;
4810
- }
4811
- function queryPos(view, event) {
4812
- let pos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4813
- return { pos, bias: findPositionSide(view, pos, event.clientX, event.clientY) };
4814
- }
4815
4793
  const BadMouseDetail = browser.ie && browser.ie_version <= 11;
4816
4794
  let lastMouseDown = null, lastMouseDownCount = 0, lastMouseDownTime = 0;
4817
4795
  function getClickType(event) {
@@ -4824,7 +4802,7 @@ function getClickType(event) {
4824
4802
  Math.abs(last.clientY - event.clientY) < 2) ? (lastMouseDownCount + 1) % 3 : 1;
4825
4803
  }
4826
4804
  function basicMouseSelection(view, event) {
4827
- let start = queryPos(view, event), type = getClickType(event);
4805
+ let start = view.posAndSideAtCoords({ x: event.clientX, y: event.clientY }, false), type = getClickType(event);
4828
4806
  let startSel = view.state.selection;
4829
4807
  return {
4830
4808
  update(update) {
@@ -4834,10 +4812,10 @@ function basicMouseSelection(view, event) {
4834
4812
  }
4835
4813
  },
4836
4814
  get(event, extend, multiple) {
4837
- let cur = queryPos(view, event), removed;
4838
- let range = rangeForClick(view, cur.pos, cur.bias, type);
4815
+ let cur = view.posAndSideAtCoords({ x: event.clientX, y: event.clientY }, false), removed;
4816
+ let range = rangeForClick(view, cur.pos, cur.assoc, type);
4839
4817
  if (start.pos != cur.pos && !extend) {
4840
- let startRange = rangeForClick(view, start.pos, start.bias, type);
4818
+ let startRange = rangeForClick(view, start.pos, start.assoc, type);
4841
4819
  let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
4842
4820
  range = from < range.from ? state.EditorSelection.range(from, to) : state.EditorSelection.range(to, from);
4843
4821
  }
package/dist/index.js CHANGED
@@ -1987,7 +1987,7 @@ class LineTile extends CompositeTile {
1987
1987
  if (this.dom.contains(tile.dom)) {
1988
1988
  if (tile.isText())
1989
1989
  return new DOMPos(tile.dom, Math.min(tile.dom.nodeValue.length, offset));
1990
- return tile.domPosFor(offset, side);
1990
+ return tile.domPosFor(offset, tile.flags & 16 /* TileFlag.Before */ ? 1 : tile.flags & 32 /* TileFlag.After */ ? -1 : side);
1991
1991
  }
1992
1992
  let parent = found.tile.parent, saw = false;
1993
1993
  for (let ch of parent.children) {
@@ -2619,12 +2619,14 @@ class TileUpdate {
2619
2619
  this.builder.addBlockWidget(widget);
2620
2620
  }
2621
2621
  else {
2622
+ this.builder.ensureLine(null);
2622
2623
  this.builder.addInlineWidget(widget, activeMarks, openMarks);
2623
2624
  openMarks = activeMarks.length;
2624
2625
  }
2625
2626
  }
2626
2627
  }
2627
2628
  else if (tile.isText()) {
2629
+ this.builder.ensureLine(null);
2628
2630
  if (!from && to == tile.length) {
2629
2631
  this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
2630
2632
  }
@@ -2643,6 +2645,7 @@ class TileUpdate {
2643
2645
  this.cache.add(tile);
2644
2646
  }
2645
2647
  else if (tile instanceof MarkTile) {
2648
+ this.builder.ensureLine(null);
2646
2649
  this.builder.addMark(tile, activeMarks, openMarks);
2647
2650
  this.cache.reused.set(tile, 1 /* Reused.Full */);
2648
2651
  openMarks = activeMarks.length;
@@ -3070,7 +3073,7 @@ class DocView {
3070
3073
  let { anchorNode, anchorOffset } = view.observer.selectionRange;
3071
3074
  if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
3072
3075
  return;
3073
- let line = this.lineAt(cursor.head);
3076
+ let line = this.lineAt(cursor.head, cursor.assoc);
3074
3077
  if (!line)
3075
3078
  return;
3076
3079
  let lineStart = line.posAtStart;
@@ -3149,7 +3152,7 @@ class DocView {
3149
3152
  let after, afterOff = -1, afterBad = false;
3150
3153
  this.tile.blockTiles((tile, off) => {
3151
3154
  if (tile.isWidget()) {
3152
- if ((tile.flags & 32 /* TileFlag.After */) && before)
3155
+ if ((tile.flags & 32 /* TileFlag.After */) && off >= pos)
3153
3156
  return true;
3154
3157
  if (tile.flags & 16 /* TileFlag.Before */)
3155
3158
  beforeBad = true;
@@ -3187,8 +3190,8 @@ class DocView {
3187
3190
  }
3188
3191
  return tile.coordsIn(offset, side);
3189
3192
  }
3190
- lineAt(pos) {
3191
- let { tile } = this.tile.resolveBlock(pos, 1);
3193
+ lineAt(pos, side) {
3194
+ let { tile } = this.tile.resolveBlock(pos, side);
3192
3195
  return tile.isLine() ? tile : null;
3193
3196
  }
3194
3197
  coordsForChar(pos) {
@@ -3734,7 +3737,10 @@ function posAtCoords(view, coords, precise, scanY) {
3734
3737
  if (block.type != BlockType.Text)
3735
3738
  return yOffset < (block.top + block.bottom) / 2 ? new PosAssoc(block.from, 1) : new PosAssoc(block.to, -1);
3736
3739
  // Here we know we're in a line, so run the logic for inline layout
3737
- return posAtCoordsInline(view, view.docView.lineAt(block.from), block.from, x, y);
3740
+ let line = view.docView.lineAt(block.from, 2);
3741
+ if (!line || line.length != block.length)
3742
+ line = view.docView.lineAt(block.from, -2);
3743
+ return posAtCoordsInline(view, line, block.from, x, y);
3738
3744
  }
3739
3745
  // Scan through the rectangles for the content of a tile, finding the
3740
3746
  // one closest to the given coordinates, prefering closeness in Y over
@@ -4653,7 +4659,8 @@ function eventBelongsToEditor(view, event) {
4653
4659
  if (event.defaultPrevented)
4654
4660
  return false;
4655
4661
  for (let node = event.target, tile; node != view.contentDOM; node = node.parentNode)
4656
- if (!node || node.nodeType == 11 || ((tile = Tile.get(node)) && tile.isWidget() && tile.widget.ignoreEvent(event)))
4662
+ if (!node || node.nodeType == 11 ||
4663
+ ((tile = Tile.get(node)) && tile.isWidget() && !tile.isHidden && tile.widget.ignoreEvent(event)))
4657
4664
  return false;
4658
4665
  return true;
4659
4666
  }
@@ -4772,42 +4779,13 @@ function rangeForClick(view, pos, bias, type) {
4772
4779
  return groupAt(view.state, pos, bias);
4773
4780
  }
4774
4781
  else { // Triple click
4775
- let visual = view.docView.lineAt(pos), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
4782
+ let visual = view.docView.lineAt(pos, bias), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
4776
4783
  let from = visual ? visual.posAtStart : line.from, to = visual ? visual.posAtEnd : line.to;
4777
4784
  if (to < view.state.doc.length && to == line.to)
4778
4785
  to++;
4779
4786
  return EditorSelection.range(from, to);
4780
4787
  }
4781
4788
  }
4782
- let inside = (x, y, rect) => y >= rect.top && y <= rect.bottom && x >= rect.left && x <= rect.right;
4783
- // Try to determine, for the given coordinates, associated with the
4784
- // given position, whether they are related to the element before or
4785
- // the element after the position.
4786
- function findPositionSide(view, pos, x, y) {
4787
- let line = view.docView.lineAt(pos);
4788
- if (!line)
4789
- return 1;
4790
- let off = pos - line.posAtStart;
4791
- // Line boundaries point into the line
4792
- if (off == 0)
4793
- return 1;
4794
- if (off == line.length)
4795
- return -1;
4796
- // Positions on top of an element point at that element
4797
- let before = line.coordsIn(off, -1);
4798
- if (before && inside(x, y, before))
4799
- return -1;
4800
- let after = line.coordsIn(off, 1);
4801
- if (after && inside(x, y, after))
4802
- return 1;
4803
- // This is probably a line wrap point. Pick before if the point is
4804
- // above its bottom.
4805
- return before && before.bottom >= y ? -1 : 1;
4806
- }
4807
- function queryPos(view, event) {
4808
- let pos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4809
- return { pos, bias: findPositionSide(view, pos, event.clientX, event.clientY) };
4810
- }
4811
4789
  const BadMouseDetail = browser.ie && browser.ie_version <= 11;
4812
4790
  let lastMouseDown = null, lastMouseDownCount = 0, lastMouseDownTime = 0;
4813
4791
  function getClickType(event) {
@@ -4820,7 +4798,7 @@ function getClickType(event) {
4820
4798
  Math.abs(last.clientY - event.clientY) < 2) ? (lastMouseDownCount + 1) % 3 : 1;
4821
4799
  }
4822
4800
  function basicMouseSelection(view, event) {
4823
- let start = queryPos(view, event), type = getClickType(event);
4801
+ let start = view.posAndSideAtCoords({ x: event.clientX, y: event.clientY }, false), type = getClickType(event);
4824
4802
  let startSel = view.state.selection;
4825
4803
  return {
4826
4804
  update(update) {
@@ -4830,10 +4808,10 @@ function basicMouseSelection(view, event) {
4830
4808
  }
4831
4809
  },
4832
4810
  get(event, extend, multiple) {
4833
- let cur = queryPos(view, event), removed;
4834
- let range = rangeForClick(view, cur.pos, cur.bias, type);
4811
+ let cur = view.posAndSideAtCoords({ x: event.clientX, y: event.clientY }, false), removed;
4812
+ let range = rangeForClick(view, cur.pos, cur.assoc, type);
4835
4813
  if (start.pos != cur.pos && !extend) {
4836
- let startRange = rangeForClick(view, start.pos, start.bias, type);
4814
+ let startRange = rangeForClick(view, start.pos, start.assoc, type);
4837
4815
  let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
4838
4816
  range = from < range.from ? EditorSelection.range(from, to) : EditorSelection.range(to, from);
4839
4817
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.39.3",
3
+ "version": "6.39.4",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",