@codemirror/view 6.36.4 → 6.36.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.36.6 (2025-04-24)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where `drawSelection` would draw selections starting at a block widget not at a line break in an odd way.
6
+
7
+ Fix an issue where the editor would inappropriately scroll when editing near the bottom of the document with line wrapping enabled, in some cases.
8
+
9
+ Fix an issue that caused unnecessary transactions on focus change.
10
+
11
+ ## 6.36.5 (2025-03-29)
12
+
13
+ ### Bug fixes
14
+
15
+ Fix an issue where some browsers wouldn't enable context menu paste when clicking on placeholder text.
16
+
17
+ Fix an issue where cursor height would unnecessarily be based on a placeholder node's dimensions, and thus be off from the text height.
18
+
1
19
  ## 6.36.4 (2025-03-03)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1072,7 +1072,7 @@ function coordsInChildren(view, pos, side) {
1072
1072
  if (child.children.length) {
1073
1073
  scan(child, pos - off);
1074
1074
  }
1075
- else if ((!after || after.isHidden && side > 0) &&
1075
+ else if ((!after || after.isHidden && (side > 0 || onSameLine(after, child))) &&
1076
1076
  (end > pos || off == end && child.getSide() > 0)) {
1077
1077
  after = child;
1078
1078
  afterPos = pos - off;
@@ -1098,6 +1098,10 @@ function fallbackRect(view) {
1098
1098
  let rects = clientRectsFor(last);
1099
1099
  return rects[rects.length - 1] || null;
1100
1100
  }
1101
+ function onSameLine(a, b) {
1102
+ let posA = a.coordsAt(0, 1), posB = b.coordsAt(0, 1);
1103
+ return posA && posB && posB.top < posA.bottom;
1104
+ }
1101
1105
 
1102
1106
  function combineAttrs(source, target) {
1103
1107
  for (let name in source) {
@@ -3656,17 +3660,26 @@ function isSuspiciousChromeCaretResult(node, offset, x) {
3656
3660
  : textRange(node, 0, Math.max(node.nodeValue.length, 1)).getBoundingClientRect();
3657
3661
  return x - rect.left > 5;
3658
3662
  }
3659
- function blockAt(view, pos) {
3663
+ function blockAt(view, pos, side) {
3660
3664
  let line = view.lineBlockAt(pos);
3661
- if (Array.isArray(line.type))
3665
+ if (Array.isArray(line.type)) {
3666
+ let best;
3662
3667
  for (let l of line.type) {
3663
- if (l.to > pos || l.to == pos && (l.to == line.to || l.type == exports.BlockType.Text))
3668
+ if (l.from > pos)
3669
+ break;
3670
+ if (l.to < pos)
3671
+ continue;
3672
+ if (l.from < pos && l.to > pos)
3664
3673
  return l;
3674
+ if (!best || (l.type == exports.BlockType.Text && (best.type != l.type || (side < 0 ? l.from < pos : l.to > pos))))
3675
+ best = l;
3665
3676
  }
3677
+ return best || line;
3678
+ }
3666
3679
  return line;
3667
3680
  }
3668
3681
  function moveToLineBoundary(view, start, forward, includeWrap) {
3669
- let line = blockAt(view, start.head);
3682
+ let line = blockAt(view, start.head, start.assoc || -1);
3670
3683
  let coords = !includeWrap || line.type != exports.BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null
3671
3684
  : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3672
3685
  if (coords) {
@@ -4901,7 +4914,7 @@ function focusChangeTransaction(state, focus) {
4901
4914
  if (effect)
4902
4915
  effects.push(effect);
4903
4916
  }
4904
- return effects ? state.update({ effects, annotations: isFocusChange.of(true) }) : null;
4917
+ return effects.length ? state.update({ effects, annotations: isFocusChange.of(true) }) : null;
4905
4918
  }
4906
4919
  function updateForFocusChange(view) {
4907
4920
  setTimeout(() => {
@@ -5937,7 +5950,7 @@ class ViewState {
5937
5950
  }
5938
5951
  else {
5939
5952
  this.scrollAnchorPos = -1;
5940
- this.scrollAnchorHeight = this.heightMap.height;
5953
+ this.scrollAnchorHeight = prevHeight;
5941
5954
  }
5942
5955
  let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
5943
5956
  if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
@@ -6619,6 +6632,7 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
6619
6632
  color: "#888",
6620
6633
  display: "inline-block",
6621
6634
  verticalAlign: "top",
6635
+ userSelect: "none"
6622
6636
  },
6623
6637
  ".cm-highlightSpace": {
6624
6638
  backgroundImage: "radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)",
@@ -8866,7 +8880,7 @@ function rectanglesForRange(view, className, range) {
8866
8880
  let leftSide = contentRect.left +
8867
8881
  (lineStyle ? parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent)) : 0);
8868
8882
  let rightSide = contentRect.right - (lineStyle ? parseInt(lineStyle.paddingRight) : 0);
8869
- let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
8883
+ let startBlock = blockAt(view, from, 1), endBlock = blockAt(view, to, -1);
8870
8884
  let visualStart = startBlock.type == exports.BlockType.Text ? startBlock : null;
8871
8885
  let visualEnd = endBlock.type == exports.BlockType.Text ? endBlock : null;
8872
8886
  if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks))
@@ -9033,7 +9047,6 @@ function layer(config) {
9033
9047
  ];
9034
9048
  }
9035
9049
 
9036
- const CanHidePrimary = !(browser.ios && browser.webkit && browser.webkit_version < 534);
9037
9050
  const selectionConfig = state.Facet.define({
9038
9051
  combine(configs) {
9039
9052
  return state.combineConfig(configs, {
@@ -9090,7 +9103,7 @@ const cursorLayer = layer({
9090
9103
  let cursors = [];
9091
9104
  for (let r of state$1.selection.ranges) {
9092
9105
  let prim = r == state$1.selection.main;
9093
- if (r.empty ? !prim || CanHidePrimary : conf.drawRangeCursor) {
9106
+ if (r.empty || conf.drawRangeCursor) {
9094
9107
  let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
9095
9108
  let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
9096
9109
  for (let piece of RectangleMarker.forRange(view, className, cursor))
@@ -9126,11 +9139,13 @@ const selectionLayer = layer({
9126
9139
  },
9127
9140
  class: "cm-selectionLayer"
9128
9141
  });
9129
- const themeSpec = {
9142
+ const hideNativeSelection = state.Prec.highest(EditorView.theme({
9130
9143
  ".cm-line": {
9131
9144
  "& ::selection, &::selection": { backgroundColor: "transparent !important" },
9145
+ caretColor: "transparent !important"
9132
9146
  },
9133
9147
  ".cm-content": {
9148
+ caretColor: "transparent !important",
9134
9149
  "& :focus": {
9135
9150
  caretColor: "initial !important",
9136
9151
  "&::selection, & ::selection": {
@@ -9138,10 +9153,7 @@ const themeSpec = {
9138
9153
  }
9139
9154
  }
9140
9155
  }
9141
- };
9142
- if (CanHidePrimary)
9143
- themeSpec[".cm-line"].caretColor = themeSpec[".cm-content"].caretColor = "transparent !important";
9144
- const hideNativeSelection = state.Prec.highest(EditorView.theme(themeSpec));
9156
+ }));
9145
9157
 
9146
9158
  const setDropCursorPos = state.StateEffect.define({
9147
9159
  map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); }
package/dist/index.js CHANGED
@@ -1070,7 +1070,7 @@ function coordsInChildren(view, pos, side) {
1070
1070
  if (child.children.length) {
1071
1071
  scan(child, pos - off);
1072
1072
  }
1073
- else if ((!after || after.isHidden && side > 0) &&
1073
+ else if ((!after || after.isHidden && (side > 0 || onSameLine(after, child))) &&
1074
1074
  (end > pos || off == end && child.getSide() > 0)) {
1075
1075
  after = child;
1076
1076
  afterPos = pos - off;
@@ -1096,6 +1096,10 @@ function fallbackRect(view) {
1096
1096
  let rects = clientRectsFor(last);
1097
1097
  return rects[rects.length - 1] || null;
1098
1098
  }
1099
+ function onSameLine(a, b) {
1100
+ let posA = a.coordsAt(0, 1), posB = b.coordsAt(0, 1);
1101
+ return posA && posB && posB.top < posA.bottom;
1102
+ }
1099
1103
 
1100
1104
  function combineAttrs(source, target) {
1101
1105
  for (let name in source) {
@@ -3652,17 +3656,26 @@ function isSuspiciousChromeCaretResult(node, offset, x) {
3652
3656
  : textRange(node, 0, Math.max(node.nodeValue.length, 1)).getBoundingClientRect();
3653
3657
  return x - rect.left > 5;
3654
3658
  }
3655
- function blockAt(view, pos) {
3659
+ function blockAt(view, pos, side) {
3656
3660
  let line = view.lineBlockAt(pos);
3657
- if (Array.isArray(line.type))
3661
+ if (Array.isArray(line.type)) {
3662
+ let best;
3658
3663
  for (let l of line.type) {
3659
- if (l.to > pos || l.to == pos && (l.to == line.to || l.type == BlockType.Text))
3664
+ if (l.from > pos)
3665
+ break;
3666
+ if (l.to < pos)
3667
+ continue;
3668
+ if (l.from < pos && l.to > pos)
3660
3669
  return l;
3670
+ if (!best || (l.type == BlockType.Text && (best.type != l.type || (side < 0 ? l.from < pos : l.to > pos))))
3671
+ best = l;
3661
3672
  }
3673
+ return best || line;
3674
+ }
3662
3675
  return line;
3663
3676
  }
3664
3677
  function moveToLineBoundary(view, start, forward, includeWrap) {
3665
- let line = blockAt(view, start.head);
3678
+ let line = blockAt(view, start.head, start.assoc || -1);
3666
3679
  let coords = !includeWrap || line.type != BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null
3667
3680
  : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3668
3681
  if (coords) {
@@ -4897,7 +4910,7 @@ function focusChangeTransaction(state, focus) {
4897
4910
  if (effect)
4898
4911
  effects.push(effect);
4899
4912
  }
4900
- return effects ? state.update({ effects, annotations: isFocusChange.of(true) }) : null;
4913
+ return effects.length ? state.update({ effects, annotations: isFocusChange.of(true) }) : null;
4901
4914
  }
4902
4915
  function updateForFocusChange(view) {
4903
4916
  setTimeout(() => {
@@ -5932,7 +5945,7 @@ class ViewState {
5932
5945
  }
5933
5946
  else {
5934
5947
  this.scrollAnchorPos = -1;
5935
- this.scrollAnchorHeight = this.heightMap.height;
5948
+ this.scrollAnchorHeight = prevHeight;
5936
5949
  }
5937
5950
  let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
5938
5951
  if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
@@ -6614,6 +6627,7 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
6614
6627
  color: "#888",
6615
6628
  display: "inline-block",
6616
6629
  verticalAlign: "top",
6630
+ userSelect: "none"
6617
6631
  },
6618
6632
  ".cm-highlightSpace": {
6619
6633
  backgroundImage: "radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)",
@@ -8861,7 +8875,7 @@ function rectanglesForRange(view, className, range) {
8861
8875
  let leftSide = contentRect.left +
8862
8876
  (lineStyle ? parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent)) : 0);
8863
8877
  let rightSide = contentRect.right - (lineStyle ? parseInt(lineStyle.paddingRight) : 0);
8864
- let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
8878
+ let startBlock = blockAt(view, from, 1), endBlock = blockAt(view, to, -1);
8865
8879
  let visualStart = startBlock.type == BlockType.Text ? startBlock : null;
8866
8880
  let visualEnd = endBlock.type == BlockType.Text ? endBlock : null;
8867
8881
  if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks))
@@ -9028,7 +9042,6 @@ function layer(config) {
9028
9042
  ];
9029
9043
  }
9030
9044
 
9031
- const CanHidePrimary = !(browser.ios && browser.webkit && browser.webkit_version < 534);
9032
9045
  const selectionConfig = /*@__PURE__*/Facet.define({
9033
9046
  combine(configs) {
9034
9047
  return combineConfig(configs, {
@@ -9085,7 +9098,7 @@ const cursorLayer = /*@__PURE__*/layer({
9085
9098
  let cursors = [];
9086
9099
  for (let r of state.selection.ranges) {
9087
9100
  let prim = r == state.selection.main;
9088
- if (r.empty ? !prim || CanHidePrimary : conf.drawRangeCursor) {
9101
+ if (r.empty || conf.drawRangeCursor) {
9089
9102
  let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
9090
9103
  let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
9091
9104
  for (let piece of RectangleMarker.forRange(view, className, cursor))
@@ -9121,11 +9134,13 @@ const selectionLayer = /*@__PURE__*/layer({
9121
9134
  },
9122
9135
  class: "cm-selectionLayer"
9123
9136
  });
9124
- const themeSpec = {
9137
+ const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme({
9125
9138
  ".cm-line": {
9126
9139
  "& ::selection, &::selection": { backgroundColor: "transparent !important" },
9140
+ caretColor: "transparent !important"
9127
9141
  },
9128
9142
  ".cm-content": {
9143
+ caretColor: "transparent !important",
9129
9144
  "& :focus": {
9130
9145
  caretColor: "initial !important",
9131
9146
  "&::selection, & ::selection": {
@@ -9133,10 +9148,7 @@ const themeSpec = {
9133
9148
  }
9134
9149
  }
9135
9150
  }
9136
- };
9137
- if (CanHidePrimary)
9138
- themeSpec[".cm-line"].caretColor = themeSpec[".cm-content"].caretColor = "transparent !important";
9139
- const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
9151
+ }));
9140
9152
 
9141
9153
  const setDropCursorPos = /*@__PURE__*/StateEffect.define({
9142
9154
  map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.36.4",
3
+ "version": "6.36.6",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",