@codemirror/view 6.39.16 → 6.39.17
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 +10 -0
- package/dist/index.cjs +62 -13
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +62 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 6.39.17 (2026-03-10)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Improve touch tap-selection on line wrapping boundaries.
|
|
6
|
+
|
|
7
|
+
Make `drawSelection` draw our own selection handles on iOS.
|
|
8
|
+
|
|
9
|
+
Fix an issue where `posAtCoords`, when querying line wrapping points, got confused by extra empty client rectangles produced by Safari.
|
|
10
|
+
|
|
1
11
|
## 6.39.16 (2026-03-02)
|
|
2
12
|
|
|
3
13
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -3863,6 +3863,9 @@ class InlineCoordsScan {
|
|
|
3863
3863
|
if (rects)
|
|
3864
3864
|
for (let i = 0; i < rects.length; i++) {
|
|
3865
3865
|
let rect = rects[i], side = 0;
|
|
3866
|
+
// Ignore empty rectangles when there are other rectangles
|
|
3867
|
+
if (rect.width == 0 && rects.length > 1)
|
|
3868
|
+
continue;
|
|
3866
3869
|
if (rect.bottom < this.y) {
|
|
3867
3870
|
if (!above || above.bottom < rect.bottom)
|
|
3868
3871
|
above = rect;
|
|
@@ -4086,7 +4089,7 @@ class DOMChange {
|
|
|
4086
4089
|
this.bounds = null;
|
|
4087
4090
|
this.text = "";
|
|
4088
4091
|
this.domChanged = start > -1;
|
|
4089
|
-
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
|
4092
|
+
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView, curSel = view.state.selection;
|
|
4090
4093
|
if (view.state.readOnly && start > -1) {
|
|
4091
4094
|
// Ignore changes when the editor is read-only
|
|
4092
4095
|
this.newSel = null;
|
|
@@ -4102,18 +4105,18 @@ class DOMChange {
|
|
|
4102
4105
|
let domSel = view.observer.selectionRange;
|
|
4103
4106
|
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
|
|
4104
4107
|
!contains(view.contentDOM, domSel.focusNode)
|
|
4105
|
-
?
|
|
4108
|
+
? curSel.main.head
|
|
4106
4109
|
: view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
|
|
4107
4110
|
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
|
|
4108
4111
|
!contains(view.contentDOM, domSel.anchorNode)
|
|
4109
|
-
?
|
|
4112
|
+
? curSel.main.anchor
|
|
4110
4113
|
: view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
|
|
4111
4114
|
// iOS will refuse to select the block gaps when doing
|
|
4112
4115
|
// select-all.
|
|
4113
4116
|
// Chrome will put the selection *inside* them, confusing
|
|
4114
4117
|
// posFromDOM
|
|
4115
4118
|
let vp = view.viewport;
|
|
4116
|
-
if ((browser.ios || browser.chrome) &&
|
|
4119
|
+
if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
|
|
4117
4120
|
(vp.from > 0 || vp.to < view.state.doc.length)) {
|
|
4118
4121
|
let from = Math.min(head, anchor), to = Math.max(head, anchor);
|
|
4119
4122
|
let offFrom = vp.from - from, offTo = vp.to - to;
|
|
@@ -4122,10 +4125,22 @@ class DOMChange {
|
|
|
4122
4125
|
anchor = view.state.doc.length;
|
|
4123
4126
|
}
|
|
4124
4127
|
}
|
|
4125
|
-
if (view.inputState.composing > -1 &&
|
|
4126
|
-
this.newSel =
|
|
4127
|
-
|
|
4128
|
+
if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
|
|
4129
|
+
this.newSel = curSel.replaceRange(state.EditorSelection.range(anchor, head));
|
|
4130
|
+
}
|
|
4131
|
+
else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
|
|
4132
|
+
view.inputState.lastTouchTime > Date.now() - 100) {
|
|
4133
|
+
// If this is a cursor selection change in a line-wrapping
|
|
4134
|
+
// editor that may have been a touch, use the last touch
|
|
4135
|
+
// position to assign a side to the cursor.
|
|
4136
|
+
let before = view.coordsAtPos(head, -1), assoc = 0;
|
|
4137
|
+
if (before)
|
|
4138
|
+
assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1;
|
|
4139
|
+
this.newSel = state.EditorSelection.create([state.EditorSelection.cursor(head, assoc)]);
|
|
4140
|
+
}
|
|
4141
|
+
else {
|
|
4128
4142
|
this.newSel = state.EditorSelection.single(anchor, head);
|
|
4143
|
+
}
|
|
4129
4144
|
}
|
|
4130
4145
|
}
|
|
4131
4146
|
}
|
|
@@ -4412,6 +4427,8 @@ class InputState {
|
|
|
4412
4427
|
this.lastKeyCode = 0;
|
|
4413
4428
|
this.lastKeyTime = 0;
|
|
4414
4429
|
this.lastTouchTime = 0;
|
|
4430
|
+
this.lastTouchX = 0;
|
|
4431
|
+
this.lastTouchY = 0;
|
|
4415
4432
|
this.lastFocusTime = 0;
|
|
4416
4433
|
this.lastScrollTop = 0;
|
|
4417
4434
|
this.lastScrollLeft = 0;
|
|
@@ -4855,8 +4872,13 @@ handlers.keydown = (view, event) => {
|
|
|
4855
4872
|
return false;
|
|
4856
4873
|
};
|
|
4857
4874
|
observers.touchstart = (view, e) => {
|
|
4858
|
-
view.inputState
|
|
4859
|
-
|
|
4875
|
+
let iState = view.inputState, touch = e.targetTouches[0];
|
|
4876
|
+
iState.lastTouchTime = Date.now();
|
|
4877
|
+
if (touch) {
|
|
4878
|
+
iState.lastTouchX = touch.clientX;
|
|
4879
|
+
iState.lastTouchY = touch.clientY;
|
|
4880
|
+
}
|
|
4881
|
+
iState.setSelectionOrigin("select.pointer");
|
|
4860
4882
|
};
|
|
4861
4883
|
observers.touchmove = view => {
|
|
4862
4884
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
@@ -6798,6 +6820,21 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
|
|
|
6798
6820
|
"&dark .cm-cursor": {
|
|
6799
6821
|
borderLeftColor: "#ddd"
|
|
6800
6822
|
},
|
|
6823
|
+
".cm-selectionHandle": {
|
|
6824
|
+
backgroundColor: "currentColor",
|
|
6825
|
+
width: "1.5px"
|
|
6826
|
+
},
|
|
6827
|
+
".cm-selectionHandle-start::before, .cm-selectionHandle-end::before": {
|
|
6828
|
+
content: '""',
|
|
6829
|
+
backgroundColor: "inherit",
|
|
6830
|
+
borderRadius: "50%",
|
|
6831
|
+
width: "8px",
|
|
6832
|
+
height: "8px",
|
|
6833
|
+
position: "absolute",
|
|
6834
|
+
left: "-3.25px"
|
|
6835
|
+
},
|
|
6836
|
+
".cm-selectionHandle-start::before": { top: "-8px" },
|
|
6837
|
+
".cm-selectionHandle-end::before": { bottom: "-8px" },
|
|
6801
6838
|
".cm-dropCursor": {
|
|
6802
6839
|
position: "absolute"
|
|
6803
6840
|
},
|
|
@@ -9362,7 +9399,8 @@ const selectionConfig = state.Facet.define({
|
|
|
9362
9399
|
combine(configs) {
|
|
9363
9400
|
return state.combineConfig(configs, {
|
|
9364
9401
|
cursorBlinkRate: 1200,
|
|
9365
|
-
drawRangeCursor: true
|
|
9402
|
+
drawRangeCursor: true,
|
|
9403
|
+
iosSelectionHandles: true
|
|
9366
9404
|
}, {
|
|
9367
9405
|
cursorBlinkRate: (a, b) => Math.min(a, b),
|
|
9368
9406
|
drawRangeCursor: (a, b) => a || b
|
|
@@ -9414,7 +9452,7 @@ const cursorLayer = layer({
|
|
|
9414
9452
|
let cursors = [];
|
|
9415
9453
|
for (let r of state$1.selection.ranges) {
|
|
9416
9454
|
let prim = r == state$1.selection.main;
|
|
9417
|
-
if (r.empty || conf.drawRangeCursor) {
|
|
9455
|
+
if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
|
|
9418
9456
|
let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
|
|
9419
9457
|
let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
|
|
9420
9458
|
for (let piece of RectangleMarker.forRange(view, className, cursor))
|
|
@@ -9442,8 +9480,19 @@ function setBlinkRate(state, dom) {
|
|
|
9442
9480
|
const selectionLayer = layer({
|
|
9443
9481
|
above: false,
|
|
9444
9482
|
markers(view) {
|
|
9445
|
-
|
|
9446
|
-
|
|
9483
|
+
let markers = [], { main, ranges } = view.state.selection;
|
|
9484
|
+
for (let r of ranges)
|
|
9485
|
+
if (!r.empty) {
|
|
9486
|
+
for (let marker of RectangleMarker.forRange(view, "cm-selectionBackground", r))
|
|
9487
|
+
markers.push(marker);
|
|
9488
|
+
}
|
|
9489
|
+
if (browser.ios && !main.empty && view.state.facet(selectionConfig).iosSelectionHandles) {
|
|
9490
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-start", state.EditorSelection.cursor(main.from, 1)))
|
|
9491
|
+
markers.push(piece);
|
|
9492
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-end", state.EditorSelection.cursor(main.to, 1)))
|
|
9493
|
+
markers.push(piece);
|
|
9494
|
+
}
|
|
9495
|
+
return markers;
|
|
9447
9496
|
},
|
|
9448
9497
|
update(update, dom) {
|
|
9449
9498
|
return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
|
package/dist/index.d.cts
CHANGED
|
@@ -1573,6 +1573,12 @@ type SelectionConfig = {
|
|
|
1573
1573
|
true.
|
|
1574
1574
|
*/
|
|
1575
1575
|
drawRangeCursor?: boolean;
|
|
1576
|
+
/**
|
|
1577
|
+
Because hiding the cursor also hides the selection handles in
|
|
1578
|
+
the iOS browser, when this is enabled (the default), the
|
|
1579
|
+
extension draws handles on the side of the selection in iOS.
|
|
1580
|
+
*/
|
|
1581
|
+
iosSelectionHandles?: boolean;
|
|
1576
1582
|
};
|
|
1577
1583
|
/**
|
|
1578
1584
|
Returns an extension that hides the browser's native selection and
|
package/dist/index.d.ts
CHANGED
|
@@ -1573,6 +1573,12 @@ type SelectionConfig = {
|
|
|
1573
1573
|
true.
|
|
1574
1574
|
*/
|
|
1575
1575
|
drawRangeCursor?: boolean;
|
|
1576
|
+
/**
|
|
1577
|
+
Because hiding the cursor also hides the selection handles in
|
|
1578
|
+
the iOS browser, when this is enabled (the default), the
|
|
1579
|
+
extension draws handles on the side of the selection in iOS.
|
|
1580
|
+
*/
|
|
1581
|
+
iosSelectionHandles?: boolean;
|
|
1576
1582
|
};
|
|
1577
1583
|
/**
|
|
1578
1584
|
Returns an extension that hides the browser's native selection and
|
package/dist/index.js
CHANGED
|
@@ -3859,6 +3859,9 @@ class InlineCoordsScan {
|
|
|
3859
3859
|
if (rects)
|
|
3860
3860
|
for (let i = 0; i < rects.length; i++) {
|
|
3861
3861
|
let rect = rects[i], side = 0;
|
|
3862
|
+
// Ignore empty rectangles when there are other rectangles
|
|
3863
|
+
if (rect.width == 0 && rects.length > 1)
|
|
3864
|
+
continue;
|
|
3862
3865
|
if (rect.bottom < this.y) {
|
|
3863
3866
|
if (!above || above.bottom < rect.bottom)
|
|
3864
3867
|
above = rect;
|
|
@@ -4082,7 +4085,7 @@ class DOMChange {
|
|
|
4082
4085
|
this.bounds = null;
|
|
4083
4086
|
this.text = "";
|
|
4084
4087
|
this.domChanged = start > -1;
|
|
4085
|
-
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
|
4088
|
+
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView, curSel = view.state.selection;
|
|
4086
4089
|
if (view.state.readOnly && start > -1) {
|
|
4087
4090
|
// Ignore changes when the editor is read-only
|
|
4088
4091
|
this.newSel = null;
|
|
@@ -4098,18 +4101,18 @@ class DOMChange {
|
|
|
4098
4101
|
let domSel = view.observer.selectionRange;
|
|
4099
4102
|
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
|
|
4100
4103
|
!contains(view.contentDOM, domSel.focusNode)
|
|
4101
|
-
?
|
|
4104
|
+
? curSel.main.head
|
|
4102
4105
|
: view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
|
|
4103
4106
|
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
|
|
4104
4107
|
!contains(view.contentDOM, domSel.anchorNode)
|
|
4105
|
-
?
|
|
4108
|
+
? curSel.main.anchor
|
|
4106
4109
|
: view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
|
|
4107
4110
|
// iOS will refuse to select the block gaps when doing
|
|
4108
4111
|
// select-all.
|
|
4109
4112
|
// Chrome will put the selection *inside* them, confusing
|
|
4110
4113
|
// posFromDOM
|
|
4111
4114
|
let vp = view.viewport;
|
|
4112
|
-
if ((browser.ios || browser.chrome) &&
|
|
4115
|
+
if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
|
|
4113
4116
|
(vp.from > 0 || vp.to < view.state.doc.length)) {
|
|
4114
4117
|
let from = Math.min(head, anchor), to = Math.max(head, anchor);
|
|
4115
4118
|
let offFrom = vp.from - from, offTo = vp.to - to;
|
|
@@ -4118,10 +4121,22 @@ class DOMChange {
|
|
|
4118
4121
|
anchor = view.state.doc.length;
|
|
4119
4122
|
}
|
|
4120
4123
|
}
|
|
4121
|
-
if (view.inputState.composing > -1 &&
|
|
4122
|
-
this.newSel =
|
|
4123
|
-
|
|
4124
|
+
if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
|
|
4125
|
+
this.newSel = curSel.replaceRange(EditorSelection.range(anchor, head));
|
|
4126
|
+
}
|
|
4127
|
+
else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
|
|
4128
|
+
view.inputState.lastTouchTime > Date.now() - 100) {
|
|
4129
|
+
// If this is a cursor selection change in a line-wrapping
|
|
4130
|
+
// editor that may have been a touch, use the last touch
|
|
4131
|
+
// position to assign a side to the cursor.
|
|
4132
|
+
let before = view.coordsAtPos(head, -1), assoc = 0;
|
|
4133
|
+
if (before)
|
|
4134
|
+
assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1;
|
|
4135
|
+
this.newSel = EditorSelection.create([EditorSelection.cursor(head, assoc)]);
|
|
4136
|
+
}
|
|
4137
|
+
else {
|
|
4124
4138
|
this.newSel = EditorSelection.single(anchor, head);
|
|
4139
|
+
}
|
|
4125
4140
|
}
|
|
4126
4141
|
}
|
|
4127
4142
|
}
|
|
@@ -4408,6 +4423,8 @@ class InputState {
|
|
|
4408
4423
|
this.lastKeyCode = 0;
|
|
4409
4424
|
this.lastKeyTime = 0;
|
|
4410
4425
|
this.lastTouchTime = 0;
|
|
4426
|
+
this.lastTouchX = 0;
|
|
4427
|
+
this.lastTouchY = 0;
|
|
4411
4428
|
this.lastFocusTime = 0;
|
|
4412
4429
|
this.lastScrollTop = 0;
|
|
4413
4430
|
this.lastScrollLeft = 0;
|
|
@@ -4851,8 +4868,13 @@ handlers.keydown = (view, event) => {
|
|
|
4851
4868
|
return false;
|
|
4852
4869
|
};
|
|
4853
4870
|
observers.touchstart = (view, e) => {
|
|
4854
|
-
view.inputState
|
|
4855
|
-
|
|
4871
|
+
let iState = view.inputState, touch = e.targetTouches[0];
|
|
4872
|
+
iState.lastTouchTime = Date.now();
|
|
4873
|
+
if (touch) {
|
|
4874
|
+
iState.lastTouchX = touch.clientX;
|
|
4875
|
+
iState.lastTouchY = touch.clientY;
|
|
4876
|
+
}
|
|
4877
|
+
iState.setSelectionOrigin("select.pointer");
|
|
4856
4878
|
};
|
|
4857
4879
|
observers.touchmove = view => {
|
|
4858
4880
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
@@ -6793,6 +6815,21 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
6793
6815
|
"&dark .cm-cursor": {
|
|
6794
6816
|
borderLeftColor: "#ddd"
|
|
6795
6817
|
},
|
|
6818
|
+
".cm-selectionHandle": {
|
|
6819
|
+
backgroundColor: "currentColor",
|
|
6820
|
+
width: "1.5px"
|
|
6821
|
+
},
|
|
6822
|
+
".cm-selectionHandle-start::before, .cm-selectionHandle-end::before": {
|
|
6823
|
+
content: '""',
|
|
6824
|
+
backgroundColor: "inherit",
|
|
6825
|
+
borderRadius: "50%",
|
|
6826
|
+
width: "8px",
|
|
6827
|
+
height: "8px",
|
|
6828
|
+
position: "absolute",
|
|
6829
|
+
left: "-3.25px"
|
|
6830
|
+
},
|
|
6831
|
+
".cm-selectionHandle-start::before": { top: "-8px" },
|
|
6832
|
+
".cm-selectionHandle-end::before": { bottom: "-8px" },
|
|
6796
6833
|
".cm-dropCursor": {
|
|
6797
6834
|
position: "absolute"
|
|
6798
6835
|
},
|
|
@@ -9357,7 +9394,8 @@ const selectionConfig = /*@__PURE__*/Facet.define({
|
|
|
9357
9394
|
combine(configs) {
|
|
9358
9395
|
return combineConfig(configs, {
|
|
9359
9396
|
cursorBlinkRate: 1200,
|
|
9360
|
-
drawRangeCursor: true
|
|
9397
|
+
drawRangeCursor: true,
|
|
9398
|
+
iosSelectionHandles: true
|
|
9361
9399
|
}, {
|
|
9362
9400
|
cursorBlinkRate: (a, b) => Math.min(a, b),
|
|
9363
9401
|
drawRangeCursor: (a, b) => a || b
|
|
@@ -9409,7 +9447,7 @@ const cursorLayer = /*@__PURE__*/layer({
|
|
|
9409
9447
|
let cursors = [];
|
|
9410
9448
|
for (let r of state.selection.ranges) {
|
|
9411
9449
|
let prim = r == state.selection.main;
|
|
9412
|
-
if (r.empty || conf.drawRangeCursor) {
|
|
9450
|
+
if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
|
|
9413
9451
|
let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
|
|
9414
9452
|
let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
|
|
9415
9453
|
for (let piece of RectangleMarker.forRange(view, className, cursor))
|
|
@@ -9437,8 +9475,19 @@ function setBlinkRate(state, dom) {
|
|
|
9437
9475
|
const selectionLayer = /*@__PURE__*/layer({
|
|
9438
9476
|
above: false,
|
|
9439
9477
|
markers(view) {
|
|
9440
|
-
|
|
9441
|
-
|
|
9478
|
+
let markers = [], { main, ranges } = view.state.selection;
|
|
9479
|
+
for (let r of ranges)
|
|
9480
|
+
if (!r.empty) {
|
|
9481
|
+
for (let marker of RectangleMarker.forRange(view, "cm-selectionBackground", r))
|
|
9482
|
+
markers.push(marker);
|
|
9483
|
+
}
|
|
9484
|
+
if (browser.ios && !main.empty && view.state.facet(selectionConfig).iosSelectionHandles) {
|
|
9485
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-start", EditorSelection.cursor(main.from, 1)))
|
|
9486
|
+
markers.push(piece);
|
|
9487
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-end", EditorSelection.cursor(main.to, 1)))
|
|
9488
|
+
markers.push(piece);
|
|
9489
|
+
}
|
|
9490
|
+
return markers;
|
|
9442
9491
|
},
|
|
9443
9492
|
update(update, dom) {
|
|
9444
9493
|
return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
|